diff options
Diffstat (limited to 'src/android')
31 files changed, 1829 insertions, 1248 deletions
diff --git a/src/android/accessibility/accessibility.pro b/src/android/accessibility/accessibility.pro deleted file mode 100644 index df5846945d..0000000000 --- a/src/android/accessibility/accessibility.pro +++ /dev/null @@ -1,2 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = jar diff --git a/src/android/accessibility/jar/AndroidManifest.xml b/src/android/accessibility/jar/AndroidManifest.xml deleted file mode 100644 index dc8343a55a..0000000000 --- a/src/android/accessibility/jar/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version='1.0' encoding='utf-8'?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - android:versionCode="1" - android:versionName="1.0" - package="org.qtproject.qt5.android.accessibility"> - <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> -</manifest> diff --git a/src/android/accessibility/jar/bundledjar.pro b/src/android/accessibility/jar/bundledjar.pro deleted file mode 100644 index 85ba810310..0000000000 --- a/src/android/accessibility/jar/bundledjar.pro +++ /dev/null @@ -1,3 +0,0 @@ -TARGET = QtAndroidAccessibility-bundled -CONFIG += bundled_jar_file -include(jar.pri) diff --git a/src/android/accessibility/jar/distributedjar.pro b/src/android/accessibility/jar/distributedjar.pro deleted file mode 100644 index d161cf0cf6..0000000000 --- a/src/android/accessibility/jar/distributedjar.pro +++ /dev/null @@ -1,2 +0,0 @@ -TARGET = QtAndroidAccessibility -include(jar.pri) diff --git a/src/android/accessibility/jar/jar.pri b/src/android/accessibility/jar/jar.pri deleted file mode 100644 index 3ae9fed59d..0000000000 --- a/src/android/accessibility/jar/jar.pri +++ /dev/null @@ -1,15 +0,0 @@ -CONFIG += java -DESTDIR = $$[QT_INSTALL_PREFIX/get]/jar -API_VERSION = android-16 - -PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/accessibility - -JAVACLASSPATH += $$PWD/src/ \ - $$DESTDIR/QtAndroid-bundled.jar -JAVASOURCES += \ - $$PATHPREFIX/QtAccessibilityDelegate.java \ - $$PATHPREFIX/QtNativeAccessibility.java - -# install -target.path = $$[QT_INSTALL_PREFIX]/jar -INSTALLS += target
\ No newline at end of file diff --git a/src/android/accessibility/jar/jar.pro b/src/android/accessibility/jar/jar.pro deleted file mode 100644 index 8d19c1b7d6..0000000000 --- a/src/android/accessibility/jar/jar.pro +++ /dev/null @@ -1,2 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS += bundledjar.pro distributedjar.pro diff --git a/src/android/android.pro b/src/android/android.pro index b17dd15cd4..1174084591 100644 --- a/src/android/android.pro +++ b/src/android/android.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs CONFIG += ordered -SUBDIRS = jar java templates accessibility +SUBDIRS = jar java templates diff --git a/src/android/jar/jar.pri b/src/android/jar/jar.pri index a962af18ab..58caacb837 100644 --- a/src/android/jar/jar.pri +++ b/src/android/jar/jar.pri @@ -6,6 +6,8 @@ PATHPREFIX = $$PWD/src/org/qtproject/qt5/android/ JAVACLASSPATH += $$PWD/src/ JAVASOURCES += \ + $$PATHPREFIX/accessibility/QtAccessibilityDelegate.java \ + $$PATHPREFIX/accessibility/QtNativeAccessibility.java \ $$PATHPREFIX/QtActivityDelegate.java \ $$PATHPREFIX/QtEditText.java \ $$PATHPREFIX/QtInputConnection.java \ @@ -15,8 +17,7 @@ JAVASOURCES += \ $$PATHPREFIX/QtNativeLibrariesDir.java \ $$PATHPREFIX/QtSurface.java \ $$PATHPREFIX/ExtractStyle.java \ - $$PATHPREFIX/QtPopupMenu.java \ - $$PATHPREFIX/QtPopupMenu14.java + $$PATHPREFIX/QtServiceDelegate.java # install target.path = $$[QT_INSTALL_PREFIX]/jar diff --git a/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java b/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java index f5dac1fa60..46a443c365 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java +++ b/src/android/jar/src/org/qtproject/qt5/android/ExtractStyle.java @@ -1,31 +1,37 @@ /**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -264,6 +270,7 @@ public class ExtractStyle { Context m_context; final int defaultBackgroundColor; final int defaultTextColor; + final boolean m_minimal; class SimpleJsonWriter { @@ -978,7 +985,7 @@ public class ExtractStyle { public JSONObject getDrawable(Object drawable, String filename, Rect padding) { - if (drawable == null) + if (drawable == null || m_minimal) return null; DrawableCache dc = m_drawableCache.get(filename); @@ -1813,11 +1820,7 @@ public class ExtractStyle { jsonWriter.name("simple_spinner_item").value(extractItemStyle(android.R.layout.simple_spinner_item, "simple_spinner_item", -1)); jsonWriter.name("simple_spinner_dropdown_item").value(extractItemStyle(android.R.layout.simple_spinner_dropdown_item, "simple_spinner_dropdown_item",android.R.style.TextAppearance_Large)); jsonWriter.name("simple_dropdown_item_1line").value(extractItemStyle(android.R.layout.simple_dropdown_item_1line, "simple_dropdown_item_1line",android.R.style.TextAppearance_Large)); - if (Build.VERSION.SDK_INT > 10) { - Class<?> layoutClass = Class.forName("android.R$layout"); - int styleId = layoutClass.getDeclaredField("simple_selectable_list_item").getInt(null); - jsonWriter.name("simple_selectable_list_item").value(extractItemStyle(styleId, "simple_selectable_list_item",android.R.style.TextAppearance_Large)); - } + jsonWriter.name("simple_selectable_list_item").value(extractItemStyle(android.R.layout.simple_selectable_list_item, "simple_selectable_list_item",android.R.style.TextAppearance_Large)); } catch (Exception e) { e.printStackTrace(); } @@ -1984,9 +1987,10 @@ public class ExtractStyle { return json; } - public ExtractStyle(Context context, String extractPath) + public ExtractStyle(Context context, String extractPath, boolean minimal) { // Log.i(MinistroService.TAG, "Extract " + extractPath); + m_minimal = minimal; m_extractPath = extractPath + "/"; new File(m_extractPath).mkdirs(); // MinistroActivity.nativeChmode(m_extractPath, 0755); @@ -2018,9 +2022,7 @@ public class ExtractStyle { extractProgressBar(jsonWriter, "progressBarStyleSmall", null); extractProgressBar(jsonWriter, "progressBarStyle", null); extractAbsSeekBar(jsonWriter, "seekBarStyle", "QSlider"); - if (Build.VERSION.SDK_INT > 13) { - extractSwitch(jsonWriter, "switchStyle", null); - } + extractSwitch(jsonWriter, "switchStyle", null); extractCompoundButton(jsonWriter, "checkboxStyle", "QCheckBox"); jsonWriter.name("editTextStyle").value(extractTextAppearanceInformations("editTextStyle", "QLineEdit", null, -1)); extractCompoundButton(jsonWriter, "radioButtonStyle", "QRadioButton"); @@ -2030,15 +2032,13 @@ public class ExtractStyle { jsonWriter.name("listSeparatorTextViewStyle").value(extractTextAppearanceInformations("listSeparatorTextViewStyle", null, null, -1)); extractItemsStyle(jsonWriter); extractCompoundButton(jsonWriter, "buttonStyleToggle", null); - if (Build.VERSION.SDK_INT > 10) { - extractCalendar(jsonWriter, "calendarViewStyle", "QCalendarWidget"); - extractToolBar(jsonWriter, "actionBarStyle", "QToolBar"); - jsonWriter.name("actionButtonStyle").value(extractTextAppearanceInformations("actionButtonStyle", "QToolButton", null, -1)); - jsonWriter.name("actionBarTabTextStyle").value(extractTextAppearanceInformations("actionBarTabTextStyle", null, null, -1)); - jsonWriter.name("actionBarTabStyle").value(extractTextAppearanceInformations("actionBarTabStyle", null, null, -1)); - jsonWriter.name("actionOverflowButtonStyle").value(extractImageViewInformations("actionOverflowButtonStyle", null)); - extractTabBar(jsonWriter, "actionBarTabBarStyle", "QTabBar"); - } + extractCalendar(jsonWriter, "calendarViewStyle", "QCalendarWidget"); + extractToolBar(jsonWriter, "actionBarStyle", "QToolBar"); + jsonWriter.name("actionButtonStyle").value(extractTextAppearanceInformations("actionButtonStyle", "QToolButton", null, -1)); + jsonWriter.name("actionBarTabTextStyle").value(extractTextAppearanceInformations("actionBarTabTextStyle", null, null, -1)); + jsonWriter.name("actionBarTabStyle").value(extractTextAppearanceInformations("actionBarTabStyle", null, null, -1)); + jsonWriter.name("actionOverflowButtonStyle").value(extractImageViewInformations("actionOverflowButtonStyle", null)); + extractTabBar(jsonWriter, "actionBarTabBarStyle", "QTabBar"); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java index 4cce86e8bb..ac471bf126 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -1,32 +1,38 @@ /**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -42,6 +48,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.graphics.Rect; import android.net.LocalServerSocket; import android.net.LocalSocket; @@ -68,6 +75,8 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.PopupMenu; import java.io.BufferedReader; import java.io.DataOutputStream; @@ -80,6 +89,8 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; +import org.qtproject.qt5.android.accessibility.QtAccessibilityDelegate; + public class QtActivityDelegate { private Activity m_activity = null; @@ -102,6 +113,7 @@ public class QtActivityDelegate private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; private static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level"; private static final String EXTRACT_STYLE_KEY = "extract.android.style"; + private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option"; private static String m_environmentVariables = null; private static String m_applicationParameters = null; @@ -118,6 +130,8 @@ public class QtActivityDelegate private HashMap<Integer, QtSurface> m_surfaces = null; private HashMap<Integer, View> m_nativeViews = null; private QtLayout m_layout = null; + private ImageView m_splashScreen = null; + private boolean m_splashScreenSticky = false; private QtEditText m_editText = null; private InputMethodManager m_imm = null; private boolean m_quitApp = true; @@ -139,35 +153,23 @@ public class QtActivityDelegate m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); try { - if (Build.VERSION.SDK_INT >= 14) { - int flags = View.class.getDeclaredField("SYSTEM_UI_FLAG_HIDE_NAVIGATION").getInt(null); - if (Build.VERSION.SDK_INT >= 16) { - flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_LAYOUT_STABLE").getInt(null); - flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION").getInt(null); - flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN").getInt(null); - flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_FULLSCREEN").getInt(null); - - if (Build.VERSION.SDK_INT >= 19) - flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY").getInt(null); - } - Method m = View.class.getMethod("setSystemUiVisibility", int.class); - m.invoke(m_activity.getWindow().getDecorView(), flags | View.INVISIBLE); - } + int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + flags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + flags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + flags |= View.SYSTEM_UI_FLAG_FULLSCREEN; + + if (Build.VERSION.SDK_INT >= 19) + flags |= View.class.getDeclaredField("SYSTEM_UI_FLAG_IMMERSIVE_STICKY").getInt(null); + + m_activity.getWindow().getDecorView().setSystemUiVisibility(flags | View.INVISIBLE); } catch (Exception e) { e.printStackTrace(); } } else { m_activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); m_activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - if (Build.VERSION.SDK_INT >= 14) { - try { - int ui_flag_visible = View.class.getDeclaredField("SYSTEM_UI_FLAG_VISIBLE").getInt(null); - Method m = View.class.getMethod("setSystemUiVisibility", int.class); - m.invoke(m_activity.getWindow().getDecorView(), ui_flag_visible); - } catch (Exception e) { - e.printStackTrace(); - } - } + m_activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } m_layout.requestLayout(); } @@ -297,8 +299,7 @@ public class QtActivityDelegate imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_NEXT; break; case EnterKeyPrevious: - if (Build.VERSION.SDK_INT > 10) - imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS; + imeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS; break; } @@ -311,7 +312,7 @@ public class QtActivityDelegate | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED); } - if (Build.VERSION.SDK_INT > 10 && (inputHints & ImhHiddenText) != 0) + if ((inputHints & ImhHiddenText) != 0) inputType |= 0x10 /* TYPE_NUMBER_VARIATION_PASSWORD */; } else if ((inputHints & ImhDialableCharactersOnly) != 0) { inputType = android.text.InputType.TYPE_CLASS_PHONE; @@ -509,7 +510,8 @@ public class QtActivityDelegate if (loaderParams.containsKey(EXTRACT_STYLE_KEY)) { String path = loaderParams.getString(EXTRACT_STYLE_KEY); - new ExtractStyle(m_activity, path); + new ExtractStyle(m_activity, path, loaderParams.containsKey(EXTRACT_STYLE_MINIMAL_KEY) && + loaderParams.getBoolean(EXTRACT_STYLE_MINIMAL_KEY)); } try { @@ -522,13 +524,7 @@ public class QtActivityDelegate m_super_onConfigurationChanged = m_activity.getClass().getMethod("super_onConfigurationChanged", Configuration.class); m_super_onActivityResult = m_activity.getClass().getMethod("super_onActivityResult", Integer.TYPE, Integer.TYPE, Intent.class); m_super_onWindowFocusChanged = m_activity.getClass().getMethod("super_onWindowFocusChanged", Boolean.TYPE); - if (Build.VERSION.SDK_INT >= 12) { - try { - m_super_dispatchGenericMotionEvent = m_activity.getClass().getMethod("super_dispatchGenericMotionEvent", MotionEvent.class); - } catch (Exception e) { - } - } - + m_super_dispatchGenericMotionEvent = m_activity.getClass().getMethod("super_dispatchGenericMotionEvent", MotionEvent.class); } catch (Exception e) { e.printStackTrace(); return false; @@ -544,10 +540,8 @@ public class QtActivityDelegate + "\tNECESSITAS_API_LEVEL=" + necessitasApiLevel + "\tHOME=" + m_activity.getFilesDir().getAbsolutePath() + "\tTMPDIR=" + m_activity.getFilesDir().getAbsolutePath(); - if (Build.VERSION.SDK_INT < 14) - additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Droid Sans;Droid Sans Fallback"; - else - additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback"; + + additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback"; additionalEnvironmentVariables += getAppIconSize(activity); @@ -885,6 +879,22 @@ public class QtActivityDelegate }; } m_layout = new QtLayout(m_activity, startApplication); + + try { + ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA); + if (info.metaData.containsKey("android.app.splash_screen_drawable")) { + m_splashScreenSticky = info.metaData.containsKey("android.app.splash_screen_sticky") && info.metaData.getBoolean("android.app.splash_screen_sticky"); + int id = info.metaData.getInt("android.app.splash_screen_drawable"); + m_splashScreen = new ImageView(m_activity); + m_splashScreen.setImageDrawable(m_activity.getResources().getDrawable(id)); + m_splashScreen.setScaleType(ImageView.ScaleType.FIT_XY); + m_splashScreen.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + m_layout.addView(m_splashScreen); + } + } catch (Exception e) { + e.printStackTrace(); + } + m_editText = new QtEditText(m_activity, this); m_imm = (InputMethodManager)m_activity.getSystemService(Context.INPUT_METHOD_SERVICE); m_surfaces = new HashMap<Integer, QtSurface>(); @@ -926,23 +936,18 @@ public class QtActivityDelegate }); } + public void hideSplashScreen() + { + if (m_splashScreen == null) + return; + m_layout.removeView(m_splashScreen); + m_splashScreen = null; + } + + public void initializeAccessibility() { - // Initialize accessibility - try { - final String a11yDelegateClassName = "org.qtproject.qt5.android.accessibility.QtAccessibilityDelegate"; - Class<?> qtDelegateClass = Class.forName(a11yDelegateClassName); - Constructor constructor = qtDelegateClass.getConstructor(android.app.Activity.class, - android.view.ViewGroup.class, - this.getClass()); - Object accessibilityDelegate = constructor.newInstance(m_activity, m_layout, this); - } catch (ClassNotFoundException e) { - // Class not found is fine since we are compatible with Android API < 16, but the function will - // only be available with that API level. - } catch (Exception e) { - // Unknown exception means something went wrong. - Log.w("Qt A11y", "Unknown exception: " + e.toString()); - } + new QtAccessibilityDelegate(m_activity, m_layout, this); } public void onWindowFocusChanged(boolean hasFocus) { @@ -962,6 +967,7 @@ public class QtActivityDelegate } catch (Exception e) { e.printStackTrace(); } + int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation(); if (rotation != m_currentRotation) { QtNative.handleOrientationChanged(rotation, m_nativeOrientation); @@ -974,6 +980,7 @@ public class QtActivityDelegate { if (m_quitApp) { QtNative.terminateQt(); + QtNative.setActivity(null, null); if (m_debuggerProcess != null) m_debuggerProcess.destroy(); System.exit(0);// FIXME remove it or find a better way @@ -1156,17 +1163,9 @@ public class QtActivityDelegate public void resetOptionsMenu() { - if (Build.VERSION.SDK_INT > 10) { - try { - Activity.class.getMethod("invalidateOptionsMenu").invoke(m_activity); - } catch (Exception e) { - e.printStackTrace(); - } - } - else - if (m_optionsMenuIsVisible) - m_activity.closeOptionsMenu(); + m_activity.invalidateOptionsMenu(); } + private boolean m_contextMenuVisible = false; public void onCreateContextMenu(ContextMenu menu, View v, @@ -1202,15 +1201,22 @@ public class QtActivityDelegate m_layout.postDelayed(new Runnable() { @Override public void run() { - if (Build.VERSION.SDK_INT < 11 || w <= 0 || h <= 0) { - m_activity.openContextMenu(m_layout); - } else if (Build.VERSION.SDK_INT < 14) { - m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(w, h, x, y), false); - QtPopupMenu.getInstance().showMenu(m_editText); - } else { - m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(w, h, x, y), false); - QtPopupMenu14.getInstance().showMenu(m_editText); - } + m_layout.setLayoutParams(m_editText, new QtLayout.LayoutParams(w, h, x, y), false); + PopupMenu popup = new PopupMenu(m_activity, m_editText); + QtActivityDelegate.this.onCreatePopupMenu(popup.getMenu()); + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + return QtActivityDelegate.this.onContextItemSelected(menuItem); + } + }); + popup.setOnDismissListener(new PopupMenu.OnDismissListener() { + @Override + public void onDismiss(PopupMenu popupMenu) { + QtActivityDelegate.this.onContextMenuClosed(popupMenu.getMenu()); + } + }); + popup.show(); } }, 100); } @@ -1220,46 +1226,14 @@ public class QtActivityDelegate m_activity.closeContextMenu(); } - private boolean hasPermanentMenuKey() - { - try { - return Build.VERSION.SDK_INT < 11 || (Build.VERSION.SDK_INT >= 14 && - (Boolean)ViewConfiguration.class.getMethod("hasPermanentMenuKey").invoke(ViewConfiguration.get(m_activity))); - } catch (Exception e) { - e.printStackTrace(); - return false; - } - } - - private Object getActionBar() - { - try { - return Activity.class.getMethod("getActionBar").invoke(m_activity); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - private void setActionBarVisibility(boolean visible) { - if (hasPermanentMenuKey() || !visible) { - if (Build.VERSION.SDK_INT > 10 && getActionBar() != null) { - try { - Class.forName("android.app.ActionBar").getMethod("hide").invoke(getActionBar()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - } else { - if (Build.VERSION.SDK_INT > 10 && getActionBar() != null) - try { - Class.forName("android.app.ActionBar").getMethod("show").invoke(getActionBar()); - } catch (Exception e) { - e.printStackTrace(); - } - } + if (m_activity.getActionBar() == null) + return; + if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible) + m_activity.getActionBar().hide(); + else + m_activity.getActionBar().show(); } public void insertNativeView(int id, View view, int x, int y, int w, int h) { @@ -1315,6 +1289,8 @@ public class QtActivityDelegate m_layout.addView(surface, surfaceCount); m_surfaces.put(id, surface); + if (!m_splashScreenSticky) + hideSplashScreen(); } public void setSurfaceGeometry(int id, int x, int y, int w, int h) { diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java index c01b0693d9..e6da5482ca 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtEditText.java @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java index 1536d60faa..7c621a11e2 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtInputConnection.java @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java b/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java index 5181cc7e11..f22b8176c8 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtLayout.java @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java b/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java index 341bc159c8..5a57633f63 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtMessageDialogHelper.java @@ -1,31 +1,37 @@ /**************************************************************************** ** ** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> - ** Contact: http://www.qt.io/licensing/ + ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** - ** $QT_BEGIN_LICENSE:LGPL21$ + ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms - ** and conditions see http://www.qt.io/terms-conditions. For further - ** information use the contact form at http://www.qt.io/contact-us. + ** and conditions see https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser - ** General Public License version 2.1 or version 3 as published by the Free - ** Software Foundation and appearing in the file LICENSE.LGPLv21 and - ** LICENSE.LGPLv3 included in the packaging of this file. Please review the - ** following information to ensure the GNU Lesser General Public License - ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and - ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** - ** As a special exception, The Qt Company gives you certain additional - ** rights. These rights are described in The Qt Company LGPL Exception - ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -100,14 +106,12 @@ public class QtMessageDialogHelper if (m_icon == 0) return null; - if (Build.VERSION.SDK_INT > 10) { - try { - TypedValue typedValue = new TypedValue(); - m_theme.resolveAttribute(Class.forName("android.R$attr").getDeclaredField("alertDialogIcon").getInt(null), typedValue, true); - return m_activity.getResources().getDrawable(typedValue.resourceId); - } catch (Exception e) { - e.printStackTrace(); - } + try { + TypedValue typedValue = new TypedValue(); + m_theme.resolveAttribute(android.R.attr.alertDialogIcon, typedValue, true); + return m_activity.getResources().getDrawable(typedValue.resourceId); + } catch (Exception e) { + e.printStackTrace(); } // Information, Warning, Critical, Question @@ -115,7 +119,7 @@ public class QtMessageDialogHelper { case 1: // Information try { - return m_activity.getResources().getDrawable(Class.forName("android.R$drawable").getDeclaredField("ic_dialog_info").getInt(null)); + return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_info); } catch (Exception e) { e.printStackTrace(); } @@ -129,14 +133,14 @@ public class QtMessageDialogHelper // break; case 3: // Critical try { - return m_activity.getResources().getDrawable(Class.forName("android.R$drawable").getDeclaredField("ic_dialog_alert").getInt(null)); + return m_activity.getResources().getDrawable(android.R.drawable.ic_dialog_alert); } catch (Exception e) { e.printStackTrace(); } break; case 4: // Question try { - return m_activity.getResources().getDrawable(Class.forName("android.R$drawable").getDeclaredField("ic_menu_help").getInt(null)); + return m_activity.getResources().getDrawable(android.R.drawable.ic_menu_help); } catch (Exception e) { e.printStackTrace(); } @@ -310,15 +314,11 @@ public class QtMessageDialogHelper for (ButtonStruct button: m_buttonsList) { Button bv; - if (Build.VERSION.SDK_INT > 10) { - try { - bv = new Button(m_activity, null, Class.forName("android.R$attr").getDeclaredField("borderlessButtonStyle").getInt(null)); - } catch (Exception e) { - bv = new Button(m_activity); - e.printStackTrace(); - } - } else { + try { + bv = new Button(m_activity, null, Class.forName("android.R$attr").getDeclaredField("borderlessButtonStyle").getInt(null)); + } catch (Exception e) { bv = new Button(m_activity); + e.printStackTrace(); } bv.setText(button.m_text); @@ -327,14 +327,12 @@ public class QtMessageDialogHelper { LinearLayout.LayoutParams layout = null; View spacer = new View(m_activity); - if (Build.VERSION.SDK_INT > 10) { - try { - layout = new LinearLayout.LayoutParams(1, RelativeLayout.LayoutParams.MATCH_PARENT); - spacer.setBackgroundDrawable(getStyledDrawable("dividerVertical")); - buttonsLayout.addView(spacer, layout); - } catch (Exception e) { - e.printStackTrace(); - } + try { + layout = new LinearLayout.LayoutParams(1, RelativeLayout.LayoutParams.MATCH_PARENT); + spacer.setBackgroundDrawable(getStyledDrawable("dividerVertical")); + buttonsLayout.addView(spacer, layout); + } catch (Exception e) { + e.printStackTrace(); } } LinearLayout.LayoutParams layout = null; @@ -343,23 +341,21 @@ public class QtMessageDialogHelper firstButton = false; } - if (Build.VERSION.SDK_INT > 10) { - try { - View horizontalDevider = new View(m_activity); - horizontalDevider.setId(id++); - horizontalDevider.setBackgroundDrawable(getStyledDrawable("dividerHorizontal")); - RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 1); - relativeParams.setMargins(0, 10, 0, 0); - if (lastView != null) { - relativeParams.addRule(RelativeLayout.BELOW, lastView.getId()); - } - else - relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); - dialogLayout.addView(horizontalDevider, relativeParams); - lastView = horizontalDevider; - } catch (Exception e) { - e.printStackTrace(); + try { + View horizontalDevider = new View(m_activity); + horizontalDevider.setId(id++); + horizontalDevider.setBackgroundDrawable(getStyledDrawable("dividerHorizontal")); + RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 1); + relativeParams.setMargins(0, 10, 0, 0); + if (lastView != null) { + relativeParams.addRule(RelativeLayout.BELOW, lastView.getId()); } + else + relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); + dialogLayout.addView(horizontalDevider, relativeParams); + lastView = horizontalDevider; + } catch (Exception e) { + e.printStackTrace(); } RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT); if (lastView != null) { @@ -367,10 +363,7 @@ public class QtMessageDialogHelper } else relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); - if (Build.VERSION.SDK_INT < 11) - relativeParams.setMargins(2, 12, 2, 4); - else - relativeParams.setMargins(2, 0, 2, 0); + relativeParams.setMargins(2, 0, 2, 0); dialogLayout.addView(buttonsLayout, relativeParams); } scrollView.addView(dialogLayout); diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index 14da281a76..48510b3fdf 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -39,6 +45,7 @@ import java.util.ArrayList; import java.util.concurrent.Semaphore; import android.app.Activity; +import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -66,7 +73,9 @@ public class QtNative { private static Activity m_activity = null; private static boolean m_activityPaused = false; + private static Service m_service = null; private static QtActivityDelegate m_activityDelegate = null; + private static QtServiceDelegate m_serviceDelegate = null; public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations public static final String QtTAG = "Qt JAVA"; // string used for Log.x @@ -84,6 +93,12 @@ public class QtNative private static final int m_moveThreshold = 0; private static ClipboardManager m_clipboardManager = null; private static Method m_checkSelfPermissionMethod = null; + private static final Runnable runPendingCppRunnablesRunnable = new Runnable() { + @Override + public void run() { + runPendingCppRunnables(); + } + }; private static ClassLoader m_classLoader = null; public static ClassLoader classLoader() @@ -103,6 +118,14 @@ public class QtNative } } + public static Service service() + { + synchronized (m_mainActivityMutex) { + return m_service; + } + } + + public static QtActivityDelegate activityDelegate() { synchronized (m_mainActivityMutex) { @@ -110,6 +133,13 @@ public class QtNative } } + public static QtServiceDelegate serviceDelegate() + { + synchronized (m_mainActivityMutex) { + return m_serviceDelegate; + } + } + public static boolean openURL(String url, String mime) { boolean ok = true; @@ -174,6 +204,14 @@ public class QtNative } } + public static void setService(Service qtMainService, QtServiceDelegate qtServiceDelegate) + { + synchronized (m_mainActivityMutex) { + m_service = qtMainService; + m_serviceDelegate = qtServiceDelegate; + } + } + public static void setApplicationState(int state) { synchronized (m_mainActivityMutex) { @@ -204,14 +242,14 @@ public class QtNative } } - private static void runQtOnUiThread(final long id) + private static void runPendingCppRunnablesOnUiThread() { - runAction(new Runnable() { - @Override - public void run() { - QtNative.onAndroidUiThread(id); - } - }); + synchronized (m_mainActivityMutex) { + if (!m_activityPaused && m_activity != null) + m_activity.runOnUiThread(runPendingCppRunnablesRunnable); + else + runAction(runPendingCppRunnablesRunnable); + } } private static void setViewVisibility(final View view, final boolean visible) @@ -307,7 +345,11 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activity.finish(); + quitQtAndroidPlugin(); + if (m_activity != null) + m_activity.finish(); + if (m_service != null) + m_service.stopSelf(); } }); } @@ -343,16 +385,14 @@ public class QtNative { int pointerType = 0; - if (Build.VERSION.SDK_INT >= 14) { - switch (event.getToolType(0)) { - case MotionEvent.TOOL_TYPE_STYLUS: - pointerType = 1; // QTabletEvent::Pen - break; - case MotionEvent.TOOL_TYPE_ERASER: - pointerType = 3; // QTabletEvent::Eraser - break; - // TODO TOOL_TYPE_MOUSE - } + switch (event.getToolType(0)) { + case MotionEvent.TOOL_TYPE_STYLUS: + pointerType = 1; // QTabletEvent::Pen + break; + case MotionEvent.TOOL_TYPE_ERASER: + pointerType = 3; // QTabletEvent::Eraser + break; + // TODO TOOL_TYPE_MOUSE } if (pointerType != 0) { @@ -442,7 +482,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.updateSelection(selStart, selEnd, candidatesStart, candidatesEnd); + if (m_activityDelegate != null) + m_activityDelegate.updateSelection(selStart, selEnd, candidatesStart, candidatesEnd); } }); } @@ -457,7 +498,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType); + if (m_activityDelegate != null) + m_activityDelegate.showSoftwareKeyboard(x, y, width, height, inputHints, enterKeyType); } }); } @@ -467,7 +509,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.resetSoftwareKeyboard(); + if (m_activityDelegate != null) + m_activityDelegate.resetSoftwareKeyboard(); } }); } @@ -477,7 +520,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.hideSoftwareKeyboard(); + if (m_activityDelegate != null) + m_activityDelegate.hideSoftwareKeyboard(); } }); } @@ -487,7 +531,9 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.setFullScreen(fullScreen); + if (m_activityDelegate != null) { + m_activityDelegate.setFullScreen(fullScreen); + } updateWindow(); } }); @@ -495,34 +541,44 @@ public class QtNative private static void registerClipboardManager() { - final Semaphore semaphore = new Semaphore(0); - runAction(new Runnable() { - @Override - public void run() { - m_clipboardManager = (android.text.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE); - semaphore.release(); + if (m_service == null || m_activity != null) { // Avoid freezing if only service + final Semaphore semaphore = new Semaphore(0); + runAction(new Runnable() { + @Override + public void run() { + if (m_activity != null) + m_clipboardManager = (android.text.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE); + semaphore.release(); + } + }); + try { + semaphore.acquire(); + } catch (Exception e) { + e.printStackTrace(); } - }); - try { - semaphore.acquire(); - } catch (Exception e) { - e.printStackTrace(); } } private static void setClipboardText(String text) { - m_clipboardManager.setText(text); + if (m_clipboardManager != null) + m_clipboardManager.setText(text); } private static boolean hasClipboardText() { - return m_clipboardManager.hasText(); + if (m_clipboardManager != null) + return m_clipboardManager.hasText(); + else + return false; } private static String getClipboardText() { - return m_clipboardManager.getText().toString(); + if (m_clipboardManager != null) + return m_clipboardManager.getText().toString(); + else + return ""; } private static void openContextMenu(final int x, final int y, final int w, final int h) @@ -530,7 +586,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.openContextMenu(x, y, w, h); + if (m_activityDelegate != null) + m_activityDelegate.openContextMenu(x, y, w, h); } }); } @@ -540,7 +597,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.closeContextMenu(); + if (m_activityDelegate != null) + m_activityDelegate.closeContextMenu(); } }); } @@ -550,7 +608,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.resetOptionsMenu(); + if (m_activityDelegate != null) + m_activityDelegate.resetOptionsMenu(); } }); } @@ -560,7 +619,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activity.openOptionsMenu(); + if (m_activity != null) + m_activity.openOptionsMenu(); } }); } @@ -597,7 +657,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.createSurface(id, onTop, x, y, w, h, imageDepth); + if (m_activityDelegate != null) + m_activityDelegate.createSurface(id, onTop, x, y, w, h, imageDepth); } }); } @@ -607,7 +668,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.insertNativeView(id, view, x, y, w, h); + if (m_activityDelegate != null) + m_activityDelegate.insertNativeView(id, view, x, y, w, h); } }); } @@ -617,7 +679,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.setSurfaceGeometry(id, x, y, w, h); + if (m_activityDelegate != null) + m_activityDelegate.setSurfaceGeometry(id, x, y, w, h); } }); } @@ -627,7 +690,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.bringChildToFront(id); + if (m_activityDelegate != null) + m_activityDelegate.bringChildToFront(id); } }); } @@ -637,7 +701,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.bringChildToBack(id); + if (m_activityDelegate != null) + m_activityDelegate.bringChildToBack(id); } }); } @@ -647,7 +712,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.destroySurface(id); + if (m_activityDelegate != null) + m_activityDelegate.destroySurface(id); } }); } @@ -662,6 +728,17 @@ public class QtNative }); } + private static void hideSplashScreen() + { + runAction(new Runnable() { + @Override + public void run() { + if (m_activityDelegate != null) + m_activityDelegate.hideSplashScreen(); + } + }); + } + // screen methods public static native void setDisplayMetrics(int screenWidthPixels, int screenHeightPixels, @@ -726,5 +803,8 @@ public class QtNative public static native void onActivityResult(int requestCode, int resultCode, Intent data); public static native void onNewIntent(Intent data); - public static native void onAndroidUiThread(long id); + public static native void runPendingCppRunnables(); + + private static native void setNativeActivity(Activity activity); + private static native void setNativeService(Service service); } diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java index a7dd96d609..ff3bf19383 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java @@ -1,32 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -34,17 +40,17 @@ package org.qtproject.qt5.android; -import android.app.Activity; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; public class QtNativeLibrariesDir { - public static String nativeLibrariesDir(Activity activity) + public static String nativeLibrariesDir(Context context) { String m_nativeLibraryDir = null; try { - ApplicationInfo ai = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), 0); - m_nativeLibraryDir = ai.nativeLibraryDir+"/"; + ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0); + m_nativeLibraryDir = ai.nativeLibraryDir + "/"; } catch (NameNotFoundException e) { e.printStackTrace(); } diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu.java b/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu.java deleted file mode 100644 index d89b454b77..0000000000 --- a/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu.java +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Android port of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -package org.qtproject.qt5.android; - -import android.view.MenuItem; -import android.view.View; -import android.widget.PopupMenu; - -public class QtPopupMenu { - private QtPopupMenu() { } - - private static class QtPopupMenuHolder { - private static final QtPopupMenu INSTANCE = new QtPopupMenu(); - } - - public static QtPopupMenu getInstance() { - return QtPopupMenuHolder.INSTANCE; - } - - public void showMenu(View anchor) - { - PopupMenu popup = new PopupMenu(QtNative.activity(), anchor); - QtNative.activityDelegate().onCreatePopupMenu(popup.getMenu()); - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem menuItem) { - boolean res = QtNative.onContextItemSelected(menuItem.getItemId(), menuItem.isChecked()); - if (res) - QtNative.activityDelegate().onContextMenuClosed(null); - return res; - } - }); - popup.show(); - } -} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu14.java b/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu14.java deleted file mode 100644 index edef682dec..0000000000 --- a/src/android/jar/src/org/qtproject/qt5/android/QtPopupMenu14.java +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the Android port of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -package org.qtproject.qt5.android; - -import android.view.MenuItem; -import android.view.View; -import android.widget.PopupMenu; - -public class QtPopupMenu14 { - private QtPopupMenu14() { } - - private static class QtPopupMenu14Holder { - private static final QtPopupMenu14 INSTANCE = new QtPopupMenu14(); - } - - public static QtPopupMenu14 getInstance() { - return QtPopupMenu14Holder.INSTANCE; - } - - public void showMenu(View anchor) - { - PopupMenu popup = new PopupMenu(QtNative.activity(), anchor); - QtNative.activityDelegate().onCreatePopupMenu(popup.getMenu()); - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem menuItem) { - return QtNative.activityDelegate().onContextItemSelected(menuItem); - } - }); - popup.setOnDismissListener(new PopupMenu.OnDismissListener() { - @Override - public void onDismiss(PopupMenu popupMenu) { - QtNative.activityDelegate().onContextMenuClosed(popupMenu.getMenu()); - } - }); - popup.show(); - } -} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java new file mode 100644 index 0000000000..634658eefb --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Android port of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.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.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; + +public class QtServiceDelegate +{ + private static final String NATIVE_LIBRARIES_KEY = "native.libraries"; + private static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; + private static final String MAIN_LIBRARY_KEY = "main.library"; + private static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; + private static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; + private static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; + private static final String APP_DISPLAY_METRIC_SCREEN_DESKTOP_KEY = "display.screen.desktop"; + private static final String APP_DISPLAY_METRIC_SCREEN_XDPI_KEY = "display.screen.dpi.x"; + private static final String APP_DISPLAY_METRIC_SCREEN_YDPI_KEY = "display.screen.dpi.y"; + private static final String APP_DISPLAY_METRIC_SCREEN_DENSITY_KEY = "display.screen.density"; + + private Service m_service = null; + private String m_mainLib; + private static String m_environmentVariables = null; + private static String m_applicationParameters = null; + + 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; + QtNative.setService(m_service, this); + QtNative.setClassLoader(classLoader); + + QtNative.setApplicationDisplayMetrics(10, 10, 10, 10, 120, 120, 1.0, 1.0); + + if (loaderParams.containsKey(STATIC_INIT_CLASSES_KEY)) { + for (String className: loaderParams.getStringArray(STATIC_INIT_CLASSES_KEY)) { + if (className.length() == 0) + continue; + + try { + Class<?> initClass = classLoader.loadClass(className); + Object staticInitDataObject = initClass.newInstance(); // create an instance + Method m = initClass.getMethod("setService", Service.class, Object.class); + m.invoke(staticInitDataObject, m_service, this); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + QtNative.loadQtLibraries(loaderParams.getStringArrayList(NATIVE_LIBRARIES_KEY)); + ArrayList<String> libraries = loaderParams.getStringArrayList(BUNDLED_LIBRARIES_KEY); + QtNative.loadBundledLibraries(libraries, QtNativeLibrariesDir.nativeLibrariesDir(m_service)); + m_mainLib = loaderParams.getString(MAIN_LIBRARY_KEY); + + m_environmentVariables = loaderParams.getString(ENVIRONMENT_VARIABLES_KEY); + String additionalEnvironmentVariables = "QT_ANDROID_FONTS_MONOSPACE=Droid Sans Mono;Droid Sans;Droid Sans Fallback" + + "\tQT_ANDROID_FONTS_SERIF=Droid Serif" + + "\tHOME=" + m_service.getFilesDir().getAbsolutePath() + + "\tTMPDIR=" + m_service.getFilesDir().getAbsolutePath(); + if (Build.VERSION.SDK_INT < 14) + additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Droid Sans;Droid Sans Fallback"; + else + additionalEnvironmentVariables += "\tQT_ANDROID_FONTS=Roboto;Droid Sans;Droid Sans Fallback"; + + if (m_environmentVariables != null && m_environmentVariables.length() > 0) + m_environmentVariables = additionalEnvironmentVariables + "\t" + m_environmentVariables; + else + m_environmentVariables = additionalEnvironmentVariables; + + if (loaderParams.containsKey(APPLICATION_PARAMETERS_KEY)) + m_applicationParameters = loaderParams.getString(APPLICATION_PARAMETERS_KEY); + else + m_applicationParameters = ""; + + return true; + } + + public boolean startApplication() + { + // start application + try { + String nativeLibraryDir = QtNativeLibrariesDir.nativeLibrariesDir(m_service); + QtNative.startApplication(m_applicationParameters, + m_environmentVariables, + m_mainLib, + nativeLibraryDir); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public void onDestroy() + { + QtNative.setService(null, null); + } +} diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java index 74433d2b65..4d8abb2117 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java @@ -1,32 +1,38 @@ /**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** @@ -62,9 +68,6 @@ public class QtSurface extends SurfaceView implements SurfaceHolder.Callback else getHolder().setFormat(PixelFormat.RGBA_8888); - if (android.os.Build.VERSION.SDK_INT < 11) - getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU); - setId(id); m_gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { diff --git a/src/android/accessibility/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java index 6f95675597..79caaf318e 100644 --- a/src/android/accessibility/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtAccessibilityDelegate.java @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/android/accessibility/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java index bfda2d55b7..a83174377d 100644 --- a/src/android/accessibility/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java +++ b/src/android/jar/src/org/qtproject/qt5/android/accessibility/QtNativeAccessibility.java @@ -1,31 +1,37 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Android port of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL21$ +** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** diff --git a/src/android/java/res/values-ro/strings.xml b/src/android/java/res/values-ro/strings.xml index f88a442b35..fef52ad3bd 100644 --- a/src/android/java/res/values-ro/strings.xml +++ b/src/android/java/res/values-ro/strings.xml @@ -3,4 +3,5 @@ <string name="ministro_not_found_msg">Serviciul Ministro nu poate fi găsit.\nAplicaţia nu poate porni.</string> <string name="ministro_needed_msg">Această aplicaţie necesită serviciul Ministro.\nDoriţi să-l instalaţi?</string> <string name="fatal_error_msg">Aplicaţia dumneavoastră a întâmpinat o eroare fatală şi nu poate continua.</string> + <string name="unsupported_android_version">Versiune Android nesuportată.</string> </resources> diff --git a/src/android/java/res/values/strings.xml b/src/android/java/res/values/strings.xml index fcc3eb097b..95b3385924 100644 --- a/src/android/java/res/values/strings.xml +++ b/src/android/java/res/values/strings.xml @@ -4,4 +4,5 @@ <string name="ministro_not_found_msg">Can\'t find Ministro service.\nThe application can\'t start.</string> <string name="ministro_needed_msg">This application requires Ministro service. Would you like to install it?</string> <string name="fatal_error_msg">Your application encountered a fatal error and cannot continue.</string> + <string name="unsupported_android_version">Unsupported Android version.</string> </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 9fc903af8e..ba16673479 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 @@ -1,5 +1,5 @@ /* - Copyright (c) 2012-2013, BogDan Vatra <bogdan@kde.org> + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> Contact: http://www.qt.io/licensing/ Commercial License Usage @@ -36,47 +36,20 @@ 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.io.DataOutputStream; -import java.io.DataInputStream; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; - -import org.kde.necessitas.ministro.IMinistro; -import org.kde.necessitas.ministro.IMinistroCallback; - import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; -import android.content.ComponentName; +import android.app.Fragment; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageInfo; 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.graphics.drawable.ColorDrawable; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; import android.util.AttributeSet; -import android.util.Log; +import android.view.ActionMode; +import android.view.ActionMode.Callback; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.KeyEvent; @@ -84,605 +57,25 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.Window; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; -import dalvik.system.DexClassLoader; - -//@ANDROID-11 -import android.app.Fragment; -import android.view.ActionMode; -import android.view.ActionMode.Callback; -//@ANDROID-11 public class QtActivity extends Activity { - private final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished - private static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file) - private static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin - private static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0 - - private static final String ERROR_CODE_KEY = "error.code"; - private static final String ERROR_MESSAGE_KEY = "error.message"; - private static final String DEX_PATH_KEY = "dex.path"; - private static final String LIB_PATH_KEY = "lib.path"; - private static final String LOADER_CLASS_NAME_KEY = "loader.class.name"; - private static final String NATIVE_LIBRARIES_KEY = "native.libraries"; - 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"; - private static final String EXTRACT_STYLE_KEY = "extract.android.style"; - - /// Ministro server parameter keys - private static final String REQUIRED_MODULES_KEY = "required.modules"; - private static final String APPLICATION_TITLE_KEY = "application.title"; - private static final String MINIMUM_MINISTRO_API_KEY = "minimum.ministro.api"; - private static final String MINIMUM_QT_VERSION_KEY = "minimum.qt.version"; - private static final String SOURCES_KEY = "sources"; // needs MINISTRO_API_LEVEL >=3 !!! - // Use this key to specify any 3rd party sources urls - // Ministro will download these repositories into their - // own folders, check http://community.kde.org/Necessitas/Ministro - // for more details. - - private static final String REPOSITORY_KEY = "repository"; // use this key to overwrite the default ministro repsitory - private static final String ANDROID_THEMES_KEY = "android.themes"; // themes that your application uses - - - public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, - // the parameters must not contain any white spaces - // and must be separated with "\t" - // e.g "-param1\t-param2=value2\t-param3\tvalue3" - - public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_STYLE=1\tQT_USE_ANDROID_NATIVE_DIALOGS=1\t"; - // use this variable to add any environment variables to your application. - // the env vars must be separated with "\t" - // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" - // Currently the following vars are used by the android plugin: - // * QT_USE_ANDROID_NATIVE_STYLE - 1 to use the android widget style if available. - // * QT_USE_ANDROID_NATIVE_DIALOGS -1 to use the android native dialogs. - - public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. - // The name of the theme must be the same with any theme from - // http://developer.android.com/reference/android/R.style.html - // The most used themes are: - // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme - // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black - // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light - // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo - // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light - // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault - // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light - - public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. - - 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://download.qt-project.org/ministro/android/qt5/qt-5.2"}; // Make sure you are using ONLY secure locations - private String m_repository = "default"; // Overwrites the default Ministro repository - // Possible values: - // * default - Ministro default repository set with "Ministro configuration tool". - // By default the stable version is used. Only this or stable repositories should - // be used in production. - // * stable - stable repository, only this and default repositories should be used - // in production. - // * testing - testing repository, DO NOT use this repository in production, - // this repository is used to push a new release, and should be used to test your application. - // * unstable - unstable repository, DO NOT use this repository in production, - // this repository is used to push Qt snapshots. - private String[] m_qtLibs = null; // required qt libs - private int m_displayDensity = -1; - + QtActivityLoader m_loader; public QtActivity() { - if (Build.VERSION.SDK_INT <= 10) { - QT_ANDROID_THEMES = new String[] {"Theme_Light"}; - QT_ANDROID_DEFAULT_THEME = "Theme_Light"; - } - else if ((Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT <= 13) || Build.VERSION.SDK_INT >= 21){ - QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; - QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; - } else { - QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; - QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light"; - } - } - - // this function is used to load and start the loader - private void loadApplication(Bundle loaderParams) - { - try { - final int errorCode = loaderParams.getInt(ERROR_CODE_KEY); - if (errorCode != 0) { - if (errorCode == INCOMPATIBLE_MINISTRO_VERSION) { - downloadUpgradeMinistro(loaderParams.getString(ERROR_MESSAGE_KEY)); - return; - } - - // fatal error, show the error and quit - AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); - errorDialog.setMessage(loaderParams.getString(ERROR_MESSAGE_KEY)); - errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - errorDialog.show(); - return; - } - - // add all bundled Qt libs to loader params - ArrayList<String> libs = new ArrayList<String>(); - if ( m_activityInfo.metaData.containsKey("android.app.bundled_libs_resource_id") ) - libs.addAll(Arrays.asList(getResources().getStringArray(m_activityInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); - - String libName = null; - if ( m_activityInfo.metaData.containsKey("android.app.lib_name") ) { - libName = m_activityInfo.metaData.getString("android.app.lib_name"); - loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function - } - - loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs); - loaderParams.putInt(NECESSITAS_API_LEVEL_KEY, NECESSITAS_API_LEVEL); - - // load and start QtLoader class - m_classLoader = new DexClassLoader(loaderParams.getString(DEX_PATH_KEY), // .jar/.apk files - 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) - getClassLoader()); // parent loader - - @SuppressWarnings("rawtypes") - Class loaderClass = m_classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class - Object qtLoader = loaderClass.newInstance(); // create an instance - Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication", - Activity.class, - ClassLoader.class, - Bundle.class); - if (!(Boolean)prepareAppMethod.invoke(qtLoader, this, m_classLoader, loaderParams)) - throw new Exception(""); - - QtApplication.setQtActivityDelegate(qtLoader); - - // now load the application library so it's accessible from this class loader - if (libName != null) - System.loadLibrary(libName); - - Method startAppMethod=qtLoader.getClass().getMethod("startApplication"); - if (!(Boolean)startAppMethod.invoke(qtLoader)) - throw new Exception(""); - - } catch (Exception e) { - e.printStackTrace(); - AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); - if (m_activityInfo.metaData.containsKey("android.app.fatal_error_msg")) - errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.fatal_error_msg")); - else - errorDialog.setMessage("Fatal error, your application can't be started."); - - errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - errorDialog.show(); - } - } - - private ServiceConnection m_ministroConnection=new ServiceConnection() { - private IMinistro m_service = null; - @Override - public void onServiceConnected(ComponentName name, IBinder service) - { - m_service = IMinistro.Stub.asInterface(service); - try { - if (m_service != null) { - Bundle parameters = new Bundle(); - parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); - parameters.putString(APPLICATION_TITLE_KEY, (String)QtActivity.this.getTitle()); - parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); - parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); - parameters.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES); - if (APPLICATION_PARAMETERS != null) - parameters.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); - parameters.putStringArray(SOURCES_KEY, m_sources); - parameters.putString(REPOSITORY_KEY, m_repository); - if (QT_ANDROID_THEMES != null) - parameters.putStringArray(ANDROID_THEMES_KEY, QT_ANDROID_THEMES); - m_service.requestLoader(m_ministroCallback, parameters); - } - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub() { - // this function is called back by Ministro. - @Override - public void loaderReady(final Bundle loaderParams) throws RemoteException { - runOnUiThread(new Runnable() { - @Override - public void run() { - unbindService(m_ministroConnection); - loadApplication(loaderParams); - } - }); - } - }; - - @Override - public void onServiceDisconnected(ComponentName name) { - m_service = null; - } - }; - - private void downloadUpgradeMinistro(String msg) - { - AlertDialog.Builder downloadDialog = new AlertDialog.Builder(this); - downloadDialog.setMessage(msg); - downloadDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - try { - Uri uri = Uri.parse("market://search?q=pname:org.kde.necessitas.ministro"); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivityForResult(intent, MINISTRO_INSTALL_REQUEST_CODE); - } catch (Exception e) { - e.printStackTrace(); - ministroNotFound(); - } - } - }); - - downloadDialog.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - QtActivity.this.finish(); - } - }); - downloadDialog.show(); - } - - private void ministroNotFound() - { - AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); - - if (m_activityInfo.metaData.containsKey("android.app.ministro_not_found_msg")) - errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.ministro_not_found_msg")); - else - errorDialog.setMessage("Can't find Ministro service.\nThe application can't start."); - - errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - 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); - - inputStream.close(); - outputStream.close(); - } - - 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); - - inputStream.close(); - outputStream.close(); - } - - private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) - { - File versionFile = new File(pluginsPrefix + "cache.version"); - - long cacheVersion = 0; - if (versionFile.exists() && versionFile.canRead()) { - try { - DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile)); - cacheVersion = inputStream.readLong(); - inputStream.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - if (cacheVersion != packageVersion) { - deleteRecursively(new File(pluginsPrefix)); - return true; + m_loader = new QtActivityLoader(this); + if (Build.VERSION.SDK_INT >= 21) { + m_loader.QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; + m_loader.QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; } else { - return false; + m_loader.QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; + m_loader.QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light"; } } - private void extractBundledPluginsAndImports(String pluginsPrefix) - throws IOException - { - ArrayList<String> libs = new ArrayList<String>(); - - String libsDir = getApplicationInfo().nativeLibraryDir + "/"; - - long packageVersion = -1; - try { - PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); - packageVersion = packageInfo.lastUpdateTime; - } catch (Exception e) { - e.printStackTrace(); - } - - if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) - return; - - { - File versionFile = new File(pluginsPrefix + "cache.version"); - - File parentDirectory = versionFile.getParentFile(); - if (!parentDirectory.exists()) - parentDirectory.mkdirs(); - - versionFile.createNewFile(); - - DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile)); - outputStream.writeLong(packageVersion); - outputStream.close(); - } - - { - 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 = libsDir + split[0]; - String destinationFileName = pluginsPrefix + 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 = pluginsPrefix + split[1]; - copyAsset(sourceFileName, destinationFileName); - } - } - - } - } - - private void deleteRecursively(File directory) - { - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isDirectory()) - deleteRecursively(file); - else - file.delete(); - } - - directory.delete(); - } - } - - private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) - { - File newCache = new File(localPrefix); - if (!newCache.exists()) { - { - File oldPluginsCache = new File(oldLocalPrefix + "plugins/"); - if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) - deleteRecursively(oldPluginsCache); - } - - { - File oldImportsCache = new File(oldLocalPrefix + "imports/"); - if (oldImportsCache.exists() && oldImportsCache.isDirectory()) - deleteRecursively(oldImportsCache); - } - - { - File oldQmlCache = new File(oldLocalPrefix + "qml/"); - if (oldQmlCache.exists() && oldQmlCache.isDirectory()) - deleteRecursively(oldQmlCache); - } - } - } - - private void startApp(final boolean firstStart) - { - try { - if (m_activityInfo.metaData.containsKey("android.app.qt_sources_resource_id")) { - int resourceId = m_activityInfo.metaData.getInt("android.app.qt_sources_resource_id"); - m_sources = getResources().getStringArray(resourceId); - } - - if (m_activityInfo.metaData.containsKey("android.app.repository")) - m_repository = m_activityInfo.metaData.getString("android.app.repository"); - - if (m_activityInfo.metaData.containsKey("android.app.qt_libs_resource_id")) { - int resourceId = m_activityInfo.metaData.getInt("android.app.qt_libs_resource_id"); - m_qtLibs = getResources().getStringArray(resourceId); - } - - if (m_activityInfo.metaData.containsKey("android.app.use_local_qt_libs") - && 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"); - - String pluginsPrefix = localPrefix; - - 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 + "/"; - pluginsPrefix = localPrefix + "qt-reserved-files/"; - cleanOldCacheIfNecessary(localPrefix, pluginsPrefix); - extractBundledPluginsAndImports(pluginsPrefix); - bundlingQtLibs = true; - } - - if (m_qtLibs != null) { - 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")) { - String[] extraLibs = m_activityInfo.metaData.getString("android.app.load_local_libs").split(":"); - for (String lib : extraLibs) { - if (lib.length() > 0) { - if (lib.startsWith("lib/")) - libraryList.add(localPrefix + lib); - else - libraryList.add(pluginsPrefix + lib); - } - } - } - - - String dexPaths = new String(); - String pathSeparator = System.getProperty("path.separator", ":"); - 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) { - if (dexPaths.length() > 0) - dexPaths += pathSeparator; - dexPaths += localPrefix + jar; - } - } - } - - Bundle loaderParams = new Bundle(); - loaderParams.putInt(ERROR_CODE_KEY, 0); - loaderParams.putString(DEX_PATH_KEY, dexPaths); - loaderParams.putString(LOADER_CLASS_NAME_KEY, "org.qtproject.qt5.android.QtActivityDelegate"); - if (m_activityInfo.metaData.containsKey("android.app.static_init_classes")) { - loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, - m_activityInfo.metaData.getString("android.app.static_init_classes").split(":")); - } - loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList); - - - String themePath = getApplicationInfo().dataDir + "/qt-reserved-files/android-style/"; - String stylePath = themePath + m_displayDensity + "/"; - if (!(new File(stylePath)).exists()) - loaderParams.putString(EXTRACT_STYLE_KEY, stylePath); - ENVIRONMENT_VARIABLES += "\tMINISTRO_ANDROID_STYLE_PATH=" + stylePath - + "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath; - - loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES - + "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" - + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" - + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"); - - if (APPLICATION_PARAMETERS != null) { - loaderParams.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); - } else { - Intent intent = getIntent(); - if (intent != null) { - String parameters = intent.getStringExtra("applicationArguments"); - if (parameters != null) - loaderParams.putString(APPLICATION_PARAMETERS_KEY, parameters.replace(' ', '\t')); - } - } - - loadApplication(loaderParams); - return; - } - - try { - if (!bindService(new Intent(org.kde.necessitas.ministro.IMinistro.class.getCanonicalName()), - m_ministroConnection, - Context.BIND_AUTO_CREATE)) { - throw new SecurityException(""); - } - } catch (Exception e) { - if (firstStart) { - String msg = "This application requires Ministro service. Would you like to install it?"; - if (m_activityInfo.metaData.containsKey("android.app.ministro_needed_msg")) - msg = m_activityInfo.metaData.getString("android.app.ministro_needed_msg"); - downloadUpgradeMinistro(msg); - } else { - ministroNotFound(); - } - } - } catch (Exception e) { - Log.e(QtApplication.QtTAG, "Can't create main activity", e); - } - } - - /////////////////////////// forward all notifications //////////////////////////// /////////////////////////// Super class calls //////////////////////////////////// @@ -753,8 +146,8 @@ public class QtActivity extends Activity QtApplication.invokeDelegateMethod(QtApplication.onActivityResult, requestCode, resultCode, data); return; } - if (requestCode == MINISTRO_INSTALL_REQUEST_CODE) - startApp(false); + if (requestCode == QtLoader.MINISTRO_INSTALL_REQUEST_CODE) + m_loader.startApp(false); super.onActivityResult(requestCode, resultCode, data); } public void super_onActivityResult(int requestCode, int resultCode, Intent data) @@ -843,68 +236,7 @@ public class QtActivity extends Activity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - try { - m_activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); - for (Field f : Class.forName("android.R$style").getDeclaredFields()) { - if (f.getInt(null) == m_activityInfo.getThemeResource()) { - QT_ANDROID_THEMES = new String[] {f.getName()}; - QT_ANDROID_DEFAULT_THEME = f.getName(); - } - } - } catch (Exception e) { - e.printStackTrace(); - finish(); - return; - } - - try { - setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null)); - } catch (Exception e) { - e.printStackTrace(); - } - - if (Build.VERSION.SDK_INT > 10) { - try { - requestWindowFeature(Window.class.getField("FEATURE_ACTION_BAR").getInt(null)); - } catch (Exception e) { - e.printStackTrace(); - } - } else { - requestWindowFeature(Window.FEATURE_NO_TITLE); - } - - if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) { - QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState); - return; - } - - m_displayDensity = getResources().getDisplayMetrics().densityDpi; - - ENVIRONMENT_VARIABLES += "\tQT_ANDROID_THEME=" + QT_ANDROID_DEFAULT_THEME - + "/\tQT_ANDROID_THEME_DISPLAY_DPI=" + m_displayDensity + "\t"; - - if (null == getLastNonConfigurationInstance()) { - // if splash screen is defined, then show it - if (m_activityInfo.metaData.containsKey("android.app.splash_screen_drawable")) - getWindow().setBackgroundDrawableResource(m_activityInfo.metaData.getInt("android.app.splash_screen_drawable")); - else - getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); - - if (m_activityInfo.metaData.containsKey("android.app.background_running") - && m_activityInfo.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"; - } - - if (m_activityInfo.metaData.containsKey("android.app.auto_screen_scale_factor") - && m_activityInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) { - ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t"; - } - - startApp(true); - } + m_loader.onCreate(savedInstanceState); } //--------------------------------------------------------------------------- diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java new file mode 100644 index 0000000000..92cea65e4b --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java @@ -0,0 +1,184 @@ +/* + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Contact: http://www.qt-project.org/legal + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and Digia. For licensing terms and + conditions see http://qt.digia.com/licensing. For further information + use the contact form at http://qt.digia.com/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.view.Window; + + +import java.lang.reflect.Field; + +public class QtActivityLoader extends QtLoader { + QtActivity m_activity; + + QtActivityLoader(QtActivity activity) + { + super(activity); + m_activity = activity; + } + @Override + protected void downloadUpgradeMinistro(String msg) { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(m_activity); + downloadDialog.setMessage(msg); + downloadDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + try { + Uri uri = Uri.parse("market://search?q=pname:org.kde.necessitas.ministro"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + m_activity.startActivityForResult(intent, MINISTRO_INSTALL_REQUEST_CODE); + } catch (Exception e) { + e.printStackTrace(); + ministroNotFound(); + } + } + }); + + downloadDialog.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + m_activity.finish(); + } + }); + downloadDialog.show(); + } + + @Override + protected String loaderClassName() { + return "org.qtproject.qt5.android.QtActivityDelegate"; + } + + @Override + protected Class<?> contextClassName() { + return android.app.Activity.class; + } + + @Override + protected void finish() { + m_activity.finish(); + } + + @Override + protected String getTitle() { + return (String) m_activity.getTitle(); + } + + @Override + protected void runOnUiThread(Runnable run) { + m_activity.runOnUiThread(run); + } + + @Override + Intent getIntent() { + return m_activity.getIntent(); + } + + public void onCreate(Bundle savedInstanceState) { + try { + m_contextInfo = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(), PackageManager.GET_META_DATA); + for (Field f : Class.forName("android.R$style").getDeclaredFields()) { + if (f.getInt(null) == ((ActivityInfo)m_contextInfo).getThemeResource()) { + QT_ANDROID_THEMES = new String[] {f.getName()}; + QT_ANDROID_DEFAULT_THEME = f.getName(); + } + } + } catch (Exception e) { + e.printStackTrace(); + finish(); + return; + } + + if (Build.VERSION.SDK_INT < 16) { + // fatal error, show the error and quit + AlertDialog errorDialog = new AlertDialog.Builder(m_activity).create(); + if (m_contextInfo.metaData.containsKey("android.app.unsupported_android_version")) + errorDialog.setMessage(m_contextInfo.metaData.getString("android.app.unsupported_android_version")); + else + errorDialog.setMessage("Unsupported Android version."); + errorDialog.setButton(m_activity.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + return; + } + + try { + m_activity.setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null)); + } catch (Exception e) { + e.printStackTrace(); + } + + m_activity.requestWindowFeature(Window.FEATURE_ACTION_BAR); + + if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) { + QtApplication.invokeDelegateMethod(QtApplication.onCreate, savedInstanceState); + 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"; + + if (null == m_activity.getLastNonConfigurationInstance()) { + 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"; + } + + if (m_contextInfo.metaData.containsKey("android.app.auto_screen_scale_factor") + && m_contextInfo.metaData.getBoolean("android.app.auto_screen_scale_factor")) { + ENVIRONMENT_VARIABLES += "QT_AUTO_SCREEN_SCALE_FACTOR=1\t"; + } + + startApp(true); + } + } +} diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java index c78aeb7f13..2afede6d66 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtApplication.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2012-2013, BogDan Vatra <bogdan@kde.org> + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> Contact: http://www.qt.io/licensing/ Commercial License Usage @@ -36,13 +36,13 @@ package org.qtproject.qt5.android.bindings; +import android.app.Application; + import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; -import android.app.Application; - public class QtApplication extends Application { public final static String QtTAG = "Qt"; @@ -64,10 +64,11 @@ public class QtApplication extends Application public static Method onKeyShortcut = null; public static Method dispatchGenericMotionEvent = null; public static Method onGenericMotionEvent = null; - - public static void setQtActivityDelegate(Object listener) + private static String activityClassName; + public static void setQtContextDelegate(Class<?> clazz, Object listener) { - QtApplication.m_delegateObject = listener; + m_delegateObject = listener; + activityClassName = clazz.getCanonicalName(); ArrayList<Method> delegateMethods = new ArrayList<Method>(); for (Method m : listener.getClass().getMethods()) { @@ -83,7 +84,7 @@ public class QtApplication extends Application for (Method delegateMethod : delegateMethods) { try { - QtActivity.class.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes()); + clazz.getDeclaredMethod(delegateMethod.getName(), delegateMethod.getParameterTypes()); if (QtApplication.m_delegateMethods.containsKey(delegateMethod.getName())) { QtApplication.m_delegateMethods.get(delegateMethod.getName()).add(delegateMethod); } else { @@ -126,7 +127,6 @@ public class QtApplication extends Application return result; StackTraceElement[] elements = Thread.currentThread().getStackTrace(); if (-1 == stackDeep) { - String activityClassName = QtActivity.class.getCanonicalName(); for (int it=0;it<elements.length;it++) if (elements[it].getClassName().equals(activityClassName)) { stackDeep = it; diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java new file mode 100644 index 0000000000..3efdbf7a7c --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java @@ -0,0 +1,669 @@ +/* + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ComponentInfo; +import android.content.pm.PackageInfo; +import android.content.res.AssetManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import org.kde.necessitas.ministro.IMinistro; +import org.kde.necessitas.ministro.IMinistroCallback; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; + +import dalvik.system.DexClassLoader; + +public abstract class QtLoader { + + public final static int MINISTRO_INSTALL_REQUEST_CODE = 0xf3ee; // request code used to know when Ministro instalation is finished + public static final int MINISTRO_API_LEVEL = 5; // Ministro api level (check IMinistro.aidl file) + public static final int NECESSITAS_API_LEVEL = 2; // Necessitas api level used by platform plugin + public static final int QT_VERSION = 0x050100; // This app requires at least Qt version 5.1.0 + + public static final String ERROR_CODE_KEY = "error.code"; + public static final String ERROR_MESSAGE_KEY = "error.message"; + 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 BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id"; + public static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id"; + public static final String MAIN_LIBRARY_KEY = "main.library"; + public static final String STATIC_INIT_CLASSES_KEY = "static.init.classes"; + public static final String NECESSITAS_API_LEVEL_KEY = "necessitas.api.level"; + public static final String EXTRACT_STYLE_KEY = "extract.android.style"; + private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option"; + + /// Ministro server parameter keys + public static final String REQUIRED_MODULES_KEY = "required.modules"; + public static final String APPLICATION_TITLE_KEY = "application.title"; + public static final String MINIMUM_MINISTRO_API_KEY = "minimum.ministro.api"; + public static final String MINIMUM_QT_VERSION_KEY = "minimum.qt.version"; + public static final String SOURCES_KEY = "sources"; // needs MINISTRO_API_LEVEL >=3 !!! + // Use this key to specify any 3rd party sources urls + // Ministro will download these repositories into their + // own folders, check http://community.kde.org/Necessitas/Ministro + // for more details. + + public static final String REPOSITORY_KEY = "repository"; // use this key to overwrite the default ministro repsitory + public static final String ANDROID_THEMES_KEY = "android.themes"; // themes that your application uses + + + public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application, + // the parameters must not contain any white spaces + // and must be separated with "\t" + // e.g "-param1\t-param2=value2\t-param3\tvalue3" + + public String ENVIRONMENT_VARIABLES = "QT_USE_ANDROID_NATIVE_DIALOGS=1"; + // use this variable to add any environment variables to your application. + // the env vars must be separated with "\t" + // e.g. "ENV_VAR1=1\tENV_VAR2=2\t" + // Currently the following vars are used by the android plugin: + // * QT_USE_ANDROID_NATIVE_DIALOGS -1 to use the android native dialogs. + + public String[] QT_ANDROID_THEMES = null; // A list with all themes that your application want to use. + // The name of the theme must be the same with any theme from + // http://developer.android.com/reference/android/R.style.html + // The most used themes are: + // * "Theme" - (fallback) check http://developer.android.com/reference/android/R.style.html#Theme + // * "Theme_Black" - check http://developer.android.com/reference/android/R.style.html#Theme_Black + // * "Theme_Light" - (default for API <=10) check http://developer.android.com/reference/android/R.style.html#Theme_Light + // * "Theme_Holo" - check http://developer.android.com/reference/android/R.style.html#Theme_Holo + // * "Theme_Holo_Light" - (default for API 11-13) check http://developer.android.com/reference/android/R.style.html#Theme_Holo_Light + // * "Theme_DeviceDefault" - check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault + // * "Theme_DeviceDefault_Light" - (default for API 14+) check http://developer.android.com/reference/android/R.style.html#Theme_DeviceDefault_Light + + public String QT_ANDROID_DEFAULT_THEME = null; // sets the default theme. + + public static final int INCOMPATIBLE_MINISTRO_VERSION = 1; // Incompatible Ministro version. Ministro needs to be upgraded. + public static final int BUFFER_SIZE = 1024; + + public String[] m_sources = {"https://download.qt-project.org/ministro/android/qt5/qt-5.7"}; // Make sure you are using ONLY secure locations + public String m_repository = "default"; // Overwrites the default Ministro repository + // Possible values: + // * default - Ministro default repository set with "Ministro configuration tool". + // By default the stable version is used. Only this or stable repositories should + // be used in production. + // * stable - stable repository, only this and default repositories should be used + // in production. + // * testing - testing repository, DO NOT use this repository in production, + // this repository is used to push a new release, and should be used to test your application. + // * unstable - unstable repository, DO NOT use this repository in production, + // this repository is used to push Qt snapshots. + public String[] m_qtLibs = null; // required qt libs + public int m_displayDensity = -1; + private ContextWrapper m_context; + protected ComponentInfo m_contextInfo; + + QtLoader(ContextWrapper context) { + m_context = context; + } + + // Implement in subclass + protected void finish() {} + + protected String getTitle() { + return "Qt"; + } + + protected void runOnUiThread(Runnable run) { + run.run(); + } + protected void downloadUpgradeMinistro(String msg) + { + Log.e(QtApplication.QtTAG, msg); + } + + protected abstract String loaderClassName(); + protected abstract Class<?> contextClassName(); + + Intent getIntent() + { + return null; + } + // Implement in subclass + + + // this function is used to load and start the loader + private void loadApplication(Bundle loaderParams) + { + try { + final int errorCode = loaderParams.getInt(ERROR_CODE_KEY); + if (errorCode != 0) { + if (errorCode == INCOMPATIBLE_MINISTRO_VERSION) { + downloadUpgradeMinistro(loaderParams.getString(ERROR_MESSAGE_KEY)); + return; + } + + // fatal error, show the error and quit + AlertDialog errorDialog = new AlertDialog.Builder(m_context).create(); + errorDialog.setMessage(loaderParams.getString(ERROR_MESSAGE_KEY)); + errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + return; + } + + // add all bundled Qt libs to loader params + ArrayList<String> libs = new ArrayList<String>(); + if ( m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id") ) + libs.addAll(Arrays.asList(m_context.getResources().getStringArray(m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); + + String libName = null; + if ( m_contextInfo.metaData.containsKey("android.app.lib_name") ) { + libName = m_contextInfo.metaData.getString("android.app.lib_name"); + loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function + } + + loaderParams.putStringArrayList(BUNDLED_LIBRARIES_KEY, libs); + loaderParams.putInt(NECESSITAS_API_LEVEL_KEY, NECESSITAS_API_LEVEL); + + // 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 + + Class<?> loaderClass = classLoader.loadClass(loaderParams.getString(LOADER_CLASS_NAME_KEY)); // load QtLoader class + Object qtLoader = loaderClass.newInstance(); // create an instance + Method prepareAppMethod = qtLoader.getClass().getMethod("loadApplication", + contextClassName(), + ClassLoader.class, + Bundle.class); + if (!(Boolean)prepareAppMethod.invoke(qtLoader, m_context, classLoader, loaderParams)) + throw new Exception(""); + + QtApplication.setQtContextDelegate(m_context.getClass(), qtLoader); + + // now load the application library so it's accessible from this class loader + if (libName != null) + System.loadLibrary(libName); + + Method startAppMethod=qtLoader.getClass().getMethod("startApplication"); + if (!(Boolean)startAppMethod.invoke(qtLoader)) + throw new Exception(""); + + } catch (Exception e) { + e.printStackTrace(); + AlertDialog errorDialog = new AlertDialog.Builder(m_context).create(); + if (m_contextInfo.metaData.containsKey("android.app.fatal_error_msg")) + errorDialog.setMessage(m_contextInfo.metaData.getString("android.app.fatal_error_msg")); + else + errorDialog.setMessage("Fatal error, your application can't be started."); + + errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + errorDialog.show(); + } + } + + private ServiceConnection m_ministroConnection=new ServiceConnection() { + private IMinistro m_service = null; + @Override + public void onServiceConnected(ComponentName name, IBinder service) + { + m_service = IMinistro.Stub.asInterface(service); + try { + if (m_service != null) { + Bundle parameters = new Bundle(); + parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); + parameters.putString(APPLICATION_TITLE_KEY, getTitle()); + parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); + parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); + parameters.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES); + if (APPLICATION_PARAMETERS != null) + parameters.putString(APPLICATION_PARAMETERS_KEY, APPLICATION_PARAMETERS); + parameters.putStringArray(SOURCES_KEY, m_sources); + parameters.putString(REPOSITORY_KEY, m_repository); + if (QT_ANDROID_THEMES != null) + parameters.putStringArray(ANDROID_THEMES_KEY, QT_ANDROID_THEMES); + m_service.requestLoader(m_ministroCallback, parameters); + } + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private IMinistroCallback m_ministroCallback = new IMinistroCallback.Stub() { + // this function is called back by Ministro. + @Override + public void loaderReady(final Bundle loaderParams) throws RemoteException { + runOnUiThread(new Runnable() { + @Override + public void run() { + m_context.unbindService(m_ministroConnection); + loadApplication(loaderParams); + } + }); + } + }; + + @Override + public void onServiceDisconnected(ComponentName name) { + m_service = null; + } + }; + + protected void ministroNotFound() + { + AlertDialog errorDialog = new AlertDialog.Builder(m_context).create(); + + if (m_contextInfo.metaData.containsKey("android.app.ministro_not_found_msg")) + errorDialog.setMessage(m_contextInfo.metaData.getString("android.app.ministro_not_found_msg")); + else + errorDialog.setMessage("Can't find Ministro service.\nThe application can't start."); + + errorDialog.setButton(m_context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + finish(); + } + }); + 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 = m_context.getAssets(); + InputStream inputStream = assetsManager.open(source); + OutputStream outputStream = new FileOutputStream(destinationFile); + copyFile(inputStream, outputStream); + + inputStream.close(); + outputStream.close(); + } + + 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); + + inputStream.close(); + outputStream.close(); + } + + private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + long cacheVersion = 0; + if (versionFile.exists() && versionFile.canRead()) { + try { + DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile)); + cacheVersion = inputStream.readLong(); + inputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (cacheVersion != packageVersion) { + deleteRecursively(new File(pluginsPrefix)); + return true; + } else { + return false; + } + } + + private void extractBundledPluginsAndImports(String pluginsPrefix) + throws IOException + { + ArrayList<String> libs = new ArrayList<String>(); + + String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/"; + + long packageVersion = -1; + try { + PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0); + packageVersion = packageInfo.lastUpdateTime; + } catch (Exception e) { + e.printStackTrace(); + } + + if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) + return; + + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + File parentDirectory = versionFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + versionFile.createNewFile(); + + DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile)); + outputStream.writeLong(packageVersion); + outputStream.close(); + } + + { + String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; + java.util.Set<String> keys = m_contextInfo.metaData.keySet(); + if (m_contextInfo.metaData.containsKey(key)) { + String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key)); + + for (String bundledImportBinary : list) { + String[] split = bundledImportBinary.split(":"); + String sourceFileName = libsDir + split[0]; + String destinationFileName = pluginsPrefix + split[1]; + createBundledBinary(sourceFileName, destinationFileName); + } + } + } + + { + String key = BUNDLED_IN_ASSETS_RESOURCE_ID_KEY; + if (m_contextInfo.metaData.containsKey(key)) { + String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key)); + + for (String fileName : list) { + String[] split = fileName.split(":"); + String sourceFileName = split[0]; + String destinationFileName = pluginsPrefix + split[1]; + copyAsset(sourceFileName, destinationFileName); + } + } + + } + } + + private void deleteRecursively(File directory) + { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) + deleteRecursively(file); + else + file.delete(); + } + + directory.delete(); + } + } + + private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) + { + File newCache = new File(localPrefix); + if (!newCache.exists()) { + { + File oldPluginsCache = new File(oldLocalPrefix + "plugins/"); + if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) + deleteRecursively(oldPluginsCache); + } + + { + File oldImportsCache = new File(oldLocalPrefix + "imports/"); + if (oldImportsCache.exists() && oldImportsCache.isDirectory()) + deleteRecursively(oldImportsCache); + } + + { + File oldQmlCache = new File(oldLocalPrefix + "qml/"); + if (oldQmlCache.exists() && oldQmlCache.isDirectory()) + deleteRecursively(oldQmlCache); + } + } + } + + public void startApp(final boolean firstStart) + { + try { + if (m_contextInfo.metaData.containsKey("android.app.qt_sources_resource_id")) { + int resourceId = m_contextInfo.metaData.getInt("android.app.qt_sources_resource_id"); + m_sources = m_context.getResources().getStringArray(resourceId); + } + + if (m_contextInfo.metaData.containsKey("android.app.repository")) + m_repository = m_contextInfo.metaData.getString("android.app.repository"); + + if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) { + int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id"); + m_qtLibs = m_context.getResources().getStringArray(resourceId); + } + + if (m_contextInfo.metaData.containsKey("android.app.use_local_qt_libs") + && m_contextInfo.metaData.getInt("android.app.use_local_qt_libs") == 1) { + ArrayList<String> libraryList = new ArrayList<String>(); + + + String localPrefix = "/data/local/tmp/qt/"; + if (m_contextInfo.metaData.containsKey("android.app.libs_prefix")) + localPrefix = m_contextInfo.metaData.getString("android.app.libs_prefix"); + + String pluginsPrefix = localPrefix; + + boolean bundlingQtLibs = false; + if (m_contextInfo.metaData.containsKey("android.app.bundle_local_qt_libs") + && m_contextInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) { + localPrefix = m_context.getApplicationInfo().dataDir + "/"; + pluginsPrefix = localPrefix + "qt-reserved-files/"; + cleanOldCacheIfNecessary(localPrefix, pluginsPrefix); + extractBundledPluginsAndImports(pluginsPrefix); + bundlingQtLibs = true; + } + + if (m_qtLibs != null) { + for (int i=0;i<m_qtLibs.length;i++) { + libraryList.add(localPrefix + + "lib/lib" + + m_qtLibs[i] + + ".so"); + } + } + + if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) { + String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":"); + for (String lib : extraLibs) { + if (lib.length() > 0) { + if (lib.startsWith("lib/")) + libraryList.add(localPrefix + lib); + else + libraryList.add(pluginsPrefix + lib); + } + } + } + + + String dexPaths = new String(); + String pathSeparator = System.getProperty("path.separator", ":"); + if (!bundlingQtLibs && m_contextInfo.metaData.containsKey("android.app.load_local_jars")) { + String[] jarFiles = m_contextInfo.metaData.getString("android.app.load_local_jars").split(":"); + for (String jar:jarFiles) { + if (jar.length() > 0) { + if (dexPaths.length() > 0) + dexPaths += pathSeparator; + dexPaths += localPrefix + jar; + } + } + } + + Bundle loaderParams = new Bundle(); + loaderParams.putInt(ERROR_CODE_KEY, 0); + loaderParams.putString(DEX_PATH_KEY, dexPaths); + loaderParams.putString(LOADER_CLASS_NAME_KEY, loaderClassName()); + if (m_contextInfo.metaData.containsKey("android.app.static_init_classes")) { + loaderParams.putStringArray(STATIC_INIT_CLASSES_KEY, + m_contextInfo.metaData.getString("android.app.static_init_classes").split(":")); + } + loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList); + + + String themePath = m_context.getApplicationInfo().dataDir + "/qt-reserved-files/android-style/"; + String stylePath = themePath + m_displayDensity + "/"; + + String extractOption = "full"; + if (m_contextInfo.metaData.containsKey("android.app.extract_android_style")) { + extractOption = m_contextInfo.metaData.getString("android.app.extract_android_style"); + if (!extractOption.equals("full") && !extractOption.equals("minimal") && !extractOption.equals("none")) { + Log.e(QtApplication.QtTAG, "Invalid extract_android_style option \"" + extractOption + "\", defaulting to full"); + extractOption = "full"; + } + } + + if (!(new File(stylePath)).exists() && !extractOption.equals("none")) { + loaderParams.putString(EXTRACT_STYLE_KEY, stylePath); + loaderParams.putBoolean(EXTRACT_STYLE_MINIMAL_KEY, extractOption.equals("minimal")); + if (extractOption.equals("full")) + ENVIRONMENT_VARIABLES += "\tQT_USE_ANDROID_NATIVE_STYLE=1"; + } + ENVIRONMENT_VARIABLES += "\tMINISTRO_ANDROID_STYLE_PATH=" + stylePath + + "\tQT_ANDROID_THEMES_ROOT_PATH=" + themePath; + + loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES + + "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" + + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" + + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"); + + String appParams = null; + if (APPLICATION_PARAMETERS != null) + appParams = APPLICATION_PARAMETERS; + + 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_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 (appParams != null) + loaderParams.putString(APPLICATION_PARAMETERS_KEY, appParams.replace(' ', '\t').trim()); + + loadApplication(loaderParams); + return; + } + + try { + if (!m_context.bindService(new Intent(org.kde.necessitas.ministro.IMinistro.class.getCanonicalName()), + m_ministroConnection, + Context.BIND_AUTO_CREATE)) { + throw new SecurityException(""); + } + } catch (Exception e) { + if (firstStart) { + String msg = "This application requires Ministro service. Would you like to install it?"; + if (m_contextInfo.metaData.containsKey("android.app.ministro_needed_msg")) + msg = m_contextInfo.metaData.getString("android.app.ministro_needed_msg"); + downloadUpgradeMinistro(msg); + } else { + ministroNotFound(); + } + } + } catch (Exception e) { + Log.e(QtApplication.QtTAG, "Can't create main activity", e); + } + } +} diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java new file mode 100644 index 0000000000..71acf9683e --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java @@ -0,0 +1,153 @@ +/* + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import android.app.Service; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.IBinder; + +public class QtService extends Service +{ + QtServiceLoader m_loader = new QtServiceLoader(this); + + + /////////////////////////// forward all notifications //////////////////////////// + /////////////////////////// Super class calls //////////////////////////////////// + /////////////// PLEASE DO NOT CHANGE THE FOLLOWING CODE ////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + @Override + public void onCreate() + { + super.onCreate(); + m_loader.onCreate(); + } + //--------------------------------------------------------------------------- + + @Override + public void onDestroy() + { + super.onDestroy(); + QtApplication.invokeDelegate(); + } + //--------------------------------------------------------------------------- + + @Override + public IBinder onBind(Intent intent) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent); + if (res.invoked) + return (IBinder)res.methodReturns; + else + return null; + } + //--------------------------------------------------------------------------- + + @Override + public void onConfigurationChanged(Configuration newConfig) + { + if (!QtApplication.invokeDelegate(newConfig).invoked) + super.onConfigurationChanged(newConfig); + } + public void super_onConfigurationChanged(Configuration newConfig) + { + super.onConfigurationChanged(newConfig); + } + //--------------------------------------------------------------------------- + + @Override + public void onLowMemory() + { + if (!QtApplication.invokeDelegate().invoked) + super.onLowMemory(); + } + //--------------------------------------------------------------------------- + + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent, flags, startId); + if (res.invoked) + return (Integer) res.methodReturns; + else + return super.onStartCommand(intent, flags, startId); + } + public int super_onStartCommand(Intent intent, int flags, int startId) + { + return super.onStartCommand(intent, flags, startId); + } + //--------------------------------------------------------------------------- + + @Override + public void onTaskRemoved(Intent rootIntent) + { + if (!QtApplication.invokeDelegate(rootIntent).invoked) + super.onTaskRemoved(rootIntent); + } + public void super_onTaskRemoved(Intent rootIntent) + { + super.onTaskRemoved(rootIntent); + } + //--------------------------------------------------------------------------- + + @Override + public void onTrimMemory(int level) + { + if (!QtApplication.invokeDelegate(level).invoked) + super.onTrimMemory(level); + } + public void super_onTrimMemory(int level) + { + super.onTrimMemory(level); + } + //--------------------------------------------------------------------------- + + @Override + public boolean onUnbind(Intent intent) + { + QtApplication.InvokeResult res = QtApplication.invokeDelegate(intent); + if (res.invoked) + return (Boolean) res.methodReturns; + else + return super.onUnbind(intent); + } + public boolean super_onUnbind(Intent intent) + { + return super.onUnbind(intent); + } + //--------------------------------------------------------------------------- +} diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java new file mode 100644 index 0000000000..e64018f0a8 --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java @@ -0,0 +1,77 @@ +/* + Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Contact: http://www.qt.io/licensing/ + + Commercial License Usage + Licensees holding valid commercial Qt licenses may use this file in + accordance with the commercial license agreement provided with the + Software or, alternatively, in accordance with the terms contained in + a written agreement between you and The Qt Company. For licensing terms + and conditions see http://www.qt.io/terms-conditions. For further + information use the contact form at http://www.qt.io/contact-us. + + BSD License Usage + Alternatively, this file may be used under the BSD license as follows: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +package org.qtproject.qt5.android.bindings; + +import android.content.ComponentName; +import android.content.pm.PackageManager; + +public class QtServiceLoader extends QtLoader { + QtService m_service; + QtServiceLoader(QtService service) { + super(service); + m_service = service; + } + + public void onCreate() { + try { + 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; + } + + if (QtApplication.m_delegateObject != null && QtApplication.onCreate != null) + QtApplication.invokeDelegateMethod(QtApplication.onCreate); + startApp(true); + } + + @Override + protected void finish() { + m_service.stopSelf(); + } + + @Override + protected String loaderClassName() { + return "org.qtproject.qt5.android.QtServiceDelegate"; + } + + @Override + protected Class<?> contextClassName() { + return android.app.Service.class; + } +} diff --git a/src/android/templates/AndroidManifest.xml b/src/android/templates/AndroidManifest.xml index 262a5f6dba..808cc82ae9 100644 --- a/src/android/templates/AndroidManifest.xml +++ b/src/android/templates/AndroidManifest.xml @@ -10,6 +10,11 @@ <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> + + <!-- Application arguments --> + <!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ --> + <!-- Application arguments --> + <meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> <meta-data android:name="android.app.repository" android:value="default"/> @@ -32,9 +37,8 @@ <!-- Messages maps --> <!-- Splash screen --> - <!-- - <meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/> - --> + <!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ --> + <!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ --> <!-- Splash screen --> <!-- Background running --> @@ -48,9 +52,64 @@ <!-- auto screen scale factor --> <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/> <!-- auto screen scale factor --> - </activity> + + <!-- extract android style --> + <!-- available android:values : + * full - useful QWidget & Quick Controls 1 apps + * minimal - useful for Quick Controls 2 apps, it is much faster than "full" + * none - useful for apps that don't use any of the above Qt modules + --> + <meta-data android:name="android.app.extract_android_style" android:value="full"/> + <!-- extract android style --> + </activity> + + <!--service android:process=":qt" android:name="org.qtproject.qt5.android.bindings.QtService"--> + <!-- android:process=":qt" is needed to force the service to run on a separate process than the Activity --> + + <!-- Application arguments --> + <!-- meta-data android:name="android.app.arguments" android:value="-service"/ --> + <!-- Application arguments --> + <!-- If you're using the same application (.so file) for activity and also for service, then you + need to use *android.app.arguments* to pass some arguments to your service in order to know which + one is which + --> + + <!-- Ministro --> + <!-- meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/> + <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/> + <meta-data android:name="android.app.repository" android:value="default"/> + <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"/ --> + <!-- Ministro --> + + <!-- Deploy Qt libs as part of package --> + <!-- meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/> + <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"/ --> + <!-- Deploy Qt libs as part of package --> + + <!-- Run with local libs --> + <!-- meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/> + <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="-- %%INSERT_LOCAL_LIBS%% --"/> + <meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/> + <meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/ --> + <!-- Run with local libs --> + + <!-- Messages maps --> + <!-- meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/> + <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/> + <meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/ --> + <!-- Messages maps --> + + + <!-- Background running --> + <!-- meta-data android:name="android.app.background_running" android:value="true"/ --> + <!-- Background running --> + <!--/service --> </application> - <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="14"/> + + <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> <!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application. |