summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");
+ }
+ }
}