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) {