summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>2013-04-19 12:20:58 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-05-01 11:24:03 +0200
commit94bed3ad148a6ecba91c0a32e0ee52deda3aec9f (patch)
tree384262c65328c164151932ba2641e614bc666806
parent75fba93fc630cf33467702d4c50f0409c6fde4fa (diff)
Make it possible to bundle Qt libraries in Android apk
Add the enablers so that Qt Creator (or another deployment tool) can add a specification in the app's meta data of which libraries are bundled and the Java code required to extract plugins and imports into the required directory structure inside the app's data directory. This is intended to be an alternative to using Ministro for deployment, and the mechanism of extracting libraries on first startup is a work-around for the requirement in Qt of having this directory structure. For Qt 5.2, the approach should be changed to load plugins directly from the app's lib directory and the other files in imports will be bundled as qrcs in the native plugins. Task-number: QTBUG-30751 Change-Id: Ibdb3a672548b4802f9bf3ecd05fc194426ac30e7 Reviewed-by: Paul Olav Tvete <paul.tvete@digia.com>
-rw-r--r--src/android/java/AndroidManifest.xml6
-rw-r--r--src/android/java/READ-THIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt6
-rw-r--r--src/android/java/res/values/libs.xml2
-rw-r--r--src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java116
4 files changed, 126 insertions, 4 deletions
diff --git a/src/android/java/AndroidManifest.xml b/src/android/java/AndroidManifest.xml
index 6a407dcb6e..48fb23ab28 100644
--- a/src/android/java/AndroidManifest.xml
+++ b/src/android/java/AndroidManifest.xml
@@ -11,8 +11,12 @@
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<meta-data android:name="android.app.lib_name" android:value=""/>
+ <!-- Deploy Qt libs as part of package -->
+ <meta-data android:name="android.app.bundle_local_qt_libs" android:value="1"/>
+ <meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
+ <meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
- <meta-data android:name="android.app.use_local_qt_libs" android:value="0"/>
+ <meta-data android:name="android.app.use_local_qt_libs" android:value="1"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value=""/>
<meta-data android:name="android.app.load_local_jars" android:value=""/>
diff --git a/src/android/java/READ-THIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt b/src/android/java/READ-THIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt
new file mode 100644
index 0000000000..978417721f
--- /dev/null
+++ b/src/android/java/READ-THIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt
@@ -0,0 +1,6 @@
+If this package is accessed by Qt Creator, certain changes can be
+overwritten without warning. In particular, Qt Creator may overwrite
+settings which are maintained using its own UI, and files and
+directories containing the string "--Managed_by_Qt_Creator--", as well
+as native libraries whose file names begin with "libQt5" may be
+deleted without warning if they are contained inside this package.
diff --git a/src/android/java/res/values/libs.xml b/src/android/java/res/values/libs.xml
index f47174e3d3..09fa409458 100644
--- a/src/android/java/res/values/libs.xml
+++ b/src/android/java/res/values/libs.xml
@@ -8,4 +8,6 @@
<item>Qt5Core</item>
</array>
<array name="bundled_libs"/>
+ <array name="bundled_in_lib" />
+ <array name="bundled_in_assets" />
</resources>
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java
index dfb638fbb0..22b34fdbef 100644
--- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java
+++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java
@@ -27,6 +27,11 @@
package org.qtproject.qt5.android.bindings;
import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -47,6 +52,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
+import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.net.Uri;
@@ -89,6 +95,8 @@ public class QtActivity extends Activity
private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables";
private static final String APPLICATION_PARAMETERS_KEY = "application.parameters";
private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries";
+ private static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id";
+ private static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id";
private static final String MAIN_LIBRARY_KEY = "main.library";
private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes";
private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level";
@@ -120,6 +128,8 @@ public class QtActivity extends Activity
// note that the android style plugin in Qt 5.1 is not fully functional.
private static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded.
+ private static final int BUFFER_SIZE = 1024;
+
private ActivityInfo m_activityInfo = null; // activity info object, used to access the libs and the strings
private DexClassLoader m_classLoader = null; // loader object
private String[] m_sources = {"https://files.kde.org/necessitas/ministro/android/necessitas/qt5/latest"}; // Make sure you are using ONLY secure locations
@@ -308,6 +318,92 @@ public class QtActivity extends Activity
errorDialog.show();
}
+ static private void copyFile(InputStream inputStream, OutputStream outputStream)
+ throws IOException
+ {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ int count;
+ while ((count = inputStream.read(buffer)) > 0)
+ outputStream.write(buffer, 0, count);
+ }
+
+
+ private void copyAsset(String source, String destination)
+ throws IOException
+ {
+ // Already exists, we don't have to do anything
+ File destinationFile = new File(destination);
+ if (destinationFile.exists())
+ return;
+
+ File parentDirectory = destinationFile.getParentFile();
+ if (!parentDirectory.exists())
+ parentDirectory.mkdirs();
+
+ destinationFile.createNewFile();
+
+ AssetManager assetsManager = getAssets();
+ InputStream inputStream = assetsManager.open(source);
+ OutputStream outputStream = new FileOutputStream(destinationFile);
+ copyFile(inputStream, outputStream);
+ }
+
+ private static void createBundledBinary(String source, String destination)
+ throws IOException
+ {
+ // Already exists, we don't have to do anything
+ File destinationFile = new File(destination);
+ if (destinationFile.exists())
+ return;
+
+ File parentDirectory = destinationFile.getParentFile();
+ if (!parentDirectory.exists())
+ parentDirectory.mkdirs();
+
+ destinationFile.createNewFile();
+
+ InputStream inputStream = new FileInputStream(source);
+ OutputStream outputStream = new FileOutputStream(destinationFile);
+ copyFile(inputStream, outputStream);
+ }
+
+ private void extractBundledPluginsAndImports(String localPrefix)
+ throws IOException
+ {
+ ArrayList<String> libs = new ArrayList<String>();
+
+ {
+ String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY;
+ java.util.Set<String> keys = m_activityInfo.metaData.keySet();
+ if (m_activityInfo.metaData.containsKey(key)) {
+ String[] list = getResources().getStringArray(m_activityInfo.metaData.getInt(key));
+
+ for (String bundledImportBinary : list) {
+ String[] split = bundledImportBinary.split(":");
+ String sourceFileName = localPrefix + "lib/" + split[0];
+ String destinationFileName = localPrefix + split[1];
+ createBundledBinary(sourceFileName, destinationFileName);
+ }
+ }
+ }
+
+ {
+ String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY;
+ if (m_activityInfo.metaData.containsKey(key)) {
+ String[] list = getResources().getStringArray(m_activityInfo.metaData.getInt(key));
+
+ for (String fileName : list) {
+ String[] split = fileName.split(":");
+ String sourceFileName = split[0];
+ String destinationFileName = localPrefix + split[1];
+ copyAsset(sourceFileName, destinationFileName);
+ }
+ }
+
+ }
+ }
+
private void startApp(final boolean firstStart)
{
try {
@@ -328,13 +424,26 @@ public class QtActivity extends Activity
&& m_activityInfo.metaData.getInt("android.app.use_local_qt_libs") == 1) {
ArrayList<String> libraryList = new ArrayList<String>();
+
String localPrefix = "/data/local/tmp/qt/";
if (m_activityInfo.metaData.containsKey("android.app.libs_prefix"))
localPrefix = m_activityInfo.metaData.getString("android.app.libs_prefix");
+ boolean bundlingQtLibs = false;
+ if (m_activityInfo.metaData.containsKey("android.app.bundle_local_qt_libs")
+ && m_activityInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) {
+ localPrefix = getApplicationInfo().dataDir + "/";
+ extractBundledPluginsAndImports(localPrefix);
+ bundlingQtLibs = true;
+ }
+
if (m_qtLibs != null) {
- for (int i=0;i<m_qtLibs.length;i++)
- libraryList.add(localPrefix+"lib/lib"+m_qtLibs[i]+".so");
+ for (int i=0;i<m_qtLibs.length;i++) {
+ libraryList.add(localPrefix
+ + "lib/lib"
+ + m_qtLibs[i]
+ + ".so");
+ }
}
if (m_activityInfo.metaData.containsKey("android.app.load_local_libs")) {
@@ -345,9 +454,10 @@ public class QtActivity extends Activity
}
}
+
String dexPaths = new String();
String pathSeparator = System.getProperty("path.separator", ":");
- if (m_activityInfo.metaData.containsKey("android.app.load_local_jars")) {
+ if (!bundlingQtLibs && m_activityInfo.metaData.containsKey("android.app.load_local_jars")) {
String[] jarFiles = m_activityInfo.metaData.getString("android.app.load_local_jars").split(":");
for (String jar:jarFiles) {
if (jar.length() > 0) {