From 3b6d288e3bf7a778f3ea8ccf51f59780a6de0dcc Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Fri, 15 Sep 2023 17:58:02 +0300 Subject: [PATCH] Android: Simplify Qt for Android hierarchy, less Java reflection! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes takes Qt for Android Java code away from the Delegate classes that uses heavily Java reflection to invoke Activity/Service calls and overrides. So instead of that, now, we have a QtActivityBase and a QtServiceBase classes which handle the override logic needed for Qt directly without reflection. These Base classes extend Android's Activity and Service directly, and are inside the internal Qt android package (under Qt6Android.jar). For example, to handle onConfigurationChanged, instead of the current way where we need this in QtActivityDelegate: public void onConfigurationChanged(Configuration configuration) { try { m_super_onConfigurationChanged.invoke(m_activity, configuration); } catch (Exception e) { e.printStackTrace(); } handleUiModeChange(configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK); } And then this in QtActivity: @Override public void onConfigurationChanged(Configuration newConfig) { if (!QtLoader.invokeDelegate(newConfig).invoked) super.onConfigurationChanged(newConfig); } public void super_onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } And having to keep it's Method handles around and then use Java reflection to call the override behavior done by Qt and the superclass methods. instead of that, we can do it now in QtActivityBase like: @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); handleUiModeChange(newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); } Then, we would still have our user facing QtActivity class which extends QtActivityBase and benefit from the same implementation of Qt logic done in the base class. An additional benefit to this approach is that now QtActivity will be very lightweight and doesn't need to have all the boilerplate code as before. [ChangeLog][Android] Simplify Qt for Android public bindings (QActivity, QtService and QtApplication) by implementing base classes which use the delegate implementions directly and avoid reflection. Task-number: QTBUG-115014 Task-number: QTBUG-114593 Change-Id: Ie1eca74f989627be4468786a27e30b16209fc521 Reviewed-by: Tinja Paavoseppä --- src/android/jar/CMakeLists.txt | 5 +- .../qtproject/qt/android/QtActivityBase.java | 349 ++++++++++++++++ .../qt/android/QtActivityDelegate.java | 334 +++------------ .../qt/android/QtActivityLoader.java | 18 +- .../qt/android/QtApplicationBase.java | 17 + .../org/qtproject/qt/android/QtLoader.java | 180 +------- .../qtproject/qt/android/QtServiceBase.java | 58 +++ .../qt/android/QtServiceDelegate.java | 23 +- .../qtproject/qt/android/QtServiceLoader.java | 18 +- .../qt/android/bindings/QtActivity.java | 392 ++---------------- .../qt/android/bindings/QtApplication.java | 22 +- .../qt/android/bindings/QtService.java | 76 +--- .../testdata/QtJniObjectTestClass.java | 3 + .../kernel/qjniobject/tst_qjniobject.cpp | 10 +- 14 files changed, 548 insertions(+), 957 deletions(-) create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index 31e09458d0..dd6a1dacba 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -8,7 +8,11 @@ set(java_sources src/org/qtproject/qt/android/EditContextView.java src/org/qtproject/qt/android/EditPopupMenu.java src/org/qtproject/qt/android/ExtractStyle.java + src/org/qtproject/qt/android/QtApplicationBase.java + src/org/qtproject/qt/android/QtActivityBase.java + src/org/qtproject/qt/android/QtServiceBase.java src/org/qtproject/qt/android/QtActivityDelegate.java + src/org/qtproject/qt/android/QtServiceDelegate.java src/org/qtproject/qt/android/QtLoader.java src/org/qtproject/qt/android/QtActivityLoader.java src/org/qtproject/qt/android/QtServiceLoader.java @@ -20,7 +24,6 @@ set(java_sources src/org/qtproject/qt/android/QtNativeLibrariesDir.java src/org/qtproject/qt/android/QtSurface.java src/org/qtproject/qt/android/QtThread.java - src/org/qtproject/qt/android/QtServiceDelegate.java src/org/qtproject/qt/android/extras//QtAndroidBinder.java src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java src/org/qtproject/qt/android/extras/QtNative.java diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java new file mode 100644 index 0000000000..fe2eaf0b9d --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java @@ -0,0 +1,349 @@ +// 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 android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.provider.Browser; +import android.text.method.MetaKeyKeyListener; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; + +public class QtActivityBase extends Activity +{ + private long m_metaState; + public boolean m_backKeyPressedSent = false; + + private boolean m_optionsMenuIsVisible = false; + + // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + public String APPLICATION_PARAMETERS = null; + + // use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_DIALOGS - 1 to use the android native dialogs. + public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; + + // A list with all themes that your application want to use. + // The name of the theme must be the same with any theme from + // http://developer.android.com/reference/android/R.style.html + // The most used themes are: + // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light + // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo + // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light + public String[] QT_ANDROID_THEMES = null; + + public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. + + private final QtActivityLoader m_loader = new QtActivityLoader(this); + + private final QtActivityDelegate m_delegate = new QtActivityDelegate(); + + protected void onCreateHook(Bundle savedInstanceState) { + m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS; + m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES; + m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES; + m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME; + m_loader.onCreate(savedInstanceState); + } + + public static final String EXTRA_SOURCE_INFO = "org.qtproject.qt.android.sourceInfo"; + + private void addReferrer(Intent intent) + { + if (intent.getExtras() != null && intent.getExtras().getString(EXTRA_SOURCE_INFO) != null) + return; + + String browserApplicationId = ""; + if (intent.getExtras() != null) + browserApplicationId = intent.getExtras().getString(Browser.EXTRA_APPLICATION_ID); + + String sourceInformation = ""; + if (browserApplicationId != null && !browserApplicationId.isEmpty()) { + sourceInformation = browserApplicationId; + } else { + Uri referrer = getReferrer(); + if (referrer != null) + sourceInformation = referrer.toString().replaceFirst("android-app://", ""); + } + + intent.putExtra(EXTRA_SOURCE_INFO, sourceInformation); + } + + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + onCreateHook(savedInstanceState); + addReferrer(getIntent()); + } + + @Override + protected void onStart() + { + super.onStart(); + } + + @Override + protected void onRestart() + { + super.onRestart(); + } + + @Override + protected void onPause() + { + super.onPause(); + if (Build.VERSION.SDK_INT < 24 || !isInMultiWindowMode()) + QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationInactive); + } + + @Override + protected void onResume() + { + super.onResume(); + QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationActive); + if (m_delegate.isStarted()) { + QtNative.updateWindow(); + m_delegate.updateFullScreen(); // Suspending the app clears the immersive mode, so we need to set it again. + } + } + + @Override + protected void onStop() + { + super.onStop(); + QtNative.setApplicationState(QtConstants.ApplicationState.ApplicationSuspended); + } + + @Override + protected void onDestroy() + { + super.onDestroy(); + if (m_delegate.isQuitApp()) { + QtNative.terminateQt(); + QtNative.setActivity(null, null); + QtNative.m_qtThread.exit(); + System.exit(0); + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + if (m_delegate.isStarted() + && event.getAction() == KeyEvent.ACTION_MULTIPLE + && event.getCharacters() != null + && event.getCharacters().length() == 1 + && event.getKeyCode() == 0) { + QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0); + QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0); + } + + if (QtNative.dispatchKeyEvent(event)) + return true; + + return super.dispatchKeyEvent(event); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + m_delegate.handleUiModeChange(newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK); + } + + @Override + public boolean onContextItemSelected(MenuItem item) + { + m_delegate.setContextMenuVisible(false); + return QtNative.onContextItemSelected(item.getItemId(), item.isChecked()); + } + + @Override + public void onContextMenuClosed(Menu menu) + { + if (!m_delegate.isContextMenuVisible()) + return; + m_delegate.setContextMenuVisible(false); + QtNative.onContextMenuClosed(menu); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) + { + menu.clearHeader(); + QtNative.onCreateContextMenu(menu); + m_delegate.setContextMenuVisible(true); + } + + private int m_lastChar = 0; + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + if (!m_delegate.isStarted() || !m_delegate.isPluginRunning()) + return false; + + m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event); + int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState) | event.getMetaState()); + int lc = c; + m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState); + + if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { + c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; + c = KeyEvent.getDeadChar(m_lastChar, c); + } + + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_MUTE) + && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) { + return false; + } + + m_lastChar = lc; + if (keyCode == KeyEvent.KEYCODE_BACK) { + m_backKeyPressedSent = !m_delegate.isKeyboardVisible(); + if (!m_backKeyPressedSent) + return true; + } + QtNative.keyDown(keyCode, c, event.getMetaState(), event.getRepeatCount() > 0); + + return true; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) + { + if (!m_delegate.isStarted() || !m_delegate.isPluginRunning()) + return false; + + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_MUTE) + && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) { + return false; + } + + if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) { + m_delegate.hideSoftwareKeyboard(); + m_delegate.setKeyboardVisibility(false, System.nanoTime()); + return true; + } + + m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event); + QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState(), event.getRepeatCount() > 0); + return true; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + menu.clear(); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) + { + m_optionsMenuIsVisible = true; + boolean res = QtNative.onPrepareOptionsMenu(menu); + m_delegate.setActionBarVisibility(res && menu.size() > 0); + return res; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked()); + } + + @Override + public void onOptionsMenuClosed(Menu menu) + { + m_optionsMenuIsVisible = false; + QtNative.onOptionsMenuClosed(menu); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) + { + super.onRestoreInstanceState(savedInstanceState); + m_delegate.setStarted(savedInstanceState.getBoolean("Started")); + // FIXME restore all surfaces + } + + @Override + public Object onRetainNonConfigurationInstance() + { + super.onRetainNonConfigurationInstance(); + m_delegate.setQuitApp(false); + return true; + + } + + @Override + protected void onSaveInstanceState(Bundle outState) + { + super.onSaveInstanceState(outState); + outState.putInt("SystemUiVisibility", m_delegate.systemUiVisibility()); + outState.putBoolean("Started", m_delegate.isStarted()); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) + { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) + m_delegate.updateFullScreen(); + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent ev) + { + if (m_delegate.isStarted() && QtNative.dispatchGenericMotionEvent(ev)) + return true; + + return super.dispatchGenericMotionEvent(ev); + } + + @Override + protected void onNewIntent(Intent intent) + { + QtNative.onNewIntent(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) + { + super.onActivityResult(requestCode, resultCode, data); + QtNative.onActivityResult(requestCode, resultCode, data); + } + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults); + } + + QtActivityDelegate 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 cc6aed569f..b180685af5 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -1,5 +1,5 @@ // Copyright (C) 2017 BogDan Vatra -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2016 Olivier Goffart // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only @@ -71,16 +71,6 @@ import static org.qtproject.qt.android.QtConstants.*; public class QtActivityDelegate { private Activity m_activity = null; - private Method m_super_dispatchKeyEvent = null; - private Method m_super_onRestoreInstanceState = null; - private Method m_super_onRetainNonConfigurationInstance = null; - private Method m_super_onSaveInstanceState = null; - private Method m_super_onKeyDown = null; - private Method m_super_onKeyUp = null; - private Method m_super_onConfigurationChanged = null; - private Method m_super_onActivityResult = null; - private Method m_super_dispatchGenericMotionEvent = null; - private Method m_super_onWindowFocusChanged = null; // Keep in sync with QtAndroid::SystemUiVisibility in androidjnimain.h public static final int SYSTEM_UI_VISIBILITY_NORMAL = 0; @@ -93,8 +83,6 @@ public class QtActivityDelegate private int m_nativeOrientation = Configuration.ORIENTATION_UNDEFINED; private String m_mainLib; - private long m_metaState; - private int m_lastChar = 0; private int m_softInputMode = 0; private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL; private boolean m_started = false; @@ -108,7 +96,6 @@ public class QtActivityDelegate private boolean m_quitApp = true; private View m_dummyView = null; private boolean m_keyboardIsVisible = false; - public boolean m_backKeyPressedSent = false; private long m_showHideTimeStamp = System.nanoTime(); private int m_portraitKeyboardHeight = 0; private int m_landscapeKeyboardHeight = 0; @@ -122,6 +109,9 @@ public class QtActivityDelegate private QtAccessibilityDelegate m_accessibilityDelegate = null; + QtActivityDelegate() { } + + public void setSystemUiVisibility(int systemUiVisibility) { if (m_systemUiVisibility == systemUiVisibility) @@ -442,6 +432,46 @@ public class QtActivityDelegate }); } + void setStarted(boolean started) + { + m_started = started; + } + + boolean isStarted() + { + return m_started; + } + + void setQuitApp(boolean quitApp) + { + m_quitApp = quitApp; + } + + boolean isQuitApp() + { + return m_quitApp; + } + + boolean isPluginRunning() + { + return m_isPluginRunning; + } + + int systemUiVisibility() + { + return m_systemUiVisibility; + } + + void setContextMenuVisible(boolean contextMenuVisible) + { + m_contextMenuVisible = contextMenuVisible; + } + + boolean isContextMenuVisible() + { + return m_contextMenuVisible; + } + int getAppIconSize(Activity a) { int size = a.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size); @@ -625,28 +655,6 @@ public class QtActivityDelegate QtNative.setActivity(m_activity, this); setActionBarVisibility(false); - Class activityClass = m_activity.getClass(); - m_super_dispatchKeyEvent = - activityClass.getMethod("super_dispatchKeyEvent", KeyEvent.class); - m_super_onRestoreInstanceState = - activityClass.getMethod("super_onRestoreInstanceState", Bundle.class); - m_super_onRetainNonConfigurationInstance = - activityClass.getMethod("super_onRetainNonConfigurationInstance"); - m_super_onSaveInstanceState = - activityClass.getMethod("super_onSaveInstanceState", Bundle.class); - m_super_onKeyDown = - activityClass.getMethod("super_onKeyDown", Integer.TYPE, KeyEvent.class); - m_super_onKeyUp = - activityClass.getMethod("super_onKeyUp", Integer.TYPE, KeyEvent.class); - m_super_onConfigurationChanged = - activityClass.getMethod("super_onConfigurationChanged", Configuration.class); - m_super_onActivityResult = - activityClass.getMethod("super_onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class); - m_super_onWindowFocusChanged = - activityClass.getMethod("super_onWindowFocusChanged", Boolean.TYPE); - m_super_dispatchGenericMotionEvent = - activityClass.getMethod("super_dispatchGenericMotionEvent", MotionEvent.class); - m_softInputMode = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), 0).softInputMode; DisplayManager displayManager = (DisplayManager)m_activity.getSystemService(Context.DISPLAY_SERVICE); @@ -948,7 +956,6 @@ public class QtActivityDelegate m_accessibilityDelegate.notifyScrolledEvent(viewId); } - public void notifyQtAndroidPluginRunning(boolean running) { m_isPluginRunning = running; @@ -959,22 +966,12 @@ public class QtActivityDelegate m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity, m_layout, this); } - public void onWindowFocusChanged(boolean hasFocus) { - try { - m_super_onWindowFocusChanged.invoke(m_activity, hasFocus); - } catch (Exception e) { - e.printStackTrace(); - } - if (hasFocus) - updateFullScreen(); - } - boolean isUiModeDark(Configuration config) { return (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; } - private void handleUiModeChange(int uiMode) + void handleUiModeChange(int uiMode) { // QTBUG-108365 if (Build.VERSION.SDK_INT >= 30) { @@ -1001,215 +998,12 @@ public class QtActivityDelegate } } - public void onConfigurationChanged(Configuration configuration) - { - try { - m_super_onConfigurationChanged.invoke(m_activity, configuration); - } catch (Exception e) { - e.printStackTrace(); - } - handleUiModeChange(configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK); - } - - public void onDestroy() - { - if (m_quitApp) { - QtNative.terminateQt(); - QtNative.setActivity(null, null); - QtNative.m_qtThread.exit(); - System.exit(0); - } - } - - public void onPause() - { - if (Build.VERSION.SDK_INT < 24 || !m_activity.isInMultiWindowMode()) - QtNative.setApplicationState(ApplicationState.ApplicationInactive); - } - - public void onResume() - { - QtNative.setApplicationState(ApplicationState.ApplicationActive); - if (m_started) { - QtNative.updateWindow(); - updateFullScreen(); // Suspending the app clears the immersive mode, so we need to set it again. - } - } - - public void onNewIntent(Intent data) - { - QtNative.onNewIntent(data); - } - - public void onActivityResult(int requestCode, int resultCode, Intent data) - { - try { - m_super_onActivityResult.invoke(m_activity, requestCode, resultCode, data); - } catch (Exception e) { - e.printStackTrace(); - } - - QtNative.onActivityResult(requestCode, resultCode, data); - } - - - public void onStop() - { - QtNative.setApplicationState(ApplicationState.ApplicationSuspended); - } - - public Object onRetainNonConfigurationInstance() - { - try { - m_super_onRetainNonConfigurationInstance.invoke(m_activity); - } catch (Exception e) { - e.printStackTrace(); - } - m_quitApp = false; - return true; - } - - public void onSaveInstanceState(Bundle outState) { - try { - m_super_onSaveInstanceState.invoke(m_activity, outState); - } catch (Exception e) { - e.printStackTrace(); - } - outState.putInt("SystemUiVisibility", m_systemUiVisibility); - outState.putBoolean("Started", m_started); - // It should never - } - - public void onRestoreInstanceState(Bundle savedInstanceState) - { - try { - m_super_onRestoreInstanceState.invoke(m_activity, savedInstanceState); - } catch (Exception e) { - e.printStackTrace(); - } - m_started = savedInstanceState.getBoolean("Started"); - // FIXME restore all surfaces - - } - - public boolean onKeyDown(int keyCode, KeyEvent event) - { - if (!m_started || !m_isPluginRunning) - return false; - - m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event); - int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState) | event.getMetaState()); - int lc = c; - m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState); - - if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { - c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; - int composed = KeyEvent.getDeadChar(m_lastChar, c); - c = composed; - } - - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_MUTE) - && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) { - return false; - } - - m_lastChar = lc; - if (keyCode == KeyEvent.KEYCODE_BACK) { - m_backKeyPressedSent = !m_keyboardIsVisible; - if (!m_backKeyPressedSent) - return true; - } - QtNative.keyDown(keyCode, c, event.getMetaState(), event.getRepeatCount() > 0); - - return true; - } - - public boolean onKeyUp(int keyCode, KeyEvent event) - { - if (!m_started || !m_isPluginRunning) - return false; - - if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP - || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_MUTE) - && System.getenv("QT_ANDROID_VOLUME_KEYS") == null) { - return false; - } - - if (keyCode == KeyEvent.KEYCODE_BACK && !m_backKeyPressedSent) { - hideSoftwareKeyboard(); - setKeyboardVisibility(false, System.nanoTime()); - return true; - } - - m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event); - QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState(), event.getRepeatCount() > 0); - return true; - } - - public boolean dispatchKeyEvent(KeyEvent event) - { - if (m_started - && event.getAction() == KeyEvent.ACTION_MULTIPLE - && event.getCharacters() != null - && event.getCharacters().length() == 1 - && event.getKeyCode() == 0) { - QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0); - QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState(), event.getRepeatCount() > 0); - } - - if (QtNative.dispatchKeyEvent(event)) - return true; - - try { - return (Boolean) m_super_dispatchKeyEvent.invoke(m_activity, event); - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - - private boolean m_optionsMenuIsVisible = false; - public boolean onCreateOptionsMenu(Menu menu) - { - menu.clear(); - return true; - } - public boolean onPrepareOptionsMenu(Menu menu) - { - m_optionsMenuIsVisible = true; - boolean res = QtNative.onPrepareOptionsMenu(menu); - setActionBarVisibility(res && menu.size() > 0); - return res; - } - - public boolean onOptionsItemSelected(MenuItem item) - { - return QtNative.onOptionsItemSelected(item.getItemId(), item.isChecked()); - } - - public void onOptionsMenuClosed(Menu menu) - { - m_optionsMenuIsVisible = false; - QtNative.onOptionsMenuClosed(menu); - } - public void resetOptionsMenu() { m_activity.invalidateOptionsMenu(); } private boolean m_contextMenuVisible = false; - public void onCreateContextMenu(ContextMenu menu, - View v, - ContextMenuInfo menuInfo) - { - menu.clearHeader(); - QtNative.onCreateContextMenu(menu); - m_contextMenuVisible = true; - } public void onCreatePopupMenu(Menu menu) { @@ -1217,20 +1011,6 @@ public class QtActivityDelegate m_contextMenuVisible = true; } - public void onContextMenuClosed(Menu menu) - { - if (!m_contextMenuVisible) - return; - m_contextMenuVisible = false; - QtNative.onContextMenuClosed(menu); - } - - public boolean onContextItemSelected(MenuItem item) - { - m_contextMenuVisible = false; - return QtNative.onContextItemSelected(item.getItemId(), item.isChecked()); - } - public void openContextMenu(final int x, final int y, final int w, final int h) { m_layout.postDelayed(new Runnable() { @@ -1242,13 +1022,13 @@ public class QtActivityDelegate popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { - return QtActivityDelegate.this.onContextItemSelected(menuItem); + return m_activity.onContextItemSelected(menuItem); } }); popup.setOnDismissListener(new PopupMenu.OnDismissListener() { @Override public void onDismiss(PopupMenu popupMenu) { - QtActivityDelegate.this.onContextMenuClosed(popupMenu.getMenu()); + m_activity.onContextMenuClosed(popupMenu.getMenu()); } }); popup.show(); @@ -1261,7 +1041,7 @@ public class QtActivityDelegate m_activity.closeContextMenu(); } - private void setActionBarVisibility(boolean visible) + void setActionBarVisibility(boolean visible) { if (m_activity.getActionBar() == null) return; @@ -1398,22 +1178,4 @@ public class QtActivityDelegate m_layout.moveChild(view, index); } } - - public boolean dispatchGenericMotionEvent (MotionEvent ev) - { - if (m_started && QtNative.dispatchGenericMotionEvent(ev)) - return true; - - try { - return (Boolean) m_super_dispatchGenericMotionEvent.invoke(m_activity, ev); - } catch (Exception e) { - e.printStackTrace(); - } - return false; - } - - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) - { - QtNative.sendRequestPermissionsResult(requestCode, permissions, grantResults); - } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java index 1c8cc99950..d9cfa2f79d 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java @@ -16,22 +16,12 @@ import java.lang.reflect.Field; public class QtActivityLoader extends QtLoader { Activity m_activity; - public QtActivityLoader(Activity activity, Class clazz) + public QtActivityLoader(Activity activity) { - super(activity, clazz); + super(activity); m_activity = activity; } - @Override - protected String loaderClassName() { - return "org.qtproject.qt.android.QtActivityDelegate"; - } - - @Override - protected Class contextClassName() { - return android.app.Activity.class; - } - @Override protected void finish() { m_activity.finish(); @@ -88,9 +78,7 @@ public class QtActivityLoader extends QtLoader { Runtime.getRuntime().exit(0); } - // there can only be a valid delegate object if the QtNative was started. - if (m_delegateObject != null && onCreate != null) - invokeDelegateMethod(onCreate, savedInstanceState); + ((QtActivityBase)m_activity).getActivityDelegate().onCreate(savedInstanceState); return; } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java new file mode 100644 index 0000000000..5ecb1597fe --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtApplicationBase.java @@ -0,0 +1,17 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +package org.qtproject.qt.android; + +import android.app.Application; + +public class QtApplicationBase extends Application { + public final static String QtTAG = "Qt"; + + @Override + public void onTerminate() { + QtNative.terminateQt(); + QtNative.m_qtThread.exit(); + super.onTerminate(); + } +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java index 168ce15ea3..74cc2e29c8 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java @@ -36,71 +36,18 @@ public abstract class QtLoader { // These parameters matter in case of deploying application as system (embedded into firmware) public static final String SYSTEM_LIB_PATH = "/system/lib/"; - public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, - // the parameters must not contain any white spaces - // and must be separated with "\t" - // e.g "-param1\t-param2=value2\t-param3\tvalue3" - + public String APPLICATION_PARAMETERS = null; public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; - // use this variable to add any environment variables to your application. - // the env vars must be separated with "\t" - // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" - // Currently the following vars are used by the android plugin: - // * QT_USE_ANDROID_NATIVE_DIALOGS -1 to use the android native dialogs. - - public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. - // The name of the theme must be the same with any theme from - // http://developer.android.com/reference/android/R.style.html - // The most used themes are: - // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme - // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black - // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light - // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo - // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light - // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault - // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light - + public String[] QT_ANDROID_THEMES = null; public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. public ArrayList m_qtLibs = null; // required qt libs public int m_displayDensity = -1; private ContextWrapper m_context; protected ComponentInfo m_contextInfo; - private Class m_delegateClass; - private static ArrayList m_fileOutputStreams = new ArrayList(); - // List of open file streams associated with files copied during installation. - - public static Object m_delegateObject = null; - public static HashMap> m_delegateMethods= new HashMap>(); - public static Method dispatchKeyEvent = null; - public static Method dispatchPopulateAccessibilityEvent = null; - public static Method dispatchTouchEvent = null; - public static Method dispatchTrackballEvent = null; - public static Method onKeyDown = null; - public static Method onKeyMultiple = null; - public static Method onKeyUp = null; - public static Method onTouchEvent = null; - public static Method onTrackballEvent = null; - public static Method onActivityResult = null; - public static Method onCreate = null; - public static Method onKeyLongPress = null; - public static Method dispatchKeyShortcutEvent = null; - public static Method onKeyShortcut = null; - public static Method dispatchGenericMotionEvent = null; - public static Method onGenericMotionEvent = null; - public static Method onRequestPermissionsResult = null; - private static String activityClassName; - private static Class qtApplicationClass = null; - - public QtLoader(ContextWrapper context, Class clazz) { + public QtLoader(ContextWrapper context) { m_context = context; - m_delegateClass = clazz; - } - - public static void setQtApplicationClass(Class qtAppClass) - { - qtApplicationClass = qtAppClass; } public static void setQtTAG(String tag) @@ -108,93 +55,6 @@ public abstract class QtLoader { QtTAG = tag; } - public static void setQtContextDelegate(Class clazz, Object listener) - { - m_delegateObject = listener; - activityClassName = clazz.getCanonicalName(); - - ArrayList delegateMethods = new ArrayList(); - for (Method m : listener.getClass().getMethods()) { - if (m.getDeclaringClass().getName().startsWith("org.qtproject.qt.android")) - delegateMethods.add(m); - } - - ArrayList applicationFields = new ArrayList(); - for (Field f : qtApplicationClass.getFields()) { - if (f.getDeclaringClass().getName().equals(qtApplicationClass.getName())) - applicationFields.add(f); - } - - for (Method delegateMethod : delegateMethods) { - try { - clazz.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes()); - if (m_delegateMethods.containsKey(delegateMethod.getName())) { - m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod); - } else { - ArrayList delegateSet = new ArrayList(); - delegateSet.add(delegateMethod); - m_delegateMethods.put(delegateMethod.getName(), delegateSet); - } - for (Field applicationField:applicationFields) { - if (applicationField.getName().equals(delegateMethod.getName())) { - try { - applicationField.set(null, delegateMethod); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } catch (Exception e) { } - } - } - - public static class InvokeResult - { - public boolean invoked = false; - public Object methodReturns = null; - } - - private static int stackDeep=-1; - public static InvokeResult invokeDelegate(Object... args) - { - InvokeResult result = new InvokeResult(); - if (m_delegateObject == null) - return result; - StackTraceElement[] elements = Thread.currentThread().getStackTrace(); - if (-1 == stackDeep) { - for (int it=0;it contextClassName(); - Intent getIntent() { return null; @@ -288,21 +145,21 @@ public abstract class QtLoader { loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists) m_context.getClassLoader()); // parent loader - Class loaderClass = classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class - Object qtLoader = loaderClass.newInstance(); // create an instance - Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication", - contextClassName(), - ClassLoader.class, - Bundle.class); - if (!(Boolean)prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams)) - throw new Exception(""); - - setQtContextDelegate(m_delegateClass, qtLoader); - - Method startAppMethod=qtLoader.getClass().getMethod("startApplication"); - if (!(Boolean)startAppMethod.invoke(qtLoader)) - throw new Exception(""); - + if (m_context instanceof QtActivityBase) { + QtActivityBase activityBase = (QtActivityBase)m_context; + QtActivityDelegate activityDelegate = activityBase.getActivityDelegate(); + if (!activityDelegate.loadApplication(activityBase, classLoader, loaderParams)) + throw new Exception(""); + if (!activityDelegate.startApplication()) + throw new Exception(""); + } else if (m_context instanceof QtServiceBase) { + QtServiceBase serviceBase = (QtServiceBase)m_context; + QtServiceDelegate serviceDelegate = serviceBase.getServiceDelegate(); + if (!serviceDelegate.loadApplication(serviceBase, classLoader, loaderParams)) + throw new Exception(""); + if (!serviceDelegate.startApplication()) + throw new Exception(""); + } } catch (Exception e) { e.printStackTrace(); AlertDialog errorDialog = new AlertDialog.Builder(m_context).create(); @@ -412,7 +269,6 @@ public abstract class QtLoader { Bundle loaderParams = new Bundle(); loaderParams.putInt(ERROR_CODE_KEY, 0); loaderParams.putString(DEX_PATH_KEY, new String()); - loaderParams.putString(LOADER_CLASS_NAME_KEY, loaderClassName()); id = resources.getIdentifier("static_init_classes", "string", packageName); loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, resources.getString(id) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java new file mode 100644 index 0000000000..f35db6436a --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java @@ -0,0 +1,58 @@ +// 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 android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +public class QtServiceBase extends Service { + + private final QtServiceDelegate m_delegate = new QtServiceDelegate(this); + QtServiceLoader m_loader = new QtServiceLoader(this); + protected void onCreateHook() { + // the application has already started + // do not reload everything again + if (QtNative.isStarted()) { + m_loader = null; + Log.w(QtNative.QtTAG, + "A QtService tried to start in the same process as an initiated " + + "QtActivity. That is not supported. This results in the service " + + "functioning as an Android Service detached from Qt."); + } else { + m_loader.onCreate(); + } + } + + @Override + public void onCreate() + { + super.onCreate(); + onCreateHook(); + } + + @Override + public void onDestroy() + { + super.onDestroy(); + QtNative.quitQtCoreApplication(); + QtNative.terminateQt(); + QtNative.setService(null, null); + QtNative.m_qtThread.exit(); + System.exit(0); + } + + @Override + public IBinder onBind(Intent intent) { + synchronized (this) { + return QtNative.onBind(intent); + } + } + + QtServiceDelegate getServiceDelegate() + { + return m_delegate; + } +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java index 0670f66756..1b6a8c9a8c 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java @@ -1,5 +1,5 @@ // Copyright (C) 2016 BogDan Vatra -// Copyright (C) 2016 The Qt Company Ltd. +// 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; @@ -58,6 +58,11 @@ public class QtServiceDelegate private Service m_service = null; private static String m_applicationParameters = null; + QtServiceDelegate(Service service) + { + m_service = service; + } + public boolean loadApplication(Service service, ClassLoader classLoader, Bundle loaderParams) { /// check parameters integrity @@ -136,20 +141,4 @@ public class QtServiceDelegate return false; } } - - public void onDestroy() - { - QtNative.quitQtCoreApplication(); - QtNative.terminateQt(); - QtNative.setService(null, null); - QtNative.m_qtThread.exit(); - System.exit(0); - } - - public IBinder onBind(Intent intent) - { - synchronized (this) { - return QtNative.onBind(intent); - } - } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java index d5f7167b7a..f6add928e1 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java @@ -14,8 +14,8 @@ import java.security.Provider; public class QtServiceLoader extends QtLoader { Service m_service; - public QtServiceLoader(Service service, Class clazz) { - super(service, clazz); + public QtServiceLoader(Service service) { + super(service); m_service = service; } @@ -29,10 +29,6 @@ public class QtServiceLoader extends QtLoader { return; } - if (m_delegateObject != null && onCreate != null) { - Bundle bundle = null; - invokeDelegateMethod(onCreate, bundle); - } startApp(true); } @@ -40,14 +36,4 @@ public class QtServiceLoader extends QtLoader { protected void finish() { m_service.stopSelf(); } - - @Override - protected String loaderClassName() { - return "org.qtproject.qt.android.QtServiceDelegate"; - } - - @Override - protected Class contextClassName() { - return android.app.Service.class; - } } diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java index 438fd403a6..9fa8d3ffae 100644 --- a/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java +++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtActivity.java @@ -4,11 +4,6 @@ package org.qtproject.qt.android.bindings; -import android.app.Activity; -import android.content.Intent; -import android.content.res.Configuration; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.provider.Browser; import android.view.ContextMenu; @@ -18,44 +13,41 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.os.Build; -import org.qtproject.qt.android.QtActivityLoader; -import org.qtproject.qt.android.QtLoader; +import org.qtproject.qt.android.QtActivityBase; -public class QtActivity extends Activity +public class QtActivity extends QtActivityBase { - public static final String EXTRA_SOURCE_INFO = "org.qtproject.qt.android.sourceInfo"; - - public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, - // the parameters must not contain any white spaces - // and must be separated with "\t" - // e.g "-param1\t-param2=value2\t-param3\tvalue3" - - public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; - // use this variable to add any environment variables to your application. - // the env vars must be separated with "\t" - // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" - // Currently the following vars are used by the android plugin: - // * QT_USE_ANDROID_NATIVE_DIALOGS - 1 to use the android native dialogs. - - public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. - // The name of the theme must be the same with any theme from - // http://developer.android.com/reference/android/R.style.html - // The most used themes are: - // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme - // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black - // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light - // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo - // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light - // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault - // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light - - public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. - - private QtActivityLoader m_loader; - public QtActivity() + @Override + public void onCreate(Bundle savedInstanceState) { - m_loader = new QtActivityLoader(this, QtActivity.class); + setAppDetails(); + super.onCreate(savedInstanceState); + } + + private void setAppDetails() + { + // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + APPLICATION_PARAMETERS = ""; + + // Use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_DIALOGS - 1 to use the android native dialogs. + ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; + + // A list with all themes that your application want to use. + // The name of the theme must be the same with any theme from + // http://developer.android.com/reference/android/R.style.html + // The most used themes are: + // * "Theme_Light" + // * "Theme_Holo" + // * "Theme_Holo_Light" if (Build.VERSION.SDK_INT < 29) { QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; @@ -65,326 +57,4 @@ public class QtActivity extends Activity QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_DayNight"; } } - - - /////////////////////////// forward all notifications //////////////////////////// - /////////////////////////// Super class calls //////////////////////////////////// - /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - - protected void onCreateHook(Bundle savedInstanceState) { - m_loader.APPLICATION_PARAMETERS = APPLICATION_PARAMETERS; - m_loader.ENVIRONMENT_VARIABLES = ENVIRONMENT_VARIABLES; - m_loader.QT_ANDROID_THEMES = QT_ANDROID_THEMES; - m_loader.QT_ANDROID_DEFAULT_THEME = QT_ANDROID_DEFAULT_THEME; - m_loader.onCreate(savedInstanceState); - } - - private void addReferrer(Intent intent) - { - if (intent.getExtras() != null && intent.getExtras().getString(EXTRA_SOURCE_INFO) != null) - return; - - String browserApplicationId = ""; - if (intent.getExtras() != null) - browserApplicationId = intent.getExtras().getString(Browser.EXTRA_APPLICATION_ID); - - String sourceInformation = ""; - if (browserApplicationId != null && !browserApplicationId.isEmpty()) { - sourceInformation = browserApplicationId; - } else { - Uri referrer = getReferrer(); - if (referrer != null) - sourceInformation = referrer.toString().replaceFirst("android-app://", ""); - } - - intent.putExtra(EXTRA_SOURCE_INFO, sourceInformation); - } - - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - QtLoader.setQtApplicationClass(QtApplication.class); - onCreateHook(savedInstanceState); - addReferrer(getIntent()); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - if (QtLoader.m_delegateObject != null && QtLoader.dispatchKeyEvent != null) - return (Boolean) QtLoader.invokeDelegateMethod(QtLoader.dispatchKeyEvent, event); - else - return super.dispatchKeyEvent(event); - } - public boolean super_dispatchKeyEvent(KeyEvent event) - { - return super.dispatchKeyEvent(event); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) - { - - if (QtLoader.m_delegateObject != null && QtLoader.onActivityResult != null) { - QtLoader.invokeDelegateMethod(QtLoader.onActivityResult, requestCode, resultCode, data); - return; - } - super.onActivityResult(requestCode, resultCode, data); - } - public void super_onActivityResult(int requestCode, int resultCode, Intent data) - { - super.onActivityResult(requestCode, resultCode, data); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) - { - if (!QtLoader.invokeDelegate(newConfig).invoked) - super.onConfigurationChanged(newConfig); - } - public void super_onConfigurationChanged(Configuration newConfig) - { - super.onConfigurationChanged(newConfig); - } - - @Override - public boolean onContextItemSelected(MenuItem item) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(item); - if (res.invoked) - return (Boolean)res.methodReturns; - else - return super.onContextItemSelected(item); - } - public boolean super_onContextItemSelected(MenuItem item) - { - return super.onContextItemSelected(item); - } - - @Override - public void onContextMenuClosed(Menu menu) - { - if (!QtLoader.invokeDelegate(menu).invoked) - super.onContextMenuClosed(menu); - } - public void super_onContextMenuClosed(Menu menu) - { - super.onContextMenuClosed(menu); - } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) - { - if (!QtLoader.invokeDelegate(menu, v, menuInfo).invoked) - super.onCreateContextMenu(menu, v, menuInfo); - } - public void super_onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) - { - super.onCreateContextMenu(menu, v, menuInfo); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(menu); - if (res.invoked) - return (Boolean)res.methodReturns; - else - return super.onCreateOptionsMenu(menu); - } - public boolean super_onCreateOptionsMenu(Menu menu) - { - return super.onCreateOptionsMenu(menu); - } - - @Override - protected void onDestroy() - { - super.onDestroy(); - QtLoader.invokeDelegate(); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - if (QtLoader.m_delegateObject != null && QtLoader.onKeyDown != null) - return (Boolean) QtLoader.invokeDelegateMethod(QtLoader.onKeyDown, keyCode, event); - else - return super.onKeyDown(keyCode, event); - } - public boolean super_onKeyDown(int keyCode, KeyEvent event) - { - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) - { - if (QtLoader.m_delegateObject != null && QtLoader.onKeyUp != null) - return (Boolean) QtLoader.invokeDelegateMethod(QtLoader.onKeyUp, keyCode, event); - else - return super.onKeyUp(keyCode, event); - } - public boolean super_onKeyUp(int keyCode, KeyEvent event) - { - return super.onKeyUp(keyCode, event); - } - - @Override - protected void onNewIntent(Intent intent) - { - addReferrer(intent); - if (!QtLoader.invokeDelegate(intent).invoked) - super.onNewIntent(intent); - } - public void super_onNewIntent(Intent intent) - { - super.onNewIntent(intent); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(item); - if (res.invoked) - return (Boolean)res.methodReturns; - else - return super.onOptionsItemSelected(item); - } - public boolean super_onOptionsItemSelected(MenuItem item) - { - return super.onOptionsItemSelected(item); - } - - @Override - public void onOptionsMenuClosed(Menu menu) - { - if (!QtLoader.invokeDelegate(menu).invoked) - super.onOptionsMenuClosed(menu); - } - public void super_onOptionsMenuClosed(Menu menu) - { - super.onOptionsMenuClosed(menu); - } - - @Override - protected void onPause() - { - super.onPause(); - QtLoader.invokeDelegate(); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(menu); - if (res.invoked) - return (Boolean)res.methodReturns; - else - return super.onPrepareOptionsMenu(menu); - } - public boolean super_onPrepareOptionsMenu(Menu menu) - { - return super.onPrepareOptionsMenu(menu); - } - - @Override - protected void onRestart() - { - super.onRestart(); - QtLoader.invokeDelegate(); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) - { - if (!QtLoader.invokeDelegate(savedInstanceState).invoked) - super.onRestoreInstanceState(savedInstanceState); - } - public void super_onRestoreInstanceState(Bundle savedInstanceState) - { - super.onRestoreInstanceState(savedInstanceState); - } - - @Override - protected void onResume() - { - super.onResume(); - QtLoader.invokeDelegate(); - } - - @Override - public Object onRetainNonConfigurationInstance() - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(); - if (res.invoked) - return res.methodReturns; - else - return super.onRetainNonConfigurationInstance(); - } - public Object super_onRetainNonConfigurationInstance() - { - return super.onRetainNonConfigurationInstance(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) - { - if (!QtLoader.invokeDelegate(outState).invoked) - super.onSaveInstanceState(outState); - } - public void super_onSaveInstanceState(Bundle outState) - { - super.onSaveInstanceState(outState); - - } - - @Override - protected void onStart() - { - super.onStart(); - QtLoader.invokeDelegate(); - } - - @Override - protected void onStop() - { - super.onStop(); - QtLoader.invokeDelegate(); - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) - { - if (!QtLoader.invokeDelegate(hasFocus).invoked) - super.onWindowFocusChanged(hasFocus); - } - public void super_onWindowFocusChanged(boolean hasFocus) - { - super.onWindowFocusChanged(hasFocus); - } - - @Override - public boolean dispatchGenericMotionEvent(MotionEvent ev) - { - if (QtLoader.m_delegateObject != null && QtLoader.dispatchGenericMotionEvent != null) - return (Boolean) QtLoader.invokeDelegateMethod(QtLoader.dispatchGenericMotionEvent, ev); - else - return super.dispatchGenericMotionEvent(ev); - } - public boolean super_dispatchGenericMotionEvent(MotionEvent event) - { - return super.dispatchGenericMotionEvent(event); - } - - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) - { - if (QtLoader.m_delegateObject != null && QtLoader.onRequestPermissionsResult != null) { - QtLoader.invokeDelegateMethod(QtLoader.onRequestPermissionsResult, requestCode , - permissions, grantResults); - } - } - //--------------------------------------------------------------------------- } diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java index b89946b9a5..d1330565c1 100644 --- a/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java +++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtApplication.java @@ -4,30 +4,12 @@ package org.qtproject.qt.android.bindings; -import android.app.Application; -import org.qtproject.qt.android.QtLoader; +import org.qtproject.qt.android.QtApplicationBase; -public class QtApplication extends Application +public class QtApplication extends QtApplicationBase { - public final static String QtTAG = "Qt"; - @Override public void onTerminate() { - if (QtLoader.m_delegateObject != null && QtLoader.m_delegateMethods.containsKey("onTerminate")) - QtLoader.invokeDelegateMethod(QtLoader.m_delegateMethods.get("onTerminate").get(0)); super.onTerminate(); } - - // TODO: only keep around for avoid build errors, will be removed. - public static class InvokeResult - { - public boolean invoked = false; - public Object methodReturns = null; - } - - public static InvokeResult invokeDelegate(Object... args) - { - InvokeResult result = new InvokeResult(); - return result; - } } diff --git a/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java b/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java index 8322af2ae7..6569446901 100644 --- a/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java +++ b/src/android/java/src/org/qtproject/qt/android/bindings/QtService.java @@ -4,85 +4,13 @@ package org.qtproject.qt.android.bindings; -import android.app.Service; -import android.util.Log; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.IBinder; +import org.qtproject.qt.android.QtServiceBase; -import org.qtproject.qt.android.QtNative; -import org.qtproject.qt.android.QtServiceLoader; -import org.qtproject.qt.android.QtLoader; - -public class QtService extends Service +public class QtService extends QtServiceBase { - QtServiceLoader m_loader = new QtServiceLoader(this, QtService.class); - - /////////////////////////// forward all notifications //////////////////////////// - /////////////////////////// Super class calls //////////////////////////////////// - /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - - protected void onCreateHook() { - // the application has already started - // do not reload everything again - if (QtNative.isStarted()) { - m_loader = null; - Log.w(QtNative.QtTAG, - "A QtService tried to start in the same process as an initiated " + - "QtActivity. That is not supported. This results in the service " + - "functioning as an Android Service detached from Qt."); - } else { - m_loader.onCreate(); - } - } @Override public void onCreate() { super.onCreate(); - onCreateHook(); } - - @Override - public void onDestroy() - { - super.onDestroy(); - QtLoader.invokeDelegate(); - } - - @Override - public IBinder onBind(Intent intent) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(intent); - if (res.invoked) - return (IBinder)res.methodReturns; - else - return null; - } - - @Override - public void onConfigurationChanged(Configuration newConfig) - { - if (!QtLoader.invokeDelegate(newConfig).invoked) - super.onConfigurationChanged(newConfig); - } - public void super_onConfigurationChanged(Configuration newConfig) - { - super.onConfigurationChanged(newConfig); - } - - @Override - public boolean onUnbind(Intent intent) - { - QtLoader.InvokeResult res = QtLoader.invokeDelegate(intent); - if (res.invoked) - return (Boolean) res.methodReturns; - else - return super.onUnbind(intent); - } - public boolean super_onUnbind(Intent intent) - { - return super.onUnbind(intent); - } - //--------------------------------------------------------------------------- } diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java index 0775f2fde6..d1b4ea480e 100644 --- a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -20,6 +20,9 @@ public class QtJniObjectTestClass // -------------------------------------------------------------------------------------------- + final int INT_FIELD = 123; + final boolean BOOL_FIELD = true; + byte BYTE_VAR; short SHORT_VAR; int INT_VAR; diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp index 6e09bfd513..47c3a707f0 100644 --- a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -1034,19 +1034,19 @@ void tst_QJniObject::getStaticCharField() void tst_QJniObject::getBooleanField() { - QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + QJniObject obj(testClassName); QVERIFY(obj.isValid()); - QVERIFY(!obj.getField("m_backKeyPressedSent")); + QVERIFY(obj.getField("BOOL_FIELD")); } void tst_QJniObject::getIntField() { - QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + QJniObject obj(testClassName); QVERIFY(obj.isValid()); - jint res = obj.getField("m_currentRotation"); - QCOMPARE(res, -1); + jint res = obj.getField("INT_FIELD"); + QCOMPARE(res, 123); } template