6package org.qtproject.qt.android;
8import android.app.Activity;
9import android.content.pm.ActivityInfo;
10import android.content.pm.PackageManager;
11import android.content.res.Configuration;
12import android.graphics.drawable.ColorDrawable;
13import android.graphics.drawable.Drawable;
14import android.graphics.Rect;
15import android.util.DisplayMetrics;
16import android.util.Log;
17import android.util.TypedValue;
18import android.view.ViewTreeObserver;
19import android.os.Build;
20import android.view.WindowInsets;
21import android.view.WindowInsetsAnimation;
22import android.view.animation.AccelerateInterpolator;
23import android.view.animation.AlphaAnimation;
24import android.view.animation.Animation;
25import android.view.Menu;
26import android.view.View;
27import android.view.ViewConfiguration;
28import android.view.ViewGroup;
29import android.widget.ImageView;
30import android.widget.PopupMenu;
32import java.util.HashMap;
34class QtActivityDelegate
extends QtActivityDelegateBase
38 private static final String QtTAG =
"QtActivityDelegate";
40 private QtRootLayout m_layout =
null;
41 private ImageView m_splashScreen =
null;
42 private boolean m_splashScreenSticky =
false;
43 private boolean m_backendsRegistered =
false;
45 private View m_dummyView =
null;
48 QtActivityDelegate(Activity activity)
57 setActionBarVisibility(
false);
58 setActivityBackgroundDrawable();
59 if (QtNativeAccessibility.accessibilitySupported())
60 m_accessibilityDelegate.initLayoutAccessibility(m_layout);
63 void registerBackends()
65 if (m_backendsRegistered || BackendRegister.isNull())
68 m_backendsRegistered =
true;
69 BackendRegister.registerBackend(QtWindowInterface.class, QtActivityDelegate.this);
70 BackendRegister.registerBackend(QtAccessibilityInterface.class, QtActivityDelegate.this);
71 BackendRegister.registerBackend(QtMenuInterface.class, QtActivityDelegate.this);
72 BackendRegister.registerBackend(QtInputInterface.class, m_inputDelegate);
75 void unregisterBackends()
77 if (!m_backendsRegistered)
80 m_backendsRegistered =
false;
82 if (BackendRegister.isNull())
85 BackendRegister.unregisterBackend(QtWindowInterface.class);
86 BackendRegister.unregisterBackend(QtAccessibilityInterface.class);
87 BackendRegister.unregisterBackend(QtMenuInterface.class);
88 BackendRegister.unregisterBackend(QtInputInterface.class);
92 final public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) {
93 if (details.isStarted)
100 void startNativeApplicationImpl(String appParams, String mainLib)
102 if (m_layout ==
null) {
103 Log.e(QtTAG,
"Unable to start native application with a null layout");
107 m_layout.getViewTreeObserver().addOnGlobalLayoutListener(
108 new ViewTreeObserver.OnGlobalLayoutListener() {
110 public void onGlobalLayout() {
111 if (m_layout != null) {
112 QtNative.startApplication(appParams, mainLib);
113 m_layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
120 protected void setUpLayout()
124 m_layout =
new QtRootLayout(m_activity);
126 int orientation = m_activity.getResources().getConfiguration().orientation;
127 setUpSplashScreen(orientation);
128 m_activity.registerForContextMenu(m_layout);
129 ViewGroup.LayoutParams rootParams =
new ViewGroup.LayoutParams(
130 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
131 m_activity.setContentView(m_layout, rootParams);
133 handleUiModeChange();
135 m_displayManager.initDisplayProperties();
137 m_layout.getViewTreeObserver().addOnPreDrawListener(() -> {
138 if (!m_inputDelegate.isKeyboardVisible())
142 m_activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(
r);
143 DisplayMetrics
metrics =
new DisplayMetrics();
144 QtDisplayManager.getDisplay(m_activity).getMetrics(
metrics);
145 final int kbHeight = metrics.heightPixels -
r.bottom;
147 m_inputDelegate.setKeyboardVisibility(
false,
System.nanoTime());
151 m_layout.getLocationOnScreen(
location);
153 r.width(), kbHeight);
159 protected void setUpSplashScreen(
int orientation)
161 if (m_layout ==
null) {
162 Log.e(QtTAG,
"Unable to setup splash screen with a null layout");
167 ActivityInfo
info = m_activity.getPackageManager().getActivityInfo(
168 m_activity.getComponentName(),
169 PackageManager.GET_META_DATA);
171 String splashScreenKey =
"android.app.splash_screen_drawable_"
172 + (orientation == Configuration.ORIENTATION_LANDSCAPE ?
"landscape" :
"portrait");
173 if (!
info.metaData.containsKey(splashScreenKey))
174 splashScreenKey =
"android.app.splash_screen_drawable";
176 if (
info.metaData.containsKey(splashScreenKey)) {
177 m_splashScreenSticky =
178 info.metaData.containsKey(
"android.app.splash_screen_sticky") &&
179 info.metaData.getBoolean(
"android.app.splash_screen_sticky");
181 int id =
info.metaData.getInt(splashScreenKey);
182 m_splashScreen =
new ImageView(m_activity);
183 m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(
184 id, m_activity.getTheme()));
185 m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
186 m_splashScreen.setLayoutParams(
new ViewGroup.LayoutParams(
187 ViewGroup.LayoutParams.MATCH_PARENT,
188 ViewGroup.LayoutParams.MATCH_PARENT));
189 m_layout.addView(m_splashScreen);
191 }
catch (Exception e) {
197 protected void hideSplashScreen(
final int duration)
200 if (m_splashScreen ==
null)
203 if (m_layout !=
null && duration <= 0) {
204 m_layout.removeView(m_splashScreen);
205 m_splashScreen =
null;
209 final Animation fadeOut =
new AlphaAnimation(1, 0);
210 fadeOut.setInterpolator(
new AccelerateInterpolator());
211 fadeOut.setDuration(duration);
213 fadeOut.setAnimationListener(
new Animation.AnimationListener() {
215 public void onAnimationEnd(Animation animation) {
220 public void onAnimationRepeat(Animation animation) {
224 public void onAnimationStart(Animation animation) {
228 m_splashScreen.startAnimation(fadeOut);
235 m_accessibilityDelegate.notifyLocationChange(viewId);
241 m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
247 m_accessibilityDelegate.notifyObjectShow(parentId);
253 m_accessibilityDelegate.notifyObjectFocus(viewId);
259 m_accessibilityDelegate.notifyObjectDestroyed(viewId, parentId);
265 m_accessibilityDelegate.notifyObjectCreated(parentId);
271 m_accessibilityDelegate.notifyValueChanged(viewId);
277 m_accessibilityDelegate.notifyDescriptionOrNameChanged(viewId);
283 m_accessibilityDelegate.notifyScrolledEvent(viewId);
289 m_accessibilityDelegate.notifyAnnouncementEvent(viewId,
message);
293 public void notifyTextChanged(
int viewId, String text, String beforeText,
294 int fromIndex,
int addedCount,
int removedCount)
296 m_accessibilityDelegate.notifyTextChanged(viewId, text, beforeText,
297 fromIndex, addedCount, removedCount);
302 public void resetOptionsMenu()
304 QtNative.runAction(m_activity::invalidateOptionsMenu);
310 QtNative.runAction(m_activity::openOptionsMenu);
314 public void closeContextMenu()
316 QtNative.runAction(m_activity::closeContextMenu);
320 public void openContextMenu(
final int x,
final int y,
final int w,
final int h)
322 if (m_layout ==
null) {
323 Log.e(QtTAG,
"Unable to open context menu with a null layout");
327 m_layout.postDelayed(() -> {
328 if (m_layout ==
null) {
329 Log.w(QtTAG,
"Unable to open context menu on null layout");
332 final QtEditText focusedEditText = m_inputDelegate.getCurrentQtEditText();
333 if (focusedEditText ==
null) {
334 Log.w(QtTAG,
"No focused view when trying to open context menu");
337 m_layout.setLayoutParams(focusedEditText,
new QtLayout.LayoutParams(
w,
h,
x,
y),
false);
338 focusedEditText.requestLayout();
339 focusedEditText.getViewTreeObserver().addOnGlobalLayoutListener(
340 new ViewTreeObserver.OnGlobalLayoutListener() {
342 public void onGlobalLayout() {
343 focusedEditText.getViewTreeObserver().removeOnGlobalLayoutListener(this);
344 PopupMenu popup = new PopupMenu(m_activity, focusedEditText);
345 QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu());
346 popup.setOnMenuItemClickListener(m_activity::onContextItemSelected);
347 popup.setOnDismissListener(popupMenu ->
348 m_activity.onContextMenuClosed(popupMenu.getMenu()));
356 void onCreatePopupMenu(Menu
menu)
358 QtNative.fillContextMenu(
menu);
359 setContextMenuVisible(
true);
363 void setActionBarVisibility(
boolean visible)
365 if (m_activity.getActionBar() ==
null)
367 if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible)
368 m_activity.getActionBar().hide();
370 m_activity.getActionBar().show();
373 private void fanOutSafeAreaMargins(WindowInsets insets)
375 for (QtWindow
w : m_topLevelWindows.values())
376 w.reportSafeAreaMarginsFromRoot(insets);
379 private void registerDecorInsetsListener()
383 final View decor = m_activity.getWindow().getDecorView();
384 decor.setOnApplyWindowInsetsListener((
view, insets) -> {
385 fanOutSafeAreaMargins(insets);
386 return view.onApplyWindowInsets(insets);
388 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
389 decor.setWindowInsetsAnimationCallback(
new WindowInsetsAnimation.Callback(
390 WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
391 private WindowInsets m_lastAnimatedInsets;
394 public WindowInsets onProgress(WindowInsets insets,
395 java.util.List<WindowInsetsAnimation> animations)
397 m_lastAnimatedInsets = insets;
402 public void onEnd(WindowInsetsAnimation animation)
406 WindowInsets insets = m_lastAnimatedInsets !=
null
407 ? m_lastAnimatedInsets : decor.getRootWindowInsets();
409 fanOutSafeAreaMargins(insets);
417 public void addTopLevelWindow(
final QtWindow
window)
419 if (m_layout ==
null ||
window ==
null)
422 QtNative.runAction(()-> {
423 if (m_layout ==
null)
426 if (m_topLevelWindows.isEmpty()) {
427 if (m_dummyView !=
null) {
428 m_layout.removeView(m_dummyView);
433 m_layout.addView(
window, m_topLevelWindows.size());
434 if (m_topLevelWindows.isEmpty())
435 registerDecorInsetsListener();
437 if (!m_splashScreenSticky)
444 public void removeTopLevelWindow(
final int id)
446 QtNative.runAction(()-> {
447 if (m_topLevelWindows.containsKey(
id)) {
448 QtWindow
window = m_topLevelWindows.remove(
id);
449 window.setOnApplyWindowInsetsListener(
null);
450 if (m_topLevelWindows.isEmpty()) {
454 }
else if (m_layout !=
null) {
455 m_layout.removeView(
window);
463 public void bringChildToFront(
final int id)
465 if (m_layout !=
null) {
466 QtNative.runAction(() -> {
467 QtWindow
window = m_topLevelWindows.get(
id);
468 if (
window !=
null && m_layout !=
null)
469 m_layout.moveChild(
window, m_topLevelWindows.size() - 1);
476 public void bringChildToBack(
int id)
478 if (m_layout !=
null) {
479 QtNative.runAction(() -> {
480 QtWindow
window = m_topLevelWindows.get(
id);
481 if (
window !=
null && m_layout !=
null)
482 m_layout.moveChild(
window, 0);
487 private void setActivityBackgroundDrawable()
489 TypedValue attr =
new TypedValue();
490 m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground,
492 Drawable backgroundDrawable;
493 if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
494 attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
495 backgroundDrawable =
new ColorDrawable(attr.data);
497 backgroundDrawable = m_activity.getResources().
498 getDrawable(attr.resourceId, m_activity.getTheme());
501 m_activity.getWindow().setBackgroundDrawable(backgroundDrawable);
void notifyDescriptionOrNameChanged(uint accessibilityObjectId)
void notifyObjectShow(uint accessibilityObjectId)
void notifyLocationChange(uint accessibilityObjectId)
void notifyObjectFocus(uint accessibilityObjectId)
void notifyObjectDestroyed(uint accessibilityObjectId)
void notifyObjectCreated(uint accessibilityObjectId)
void notifyObjectHide(uint accessibilityObjectId)
void notifyValueChanged(uint accessibilityObjectId)
void notifyAnnouncementEvent(uint accessibilityObjectId, const QString &message)
void notifyScrolledEvent(uint accessiblityObjectId)
GLuint GLsizei const GLchar * message
GLint GLint GLint GLint GLint x
GLfloat GLfloat GLfloat w
[0]
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLfloat GLfloat GLfloat GLfloat h