From 94bed3ad148a6ecba91c0a32e0ee52deda3aec9f Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Fri, 19 Apr 2013 12:20:58 +0200 Subject: 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 --- src/android/java/AndroidManifest.xml | 6 +- ...HIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt | 6 ++ src/android/java/res/values/libs.xml | 2 + .../qtproject/qt5/android/bindings/QtActivity.java | 116 ++++++++++++++++++++- 4 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 src/android/java/READ-THIS-BEFORE-MANUALLY-ADDING-FILES-TO-PACKAGE.txt 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 @@ + + + + - + 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 @@ Qt5Core + + 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 libs = new ArrayList(); + + { + String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; + java.util.Set 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 libraryList = new ArrayList(); + 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 0) { -- cgit v1.2.3