summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAssam Boudjelthia <assam.boudjelthia@qt.io>2023-10-25 11:40:17 +0300
committerAssam Boudjelthia <assam.boudjelthia@qt.io>2023-10-27 17:22:59 +0300
commit3a505a859a9ffd4a79c08e1aa7c97a6979fcef92 (patch)
tree16f85c74991aa73879962211a514035b90b845b0
parent7c59c5ed13dd796b4a77a56cb7badc34aead1adb (diff)
Android: modularize and simplify QtLoader classes
The Qt loader is now expected to work in the following way: QtActivity.onCreate() --| QtActivityBase.onCreate() ----| Initiaze the delegate ----| Creates QtActivityLoader() ------| Creates QtLoader() --------| Initialize the class loader --------| Initialize static classes context --------| Initialize Context Info ------| Sets common environment variables and parameters from metadata ------| Setup style extraction ------| Sets Activity specific metadata --| Handles Activity themes --| Call QtLoader.loadQtLibraries() ----| Fetch and load Qt native libraries ----| Fetch and load the app's main library --| Start the Qt Native C++ app via the delegate Few things are done in patch to simplify the Qt loader mechanism: 1) Get rid of some unused methods, and move the loader instances to be local instead of global. 2) Split the awfully long methods in QtLoader into smaller methods for readability and as preparation for next simplifications. 3) Refactor Qt libraries loading code from the Delegate classes to the Loader classes where it makes more sense to be at. At the same time simplify some code into smaller logical blocks. 4) The same boilerplate code for loading (with System.load()) Qt libs and the main library was done twice between the Activity and Service loaders, that is now done directly under the QtLoader. Same story for initializing static Java classes with activity/service/context. With this change All relevant Qt library loading logic is now under QtLoader classes, and the latter have clear loader responsibilities reflected in the code. Task-number: QTBUG-115016 Change-Id: Ib76621d8beff4917c932456c5401ea4586942213 Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java48
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java65
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java231
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java170
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtConstants.java12
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtLoader.java669
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtNative.java143
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java27
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java126
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java32
10 files changed, 702 insertions, 821 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java b/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java
index 672a2e28d3..67e93a6e29 100644
--- a/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java
+++ b/src/android/jar/src/org/qtproject/qt/android/ExtractStyle.java
@@ -5,8 +5,10 @@
package org.qtproject.qt.android;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -136,26 +138,50 @@ public class ExtractStyle {
Context m_context;
private final HashMap<String, DrawableCache> m_drawableCache = new HashMap<>();
- private static final String EXTRACT_STYLE_KEY = "extract.android.style";
- private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
-
private static boolean m_missingNormalStyle = false;
private static boolean m_missingDarkStyle = false;
private static String m_stylePath = null;
private static boolean m_extractMinimal = false;
- public static void setup(Bundle loaderParams) {
- if (loaderParams.containsKey(EXTRACT_STYLE_KEY)) {
- m_stylePath = loaderParams.getString(EXTRACT_STYLE_KEY);
+ private static final String QtTAG = "QtExtractStyle";
+
+ private static boolean isUiModeDark(Configuration config)
+ {
+ return (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ public static String setup(Activity activity, String extractOption, int dpi) {
- boolean darkModeFileMissing = !(new File(m_stylePath + "darkUiMode/style.json").exists());
- m_missingDarkStyle = Build.VERSION.SDK_INT > 28 && darkModeFileMissing;
+ String dataDir = activity.getApplicationInfo().dataDir;
+ m_stylePath = dataDir + "/qt-reserved-files/android-style/" + dpi + "/";
- m_missingNormalStyle = !(new File(m_stylePath + "style.json").exists());
+ if (!extractOption.equals("default") && !extractOption.equals("full")
+ && !extractOption.equals("minimal") && !extractOption.equals("none")) {
+ Log.e(QtTAG, "Invalid extract_android_style option \"" + extractOption
+ + "\", defaulting to \"default\"");
+ extractOption = "default";
+ }
- m_extractMinimal = loaderParams.containsKey(EXTRACT_STYLE_MINIMAL_KEY) &&
- loaderParams.getBoolean(EXTRACT_STYLE_MINIMAL_KEY);
+ // QTBUG-69810: The extraction code will trigger compatibility warnings on Android
+ // SDK version >= 28 when the target SDK version is set to something lower then 28,
+ // so default to "none" and issue a warning if that is the case.
+ if (extractOption.equals("default")) {
+ int targetSdk = activity.getApplicationInfo().targetSdkVersion;
+ if (targetSdk < 28 && Build.VERSION.SDK_INT >= 28) {
+ Log.e(QtTAG, "extract_android_style option set to \"none\" when " +
+ "targetSdkVersion is less then 28");
+ extractOption = "none";
+ }
}
+
+ boolean darkModeFileMissing = !(new File(m_stylePath + "darkUiMode/style.json").exists());
+ m_missingDarkStyle = Build.VERSION.SDK_INT > 28 && darkModeFileMissing;
+ m_missingNormalStyle = !(new File(m_stylePath + "style.json").exists());
+ m_extractMinimal = extractOption.equals("minimal");
+
+ ExtractStyle.runIfNeeded(activity, isUiModeDark(activity.getResources().getConfiguration()));
+
+ return m_stylePath;
}
public static void runIfNeeded(Context context, boolean extractDarkMode) {
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 77d772da45..e067cf5de3 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityBase.java
@@ -10,15 +10,16 @@ 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;
+import android.view.Window;
+
+import java.lang.reflect.Field;
public class QtActivityBase extends Activity
{
@@ -48,17 +49,7 @@ public class QtActivityBase extends Activity
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);
- }
+ private QtActivityDelegate m_delegate;
public static final String EXTRA_SOURCE_INFO = "org.qtproject.qt.android.sourceInfo";
@@ -83,12 +74,58 @@ public class QtActivityBase extends Activity
intent.putExtra(EXTRA_SOURCE_INFO, sourceInformation);
}
+ private void handleActivityRestart() {
+ if (QtNative.isStarted()) {
+ boolean updated = m_delegate.updateActivityAfterRestart(this);
+ if (!updated) {
+ // could not update the activity so restart the application
+ Intent intent = Intent.makeRestartActivityTask(getComponentName());
+ startActivity(intent);
+ QtNative.quitApp();
+ Runtime.getRuntime().exit(0);
+ }
+ }
+ }
+
+ void configureActivityTheme() {
+ if (QT_ANDROID_THEMES == null || QT_ANDROID_DEFAULT_THEME == null) {
+ if (Build.VERSION.SDK_INT < 29) {
+ QT_ANDROID_THEMES = new String[]{"Theme_Holo_Light"};
+ QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light";
+ } else {
+ QT_ANDROID_THEMES = new String[]{"Theme_DeviceDefault_DayNight"};
+ QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_DayNight";
+ }
+ }
+ try {
+ Field f = Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME);
+ int themeId = f.getInt(null);
+ setTheme(themeId);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
- onCreateHook(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_ACTION_BAR);
+
+ m_delegate = new QtActivityDelegate(this);
+
+ handleActivityRestart();
addReferrer(getIntent());
+ configureActivityTheme();
+
+ QtActivityLoader loader = new QtActivityLoader(this);
+ loader.setApplicationParameters(APPLICATION_PARAMETERS);
+ loader.setEnvironmentVariables(ENVIRONMENT_VARIABLES);
+ loader.setEnvironmentVariable("QT_ANDROID_THEME", QT_ANDROID_DEFAULT_THEME);
+
+ loader.loadQtLibraries();
+ m_delegate.startNativeApplication(loader.getApplicationParameters(),
+ loader.getMainLibrary());
}
@Override
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 e7acb06ca6..ad4931a1fc 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java
@@ -8,15 +8,12 @@ package org.qtproject.qt.android;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.Rect;
import android.os.Build;
-import android.os.Bundle;
-import android.util.Base64;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -39,13 +36,10 @@ import android.widget.ImageView;
import android.widget.PopupMenu;
import android.hardware.display.DisplayManager;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Objects;
import org.qtproject.qt.android.accessibility.QtAccessibilityDelegate;
-import static org.qtproject.qt.android.QtConstants.*;
public class QtActivityDelegate
{
@@ -57,13 +51,9 @@ public class QtActivityDelegate
public static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2;
private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
- private static String m_applicationParameters = null;
-
private int m_currentRotation = -1; // undefined
private int m_nativeOrientation = Configuration.ORIENTATION_UNDEFINED;
- private String m_mainLib;
-
private boolean m_started = false;
private boolean m_quitApp = true;
private boolean m_isPluginRunning = false;
@@ -88,10 +78,25 @@ public class QtActivityDelegate
};
private final QtInputDelegate m_inputDelegate = new QtInputDelegate(m_keyboardVisibilityListener);
- QtActivityDelegate() { }
-
- QtInputDelegate getInputDelegate()
+ QtActivityDelegate(Activity activity)
{
+ m_activity = activity;
+ QtNative.setActivity(m_activity, this);
+
+ setActionBarVisibility(false);
+
+ try {
+ m_inputDelegate.setSoftInputMode(m_activity.getPackageManager()
+ .getActivityInfo(m_activity.getComponentName(), 0).softInputMode);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ DisplayManager displayManager = (DisplayManager) m_activity.getSystemService(Context.DISPLAY_SERVICE);
+ displayManager.registerDisplayListener(m_displayListener, null);
+ }
+
+ QtInputDelegate getInputDelegate() {
return m_inputDelegate;
}
@@ -194,24 +199,7 @@ public class QtActivityDelegate
return m_contextMenuVisible;
}
- int getAppIconSize(Activity a)
- {
- int size = a.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
- if (size < 36 || size > 512) { // check size sanity
- DisplayMetrics metrics = new DisplayMetrics();
- a.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- size = metrics.densityDpi / 10 * 3;
- if (size < 36)
- size = 36;
-
- if (size > 512)
- size = 512;
- }
-
- return size;
- }
-
- private final DisplayManager.DisplayListener displayListener = new DisplayManager.DisplayListener()
+ private final DisplayManager.DisplayListener m_displayListener = new DisplayManager.DisplayListener()
{
@Override
public void onDisplayAdded(int displayId) {
@@ -252,14 +240,14 @@ public class QtActivityDelegate
}
};
- public boolean updateActivity(Activity activity)
- {
+ public boolean updateActivityAfterRestart(Activity activity) {
try {
// set new activity
- loadActivity(activity);
+ m_activity = activity;
+ QtNative.setActivity(m_activity, this);
// update the new activity content view to old layout
- ViewGroup layoutParent = (ViewGroup)m_layout.getParent();
+ ViewGroup layoutParent = (ViewGroup) m_layout.getParent();
if (layoutParent != null)
layoutParent.removeView(m_layout);
@@ -274,169 +262,37 @@ public class QtActivityDelegate
}
}
- private void loadActivity(Activity activity)
- throws NoSuchMethodException, PackageManager.NameNotFoundException
- {
- m_activity = activity;
-
- QtNative.setActivity(m_activity, this);
- setActionBarVisibility(false);
-
- m_inputDelegate.setSoftInputMode(m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), 0).softInputMode);
-
- DisplayManager displayManager = (DisplayManager)m_activity.getSystemService(Context.DISPLAY_SERVICE);
- displayManager.registerDisplayListener(displayListener, null);
+ public void onTerminate() {
+ QtNative.terminateQt();
+ QtNative.m_qtThread.exit();
}
- public boolean loadApplication(Activity activity, ClassLoader classLoader, Bundle loaderParams)
+ public void startNativeApplication(ArrayList<String> appParams, String mainLib)
{
- /// check parameters integrity
- if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY)
- || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY)
- || !loaderParams.containsKey(ENVIRONMENT_VARIABLES_KEY)) {
- return false;
- }
-
- try {
- loadActivity(activity);
- QtNative.setClassLoader(classLoader);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
-
- if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) {
- for (String className: Objects.requireNonNull(loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY))) {
- if (className.length() == 0)
- continue;
+ if (m_surfaces != null)
+ return;
+ Runnable startApplication = new Runnable() {
+ @Override
+ public void run() {
try {
- Class<?> initClass = classLoader.loadClass(className);
- Object staticInitDataObject = initClass.newInstance(); // create an instance
- try {
- Method m = initClass.getMethod("setActivity", Activity.class, Object.class);
- m.invoke(staticInitDataObject, m_activity, this);
- } catch (Exception e) {
- Log.d(QtNative.QtTAG, "Class " + className + " does not implement setActivity method");
- }
-
- // For modules that don't need/have setActivity
- try {
- Method m = initClass.getMethod("setContext", Context.class);
- m.invoke(staticInitDataObject, (Context)m_activity);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ QtNative.startApplication(appParams, mainLib);
+ m_started = true;
} catch (Exception e) {
e.printStackTrace();
+ m_activity.finish();
}
}
- }
- QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY));
- ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY);
- String nativeLibsDir = QtNativeLibrariesDir.nativeLibrariesDir(m_activity);
- QtNative.loadBundledLibraries(libraries, nativeLibsDir);
- m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY);
- // older apps provide the main library as the last bundled library; look for this if the main library isn't provided
- if (null == m_mainLib && libraries.size() > 0) {
- m_mainLib = libraries.get(libraries.size() - 1);
- libraries.remove(libraries.size() - 1);
- }
-
- ExtractStyle.setup(loaderParams);
- ExtractStyle.runIfNeeded(m_activity, isUiModeDark(m_activity.getResources().getConfiguration()));
-
- QtNative.setEnvironmentVariables(loaderParams.getString(ENVIRONMENT_VARIABLES_KEY));
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_MONOSPACE",
- "Droid Sans Mono;Droid Sans;Droid Sans Fallback");
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_SERIF", "Droid Serif");
- QtNative.setEnvironmentVariable("HOME", m_activity.getFilesDir().getAbsolutePath());
- QtNative.setEnvironmentVariable("TMPDIR", m_activity.getCacheDir().getAbsolutePath());
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS",
- "Roboto;Droid Sans;Droid Sans Fallback");
- QtNative.setEnvironmentVariable("QT_ANDROID_APP_ICON_SIZE",
- String.valueOf(getAppIconSize(activity)));
-
- if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY))
- m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY);
- else
- m_applicationParameters = "";
-
- m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
- return m_mainLib != null;
- }
-
- public boolean startApplication()
- {
- // start application
- try {
-
- Bundle extras = m_activity.getIntent().getExtras();
- if (extras != null) {
- try {
- final boolean isDebuggable = (m_activity.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
- if (!isDebuggable)
- throw new Exception();
-
- if (extras.containsKey("extraenvvars")) {
- try {
- QtNative.setEnvironmentVariables(new String(
- Base64.decode(extras.getString("extraenvvars"), Base64.DEFAULT),
- "UTF-8"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- if (extras.containsKey("extraappparams")) {
- try {
- m_applicationParameters += "\t" + new String(Base64.decode(extras.getString("extraappparams"), Base64.DEFAULT), "UTF-8");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- } catch (Exception e) {
- Log.e(QtNative.QtTAG, "Not in debug mode! It is not allowed to use " +
- "extra arguments in non-debug mode.");
- // This is not an error, so keep it silent
- // e.printStackTrace();
- }
- } // extras != null
+ };
- if (null == m_surfaces)
- onCreate(null);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
+ initMembers(startApplication);
}
-
- public void onTerminate()
- {
- QtNative.terminateQt();
- QtNative.m_qtThread.exit();
- }
-
- public void onCreate(Bundle savedInstanceState)
+
+ private void initMembers(Runnable startApplicationRunnable)
{
m_quitApp = true;
- Runnable startApplication = null;
- if (null == savedInstanceState) {
- startApplication = new Runnable() {
- @Override
- public void run() {
- try {
- QtNative.startApplication(m_applicationParameters, m_mainLib);
- m_started = true;
- } catch (Exception e) {
- e.printStackTrace();
- m_activity.finish();
- }
- }
- };
- }
- m_layout = new QtLayout(m_activity, startApplication);
+
+ m_layout = new QtLayout(m_activity, startApplicationRunnable);
int orientation = m_activity.getResources().getConfiguration().orientation;
@@ -593,11 +449,6 @@ public class QtActivityDelegate
m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity, m_layout, this);
}
- boolean isUiModeDark(Configuration config)
- {
- return (config.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
- }
-
void handleUiModeChange(int uiMode)
{
// QTBUG-108365
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 d9cfa2f79d..99a1d49d83 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityLoader.java
@@ -4,98 +4,156 @@
package org.qtproject.qt.android;
+import android.annotation.SuppressLint;
import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.Bundle;
-import android.view.Window;
+import android.util.Base64;
+import android.util.DisplayMetrics;
+import android.util.Log;
-import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
public class QtActivityLoader extends QtLoader {
- Activity m_activity;
+ private final Activity m_activity;
public QtActivityLoader(Activity activity)
{
super(activity);
m_activity = activity;
- }
- @Override
- protected void finish() {
- m_activity.finish();
+ extractContextMetaData();
}
@Override
- protected String getTitle() {
- return (String) m_activity.getTitle();
+ protected void initContextInfo() {
+ try {
+ m_contextInfo = m_context.getPackageManager().getActivityInfo(
+ ((Activity)m_context).getComponentName(), PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ finish();
+ }
}
- @Override
- protected void runOnUiThread(Runnable run) {
- m_activity.runOnUiThread(run);
+ private void showErrorDialog() {
+ Resources resources = m_activity.getResources();
+ String packageName = m_activity.getPackageName();
+ AlertDialog errorDialog = new AlertDialog.Builder(m_activity).create();
+ @SuppressLint("DiscouragedApi") int id = resources.getIdentifier(
+ "fatal_error_msg", "string", packageName);
+ errorDialog.setMessage(resources.getString(id));
+ errorDialog.setButton(Dialog.BUTTON_POSITIVE, resources.getString(android.R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+ errorDialog.show();
}
@Override
- Intent getIntent() {
- return m_activity.getIntent();
+ protected void finish() {
+ showErrorDialog();
+ m_activity.finish();
}
- public void onCreate(Bundle savedInstanceState) {
+ @Override
+ protected void initStaticClassesImpl(Class<?> initClass, Object staticInitDataObject) {
try {
- m_contextInfo = m_activity.getPackageManager().getActivityInfo(
- m_activity.getComponentName(), PackageManager.GET_META_DATA);
- int theme = ((ActivityInfo)m_contextInfo).getThemeResource();
- for (Field f : Class.forName("android.R$style").getDeclaredFields()) {
- if (f.getInt(null) == theme) {
- QT_ANDROID_THEMES = new String[] {f.getName()};
- QT_ANDROID_DEFAULT_THEME = f.getName();
- break;
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- finish();
- return;
+ Method m = initClass.getMethod("setActivity", Activity.class, Object.class);
+ m.invoke(staticInitDataObject, m_activity, this);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ Log.d(QtTAG, "Class " + initClass.getName() + " does not implement setActivity method");
}
+ }
- try {
- m_activity.setTheme(Class.forName("android.R$style").getDeclaredField(
- QT_ANDROID_DEFAULT_THEME).getInt(null));
- } catch (Exception e) {
- e.printStackTrace();
+ private String getDecodedUtfString(String str)
+ {
+ byte[] decodedExtraEnvVars = Base64.decode(str, Base64.DEFAULT);
+ return new String(decodedExtraEnvVars, StandardCharsets.UTF_8);
+ }
+
+ int getAppIconSize()
+ {
+ int size = m_activity.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+ if (size < 36 || size > 512) { // check size sanity
+ DisplayMetrics metrics = new DisplayMetrics();
+ m_activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ size = metrics.densityDpi / 10 * 3;
+ if (size < 36)
+ size = 36;
+
+ if (size > 512)
+ size = 512;
}
- m_activity.requestWindowFeature(Window.FEATURE_ACTION_BAR);
- if (QtNative.isStarted()) {
- boolean updated = QtNative.activityDelegate().updateActivity(m_activity);
- if (!updated) {
- // could not update the activity so restart the application
- Intent intent = Intent.makeRestartActivityTask(m_activity.getComponentName());
- m_activity.startActivity(intent);
- QtNative.quitApp();
- Runtime.getRuntime().exit(0);
- }
+ return size;
+ }
+
+ private void setupStyleExtraction()
+ {
+ int displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
+ setEnvironmentVariable("QT_ANDROID_THEME_DISPLAY_DPI", String.valueOf(displayDensity));
- ((QtActivityBase)m_activity).getActivityDelegate().onCreate(savedInstanceState);
+ String extractOption = getMetaData("android.app.extract_android_style");
+ if (extractOption.equals("full"))
+ setEnvironmentVariable("QT_USE_ANDROID_NATIVE_STYLE", String.valueOf(1));
+
+ String stylePath = ExtractStyle.setup(m_activity, extractOption, displayDensity);
+ setEnvironmentVariable("ANDROID_STYLE_PATH", stylePath);
+ }
+
+ @Override
+ protected void extractContextMetaData()
+ {
+ super.extractContextMetaData();
+ setEnvironmentVariable("QT_ANDROID_APP_ICON_SIZE", String.valueOf(getAppIconSize()));
+
+ setupStyleExtraction();
+
+ Intent intent = m_activity.getIntent();
+ if (intent == null) {
+ Log.w(QtTAG, "Null Intent from the current Activity.");
return;
}
- m_displayDensity = m_activity.getResources().getDisplayMetrics().densityDpi;
-
- ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME
- + "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t";
+ String intentArgs = intent.getStringExtra("applicationArguments");
+ if (intentArgs != null)
+ setApplicationParameters(intentArgs);
- if (m_contextInfo.metaData.containsKey("android.app.background_running")
- && m_contextInfo.metaData.getBoolean("android.app.background_running")) {
- ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=0\t";
- } else {
- ENVIRONMENT_VARIABLES += "QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED=1\t";
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ Log.w(QtTAG, "Null extras from the Activity's intent.");
+ return;
}
- startApp(true);
+ int flags = m_activity.getApplicationInfo().flags;
+ boolean isDebuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ if (isDebuggable) {
+ if (extras.containsKey("extraenvvars")) {
+ String extraEnvVars = extras.getString("extraenvvars");
+ setEnvironmentVariables(getDecodedUtfString(extraEnvVars));
+ }
+
+ if (extras.containsKey("extraappparams")) {
+ String extraAppParams = extras.getString("extraappparams");
+ setApplicationParameters(getDecodedUtfString(extraAppParams));
+ }
+ } else {
+ Log.d(QtNative.QtTAG, "Not in debug mode! It is not allowed to use extra arguments " +
+ "in non-debug mode.");
+ }
}
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtConstants.java b/src/android/jar/src/org/qtproject/qt/android/QtConstants.java
index a1026f515a..c3dfc3d9f9 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtConstants.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtConstants.java
@@ -4,18 +4,6 @@
package org.qtproject.qt.android;
public class QtConstants {
- public static final String DEX_PATH_KEY = "dex.path";
- public static final String LIB_PATH_KEY = "lib.path";
- public static final String LOADER_CLASS_NAME_KEY = "loader.class.name";
- public static final String NATIVE_LIBRARIES_KEY = "native.libraries";
- public static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
- public static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
- public static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
- public static final String MAIN_LIBRARY_KEY = "main.library";
- public static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
- public static final String EXTRACT_STYLE_KEY = "extract.android.style";
- public static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
-
// Application states
public static class ApplicationState {
public static final int ApplicationSuspended = 0x0;
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 e7f260fd51..7570299e1a 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtLoader.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtLoader.java
@@ -4,12 +4,10 @@
package org.qtproject.qt.android;
-import android.app.AlertDialog;
-import android.app.Dialog;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.DialogInterface;
-import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.res.Resources;
import android.os.Build;
@@ -17,318 +15,487 @@ import android.os.Bundle;
import android.util.Log;
import java.io.File;
-import java.io.FileOutputStream;
-import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
-import java.util.List;
+import java.util.Objects;
import dalvik.system.DexClassLoader;
-import static org.qtproject.qt.android.QtConstants.*;
-
public abstract class QtLoader {
- static String QtTAG = "Qt";
-
- // These parameters matter in case of deploying application as system (embedded into firmware)
- public static final String SYSTEM_LIB_PATH = "/system/lib/";
+ protected static final String QtTAG = "QtLoader";
- public String APPLICATION_PARAMETERS = null;
- public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1";
- public String[] QT_ANDROID_THEMES = null;
- public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme.
+ private final Resources m_resources;
+ private final String m_packageName;
+ private String m_preferredAbi = null;
+ private String m_nativeLibrariesDir = null;
+ private ClassLoader m_classLoader;
- public ArrayList<String> m_qtLibs = null; // required qt libs
- public int m_displayDensity = -1;
- private ContextWrapper m_context;
+ protected final ContextWrapper m_context;
protected ComponentInfo m_contextInfo;
+ protected String m_mainLib;
+ protected ArrayList<String> m_applicationParameters = new ArrayList<>();
+ protected HashMap<String, String> m_environmentVariables = new HashMap<>();
+
+ /**
+ * Sets and initialize the basic pieces.
+ * Initializes the class loader since it doesn't rely on anything
+ * other than the context.
+ * Also, we can already initialize the static classes contexts here.
+ **/
public QtLoader(ContextWrapper context) {
m_context = context;
+ m_resources = m_context.getResources();
+ m_packageName = m_context.getPackageName();
+
+ initClassLoader();
+ initStaticClasses();
+ initContextInfo();
}
- public static void setQtTAG(String tag)
- {
- QtTAG = tag;
+ /**
+ * Initializes the context info instance which is used to retrieve
+ * the app metadata from the AndroidManifest.xml or other xml resources.
+ * Some values are dependent on the context being an Activity or Service.
+ **/
+ abstract protected void initContextInfo();
+
+ /**
+ * Implements the logic for finish the extended context, mostly called
+ * in error cases.
+ **/
+ abstract protected void finish();
+
+ /**
+ * Context specific (Activity/Service) implementation for static classes.
+ * The Activity and Service loaders call different methods.
+ **/
+ abstract protected void initStaticClassesImpl(Class<?> initClass, Object staticInitDataObject);
+
+ /**
+ * Extract the common metadata in the base implementation. And the extended methods
+ * call context specific metadata extraction. This also sets the various environment
+ * variables and application parameters.
+ **/
+ protected void extractContextMetaData() {
+ setEnvironmentVariable("QT_ANDROID_FONTS", "Roboto;Droid Sans;Droid Sans Fallback");
+ String monospaceFonts = "Droid Sans Mono;Droid Sans;Droid Sans Fallback";
+ setEnvironmentVariable("QT_ANDROID_FONTS_MONOSPACE", monospaceFonts);
+ setEnvironmentVariable("QT_ANDROID_FONTS_SERIF", "Droid Serif");
+ setEnvironmentVariable("HOME", m_context.getFilesDir().getAbsolutePath());
+ setEnvironmentVariable("TMPDIR", m_context.getCacheDir().getAbsolutePath());
+ String backgroundRunning = getMetaData("android.app.background_running");
+ setEnvironmentVariable("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED", backgroundRunning);
+ setEnvironmentVariable("QTRACE_LOCATION", getMetaData("android.app.trace_location"));
+ setApplicationParameters(getMetaData("android.app.arguments"));
}
- // Implement in subclass
- protected void finish() {}
+ private ArrayList<String> preferredAbiLibs(String[] libs) {
+ HashMap<String, ArrayList<String>> abiLibs = new HashMap<>();
+ for (String lib : libs) {
+ String[] archLib = lib.split(";", 2);
+ if (m_preferredAbi != null && !archLib[0].equals(m_preferredAbi))
+ continue;
+ if (!abiLibs.containsKey(archLib[0]))
+ abiLibs.put(archLib[0], new ArrayList<>());
+ Objects.requireNonNull(abiLibs.get(archLib[0])).add(archLib[1]);
+ }
+
+ if (m_preferredAbi != null) {
+ if (abiLibs.containsKey(m_preferredAbi)) {
+ return abiLibs.get(m_preferredAbi);
+ }
+ return new ArrayList<>();
+ }
- protected String getTitle() {
- return "Qt";
+ for (String abi : Build.SUPPORTED_ABIS) {
+ if (abiLibs.containsKey(abi)) {
+ m_preferredAbi = abi;
+ return abiLibs.get(abi);
+ }
+ }
+ return new ArrayList<>();
}
- protected void runOnUiThread(Runnable run) {
- run.run();
+ private void initStaticClasses() {
+ for (String className : getStaticInitClasses()) {
+ try {
+ Class<?> initClass = m_classLoader.loadClass(className);
+ Object staticInitDataObject = initClass.newInstance(); // create an instance
+ initStaticClassesImpl(initClass, staticInitDataObject);
+
+ // For modules that don't need/have setActivity/setService
+ Method m = initClass.getMethod("setContext", Context.class);
+ m.invoke(staticInitDataObject, m_context);
+ } catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
+ NoSuchMethodException | InvocationTargetException e) {
+ Log.d(QtTAG, "Class " + className + " does not implement setContext method");
+ }
+ }
}
- Intent getIntent()
+ /**
+ * Initialize the class loader instance and sets it via QtNative.
+ * This would also be used by QJniObject API.
+ **/
+ private void initClassLoader()
{
- return null;
+ // directory where optimized DEX files should be written.
+ String outDexPath = m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath();
+ m_classLoader = new DexClassLoader("", outDexPath, null, m_context.getClassLoader());
+ QtNative.setClassLoader(m_classLoader);
}
- // Implement in subclass
- private final List<String> supportedAbis = Arrays.asList(Build.SUPPORTED_ABIS);
- private String preferredAbi = null;
+ /**
+ * Returns the context's main library absolute path,
+ * or null if the library hasn't been loaded yet.
+ **/
+ public String getMainLibrary() {
+ return m_mainLib;
+ }
+
+ /**
+ * Returns the context's parameters that are used when calling
+ * the main library's main() function. This is assembled from
+ * a combination of static values and also metadata dependent values.
+ **/
+ public ArrayList<String> getApplicationParameters() {
+ return m_applicationParameters;
+ }
- private ArrayList<String> prefferedAbiLibs(String []libs)
+ /**
+ * Adds a parameter string to the internal array list of parameters.
+ **/
+ public void setApplicationParameter(String param)
{
- HashMap<String, ArrayList<String>> abisLibs = new HashMap<>();
- for (String lib : libs) {
- String[] archLib = lib.split(";", 2);
- if (preferredAbi != null && !archLib[0].equals(preferredAbi))
- continue;
- if (!abisLibs.containsKey(archLib[0]))
- abisLibs.put(archLib[0], new ArrayList<String>());
- abisLibs.get(archLib[0]).add(archLib[1]);
- }
+ if (param == null || param.isEmpty())
+ return;
+ m_applicationParameters.add(param);
+ }
- if (preferredAbi != null) {
- if (abisLibs.containsKey(preferredAbi)) {
- return abisLibs.get(preferredAbi);
- }
- return new ArrayList<String>();
- }
+ /**
+ * Adds a list of parameters to the internal array list of parameters.
+ * This expects the parameters separated by '\t'.
+ **/
+ public void setApplicationParameters(String params)
+ {
+ if (params == null || params.isEmpty())
+ return;
- for (String abi: supportedAbis) {
- if (abisLibs.containsKey(abi)) {
- preferredAbi = abi;
- return abisLibs.get(abi);
- }
- }
- return new ArrayList<String>();
+ for (String param : params.split("\t"))
+ setApplicationParameter(param);
}
- // this function is used to load and start the loader
- private void loadApplication(Bundle loaderParams)
+ /**
+ * Sets a single key/value environment variable pair.
+ **/
+ public void setEnvironmentVariable(String key, String value)
{
- final Resources resources = m_context.getResources();
- final String packageName = m_context.getPackageName();
try {
- // add all bundled Qt libs to loader params
- int id = resources.getIdentifier("bundled_libs", "array", packageName);
- final String[] bundledLibs = resources.getStringArray(id);
- ArrayList<String> libs = new ArrayList<>(prefferedAbiLibs(bundledLibs));
-
- String libName = null;
- if (m_contextInfo.metaData.containsKey("android.app.lib_name")) {
- libName = m_contextInfo.metaData.getString("android.app.lib_name") + "_" + preferredAbi;
- loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
- }
-
- loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs);
-
- // load and start QtLoader class
- DexClassLoader classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files
- m_context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), // directory where optimized DEX files should be written.
- loaderParams.containsKey(LIB_PATH_KEY) ? loaderParams.getString(LIB_PATH_KEY) : null, // libs folder (if exists)
- m_context.getClassLoader()); // parent loader
-
- 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("");
- }
+ android.system.Os.setenv(key, value, true);
+ m_environmentVariables.put(key, value);
} catch (Exception e) {
+ Log.e(QtTAG, "Could not set environment variable:" + key + "=" + value);
e.printStackTrace();
- AlertDialog errorDialog = new AlertDialog.Builder(m_context).create();
- int id = resources.getIdentifier("fatal_error_msg", "string",
- packageName);
- errorDialog.setMessage(resources.getString(id));
- errorDialog.setButton(Dialog.BUTTON_POSITIVE,
- resources.getString(android.R.string.ok),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- errorDialog.show();
}
}
- public void startApp(final boolean firstStart)
+ /**
+ * Sets a list of keys/values string to as environment variables.
+ * This expects the key/value to be separated by '=', and parameters
+ * to be separated by '\t'.
+ * Ex: key1=val1\tkey2=val2\tkey3=val3
+ **/
+ public void setEnvironmentVariables(String environmentVariables)
{
- try {
- final Resources resources = m_context.getResources();
- final String packageName = m_context.getPackageName();
- int id = resources.getIdentifier("qt_libs", "array", packageName);
- m_qtLibs = prefferedAbiLibs(resources.getStringArray(id));
-
- id = resources.getIdentifier("use_local_qt_libs", "string", packageName);
- final int useLocalLibs = Integer.parseInt(resources.getString(id));
-
- if (useLocalLibs == 1) {
- ArrayList<String> libraryList = new ArrayList<>();
- String libsDir = null;
- String bundledLibsDir = null;
-
- id = resources.getIdentifier("bundle_local_qt_libs", "string", packageName);
- final int bundleLocalLibs = Integer.parseInt(resources.getString(id));
- if (bundleLocalLibs == 0) {
- String systemLibsPrefix;
- final String systemLibsKey = "android.app.system_libs_prefix";
- // First check if user has provided system libs prefix in AndroidManifest
- if (m_contextInfo.applicationInfo.metaData != null &&
- m_contextInfo.applicationInfo.metaData.containsKey(systemLibsKey)) {
- systemLibsPrefix = m_contextInfo.applicationInfo.metaData.getString(systemLibsKey);
- } else {
- // If not, check if it's provided by androiddeployqt in libs.xml
- id = resources.getIdentifier("system_libs_prefix","string",
- packageName);
- systemLibsPrefix = resources.getString(id);
- }
- if (systemLibsPrefix.isEmpty()) {
- systemLibsPrefix = SYSTEM_LIB_PATH;
- Log.e(QtTAG, "It looks like app deployed using Unbundled "
- + "deployment. It may be necessary to specify path to directory "
- + "where Qt libraries are installed using either "
- + "android.app.system_libs_prefix metadata variable in your "
- + "AndroidManifest.xml or QT_ANDROID_SYSTEM_LIBS_PATH in your "
- + "CMakeLists.txt");
- Log.e(QtTAG, "Using " + SYSTEM_LIB_PATH + " as default path");
- }
+ if (environmentVariables == null || environmentVariables.isEmpty())
+ return;
- File systemLibraryDir = new File(systemLibsPrefix);
- if (systemLibraryDir.exists() && systemLibraryDir.isDirectory()
- && systemLibraryDir.list().length > 0) {
- libsDir = systemLibsPrefix;
- bundledLibsDir = systemLibsPrefix;
- } else {
- Log.e(QtTAG,
- "System library directory " + systemLibsPrefix
- + " does not exist or is empty.");
- }
- } else {
- String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/";
- File nativeLibraryDir = new File(nativeLibraryPrefix);
- if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) {
- libsDir = nativeLibraryPrefix;
- bundledLibsDir = nativeLibraryPrefix;
- } else {
- Log.e(QtTAG,
- "Native library directory " + nativeLibraryPrefix
- + " does not exist or is empty.");
- }
+ for (String variable : environmentVariables.split("\t")) {
+ String[] keyValue = variable.split("=", 2);
+ if (keyValue.length < 2 || keyValue[0].isEmpty())
+ continue;
+
+ setEnvironmentVariable(keyValue[0], keyValue[1]);
+ }
+ }
+
+ /**
+ * Parses the native libraries dir. If the libraries are part of the APK,
+ * the path is set to the APK extracted libs path.
+ * Otherwise, it looks for the system level dir, that's either set in the Manifest,
+ * the deployment libs.xml.
+ * If none of the above are valid, it falls back to predefined system path.
+ **/
+ private void parseNativeLibrariesDir() {
+ if (isBundleQtLibs()) {
+ String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/";
+ File nativeLibraryDir = new File(nativeLibraryPrefix);
+ if (nativeLibraryDir.exists()) {
+ String[] list = nativeLibraryDir.list();
+ if (nativeLibraryDir.isDirectory() && list != null && list.length > 0) {
+ m_nativeLibrariesDir = nativeLibraryPrefix;
}
+ }
+ } else {
+ // First check if user has provided system libs prefix in AndroidManifest
+ String systemLibsPrefix = getApplicationMetaData("android.app.system_libs_prefix");
+
+ // If not, check if it's provided by androiddeployqt in libs.xml
+ if (systemLibsPrefix.isEmpty())
+ systemLibsPrefix = getSystemLibsPrefix();
+
+ if (systemLibsPrefix.isEmpty()) {
+ final String SYSTEM_LIB_PATH = "/system/lib/";
+ systemLibsPrefix = SYSTEM_LIB_PATH;
+ Log.e(QtTAG, "Using " + SYSTEM_LIB_PATH + " as default libraries path. "
+ + "It looks like the app is deployed using Unbundled "
+ + "deployment. It may be necessary to specify the path to "
+ + "the directory where Qt libraries are installed using either "
+ + "android.app.system_libs_prefix metadata variable in your "
+ + "AndroidManifest.xml or QT_ANDROID_SYSTEM_LIBS_PATH in your "
+ + "CMakeLists.txt");
+ }
- if (libsDir == null)
- throw new Exception("");
+ File systemLibraryDir = new File(systemLibsPrefix);
+ String[] list = systemLibraryDir.list();
+ if (systemLibraryDir.exists()) {
+ if (systemLibraryDir.isDirectory() && list != null && list.length > 0)
+ m_nativeLibrariesDir = systemLibsPrefix;
+ else
+ Log.e(QtTAG, "System library directory " + systemLibsPrefix + " is empty.");
+ } else {
+ Log.e(QtTAG, "System library directory " + systemLibsPrefix + " does not exist.");
+ }
+ }
+ if (m_nativeLibrariesDir != null && !m_nativeLibrariesDir.endsWith("/"))
+ m_nativeLibrariesDir += "/";
+ }
- if (m_qtLibs != null) {
- String libPrefix = libsDir + "lib";
- for (String lib : m_qtLibs)
- libraryList.add(libPrefix + lib + ".so");
- }
+ /**
+ * Returns the application level metadata.
+ **/
+ private String getApplicationMetaData(String key) {
+ if (m_contextInfo == null)
+ return "";
- id = resources.getIdentifier("load_local_libs", "array", packageName);
- ArrayList<String> localLibs = prefferedAbiLibs(resources.getStringArray(id));
- for (String libs : localLibs) {
- for (String lib : libs.split(":")) {
- if (!lib.isEmpty())
- libraryList.add(libsDir + lib);
- }
- }
- if (bundledLibsDir != null) {
- ENVIRONMENT_VARIABLES += "\tQT_PLUGIN_PATH=" + bundledLibsDir;
- ENVIRONMENT_VARIABLES += "\tQML_PLUGIN_PATH=" + bundledLibsDir;
- }
+ ApplicationInfo applicationInfo = m_contextInfo.applicationInfo;
+ if (applicationInfo == null)
+ return "";
- Bundle loaderParams = new Bundle();
- loaderParams.putString(DEX_PATH_KEY, new String());
+ Bundle metadata = applicationInfo.metaData;
+ if (metadata == null || !metadata.containsKey(key))
+ return "";
- id = resources.getIdentifier("static_init_classes", "string", packageName);
- loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, resources.getString(id)
- .split(":"));
+ return metadata.getString(key);
+ }
- loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList);
+ /**
+ * Returns the context level metadata.
+ **/
+ protected String getMetaData(String key) {
+ if (m_contextInfo == null)
+ return "";
+ Bundle metadata = m_contextInfo.metaData;
+ if (metadata == null || !metadata.containsKey(key))
+ return "";
- String themePath = m_context.getApplicationInfo().dataDir + "/qt-reserved-files/android-style/";
- String stylePath = themePath + m_displayDensity + "/";
+ return metadata.getString(key);
+ }
- String extractOption = "default";
- if (m_contextInfo.metaData.containsKey("android.app.extract_android_style")) {
- extractOption = m_contextInfo.metaData.getString("android.app.extract_android_style");
- if (!extractOption.equals("default") && !extractOption.equals("full") && !extractOption.equals("minimal") && !extractOption.equals("none")) {
- Log.e(QtTAG, "Invalid extract_android_style option \"" + extractOption + "\", defaulting to \"default\"");
- extractOption = "default";
- }
- }
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getQtLibrariesList() {
+ int id = m_resources.getIdentifier("qt_libs", "array", m_packageName);
+ return preferredAbiLibs(m_resources.getStringArray(id));
+ }
- // QTBUG-69810: The extraction code will trigger compatibility warnings on Android SDK version >= 28
- // when the target SDK version is set to something lower then 28, so default to "none" and issue a warning
- // if that is the case.
- if (extractOption.equals("default")) {
- final int targetSdkVersion = m_context.getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion < 28 && Build.VERSION.SDK_INT >= 28) {
- Log.e(QtTAG, "extract_android_style option set to \"none\" when targetSdkVersion is less then 28");
- extractOption = "none";
- }
- }
+ @SuppressLint("DiscouragedApi")
+ private boolean useLocalQtLibs() {
+ int id = m_resources.getIdentifier("use_local_qt_libs", "string", m_packageName);
+ return Integer.parseInt(m_resources.getString(id)) == 1;
+ }
- if (!extractOption.equals("none")) {
- loaderParams.putString(EXTRACT_STYLE_KEY, stylePath);
- loaderParams.putBoolean(EXTRACT_STYLE_MINIMAL_KEY, extractOption.equals("minimal"));
- }
+ @SuppressLint("DiscouragedApi")
+ private boolean isBundleQtLibs() {
+ int id = m_resources.getIdentifier("bundle_local_qt_libs", "string", m_packageName);
+ return Integer.parseInt(m_resources.getString(id)) == 1;
+ }
- if (extractOption.equals("full"))
- ENVIRONMENT_VARIABLES += "\tQT_USE_ANDROID_NATIVE_STYLE=1";
+ @SuppressLint("DiscouragedApi")
+ private String getSystemLibsPrefix() {
+ int id = m_resources.getIdentifier("system_libs_prefix", "string", m_packageName);
+ return m_resources.getString(id);
+ }
- ENVIRONMENT_VARIABLES += "\tANDROID_STYLE_PATH=" + stylePath;
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getLocalLibrariesList() {
+ int id = m_resources.getIdentifier("load_local_libs", "array", m_packageName);
+ return preferredAbiLibs(m_resources.getStringArray(id));
+ }
- if (m_contextInfo.metaData.containsKey("android.app.trace_location")) {
- String loc = m_contextInfo.metaData.getString("android.app.trace_location");
- ENVIRONMENT_VARIABLES += "\tQTRACE_LOCATION="+loc;
- }
+ @SuppressLint("DiscouragedApi")
+ private ArrayList<String> getStaticInitClasses() {
+ int id = m_resources.getIdentifier("static_init_classes", "string", m_packageName);
+ String[] classes = m_resources.getString(id).split(":");
+ ArrayList<String> finalClasses = new ArrayList<>();
+ for (String element : classes) {
+ if (!element.isEmpty()) {
+ finalClasses.add(element);
+ }
+ }
+ return finalClasses;
+ }
- loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES);
+ @SuppressLint("DiscouragedApi")
+ private String[] getBundledLibs() {
+ int id = m_resources.getIdentifier("bundled_libs", "array", m_packageName);
+ return m_resources.getStringArray(id);
+ }
- String appParams = null;
- if (APPLICATION_PARAMETERS != null)
- appParams = APPLICATION_PARAMETERS;
+ /**
+ * Loads all Qt native bundled libraries and main library.
+ **/
+ public void loadQtLibraries() {
+ if (!useLocalQtLibs()) {
+ Log.w(QtTAG, "Use local Qt libs is false");
+ finish();
+ return;
+ }
- Intent intent = getIntent();
- if (intent != null) {
- String parameters = intent.getStringExtra("applicationArguments");
- if (parameters != null)
- if (appParams == null)
- appParams = parameters;
- else
- appParams += '\t' + parameters;
- }
+ if (m_nativeLibrariesDir == null)
+ parseNativeLibrariesDir();
- if (m_contextInfo.metaData.containsKey("android.app.arguments")) {
- String parameters = m_contextInfo.metaData.getString("android.app.arguments");
- if (appParams == null)
- appParams = parameters;
- else
- appParams += '\t' + parameters;
- }
+ if (m_nativeLibrariesDir == null || m_nativeLibrariesDir.isEmpty()) {
+ Log.e(QtTAG, "The native libraries directory is null or empty");
+ finish();
+ return;
+ }
+
+ setEnvironmentVariable("QT_PLUGIN_PATH", m_nativeLibrariesDir);
+ setEnvironmentVariable("QML_PLUGIN_PATH", m_nativeLibrariesDir);
- if (appParams != null)
- loaderParams.putString(APPLICATION_PARAMETERS_KEY, appParams);
+ // Load native Qt APK libraries
+ ArrayList<String> nativeLibraries = getQtLibrariesList();
+ nativeLibraries.addAll(getLocalLibrariesList());
- loadApplication(loaderParams);
- return;
+ if (!loadLibraries(nativeLibraries)) {
+ Log.e(QtTAG, "Loading Qt native libraries failed");
+ finish();
+ return;
+ }
+
+ // add all bundled Qt libs to loader params
+ ArrayList<String> bundledLibraries = new ArrayList<>(preferredAbiLibs(getBundledLibs()));
+ if (!loadLibraries(bundledLibraries)) {
+ Log.e(QtTAG, "Loading Qt bundled libraries failed");
+ finish();
+ return;
+ }
+
+ // Load main lib
+ if (!loadMainLibrary(getMetaData("android.app.lib_name") + "_" + m_preferredAbi)) {
+ Log.e(QtTAG, "Loading main library failed");
+ finish();
+ }
+ }
+
+ // Loading libraries using System.load() uses full lib paths
+ @SuppressLint("UnsafeDynamicallyLoadedCode")
+ private String loadLibraryHelper(String library)
+ {
+ String loadedLib = null;
+ try {
+ File libFile = new File(library);
+ if (libFile.exists()) {
+ System.load(library);
+ loadedLib = library;
+ } else {
+ Log.i(QtTAG, "Can't find '" + library + "'");
}
} catch (Exception e) {
- Log.e(QtTAG, "Can't create main activity", e);
+ Log.i(QtTAG, "Can't load '" + library + "'", e);
}
+
+ return loadedLib;
+ }
+
+ /**
+ * Returns an array with absolute library paths from a list of file names only.
+ **/
+ private ArrayList<String> getLibrariesFullPaths(final ArrayList<String> libraries)
+ {
+ if (libraries == null)
+ return null;
+
+ ArrayList<String> absolutePathLibraries = new ArrayList<>();
+ for (String libName : libraries) {
+ if (!libName.startsWith("lib"))
+ libName = "lib" + libName;
+ if (!libName.endsWith(".so"))
+ libName = libName + ".so";
+ File file = new File(m_nativeLibrariesDir + libName);
+ absolutePathLibraries.add(file.getAbsolutePath());
+ }
+
+ return absolutePathLibraries;
+ }
+
+ /**
+ * Loads the main library.
+ * Returns true if loading was successful, and sets the absolute
+ * path to the main library. Otherwise, returns false and the path
+ * to the main library is null.
+ **/
+ private boolean loadMainLibrary(String mainLibName)
+ {
+ ArrayList<String> oneEntryArray = new ArrayList<>(Collections.singletonList(mainLibName));
+ String mainLibPath = getLibrariesFullPaths(oneEntryArray).get(0);
+ final boolean[] success = {true};
+ QtNative.getQtThread().run(new Runnable() {
+ @Override
+ public void run() {
+ m_mainLib = loadLibraryHelper(mainLibPath);
+ if (m_mainLib == null)
+ success[0] = false;
+ }
+ });
+
+ return success[0];
+ }
+
+ /**
+ * Loads a list of libraries.
+ * Returns true if all libraries were loaded successfully,
+ * and false if any library failed. Stops loading at the first failure.
+ **/
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean loadLibraries(final ArrayList<String> libraries)
+ {
+ if (libraries == null)
+ return false;
+
+ ArrayList<String> fullPathLibs = getLibrariesFullPaths(libraries);
+
+ final boolean[] success = {true};
+ QtNative.getQtThread().run(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < fullPathLibs.size(); ++i) {
+ String libName = fullPathLibs.get(i);
+ if (loadLibraryHelper(libName) == null) {
+ success[0] = false;
+ break;
+ }
+ }
+ }
+ });
+
+ return success[0];
}
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
index dff4a27a36..f5df382b00 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
@@ -215,102 +215,8 @@ public class QtNative
}
}
- // this method loads full path libs
- public static void loadQtLibraries(final ArrayList<String> libraries)
- {
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- if (libraries == null)
- return;
- for (String libName : libraries) {
- try {
- File f = new File(libName);
- if (f.exists())
- System.load(libName);
- else
- Log.i(QtTAG, "Can't find '" + libName + "'");
- } catch (SecurityException e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- }
- }
- }
- });
- }
-
- // this method loads bundled libs by name.
- public static void loadBundledLibraries(final ArrayList<String> libraries, final String nativeLibraryDir)
- {
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- if (libraries == null)
- return;
-
- for (String libName : libraries) {
- try {
- String libNameTemplate = "lib" + libName + ".so";
- File f = new File(nativeLibraryDir + libNameTemplate);
- if (!f.exists()) {
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
- try {
- ApplicationInfo info = getContext().getApplicationContext().getPackageManager()
- .getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
- String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
- if (info.metaData.containsKey("android.app.system_libs_prefix"))
- systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
- f = new File(systemLibraryDir + libNameTemplate);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- if (f.exists())
- System.load(f.getAbsolutePath());
- else
- Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
- } catch (Exception e) {
- Log.i(QtTAG, "Can't load '" + libName + "'", e);
- }
- }
- }
- });
- }
-
- public static String loadMainLibrary(final String mainLibrary, final String nativeLibraryDir)
- {
- final String[] res = new String[1];
- res[0] = null;
- m_qtThread.run(new Runnable() {
- @Override
- public void run() {
- try {
- String mainLibNameTemplate = "lib" + mainLibrary + ".so";
- File f = new File(nativeLibraryDir + mainLibNameTemplate);
- if (!f.exists()) {
- try {
- ApplicationInfo info = getContext().getApplicationContext().getPackageManager()
- .getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
- String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
- if (info.metaData.containsKey("android.app.system_libs_prefix"))
- systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
- f = new File(systemLibraryDir + mainLibNameTemplate);
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
- }
- if (!f.exists())
- return;
- System.load(f.getAbsolutePath());
- res[0] = f.getAbsolutePath();
- } catch (Exception e) {
- Log.e(QtTAG, "Can't load '" + mainLibrary + "'", e);
- }
- }
- });
- return res[0];
+ static QtThread getQtThread() {
+ return m_qtThread;
}
public static void setActivity(Activity qtMainActivity, QtActivityDelegate qtActivityDelegate)
@@ -430,17 +336,12 @@ public class QtNative
return new Size(bounds.width(), bounds.height());
}
- public static boolean startApplication(String params, String mainLib) throws Exception
+ public static boolean startApplication(ArrayList<String> params, String mainLib)
{
- if (params == null)
- params = "-platform\tandroid";
-
final boolean[] res = new boolean[1];
- res[0] = false;
synchronized (m_mainActivityMutex) {
- if (params.length() > 0 && !params.startsWith("\t"))
- params = "\t" + params;
- final String qtParams = mainLib + params;
+ String paramsStr = String.join("\t", params);
+ final String qtParams = mainLib + "\t" + paramsStr;
m_qtThread.run(new Runnable() {
@Override
public void run() {
@@ -972,40 +873,6 @@ public class QtNative
return res.toArray(new String[res.size()]);
}
- /**
- *Sets a single environment variable
- *
- * returns true if the value was set, false otherwise.
- * in case it cannot set value will log the exception
- **/
- public static void setEnvironmentVariable(String key, String value)
- {
- try {
- android.system.Os.setenv(key, value, true);
- } catch (Exception e) {
- Log.e(QtNative.QtTAG, "Could not set environment variable:" + key + "=" + value);
- e.printStackTrace();
- }
- }
-
- /**
- *Sets multiple environment variables
- *
- * Uses '\t' as divider between variables and '=' between key/value
- * Ex: key1=val1\tkey2=val2\tkey3=val3
- * Note: it assumed that the key cannot have '=' but the value can
- **/
- public static void setEnvironmentVariables(String environmentVariables)
- {
- for (String variable : environmentVariables.split("\t")) {
- String[] keyvalue = variable.split("=", 2);
- if (keyvalue.length < 2 || keyvalue[0].isEmpty())
- continue;
-
- setEnvironmentVariable(keyvalue[0], keyvalue[1]);
- }
- }
-
// screen methods
public static native void setDisplayMetrics(int screenWidthPixels, int screenHeightPixels,
int availableLeftPixels, int availableTopPixels,
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java
index f35db6436a..268a53044f 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceBase.java
@@ -9,28 +9,27 @@ import android.os.IBinder;
import android.util.Log;
public class QtServiceBase extends Service {
+ private QtServiceDelegate m_delegate;
- 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
+ @Override
+ public void onCreate()
+ {
+ super.onCreate();
+
+ m_delegate = new QtServiceDelegate(this);
+
+ // 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();
+ return;
}
- }
- @Override
- public void onCreate()
- {
- super.onCreate();
- onCreateHook();
+ QtServiceLoader loader = new QtServiceLoader(this);
+ loader.loadQtLibraries();
+ QtNative.startApplication(loader.getApplicationParameters(), loader.getMainLibrary());
}
@Override
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 1b6a8c9a8c..9791bf78fc 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceDelegate.java
@@ -5,140 +5,16 @@
package org.qtproject.qt.android;
import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
-import android.graphics.drawable.ColorDrawable;
-import android.net.LocalServerSocket;
-import android.net.LocalSocket;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ResultReceiver;
-import android.text.method.MetaKeyKeyListener;
-import android.util.Base64;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.TypedValue;
-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.Surface;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Objects;
-
-import static org.qtproject.qt.android.QtConstants.*;
public class QtServiceDelegate
{
- private String m_mainLib = null;
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
- if (!loaderParams.containsKey(NATIVE_LIBRARIES_KEY)
- || !loaderParams.containsKey(BUNDLED_LIBRARIES_KEY)) {
- return false;
- }
- m_service = service;
+ // Set native context
QtNative.setService(m_service, this);
- QtNative.setClassLoader(classLoader);
-
- QtNative.setApplicationDisplayMetrics(10, 10, 0, 0, 10, 10, 120, 120, 1.0, 1.0, 60.0f);
-
- if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) {
- for (String className :
- Objects.requireNonNull(loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY))) {
- if (className.length() == 0)
- continue;
- try {
- Class<?> initClass = classLoader.loadClass(className);
- Object staticInitDataObject = initClass.newInstance(); // create an instance
- try {
- Method m = initClass.getMethod("setService", Service.class, Object.class);
- m.invoke(staticInitDataObject, m_service, this);
- } catch (Exception e) {
- Log.d(QtNative.QtTAG,
- "Class " + className + " does not implement setService method");
- }
-
- // For modules that don't need/have setService
- try {
- Method m = initClass.getMethod("setContext", Context.class);
- m.invoke(staticInitDataObject, (Context)m_service);
- } catch (Exception e) {
- e.printStackTrace();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY));
- ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY);
- String nativeLibsDir = QtNativeLibrariesDir.nativeLibrariesDir(m_service);
- QtNative.loadBundledLibraries(libraries, nativeLibsDir);
- m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY);
-
- QtNative.setEnvironmentVariables(loaderParams.getString(ENVIRONMENT_VARIABLES_KEY));
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_MONOSPACE",
- "Droid Sans Mono;Droid Sans;Droid Sans Fallback");
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS_SERIF", "Droid Serif");
- QtNative.setEnvironmentVariable("HOME", m_service.getFilesDir().getAbsolutePath());
- QtNative.setEnvironmentVariable("TMPDIR", m_service.getCacheDir().getAbsolutePath());
- QtNative.setEnvironmentVariable("QT_ANDROID_FONTS",
- "Roboto;Droid Sans;Droid Sans Fallback");
-
- if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY))
- m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY);
- else
- m_applicationParameters = "";
-
- m_mainLib = QtNative.loadMainLibrary(m_mainLib, nativeLibsDir);
- return m_mainLib != null;
- }
-
- public boolean startApplication()
- {
- // start application
- try {
- String nativeLibraryDir = QtNativeLibrariesDir.nativeLibrariesDir(m_service);
- QtNative.startApplication(m_applicationParameters, m_mainLib);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
}
}
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 f6add928e1..b045b9abff 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceLoader.java
@@ -5,35 +5,47 @@
package org.qtproject.qt.android;
import android.app.Service;
-import android.os.Bundle;
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.util.Log;
-import java.security.Provider;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
public class QtServiceLoader extends QtLoader {
- Service m_service;
+ private final Service m_service;
public QtServiceLoader(Service service) {
super(service);
m_service = service;
+
+ extractContextMetaData();
}
- public void onCreate() {
+ @Override
+ protected void initContextInfo() {
try {
- m_contextInfo = m_service.getPackageManager().getServiceInfo(new ComponentName(
- m_service, m_service.getClass()), PackageManager.GET_META_DATA);
+ m_contextInfo = m_service.getPackageManager().getServiceInfo(
+ new ComponentName(m_service, m_service.getClass()),
+ PackageManager.GET_META_DATA);
} catch (Exception e) {
e.printStackTrace();
- m_service.stopSelf();
- return;
+ finish();
}
-
- startApp(true);
}
@Override
protected void finish() {
m_service.stopSelf();
}
+
+ @Override
+ protected void initStaticClassesImpl(Class<?> initClass, Object staticInitDataObject) {
+ try {
+ Method m = initClass.getMethod("setService", Service.class, Object.class);
+ m.invoke(staticInitDataObject, m_service, this);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ Log.d(QtTAG, "Class " + initClass.getName() + " does not implement setService method");
+ }
+ }
}