Android: rework app state tracking
Cleanup 'isStarted' and state management logic and move it to QtNative. Currently, it's spread between QtActivityDelegate and QtNative where multiple variables are used for overlapping use cases. Task-number: QTBUG-118077 Change-Id: I2c98b48be78809c30636bfdf6d6640317470cec6 Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
This commit is contained in:
parent
86cfafcb5a
commit
aea816b197
@ -26,7 +26,6 @@ set(java_sources
|
|||||||
src/org/qtproject/qt/android/extras/QtAndroidBinder.java
|
src/org/qtproject/qt/android/extras/QtAndroidBinder.java
|
||||||
src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
|
src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
|
||||||
src/org/qtproject/qt/android/extras/QtNative.java
|
src/org/qtproject/qt/android/extras/QtNative.java
|
||||||
src/org/qtproject/qt/android/QtConstants.java
|
|
||||||
src/org/qtproject/qt/android/QtClipboardManager.java
|
src/org/qtproject/qt/android/QtClipboardManager.java
|
||||||
src/org/qtproject/qt/android/QtDisplayManager.java
|
src/org/qtproject/qt/android/QtDisplayManager.java
|
||||||
src/org/qtproject/qt/android/UsedFromNativeCode.java
|
src/org/qtproject/qt/android/UsedFromNativeCode.java
|
||||||
|
@ -59,7 +59,7 @@ public class QtActivityBase extends Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleActivityRestart() {
|
private void handleActivityRestart() {
|
||||||
if (QtNative.isStarted()) {
|
if (QtNative.getStateDetails().isStarted) {
|
||||||
boolean updated = m_delegate.updateActivityAfterRestart(this);
|
boolean updated = m_delegate.updateActivityAfterRestart(this);
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
// could not update the activity so restart the application
|
// could not update the activity so restart the application
|
||||||
@ -119,15 +119,15 @@ public class QtActivityBase extends Activity
|
|||||||
{
|
{
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (Build.VERSION.SDK_INT < 24 || !isInMultiWindowMode())
|
if (Build.VERSION.SDK_INT < 24 || !isInMultiWindowMode())
|
||||||
QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationInactive);
|
QtNative.setApplicationState(QtNative.ApplicationState.ApplicationInactive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume()
|
protected void onResume()
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationActive);
|
QtNative.setApplicationState(QtNative.ApplicationState.ApplicationActive);
|
||||||
if (m_delegate.isStarted()) {
|
if (QtNative.getStateDetails().isStarted) {
|
||||||
QtNative.updateWindow();
|
QtNative.updateWindow();
|
||||||
// Suspending the app clears the immersive mode, so we need to set it again.
|
// Suspending the app clears the immersive mode, so we need to set it again.
|
||||||
m_delegate.displayManager().updateFullScreen(this);
|
m_delegate.displayManager().updateFullScreen(this);
|
||||||
@ -138,7 +138,7 @@ public class QtActivityBase extends Activity
|
|||||||
protected void onStop()
|
protected void onStop()
|
||||||
{
|
{
|
||||||
super.onStop();
|
super.onStop();
|
||||||
QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationSuspended);
|
QtNative.setApplicationState(QtNative.ApplicationState.ApplicationSuspended);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -187,7 +187,8 @@ public class QtActivityBase extends Activity
|
|||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyEvent(KeyEvent event)
|
public boolean dispatchKeyEvent(KeyEvent event)
|
||||||
{
|
{
|
||||||
if (m_delegate.isStarted() && m_delegate.getInputDelegate().handleDispatchKeyEvent(event))
|
boolean handleResult = m_delegate.getInputDelegate().handleDispatchKeyEvent(event);
|
||||||
|
if (QtNative.getStateDetails().isStarted && handleResult)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return super.dispatchKeyEvent(event);
|
return super.dispatchKeyEvent(event);
|
||||||
@ -197,7 +198,7 @@ public class QtActivityBase extends Activity
|
|||||||
public boolean dispatchGenericMotionEvent(MotionEvent event)
|
public boolean dispatchGenericMotionEvent(MotionEvent event)
|
||||||
{
|
{
|
||||||
boolean handled = m_delegate.getInputDelegate().handleDispatchGenericMotionEvent(event);
|
boolean handled = m_delegate.getInputDelegate().handleDispatchGenericMotionEvent(event);
|
||||||
if (m_delegate.isStarted() && handled)
|
if (QtNative.getStateDetails().isStarted && handled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return super.dispatchGenericMotionEvent(event);
|
return super.dispatchGenericMotionEvent(event);
|
||||||
@ -206,7 +207,8 @@ public class QtActivityBase extends Activity
|
|||||||
@Override
|
@Override
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||||
{
|
{
|
||||||
if (!m_delegate.isStarted() || !m_delegate.isPluginRunning())
|
QtNative.ApplicationStateDetails stateDetails = QtNative.getStateDetails();
|
||||||
|
if (!stateDetails.isStarted || !m_delegate.isPluginRunning())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return m_delegate.getInputDelegate().onKeyDown(keyCode, event);
|
return m_delegate.getInputDelegate().onKeyDown(keyCode, event);
|
||||||
@ -215,7 +217,8 @@ public class QtActivityBase extends Activity
|
|||||||
@Override
|
@Override
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||||
{
|
{
|
||||||
if (!m_delegate.isStarted() || !m_delegate.isPluginRunning())
|
QtNative.ApplicationStateDetails stateDetails = QtNative.getStateDetails();
|
||||||
|
if (!stateDetails.isStarted || !m_delegate.isPluginRunning())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return m_delegate.getInputDelegate().onKeyUp(keyCode, event);
|
return m_delegate.getInputDelegate().onKeyUp(keyCode, event);
|
||||||
@ -252,7 +255,7 @@ public class QtActivityBase extends Activity
|
|||||||
protected void onRestoreInstanceState(Bundle savedInstanceState)
|
protected void onRestoreInstanceState(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onRestoreInstanceState(savedInstanceState);
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
m_delegate.setStarted(savedInstanceState.getBoolean("Started"));
|
QtNative.setStarted(savedInstanceState.getBoolean("Started"));
|
||||||
// FIXME restore all surfaces
|
// FIXME restore all surfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +272,7 @@ public class QtActivityBase extends Activity
|
|||||||
{
|
{
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt("SystemUiVisibility", m_delegate.displayManager().systemUiVisibility());
|
outState.putInt("SystemUiVisibility", m_delegate.displayManager().systemUiVisibility());
|
||||||
outState.putBoolean("Started", m_delegate.isStarted());
|
outState.putBoolean("Started", QtNative.getStateDetails().isStarted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,7 +37,6 @@ public class QtActivityDelegate
|
|||||||
{
|
{
|
||||||
private Activity m_activity;
|
private Activity m_activity;
|
||||||
|
|
||||||
private boolean m_started = false;
|
|
||||||
private boolean m_isPluginRunning = false;
|
private boolean m_isPluginRunning = false;
|
||||||
|
|
||||||
private HashMap<Integer, QtSurface> m_surfaces = null;
|
private HashMap<Integer, QtSurface> m_surfaces = null;
|
||||||
@ -98,16 +97,6 @@ public class QtActivityDelegate
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void setStarted(boolean started)
|
|
||||||
{
|
|
||||||
m_started = started;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isStarted()
|
|
||||||
{
|
|
||||||
return m_started;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPluginRunning()
|
boolean isPluginRunning()
|
||||||
{
|
{
|
||||||
return m_isPluginRunning;
|
return m_isPluginRunning;
|
||||||
@ -153,13 +142,11 @@ public class QtActivityDelegate
|
|||||||
Runnable startApplication = () -> {
|
Runnable startApplication = () -> {
|
||||||
try {
|
try {
|
||||||
QtNative.startApplication(appParams, mainLib);
|
QtNative.startApplication(appParams, mainLib);
|
||||||
m_started = true;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
m_activity.finish();
|
m_activity.finish();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
initMembers(startApplication);
|
initMembers(startApplication);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2023 The Qt Company Ltd.
|
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
|
||||||
|
|
||||||
package org.qtproject.qt.android;
|
|
||||||
|
|
||||||
public class QtConstants {
|
|
||||||
// Application states
|
|
||||||
public static class ApplicationState {
|
|
||||||
public static final int ApplicationSuspended = 0x0;
|
|
||||||
public static final int ApplicationHidden = 0x1;
|
|
||||||
public static final int ApplicationInactive = 0x2;
|
|
||||||
public static final int ApplicationActive = 0x4;
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,26 +33,21 @@ import javax.net.ssl.X509TrustManager;
|
|||||||
public class QtNative
|
public class QtNative
|
||||||
{
|
{
|
||||||
private static WeakReference<Activity> m_activity = null;
|
private static WeakReference<Activity> m_activity = null;
|
||||||
private static boolean m_activityPaused = false;
|
|
||||||
private static WeakReference<Service> m_service = null;
|
private static WeakReference<Service> m_service = null;
|
||||||
public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
|
public static final Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
|
||||||
|
|
||||||
|
private static final ApplicationStateDetails m_stateDetails = new ApplicationStateDetails();
|
||||||
|
|
||||||
public static final String QtTAG = "Qt JAVA";
|
public static final String QtTAG = "Qt JAVA";
|
||||||
|
|
||||||
// a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
|
// a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
|
||||||
private static final ArrayList<Runnable> m_lostActions = new ArrayList<>();
|
private static final ArrayList<Runnable> m_lostActions = new ArrayList<>();
|
||||||
private static boolean m_started = false;
|
|
||||||
|
|
||||||
private static final QtThread m_qtThread = new QtThread();
|
private static final QtThread m_qtThread = new QtThread();
|
||||||
private static ClassLoader m_classLoader = null;
|
private static ClassLoader m_classLoader = null;
|
||||||
|
|
||||||
private static final Runnable runPendingCppRunnablesRunnable = QtNative::runPendingCppRunnables;
|
private static final Runnable runPendingCppRunnablesRunnable = QtNative::runPendingCppRunnables;
|
||||||
|
|
||||||
public static boolean isStarted()
|
|
||||||
{
|
|
||||||
return m_started && (isActivityValid() || isServiceValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
@UsedFromNativeCode
|
@UsedFromNativeCode
|
||||||
public static ClassLoader classLoader()
|
public static ClassLoader classLoader()
|
||||||
{
|
{
|
||||||
@ -198,16 +193,37 @@ public class QtNative
|
|||||||
return m_qtThread;
|
return m_qtThread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep in sync with src/corelib/global/qnamespace.h
|
||||||
|
public static class ApplicationState {
|
||||||
|
static final int ApplicationSuspended = 0x0;
|
||||||
|
static final int ApplicationHidden = 0x1;
|
||||||
|
static final int ApplicationInactive = 0x2;
|
||||||
|
static final int ApplicationActive = 0x4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ApplicationStateDetails {
|
||||||
|
int state = ApplicationState.ApplicationSuspended;
|
||||||
|
boolean isStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApplicationStateDetails getStateDetails()
|
||||||
|
{
|
||||||
|
return m_stateDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setStarted(boolean started)
|
||||||
|
{
|
||||||
|
m_stateDetails.isStarted = started;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setApplicationState(int state)
|
public static void setApplicationState(int state)
|
||||||
{
|
{
|
||||||
synchronized (m_mainActivityMutex) {
|
synchronized (m_mainActivityMutex) {
|
||||||
if (state == QtConstants.ApplicationState.ApplicationActive) {
|
m_stateDetails.state = state;
|
||||||
m_activityPaused = false;
|
if (state == ApplicationState.ApplicationActive) {
|
||||||
for (Runnable mLostAction : m_lostActions)
|
for (Runnable mLostAction : m_lostActions)
|
||||||
runAction(mLostAction);
|
runAction(mLostAction);
|
||||||
m_lostActions.clear();
|
m_lostActions.clear();
|
||||||
} else {
|
|
||||||
m_activityPaused = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateApplicationState(state);
|
updateApplicationState(state);
|
||||||
@ -220,7 +236,8 @@ public class QtNative
|
|||||||
synchronized (m_mainActivityMutex) {
|
synchronized (m_mainActivityMutex) {
|
||||||
final Looper mainLooper = Looper.getMainLooper();
|
final Looper mainLooper = Looper.getMainLooper();
|
||||||
final Handler handler = new Handler(mainLooper);
|
final Handler handler = new Handler(mainLooper);
|
||||||
final boolean active = (isActivityValid() && !m_activityPaused) || isServiceValid();
|
final boolean isStateActive = m_stateDetails.state == ApplicationState.ApplicationActive;
|
||||||
|
final boolean active = (isActivityValid() && isStateActive) || isServiceValid();
|
||||||
if (!active || !handler.post(action))
|
if (!active || !handler.post(action))
|
||||||
m_lostActions.add(action);
|
m_lostActions.add(action);
|
||||||
}
|
}
|
||||||
@ -231,7 +248,7 @@ public class QtNative
|
|||||||
{
|
{
|
||||||
synchronized (m_mainActivityMutex) {
|
synchronized (m_mainActivityMutex) {
|
||||||
if (isActivityValid()) {
|
if (isActivityValid()) {
|
||||||
if (!m_activityPaused)
|
if (m_stateDetails.state == ApplicationState.ApplicationActive)
|
||||||
m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable);
|
m_activity.get().runOnUiThread(runPendingCppRunnablesRunnable);
|
||||||
else
|
else
|
||||||
runAction(runPendingCppRunnablesRunnable);
|
runAction(runPendingCppRunnablesRunnable);
|
||||||
@ -264,7 +281,7 @@ public class QtNative
|
|||||||
});
|
});
|
||||||
m_qtThread.post(QtNative::startQtApplication);
|
m_qtThread.post(QtNative::startQtApplication);
|
||||||
waitForServiceSetup();
|
waitForServiceSetup();
|
||||||
m_started = true;
|
m_stateDetails.isStarted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +293,7 @@ public class QtNative
|
|||||||
m_activity.get().finish();
|
m_activity.get().finish();
|
||||||
if (isServiceValid())
|
if (isServiceValid())
|
||||||
m_service.get().stopSelf();
|
m_service.get().stopSelf();
|
||||||
|
m_stateDetails.isStarted = false;
|
||||||
m_started = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,8 +14,10 @@ public class QtServiceBase extends Service {
|
|||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
QtNative.setApplicationState(QtNative.ApplicationState.ApplicationHidden);
|
||||||
|
|
||||||
// the application has already started, do not reload everything again
|
// the application has already started, do not reload everything again
|
||||||
if (QtNative.isStarted()) {
|
if (QtNative.getStateDetails().isStarted) {
|
||||||
Log.w(QtNative.QtTAG,
|
Log.w(QtNative.QtTAG,
|
||||||
"A QtService tried to start in the same process as an initiated " +
|
"A QtService tried to start in the same process as an initiated " +
|
||||||
"QtActivity. That is not supported. This results in the service " +
|
"QtActivity. That is not supported. This results in the service " +
|
||||||
|
Loading…
x
Reference in New Issue
Block a user