summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTinja Paavoseppä <tinja.paavoseppa@qt.io>2024-01-10 16:21:57 +0200
committerTinja Paavoseppä <tinja.paavoseppa@qt.io>2024-01-21 15:46:30 +0200
commit06d2aa91eb4faafc0e52faafeb184d6d3da671ab (patch)
treef2dd96b93fc663ce2cfcc5319c5233eb822c1f0b
parent52a41bac775bea9057acbc4344a29bb7e63297e3 (diff)
Android: Add a context delegate for embedding QML to native Android
When we are embedding a QML view to a non-Qt Android app, there are a lot of functionalities that are shared with the refular Qt Android app, but some are not. We should not, for example, try to control the hosting Activity. Create a base class that both the QtActivityDelegate, used for the standard Qt for Android app, and the delegate for the embedding case can extend. In this commit, the QtEmbeddedDelegate is very simple, the biggest difference to QtActivityDelegate being it does not create a QtLayout or instantiate a QtAccessibilityDelegate. It does start the Qt app, without waiting for a layout, and register to listen for changes in the state of the Qt app. Taking the embedded delegate into use, loading the embedded QML views and their handling is added in a follow up commit. Task-number: QTBUG-118872 Pick-to: 6.7 Change-Id: Id390a2b35c70b35880523886bf6fcf59d420cb42 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
-rw-r--r--src/android/jar/CMakeLists.txt2
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java2
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java225
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java240
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java121
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp6
-rw-r--r--src/plugins/platforms/android/androidjnimain.h4
-rw-r--r--tests/auto/corelib/platform/android/tst_android.cpp5
8 files changed, 455 insertions, 150 deletions
diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt
index 7c062120f0..af47cd779f 100644
--- a/src/android/jar/CMakeLists.txt
+++ b/src/android/jar/CMakeLists.txt
@@ -33,6 +33,8 @@ set(java_sources
src/org/qtproject/qt/android/UsedFromNativeCode.java
src/org/qtproject/qt/android/QtRootLayout.java
src/org/qtproject/qt/android/QtWindow.java
+ src/org/qtproject/qt/android/QtActivityDelegateBase.java
+ src/org/qtproject/qt/android/QtEmbeddedDelegate.java
)
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
index 08fe74ebd6..11900ff270 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
@@ -318,7 +318,7 @@ public class QtActivityBase extends Activity
}
@UsedFromNativeCode
- QtActivityDelegate getActivityDelegate()
+ QtActivityDelegateBase getActivityDelegate()
{
return m_delegate;
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
index 8f6114a7c2..513207e4a0 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
@@ -33,48 +33,36 @@ import android.widget.PopupMenu;
import java.util.HashMap;
-class QtActivityDelegate
+class QtActivityDelegate extends QtActivityDelegateBase
{
- private Activity m_activity;
+ private static final String QtTAG = "QtActivityDelegate";
private QtRootLayout m_layout = null;
- private HashMap<Integer, QtWindow> m_topLevelWindows;
private ImageView m_splashScreen = null;
private boolean m_splashScreenSticky = false;
private View m_dummyView = null;
- private QtAccessibilityDelegate m_accessibilityDelegate = null;
- private QtDisplayManager m_displayManager = null;
- private QtInputDelegate m_inputDelegate = null;
- private boolean m_membersInitialized = false;
QtActivityDelegate(Activity activity)
{
- m_activity = activity;
- QtNative.setActivity(m_activity);
+ super(activity);
setActionBarVisibility(false);
setActivityBackgroundDrawable();
}
- QtDisplayManager displayManager() {
- return m_displayManager;
- }
-
- @UsedFromNativeCode
- QtInputDelegate getInputDelegate() {
- return m_inputDelegate;
- }
@UsedFromNativeCode
+ @Override
QtLayout getQtLayout()
{
return m_layout;
}
@UsedFromNativeCode
- public void setSystemUiVisibility(int systemUiVisibility)
+ @Override
+ void setSystemUiVisibility(int systemUiVisibility)
{
QtNative.runAction(() -> {
m_displayManager.setSystemUiVisibility(systemUiVisibility);
@@ -83,45 +71,24 @@ class QtActivityDelegate
});
}
- void setContextMenuVisible(boolean contextMenuVisible)
- {
- m_contextMenuVisible = contextMenuVisible;
- }
-
- boolean isContextMenuVisible()
- {
- return m_contextMenuVisible;
- }
-
+ @Override
public boolean updateActivityAfterRestart(Activity activity) {
- try {
- // set new activity
- m_activity = activity;
- QtNative.setActivity(m_activity);
-
- // update the new activity content view to old layout
- ViewGroup layoutParent = (ViewGroup) m_layout.getParent();
- if (layoutParent != null)
- layoutParent.removeView(m_layout);
+ boolean updated = super.updateActivityAfterRestart(activity);
+ // TODO verify whether this is even needed, the last I checked the initMembers
+ // recreates the layout anyway
+ // update the new activity content view to old layout
+ ViewGroup layoutParent = (ViewGroup)m_layout.getParent();
+ if (layoutParent != null)
+ layoutParent.removeView(m_layout);
- m_activity.setContentView(m_layout);
+ m_activity.setContentView(m_layout);
- // force c++ native activity object to update
- return QtNative.updateNativeActivity();
- } catch (Exception e) {
- Log.w(QtNative.QtTAG, "Failed to update the activity.");
- e.printStackTrace();
- return false;
- }
+ return updated;
}
- public void startNativeApplication(String appParams, String mainLib)
+ @Override
+ void startNativeApplicationImpl(String appParams, String mainLib)
{
- if (m_membersInitialized)
- return;
-
- initMembers();
-
m_layout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
@@ -132,50 +99,13 @@ class QtActivityDelegate
});
}
- private void initMembers()
+ @Override
+ protected void setUpLayout()
{
- m_layout = new QtRootLayout(m_activity);
- m_membersInitialized = true;
- m_topLevelWindows = new HashMap<Integer, QtWindow>();
-
- m_displayManager = new QtDisplayManager(m_activity);
- m_displayManager.registerDisplayListener();
-
- QtInputDelegate.KeyboardVisibilityListener keyboardVisibilityListener =
- () -> m_displayManager.updateFullScreen();
- m_inputDelegate = new QtInputDelegate(m_activity, keyboardVisibilityListener);
-
- try {
- PackageManager pm = m_activity.getPackageManager();
- ActivityInfo activityInfo = pm.getActivityInfo(m_activity.getComponentName(), 0);
- m_inputDelegate.setSoftInputMode(activityInfo.softInputMode);
- } catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
- }
-
int orientation = m_activity.getResources().getConfiguration().orientation;
+ m_layout = new QtRootLayout(m_activity);
- try {
- ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA);
-
- String splashScreenKey = "android.app.splash_screen_drawable_"
- + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
- if (!info.metaData.containsKey(splashScreenKey))
- splashScreenKey = "android.app.splash_screen_drawable";
-
- if (info.metaData.containsKey(splashScreenKey)) {
- m_splashScreenSticky = info.metaData.containsKey("android.app.splash_screen_sticky") && info.metaData.getBoolean("android.app.splash_screen_sticky");
- int id = info.metaData.getInt(splashScreenKey);
- m_splashScreen = new ImageView(m_activity);
- m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(id, m_activity.getTheme()));
- m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
- m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- m_layout.addView(m_splashScreen);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
+ setUpSplashScreen(orientation);
m_activity.registerForContextMenu(m_layout);
m_activity.setContentView(m_layout,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@@ -211,12 +141,41 @@ class QtActivityDelegate
m_inputDelegate.setEditPopupMenu(new EditPopupMenu(m_activity, m_layout));
}
- public void hideSplashScreen()
+ @Override
+ protected void setUpSplashScreen(int orientation)
{
- hideSplashScreen(0);
+ try {
+ ActivityInfo info = m_activity.getPackageManager().getActivityInfo(
+ m_activity.getComponentName(),
+ PackageManager.GET_META_DATA);
+
+ String splashScreenKey = "android.app.splash_screen_drawable_"
+ + (orientation == Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
+ if (!info.metaData.containsKey(splashScreenKey))
+ splashScreenKey = "android.app.splash_screen_drawable";
+
+ if (info.metaData.containsKey(splashScreenKey)) {
+ m_splashScreenSticky =
+ info.metaData.containsKey("android.app.splash_screen_sticky") &&
+ info.metaData.getBoolean("android.app.splash_screen_sticky");
+
+ int id = info.metaData.getInt(splashScreenKey);
+ m_splashScreen = new ImageView(m_activity);
+ m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(
+ id, m_activity.getTheme()));
+ m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY);
+ m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ m_layout.addView(m_splashScreen);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
- public void hideSplashScreen(final int duration)
+ @Override
+ protected void hideSplashScreen(final int duration)
{
QtNative.runAction(() -> {
if (m_splashScreen == null)
@@ -252,49 +211,15 @@ class QtActivityDelegate
}
@UsedFromNativeCode
- public void notifyLocationChange(int viewId)
- {
- if (m_accessibilityDelegate == null)
- return;
- m_accessibilityDelegate.notifyLocationChange(viewId);
- }
-
- @UsedFromNativeCode
- public void notifyObjectHide(int viewId, int parentId)
- {
- if (m_accessibilityDelegate == null)
- return;
- m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
- }
-
- @UsedFromNativeCode
- public void notifyObjectFocus(int viewId)
- {
- if (m_accessibilityDelegate == null)
- return;
- m_accessibilityDelegate.notifyObjectFocus(viewId);
- }
-
- @UsedFromNativeCode
- public void notifyValueChanged(int viewId, String value)
- {
- if (m_accessibilityDelegate == null)
- return;
- m_accessibilityDelegate.notifyValueChanged(viewId, value);
- }
-
- @UsedFromNativeCode
- public void notifyScrolledEvent(int viewId)
- {
- if (m_accessibilityDelegate == null)
- return;
- m_accessibilityDelegate.notifyScrolledEvent(viewId);
- }
-
- @UsedFromNativeCode
public void initializeAccessibility()
{
- QtNative.runAction(() -> m_accessibilityDelegate = new QtAccessibilityDelegate(m_layout));
+ QtNative.runAction(() -> {
+ // FIXME make QtAccessibilityDelegate window based
+ if (m_layout != null)
+ m_accessibilityDelegate = new QtAccessibilityDelegate(m_layout);
+ else
+ Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
+ });
}
void handleUiModeChange(int uiMode)
@@ -345,6 +270,7 @@ class QtActivityDelegate
}
@UsedFromNativeCode
+ @Override
public void openContextMenu(final int x, final int y, final int w, final int h)
{
m_layout.postDelayed(() -> {
@@ -365,6 +291,7 @@ class QtActivityDelegate
QtNative.runAction(() -> m_activity.closeContextMenu());
}
+ @Override
void setActionBarVisibility(boolean visible)
{
if (m_activity.getActionBar() == null)
@@ -376,8 +303,12 @@ class QtActivityDelegate
}
@UsedFromNativeCode
+ @Override
public void addTopLevelWindow(final QtWindow window)
{
+ if (window == null)
+ return;
+
QtNative.runAction(()-> {
if (m_topLevelWindows.size() == 0) {
if (m_dummyView != null) {
@@ -398,7 +329,8 @@ class QtActivityDelegate
}
@UsedFromNativeCode
- public void removeTopLevelWindow(final int id)
+ @Override
+ void removeTopLevelWindow(final int id)
{
QtNative.runAction(()-> {
if (m_topLevelWindows.containsKey(id)) {
@@ -415,18 +347,19 @@ class QtActivityDelegate
}
@UsedFromNativeCode
- public void bringChildToFront(final int id)
+ @Override
+ void bringChildToFront(final int id)
{
QtNative.runAction(() -> {
QtWindow window = m_topLevelWindows.get(id);
- if (window != null) {
+ if (window != null)
m_layout.moveChild(window.getLayout(), m_topLevelWindows.size() - 1);
- }
});
}
@UsedFromNativeCode
- public void bringChildToBack(int id)
+ @Override
+ void bringChildToBack(int id)
{
QtNative.runAction(() -> {
QtWindow window = m_topLevelWindows.get(id);
@@ -435,6 +368,16 @@ class QtActivityDelegate
});
}
+ @Override
+ QtAccessibilityDelegate createAccessibilityDelegate()
+ {
+ if (m_layout != null)
+ return new QtAccessibilityDelegate(m_layout);
+
+ Log.w(QtTAG, "Null layout, failed to initialize accessibility delegate.");
+ return null;
+ }
+
private void setActivityBackgroundDrawable()
{
TypedValue attr = new TypedValue();
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java
new file mode 100644
index 0000000000..3b10d573c0
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegateBase.java
@@ -0,0 +1,240 @@
+// Copyright (C) 2017 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.ViewTreeObserver;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowInsetsController;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+
+import java.util.HashMap;
+
+abstract class QtActivityDelegateBase
+{
+ protected Activity m_activity;
+ protected HashMap<Integer, QtWindow> m_topLevelWindows;
+ protected QtAccessibilityDelegate m_accessibilityDelegate = null;
+ protected QtDisplayManager m_displayManager = null;
+ protected QtInputDelegate m_inputDelegate = null;
+
+ private boolean m_membersInitialized = false;
+ private boolean m_contextMenuVisible = false;
+
+ // Subclass must implement these
+ abstract void startNativeApplicationImpl(String appParams, String mainLib);
+ abstract QtAccessibilityDelegate createAccessibilityDelegate();
+ abstract QtLayout getQtLayout();
+
+ // With these we are okay with default implementation doing nothing
+ void setUpLayout() {}
+ void setUpSplashScreen(int orientation) {}
+ void hideSplashScreen(final int duration) {}
+ void openContextMenu(final int x, final int y, final int w, final int h) {}
+ void setActionBarVisibility(boolean visible) {}
+ void addTopLevelWindow(final QtWindow window) {}
+ void removeTopLevelWindow(final int id) {}
+ void bringChildToFront(final int id) {}
+ void bringChildToBack(int id) {}
+ void setSystemUiVisibility(int systemUiVisibility) {}
+
+ QtActivityDelegateBase(Activity activity)
+ {
+ m_activity = activity;
+ // Set native context
+ QtNative.setActivity(m_activity);
+ }
+
+ QtDisplayManager displayManager() {
+ return m_displayManager;
+ }
+
+ @UsedFromNativeCode
+ QtInputDelegate getInputDelegate() {
+ return m_inputDelegate;
+ }
+
+ void setContextMenuVisible(boolean contextMenuVisible)
+ {
+ m_contextMenuVisible = contextMenuVisible;
+ }
+
+ boolean isContextMenuVisible()
+ {
+ return m_contextMenuVisible;
+ }
+
+ public boolean updateActivityAfterRestart(Activity activity) {
+ try {
+ // set new activity
+ m_activity = activity;
+ QtNative.setActivity(m_activity);
+
+ // force c++ native activity object to update
+ return QtNative.updateNativeActivity();
+ } catch (Exception e) {
+ Log.w(QtNative.QtTAG, "Failed to update the activity.");
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public void startNativeApplication(String appParams, String mainLib)
+ {
+ if (m_membersInitialized)
+ return;
+ initMembers();
+ startNativeApplicationImpl(appParams, mainLib);
+ }
+
+ void initMembers()
+ {
+ m_membersInitialized = true;
+ m_topLevelWindows = new HashMap<Integer, QtWindow>();
+
+ m_displayManager = new QtDisplayManager(m_activity);
+ m_displayManager.registerDisplayListener();
+
+ QtInputDelegate.KeyboardVisibilityListener keyboardVisibilityListener =
+ () -> m_displayManager.updateFullScreen();
+ m_inputDelegate = new QtInputDelegate(m_activity, keyboardVisibilityListener);
+
+ try {
+ PackageManager pm = m_activity.getPackageManager();
+ ActivityInfo activityInfo = pm.getActivityInfo(m_activity.getComponentName(), 0);
+ m_inputDelegate.setSoftInputMode(activityInfo.softInputMode);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ setUpLayout();
+ }
+
+
+ public void hideSplashScreen()
+ {
+ hideSplashScreen(0);
+ }
+
+ @UsedFromNativeCode
+ public void notifyLocationChange(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyLocationChange(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyObjectHide(int viewId, int parentId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyObjectHide(viewId, parentId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyObjectFocus(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyObjectFocus(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void notifyValueChanged(int viewId, String value)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyValueChanged(viewId, value);
+ }
+
+ @UsedFromNativeCode
+ public void notifyScrolledEvent(int viewId)
+ {
+ if (m_accessibilityDelegate == null)
+ return;
+ m_accessibilityDelegate.notifyScrolledEvent(viewId);
+ }
+
+ @UsedFromNativeCode
+ public void initializeAccessibility()
+ {
+ QtNative.runAction(() -> {
+ m_accessibilityDelegate = createAccessibilityDelegate();
+ });
+ }
+
+ void handleUiModeChange(int uiMode)
+ {
+ // QTBUG-108365
+ if (Build.VERSION.SDK_INT >= 30) {
+ // Since 29 version we are using Theme_DeviceDefault_DayNight
+ Window window = m_activity.getWindow();
+ WindowInsetsController controller = window.getInsetsController();
+ if (controller != null) {
+ // set APPEARANCE_LIGHT_STATUS_BARS if needed
+ int appearanceLight = Color.luminance(window.getStatusBarColor()) > 0.5 ?
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS : 0;
+ controller.setSystemBarsAppearance(appearanceLight,
+ WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
+ }
+ }
+ switch (uiMode) {
+ case Configuration.UI_MODE_NIGHT_NO:
+ ExtractStyle.runIfNeeded(m_activity, false);
+ QtDisplayManager.handleUiDarkModeChanged(0);
+ break;
+ case Configuration.UI_MODE_NIGHT_YES:
+ ExtractStyle.runIfNeeded(m_activity, true);
+ QtDisplayManager.handleUiDarkModeChanged(1);
+ break;
+ }
+ }
+
+ @UsedFromNativeCode
+ public void resetOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.invalidateOptionsMenu());
+ }
+
+ @UsedFromNativeCode
+ public void openOptionsMenu()
+ {
+ QtNative.runAction(() -> m_activity.openOptionsMenu());
+ }
+
+ public void onCreatePopupMenu(Menu menu)
+ {
+ QtNative.fillContextMenu(menu);
+ m_contextMenuVisible = true;
+ }
+
+ @UsedFromNativeCode
+ public void closeContextMenu()
+ {
+ QtNative.runAction(() -> m_activity.closeContextMenu());
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
new file mode 100644
index 0000000000..f8438003e1
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
@@ -0,0 +1,121 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+package org.qtproject.qt.android;
+
+import static org.qtproject.qt.android.QtNative.ApplicationState.*;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppStateDetailsListener {
+ private QtNative.ApplicationStateDetails m_stateDetails;
+
+ public QtEmbeddedDelegate(Activity context) {
+ super(context);
+
+ m_stateDetails = QtNative.getStateDetails();
+ QtNative.registerAppStateListener(this);
+
+ m_activity.getApplication().registerActivityLifecycleCallbacks(
+ new Application.ActivityLifecycleCallbacks() {
+ @Override
+ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
+
+ @Override
+ public void onActivityStarted(Activity activity) {}
+
+ @Override
+ public void onActivityResumed(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ QtNative.setApplicationState(ApplicationActive);
+ QtNative.updateWindow();
+ }
+ }
+
+ @Override
+ public void onActivityPaused(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N ||
+ !activity.isInMultiWindowMode()) {
+ QtNative.setApplicationState(ApplicationInactive);
+ }
+ }
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ QtNative.setApplicationState(ApplicationSuspended);
+ }
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ if (m_activity == activity && m_stateDetails.isStarted) {
+ m_activity.getApplication().unregisterActivityLifecycleCallbacks(this);
+ QtNative.unregisterAppStateListener(QtEmbeddedDelegate.this);
+ QtNative.terminateQt();
+ QtNative.setActivity(null);
+ QtNative.getQtThread().exit();
+ onDestroy();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) {
+ m_stateDetails = details;
+ if (m_stateDetails.nativePluginIntegrationReady) {
+ QtNative.runAction(() -> {
+ DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
+ QtDisplayManager.setApplicationDisplayMetrics(m_activity,
+ metrics.widthPixels,
+ metrics.heightPixels);
+ });
+ }
+ }
+
+ @Override
+ void startNativeApplicationImpl(String appParams, String mainLib)
+ {
+ QtNative.startApplication(appParams, mainLib);
+ }
+
+ @Override
+ QtAccessibilityDelegate createAccessibilityDelegate()
+ {
+ // FIXME make QtAccessibilityDelegate window based or verify current way works
+ // also for child windows: QTBUG-120685
+ return null;
+ }
+
+ @UsedFromNativeCode
+ @Override
+ QtLayout getQtLayout()
+ {
+ // TODO could probably use QtView here when it's added?
+ return null;
+ }
+
+ public void onDestroy() {
+ // TODO delete the window once it's added
+ }
+}
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 7beae1993d..bf15213b66 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -53,7 +53,7 @@ static jobject m_resourcesObj = nullptr;
static jclass m_qtActivityClass = nullptr;
static jclass m_qtServiceClass = nullptr;
-static QtJniTypes::QtActivityDelegate m_activityDelegate = nullptr;
+static QtJniTypes::QtActivityDelegateBase m_activityDelegate = nullptr;
static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
static int m_pendingApplicationState = -1;
@@ -185,11 +185,11 @@ namespace QtAndroid
}
// FIXME: avoid direct access to QtActivityDelegate
- QtJniTypes::QtActivityDelegate qtActivityDelegate()
+ QtJniTypes::QtActivityDelegateBase qtActivityDelegate()
{
if (!m_activityDelegate.isValid()) {
auto activity = QtAndroidPrivate::activity();
- m_activityDelegate = activity.callMethod<QtJniTypes::QtActivityDelegate>(
+ m_activityDelegate = activity.callMethod<QtJniTypes::QtActivityDelegateBase>(
"getActivityDelegate");
}
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index 2d9ac730f5..57a71a8650 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -26,7 +26,7 @@ class QWindow;
class QAndroidPlatformWindow;
class QBasicMutex;
-Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
+Q_DECLARE_JNI_CLASS(QtActivityDelegateBase, "org/qtproject/qt/android/QtActivityDelegateBase")
Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
namespace QtAndroid
@@ -48,7 +48,7 @@ namespace QtAndroid
AAssetManager *assetManager();
jclass applicationClass();
- QtJniTypes::QtActivityDelegate qtActivityDelegate();
+ QtJniTypes::QtActivityDelegateBase qtActivityDelegate();
QtJniTypes::QtInputDelegate qtInputDelegate();
// Keep synchronized with flags in ActivityDelegate.java
diff --git a/tests/auto/corelib/platform/android/tst_android.cpp b/tests/auto/corelib/platform/android/tst_android.cpp
index 02204abcb5..f597499782 100644
--- a/tests/auto/corelib/platform/android/tst_android.cpp
+++ b/tests/auto/corelib/platform/android/tst_android.cpp
@@ -203,13 +203,13 @@ void tst_Android::testRunOnAndroidMainThread()
}
}
-Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
+Q_DECLARE_JNI_CLASS(QtActivityDelegateBase, "org/qtproject/qt/android/QtActivityDelegateBase")
void setSystemUiVisibility(int visibility)
{
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([visibility] {
auto context = QNativeInterface::QAndroidApplication::context();
- auto activityDelegate = context.callMethod<QtJniTypes::QtActivityDelegate>("getActivityDelegate");
+ auto activityDelegate = context.callMethod<QtJniTypes::QtActivityDelegateBase>("getActivityDelegate");
activityDelegate.callMethod<void>("setSystemUiVisibility", jint(visibility));
}).waitForFinished();
}
@@ -342,4 +342,3 @@ void tst_Android::orientationChange()
QTEST_MAIN(tst_Android)
#include "tst_android.moc"
-