diff options
67 files changed, 2051 insertions, 1153 deletions
@@ -3087,7 +3087,7 @@ elif [ $COMMERCIAL_USER = "yes" ]; then exit 1 fi elif [ $COMMERCIAL_USER = "no" ]; then - # Open Source edition - may only be used under the terms of the LGPLv3 or LGPLv21. + # Open Source edition - may only be used under the terms of the LGPLv3 or GPLv2. Licensee="Open Source" Edition="OpenSource" EditionString="Open Source" @@ -3103,15 +3103,15 @@ if [ "$Edition" = "OpenSource" ]; then while true; do if [ "$CFG_ANDROID_STYLE_ASSETS" = "no" ] || [ "$XPLATFORM_ANDROID" = "no" ]; then echo "You are licensed to use this software under the terms of" - echo "the Lesser GNU General Public License (LGPL) versions 2.1." - echo "You are also licensed to use this software under the terms of" echo "the GNU Lesser General Public License (LGPL) versions 3." + echo "You are also licensed to use this software under the terms of" + echo "the GNU General Public License (GPL) versions 2." affix="either" - showLGPL2="yes" + showGPL2="yes" else echo "You are licensed to use this software under the terms of" echo "the GNU Lesser General Public License (LGPL) versions 3." - showLGPL2="no" + showGPL2="no" affix="the" fi @@ -3120,11 +3120,11 @@ if [ "$Edition" = "OpenSource" ]; then echo "You have already accepted the terms of the $EditionString license." acceptance=yes else - if [ -f "$relpath/LICENSE.LGPLv3" ]; then - echo "Type '3' to view the GNU Lesser General Public License version 3." + if [ -f "$relpath/LICENSE.LGPL3" ]; then + echo "Type 'L' to view the GNU Lesser General Public License version 3." fi - if [ "$showLGPL2" = "yes" ]; then - echo "Type 'L' to view the Lesser GNU General Public License version 2.1." + if [ "$showGPL2" = "yes" ]; then + echo "Type 'G' to view the GNU General Public License version 2." fi echo "Type 'yes' to accept this license offer." echo "Type 'no' to decline this license offer." @@ -3139,10 +3139,10 @@ if [ "$Edition" = "OpenSource" ]; then echo "You are not licensed to use this software." echo exit 1 - elif [ "$acceptance" = "3" ]; then - more "$relpath/LICENSE.LGPLv3" - elif [ "$acceptance" = "L" ] && [ "$showLGPL2" = "yes" ]; then - more "$relpath/LICENSE.LGPLv21" + elif [ "$acceptance" = "L" ]; then + more "$relpath/LICENSE.LGPL3" + elif [ "$acceptance" = "G" ] && [ "$showGPL2" = "yes" ]; then + more "$relpath/LICENSE.GPL2" fi done elif [ "$Edition" = "Preview" ]; then diff --git a/mkspecs/features/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index 46ed51deb4..ab37b1740f 100644 --- a/mkspecs/features/mac/sdk.prf +++ b/mkspecs/features/mac/sdk.prf @@ -17,7 +17,7 @@ defineReplace(xcodeSDKInfo) { cache(QMAKE_MAC_SDK.$${sdk}.$${info}, set stash, QMAKE_MAC_SDK.$${sdk}.$${info}) } - return($$eval(QMAKE_MAC_SDK.$${QMAKE_MAC_SDK}.$${info})) + return($$eval(QMAKE_MAC_SDK.$${sdk}.$${info})) } QMAKE_MAC_SDK_PATH = $$xcodeSDKInfo(Path) diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index b53617ba2a..4b40451c96 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -122,7 +122,7 @@ for(ever) { QMAKE_FRAMEWORKPATH *= $$MODULE_FRAMEWORKS !isEmpty(MODULE_MODULE) { contains(MODULE_CONFIG, lib_bundle) { - LIBS$$var_sfx += -framework $${MODULE_MODULE}$$qtFrameworkPlatformTargetSuffix() + LIBS$$var_sfx += -framework $$MODULE_MODULE } else { !isEmpty(MODULE_LIBS_ADD): \ LIBS$$var_sfx += -L$$MODULE_LIBS_ADD diff --git a/mkspecs/features/qt_functions.prf b/mkspecs/features/qt_functions.prf index 0046cce9ad..2756f90b01 100644 --- a/mkspecs/features/qt_functions.prf +++ b/mkspecs/features/qt_functions.prf @@ -14,16 +14,6 @@ defineReplace(qtPlatformTargetSuffix) { return($$suffix) } -# suffix for the -framework linker flag when the exectuable's name -# differs from the bundle's, for example -framework QtCore,_debug -# links to QtCore.framework/QtCore_debug -defineReplace(qtFrameworkPlatformTargetSuffix) { - suffix = $$qtPlatformTargetSuffix() - !isEmpty(suffix): \ - suffix = ,$$suffix - return($$suffix) -} - defineReplace(qtLibraryTarget) { LIBRARY_NAME = $$1 CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp index cc6e5754ec..5e655464b7 100644 --- a/qmake/generators/win32/msvc_objectmodel.cpp +++ b/qmake/generators/win32/msvc_objectmodel.cpp @@ -1758,10 +1758,6 @@ VCManifestTool::VCManifestTool() { } -VCManifestTool::~VCManifestTool() -{ -} - bool VCManifestTool::parseOption(const char *option) { Q_UNUSED(option); diff --git a/qmake/generators/win32/msvc_objectmodel.h b/qmake/generators/win32/msvc_objectmodel.h index 35aa288887..dfbfaacfa8 100644 --- a/qmake/generators/win32/msvc_objectmodel.h +++ b/qmake/generators/win32/msvc_objectmodel.h @@ -477,7 +477,7 @@ class VCCLCompilerTool : public VCToolBase public: // Functions VCCLCompilerTool(); - virtual ~VCCLCompilerTool(){} + bool parseOption(const char* option); // Variables @@ -574,7 +574,7 @@ class VCLinkerTool : public VCToolBase public: // Functions VCLinkerTool(); - virtual ~VCLinkerTool(){} + bool parseOption(const char* option); // Variables @@ -668,7 +668,7 @@ class VCManifestTool : public VCToolBase { public: VCManifestTool(); - ~VCManifestTool(); + bool parseOption(const char* option); triState EmbedManifest; @@ -679,7 +679,7 @@ class VCMIDLTool : public VCToolBase public: // Functions VCMIDLTool(); - virtual ~VCMIDLTool(){} + bool parseOption(const char* option); // Variables @@ -733,7 +733,7 @@ class VCLibrarianTool : public VCToolBase public: // Functions VCLibrarianTool(); - virtual ~VCLibrarianTool(){} + bool parseOption(const char*){ return false; } // Variables @@ -754,7 +754,7 @@ class VCCustomBuildTool : public VCToolBase public: // Functions VCCustomBuildTool(); - virtual ~VCCustomBuildTool(){} + bool parseOption(const char*){ return false; } // Variables @@ -773,7 +773,7 @@ class VCResourceCompilerTool : public VCToolBase public: // Functions VCResourceCompilerTool(); - virtual ~VCResourceCompilerTool(){} + bool parseOption(const char*){ return false; } // Variables @@ -794,7 +794,6 @@ class VCDeploymentTool public: // Functions VCDeploymentTool(); - virtual ~VCDeploymentTool() {} // Variables QString DeploymentTag; @@ -808,7 +807,7 @@ class VCEventTool : public VCToolBase protected: // Functions VCEventTool(const QString &eventName); - virtual ~VCEventTool(){} + bool parseOption(const char*){ return false; } public: @@ -825,28 +824,24 @@ class VCPostBuildEventTool : public VCEventTool { public: VCPostBuildEventTool(); - ~VCPostBuildEventTool(){} }; class VCPreBuildEventTool : public VCEventTool { public: VCPreBuildEventTool(); - ~VCPreBuildEventTool(){} }; class VCPreLinkEventTool : public VCEventTool { public: VCPreLinkEventTool(); - ~VCPreLinkEventTool(){} }; class VCWinDeployQtTool : public VCToolBase { public: VCWinDeployQtTool() {} - ~VCWinDeployQtTool() {} protected: bool parseOption(const char *) { return false; } diff --git a/src/android/jar/jar.pri b/src/android/jar/jar.pri index b45b353f95..58caacb837 100644 --- a/src/android/jar/jar.pri +++ b/src/android/jar/jar.pri @@ -16,7 +16,8 @@ JAVASOURCES += \ $$PATHPREFIX/QtNative.java \ $$PATHPREFIX/QtNativeLibrariesDir.java \ $$PATHPREFIX/QtSurface.java \ - $$PATHPREFIX/ExtractStyle.java + $$PATHPREFIX/ExtractStyle.java \ + $$PATHPREFIX/QtServiceDelegate.java # install target.path = $$[QT_INSTALL_PREFIX]/jar 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 14c7a03a36..c434cc24b3 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -953,6 +953,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 @@ -1200,6 +1201,8 @@ public class QtActivityDelegate private void setActionBarVisibility(boolean visible) { + if (m_activity.getActionBar() == null) + return; if (ViewConfiguration.get(m_activity).hasPermanentMenuKey() || !visible) m_activity.getActionBar().hide(); else 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 d9be4c7d9f..5d4f6e791a 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2016 BogDan Vatra <bogdan@kde.org> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -45,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; @@ -72,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 @@ -115,6 +118,14 @@ public class QtNative } } + public static Service service() + { + synchronized (m_mainActivityMutex) { + return m_service; + } + } + + public static QtActivityDelegate activityDelegate() { synchronized (m_mainActivityMutex) { @@ -122,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; @@ -186,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) { @@ -319,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(); } }); } @@ -452,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); } }); } @@ -467,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); } }); } @@ -477,7 +509,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.resetSoftwareKeyboard(); + if (m_activityDelegate != null) + m_activityDelegate.resetSoftwareKeyboard(); } }); } @@ -487,7 +520,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.hideSoftwareKeyboard(); + if (m_activityDelegate != null) + m_activityDelegate.hideSoftwareKeyboard(); } }); } @@ -497,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(); } }); @@ -505,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) @@ -540,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); } }); } @@ -550,7 +597,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.closeContextMenu(); + if (m_activityDelegate != null) + m_activityDelegate.closeContextMenu(); } }); } @@ -560,7 +608,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activityDelegate.resetOptionsMenu(); + if (m_activityDelegate != null) + m_activityDelegate.resetOptionsMenu(); } }); } @@ -570,7 +619,8 @@ public class QtNative runAction(new Runnable() { @Override public void run() { - m_activity.openOptionsMenu(); + if (m_activity != null) + m_activity.openOptionsMenu(); } }); } @@ -607,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); } }); } @@ -617,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); } }); } @@ -627,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); } }); } @@ -637,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); } }); } @@ -647,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); } }); } @@ -657,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); } }); } @@ -737,4 +793,7 @@ public class QtNative public static native void onNewIntent(Intent data); 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 8481baec54..ff3bf19383 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNativeLibrariesDir.java @@ -40,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/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/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java index 4c2e612238..678420dae7 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,601 +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() { + m_loader = new QtActivityLoader(this); if (Build.VERSION.SDK_INT >= 21) { - QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; - QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; + m_loader.QT_ANDROID_THEMES = new String[] {"Theme_Holo_Light"}; + m_loader.QT_ANDROID_DEFAULT_THEME = "Theme_Holo_Light"; } else { - QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; - QT_ANDROID_DEFAULT_THEME = "Theme_DeviceDefault_Light"; + m_loader.QT_ANDROID_THEMES = new String[] {"Theme_DeviceDefault_Light"}; + m_loader.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; - } else { - return false; - } - } - - 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 //////////////////////////////////// @@ -749,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) @@ -839,79 +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; - } - - if (Build.VERSION.SDK_INT < 16) { - // fatal error, show the error and quit - AlertDialog errorDialog = new AlertDialog.Builder(QtActivity.this).create(); - if (m_activityInfo.metaData.containsKey("android.app.unsupported_android_version")) - errorDialog.setMessage(m_activityInfo.metaData.getString("android.app.unsupported_android_version")); - else - errorDialog.setMessage("Unsupported Android version."); - errorDialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - finish(); - } - }); - errorDialog.show(); - return; - } - - try { - setTheme(Class.forName("android.R$style").getDeclaredField(QT_ANDROID_DEFAULT_THEME).getInt(null)); - } catch (Exception e) { - e.printStackTrace(); - } - - requestWindowFeature(Window.FEATURE_ACTION_BAR); - - 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 - // Note: QtActivityDelegate handles updating the splash screen - // in onConfigurationChanged, change that too if you are changing - // how the splash screen should be displayed - 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..836a7677b3 --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java @@ -0,0 +1,193 @@ +/* + 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 splash screen is defined, then show it + // Note: QtActivityDelegate handles updating the splash screen + // in onConfigurationChanged, change that too if you are changing + // how the splash screen should be displayed + if (m_contextInfo.metaData.containsKey("android.app.splash_screen_drawable")) + m_activity.getWindow().setBackgroundDrawableResource(m_contextInfo.metaData.getInt("android.app.splash_screen_drawable")); + else + m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000)); + + 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..0963220b0d --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java @@ -0,0 +1,655 @@ +/* + 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"; + + /// 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_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. + + 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 + "/"; + 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"); + + 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..c84bdd63c3 --- /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 (int) 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 2a6d0b6fa3..02fd0ce23b 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,7 @@ <!-- 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"/ --> <!-- Splash screen --> <!-- Background running --> @@ -49,7 +52,53 @@ <meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/> <!-- auto screen scale factor --> </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="16" android:targetSdkVersion="16"/> <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index e4fa0f7391..3ea121fcc2 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -925,17 +925,17 @@ Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined in \relates <QtGlobal> \since 5.7 - qOverload() returns a pointer to an overloaded function. The template + Returns a pointer to an overloaded function. The template parameter is the list of the argument types of the function. \a functionPointer is the pointer to the (member) function: \snippet code/src_corelib_global_qglobal.cpp 52 - If a member function is also const-overladed \l qConstOverload and - \l qNonConstOverload needs to be used. + If a member function is also const-overloaded \l qConstOverload and + \l qNonConstOverload need to be used. - qOverload() needs C++14 enabled. In C++11 only code the helper - classes QOverload, QConstOverload, and QNonConstOverload could be used directly: + qOverload() requires C++14 enabled. In C++11-only code, the helper + classes QOverload, QConstOverload, and QNonConstOverload can be used directly: \snippet code/src_corelib_global_qglobal.cpp 53 @@ -946,7 +946,7 @@ Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined in \relates <QtGlobal> \since 5.7 - qConstOverload() returns a pointer to an constant member function: + Returns a pointer to a constant member function: \snippet code/src_corelib_global_qglobal.cpp 54 @@ -957,7 +957,7 @@ Q_STATIC_ASSERT_X(QT_POINTER_SIZE == sizeof(void *), "QT_POINTER_SIZE defined in \relates <QtGlobal> \since 5.7 - qNonConstOverload() eturns a pointer to an non constant member function: + Returns a pointer to a non-constant member function: \snippet code/src_corelib_global_qglobal.cpp 54 diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 25973a3ae4..b6d8e6fdc8 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -506,6 +506,7 @@ public: AA_EnableHighDpiScaling = 20, AA_DisableHighDpiScaling = 21, AA_UseStyleSheetPropagationInWidgetStyles = 22, // ### Qt 6: remove me + AA_DontUseNativeDialogs = 23, // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 1efcce393d..b17a1ab48b 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -230,6 +230,10 @@ \l{The Style Sheet Syntax#Inheritance}{The Style Sheet Syntax - Inheritance} for more details. This value has been added in Qt 5.7. + \value AA_DontUseNativeDialogs All dialogs created while this attribute is + set to true won't use the native dialogs provided by the platform. + This value has been added in Qt 5.7. + The following values are obsolete: \value AA_ImmediateWidgetCreation This attribute is no longer fully diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp index 1b0db64815..2a44daf8b5 100644 --- a/src/corelib/io/qstandardpaths_android.cpp +++ b/src/corelib/io/qstandardpaths_android.cpp @@ -63,12 +63,15 @@ static QJNIObjectPrivate applicationContext() if (appCtx.isValid()) return appCtx; - QJNIObjectPrivate activity(QtAndroidPrivate::activity()); - if (!activity.isValid()) - return appCtx; + QJNIObjectPrivate context(QtAndroidPrivate::activity()); + if (!context.isValid()) { + context = QtAndroidPrivate::service(); + if (!context.isValid()) + return appCtx; + } - appCtx = activity.callObjectMethod("getApplicationContext", - "()Landroid/content/Context;"); + appCtx = context.callObjectMethod("getApplicationContext", + "()Landroid/content/Context;"); return appCtx; } @@ -137,10 +140,6 @@ static QString getExternalFilesDir(const char *directoryField = 0) if (!path.isEmpty()) return path; - QJNIObjectPrivate activity(QtAndroidPrivate::activity()); - if (!activity.isValid()) - return QString(); - QJNIObjectPrivate appCtx = applicationContext(); if (!appCtx.isValid()) return QString(); diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp index 2324a615d5..f576ed0d1c 100644 --- a/src/corelib/kernel/qjnihelpers.cpp +++ b/src/corelib/kernel/qjnihelpers.cpp @@ -51,6 +51,7 @@ QT_BEGIN_NAMESPACE static JavaVM *g_javaVM = Q_NULLPTR; static jobject g_jActivity = Q_NULLPTR; +static jobject g_jService = Q_NULLPTR; static jobject g_jClassLoader = Q_NULLPTR; static jint g_androidSdkVersion = 0; static jclass g_jNativeClass = Q_NULLPTR; @@ -239,6 +240,32 @@ static void setAndroidSdkVersion(JNIEnv *env) g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID); } +static void setNativeActivity(JNIEnv *env, jclass, jobject activity) +{ + if (g_jActivity != 0) + env->DeleteGlobalRef(g_jActivity); + + if (activity != 0) { + g_jActivity = env->NewGlobalRef(activity); + env->DeleteLocalRef(activity); + } else { + g_jActivity = 0; + } +} + +static void setNativeService(JNIEnv *env, jclass, jobject service) +{ + if (g_jService != 0) + env->DeleteGlobalRef(g_jService); + + if (service != 0) { + g_jService = env->NewGlobalRef(service); + env->DeleteLocalRef(service); + } else { + g_jService = 0; + } +} + jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) { jclass jQtNative = env->FindClass("org/qtproject/qt5/android/QtNative"); @@ -254,10 +281,21 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) return JNI_ERR; jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID); + + if (exceptionCheck(env)) + return JNI_ERR; + + jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative, + "service", + "()Landroid/app/Service;"); + if (exceptionCheck(env)) return JNI_ERR; + jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID); + if (exceptionCheck(env)) + return JNI_ERR; jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative, "classLoader", @@ -274,14 +312,22 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) g_jClassLoader = env->NewGlobalRef(classLoader); env->DeleteLocalRef(classLoader); - g_jActivity = env->NewGlobalRef(activity); - env->DeleteLocalRef(activity); + if (activity) { + g_jActivity = env->NewGlobalRef(activity); + env->DeleteLocalRef(activity); + } + if (service) { + g_jService = env->NewGlobalRef(service); + env->DeleteLocalRef(service); + } g_javaVM = vm; static const JNINativeMethod methods[] = { {"runPendingCppRunnables", "()V", reinterpret_cast<void *>(runPendingCppRunnables)}, {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)}, {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)}, + {"setNativeActivity", "(Landroid/app/Activity;)V", reinterpret_cast<void *>(setNativeActivity)}, + {"setNativeService", "(Landroid/app/Service;)V", reinterpret_cast<void *>(setNativeService)} }; const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK); @@ -305,6 +351,11 @@ jobject QtAndroidPrivate::activity() return g_jActivity; } +jobject QtAndroidPrivate::service() +{ + return g_jService; +} + JavaVM *QtAndroidPrivate::javaVM() { return g_javaVM; diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h index 1bffd26e08..34bdbf6c80 100644 --- a/src/corelib/kernel/qjnihelpers_p.h +++ b/src/corelib/kernel/qjnihelpers_p.h @@ -100,6 +100,7 @@ namespace QtAndroidPrivate typedef std::function<void()> Runnable; Q_CORE_EXPORT jobject activity(); + Q_CORE_EXPORT jobject service(); Q_CORE_EXPORT JavaVM *javaVM(); Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env); jobject classLoader(); diff --git a/src/corelib/mimetypes/qmimeglobpattern_p.h b/src/corelib/mimetypes/qmimeglobpattern_p.h index 0088931b5c..7b4ecd2f6a 100644 --- a/src/corelib/mimetypes/qmimeglobpattern_p.h +++ b/src/corelib/mimetypes/qmimeglobpattern_p.h @@ -88,7 +88,14 @@ public: m_pattern = m_pattern.toLower(); } } - ~QMimeGlobPattern() {} + + void swap(QMimeGlobPattern &other) Q_DECL_NOTHROW + { + qSwap(m_pattern, other.m_pattern); + qSwap(m_mimeType, other.m_mimeType); + qSwap(m_weight, other.m_weight); + qSwap(m_caseSensitivity, other.m_caseSensitivity); + } bool matchFileName(const QString &filename) const; @@ -103,6 +110,7 @@ private: int m_weight; Qt::CaseSensitivity m_caseSensitivity; }; +Q_DECLARE_SHARED(QMimeGlobPattern) class QMimeGlobPatternList : public QList<QMimeGlobPattern> { diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index 1b8acc0f6e..09473caa78 100644 --- a/src/corelib/mimetypes/qmimemagicrule.cpp +++ b/src/corelib/mimetypes/qmimemagicrule.cpp @@ -82,36 +82,17 @@ QByteArray QMimeMagicRule::typeName(QMimeMagicRule::Type theType) return magicRuleTypes_string + magicRuleTypes_indices[theType]; } -class QMimeMagicRulePrivate -{ -public: - bool operator==(const QMimeMagicRulePrivate &other) const; - - QMimeMagicRule::Type type; - QByteArray value; - int startPos; - int endPos; - QByteArray mask; - - QByteArray pattern; - quint32 number; - quint32 numberMask; - - typedef bool (*MatchFunction)(const QMimeMagicRulePrivate *d, const QByteArray &data); - MatchFunction matchFunction; -}; - -bool QMimeMagicRulePrivate::operator==(const QMimeMagicRulePrivate &other) const +bool QMimeMagicRule::operator==(const QMimeMagicRule &other) const { - return type == other.type && - value == other.value && - startPos == other.startPos && - endPos == other.endPos && - mask == other.mask && - pattern == other.pattern && - number == other.number && - numberMask == other.numberMask && - matchFunction == other.matchFunction; + return m_type == other.m_type && + m_value == other.m_value && + m_startPos == other.m_startPos && + m_endPos == other.m_endPos && + m_mask == other.m_mask && + m_pattern == other.m_pattern && + m_number == other.m_number && + m_numberMask == other.m_numberMask && + m_matchFunction == other.m_matchFunction; } // Used by both providers @@ -164,23 +145,23 @@ bool QMimeMagicRule::matchSubstring(const char *dataPtr, int dataSize, int range return true; } -static bool matchString(const QMimeMagicRulePrivate *d, const QByteArray &data) +bool QMimeMagicRule::matchString(const QByteArray &data) const { - const int rangeLength = d->endPos - d->startPos + 1; - return QMimeMagicRule::matchSubstring(data.constData(), data.size(), d->startPos, rangeLength, d->pattern.size(), d->pattern.constData(), d->mask.constData()); + const int rangeLength = m_endPos - m_startPos + 1; + return QMimeMagicRule::matchSubstring(data.constData(), data.size(), m_startPos, rangeLength, m_pattern.size(), m_pattern.constData(), m_mask.constData()); } template <typename T> -static bool matchNumber(const QMimeMagicRulePrivate *d, const QByteArray &data) +bool QMimeMagicRule::matchNumber(const QByteArray &data) const { - const T value(d->number); - const T mask(d->numberMask); + const T value(m_number); + const T mask(m_numberMask); - //qDebug() << "matchNumber" << "0x" << QString::number(d->number, 16) << "size" << sizeof(T); - //qDebug() << "mask" << QString::number(d->numberMask, 16); + //qDebug() << "matchNumber" << "0x" << QString::number(m_number, 16) << "size" << sizeof(T); + //qDebug() << "mask" << QString::number(m_numberMask, 16); - const char *p = data.constData() + d->startPos; - const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), d->endPos + 1); + const char *p = data.constData() + m_startPos; + const char *e = data.constData() + qMin(data.size() - int(sizeof(T)), m_endPos + 1); for ( ; p <= e; ++p) { if ((*reinterpret_cast<const T*>(p) & mask) == (value & mask)) return true; @@ -242,105 +223,102 @@ static inline QByteArray makePattern(const QByteArray &value) // <match value="must be converted with BinHex" type="string" offset="11"/> // <match value="0x9501" type="big16" offset="0:64"/> -QMimeMagicRule::QMimeMagicRule(const QString &typeStr, - const QByteArray &theValue, +QMimeMagicRule::QMimeMagicRule(const QString &type, + const QByteArray &value, const QString &offsets, - const QByteArray &theMask, - QString *errorString) : - d(new QMimeMagicRulePrivate) + const QByteArray &mask, + QString *errorString) + : m_type(QMimeMagicRule::type(type.toLatin1())), + m_value(value), + m_mask(mask), + m_matchFunction(nullptr) { - d->value = theValue; - d->mask = theMask; - d->matchFunction = 0; - - d->type = QMimeMagicRule::type(typeStr.toLatin1()); - if (d->type == Invalid) { - *errorString = QStringLiteral("Type %s is not supported").arg(typeStr); - } + if (m_type == Invalid) + *errorString = QStringLiteral("Type %s is not supported").arg(type); // Parse for offset as "1" or "1:10" const int colonIndex = offsets.indexOf(QLatin1Char(':')); const QString startPosStr = colonIndex == -1 ? offsets : offsets.mid(0, colonIndex); const QString endPosStr = colonIndex == -1 ? offsets : offsets.mid(colonIndex + 1); - if (!QMimeTypeParserBase::parseNumber(startPosStr, &d->startPos, errorString) || - !QMimeTypeParserBase::parseNumber(endPosStr, &d->endPos, errorString)) { - d->type = Invalid; + if (!QMimeTypeParserBase::parseNumber(startPosStr, &m_startPos, errorString) || + !QMimeTypeParserBase::parseNumber(endPosStr, &m_endPos, errorString)) { + m_type = Invalid; return; } - if (d->value.isEmpty()) { - d->type = Invalid; + if (m_value.isEmpty()) { + m_type = Invalid; if (errorString) *errorString = QLatin1String("Invalid empty magic rule value"); return; } - if (d->type >= Host16 && d->type <= Byte) { + if (m_type >= Host16 && m_type <= Byte) { bool ok; - d->number = d->value.toUInt(&ok, 0); // autodetect + m_number = m_value.toUInt(&ok, 0); // autodetect base if (!ok) { - d->type = Invalid; + m_type = Invalid; if (errorString) *errorString = QString::fromLatin1("Invalid magic rule value \"%1\"").arg( - QString::fromLatin1(d->value)); + QString::fromLatin1(m_value)); return; } - d->numberMask = !d->mask.isEmpty() ? d->mask.toUInt(&ok, 0) : 0; // autodetect + m_numberMask = !m_mask.isEmpty() ? m_mask.toUInt(&ok, 0) : 0; // autodetect base } - switch (d->type) { + switch (m_type) { case String: - d->pattern = makePattern(d->value); - d->pattern.squeeze(); - if (!d->mask.isEmpty()) { - if (d->mask.size() < 4 || !d->mask.startsWith("0x")) { - d->type = Invalid; + m_pattern = makePattern(m_value); + m_pattern.squeeze(); + if (!m_mask.isEmpty()) { + if (m_mask.size() < 4 || !m_mask.startsWith("0x")) { + m_type = Invalid; if (errorString) *errorString = QString::fromLatin1("Invalid magic rule mask \"%1\"").arg( - QString::fromLatin1(d->mask)); + QString::fromLatin1(m_mask)); return; } const QByteArray &tempMask = QByteArray::fromHex(QByteArray::fromRawData( - d->mask.constData() + 2, d->mask.size() - 2)); - if (tempMask.size() != d->pattern.size()) { - d->type = Invalid; + m_mask.constData() + 2, m_mask.size() - 2)); + if (tempMask.size() != m_pattern.size()) { + m_type = Invalid; if (errorString) *errorString = QString::fromLatin1("Invalid magic rule mask size \"%1\"").arg( - QString::fromLatin1(d->mask)); + QString::fromLatin1(m_mask)); return; } - d->mask = tempMask; + m_mask = tempMask; } else { - d->mask.fill(char(-1), d->pattern.size()); + m_mask.fill(char(-1), m_pattern.size()); } - d->mask.squeeze(); - d->matchFunction = matchString; + m_mask.squeeze(); + m_matchFunction = &QMimeMagicRule::matchString; break; case Byte: - if (d->number <= quint8(-1)) { - if (d->numberMask == 0) - d->numberMask = quint8(-1); - d->matchFunction = matchNumber<quint8>; + if (m_number <= quint8(-1)) { + if (m_numberMask == 0) + m_numberMask = quint8(-1); + m_matchFunction = &QMimeMagicRule::matchNumber<quint8>; } break; case Big16: case Host16: case Little16: - if (d->number <= quint16(-1)) { - d->number = d->type == Little16 ? qFromLittleEndian<quint16>(d->number) : qFromBigEndian<quint16>(d->number); - if (d->numberMask == 0) - d->numberMask = quint16(-1); - d->matchFunction = matchNumber<quint16>; + if (m_number <= quint16(-1)) { + m_number = m_type == Little16 ? qFromLittleEndian<quint16>(m_number) : qFromBigEndian<quint16>(m_number); + if (m_numberMask == 0) + m_numberMask = quint16(-1); + m_matchFunction = &QMimeMagicRule::matchNumber<quint16>; } break; case Big32: case Host32: case Little32: - if (d->number <= quint32(-1)) { - d->number = d->type == Little32 ? qFromLittleEndian<quint32>(d->number) : qFromBigEndian<quint32>(d->number); - if (d->numberMask == 0) - d->numberMask = quint32(-1); - d->matchFunction = matchNumber<quint32>; + if (m_number <= quint32(-1)) { + m_number = m_type == Little32 ? qFromLittleEndian<quint32>(m_number) : qFromBigEndian<quint32>(m_number); + if (m_numberMask == 0) + m_numberMask = quint32(-1); + m_matchFunction = &QMimeMagicRule::matchNumber<quint32>; } break; default: @@ -348,65 +326,19 @@ QMimeMagicRule::QMimeMagicRule(const QString &typeStr, } } -QMimeMagicRule::QMimeMagicRule(const QMimeMagicRule &other) : - d(new QMimeMagicRulePrivate(*other.d)) -{ -} - -QMimeMagicRule::~QMimeMagicRule() -{ -} - -QMimeMagicRule &QMimeMagicRule::operator=(const QMimeMagicRule &other) -{ - *d = *other.d; - return *this; -} - -bool QMimeMagicRule::operator==(const QMimeMagicRule &other) const -{ - return d == other.d || - *d == *other.d; -} - -QMimeMagicRule::Type QMimeMagicRule::type() const -{ - return d->type; -} - -QByteArray QMimeMagicRule::value() const -{ - return d->value; -} - -int QMimeMagicRule::startPos() const -{ - return d->startPos; -} - -int QMimeMagicRule::endPos() const -{ - return d->endPos; -} - QByteArray QMimeMagicRule::mask() const { - QByteArray result = d->mask; - if (d->type == String) { + QByteArray result = m_mask; + if (m_type == String) { // restore '0x' result = "0x" + result.toHex(); } return result; } -bool QMimeMagicRule::isValid() const -{ - return d->matchFunction; -} - bool QMimeMagicRule::matches(const QByteArray &data) const { - const bool ok = d->matchFunction && d->matchFunction(d.data(), data); + const bool ok = m_matchFunction && (this->*m_matchFunction)(data); if (!ok) return false; diff --git a/src/corelib/mimetypes/qmimemagicrule_p.h b/src/corelib/mimetypes/qmimemagicrule_p.h index 84c2fe2174..26f93d96cc 100644 --- a/src/corelib/mimetypes/qmimemagicrule_p.h +++ b/src/corelib/mimetypes/qmimemagicrule_p.h @@ -61,7 +61,6 @@ QT_BEGIN_NAMESPACE -class QMimeMagicRulePrivate; class QMimeMagicRule { public: @@ -69,20 +68,29 @@ public: QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, const QByteArray &mask, QString *errorString); - QMimeMagicRule(const QMimeMagicRule &other); - ~QMimeMagicRule(); - QMimeMagicRule &operator=(const QMimeMagicRule &other); + void swap(QMimeMagicRule &other) Q_DECL_NOTHROW + { + qSwap(m_type, other.m_type); + qSwap(m_value, other.m_value); + qSwap(m_startPos, other.m_startPos); + qSwap(m_endPos, other.m_endPos); + qSwap(m_mask, other.m_mask); + qSwap(m_pattern, other.m_pattern); + qSwap(m_number, other.m_number); + qSwap(m_numberMask, other.m_numberMask); + qSwap(m_matchFunction, other.m_matchFunction); + } bool operator==(const QMimeMagicRule &other) const; - Type type() const; - QByteArray value() const; - int startPos() const; - int endPos() const; + Type type() const { return m_type; } + QByteArray value() const { return m_value; } + int startPos() const { return m_startPos; } + int endPos() const { return m_endPos; } QByteArray mask() const; - bool isValid() const; + bool isValid() const { return m_matchFunction != Q_NULLPTR; } bool matches(const QByteArray &data) const; @@ -94,9 +102,26 @@ public: static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask); private: - const QScopedPointer<QMimeMagicRulePrivate> d; + Type m_type; + QByteArray m_value; + int m_startPos; + int m_endPos; + QByteArray m_mask; + + QByteArray m_pattern; + quint32 m_number; + quint32 m_numberMask; + + typedef bool (QMimeMagicRule::*MatchFunction)(const QByteArray &data) const; + MatchFunction m_matchFunction; + +private: + // match functions + bool matchString(const QByteArray &data) const; + template <typename T> + bool matchNumber(const QByteArray &data) const; }; -Q_DECLARE_TYPEINFO(QMimeMagicRule, Q_MOVABLE_TYPE); +Q_DECLARE_SHARED(QMimeMagicRule) QT_END_NAMESPACE diff --git a/src/corelib/mimetypes/qmimemagicrulematcher_p.h b/src/corelib/mimetypes/qmimemagicrulematcher_p.h index b2e171ec1f..5e3d5306c0 100644 --- a/src/corelib/mimetypes/qmimemagicrulematcher_p.h +++ b/src/corelib/mimetypes/qmimemagicrulematcher_p.h @@ -66,6 +66,13 @@ class QMimeMagicRuleMatcher public: explicit QMimeMagicRuleMatcher(const QString &mime, unsigned priority = 65535); + void swap(QMimeMagicRuleMatcher &other) Q_DECL_NOTHROW + { + qSwap(m_list, other.m_list); + qSwap(m_priority, other.m_priority); + qSwap(m_mimetype, other.m_mimetype); + } + bool operator==(const QMimeMagicRuleMatcher &other) const; void addRule(const QMimeMagicRule &rule); @@ -83,6 +90,7 @@ private: unsigned m_priority; QString m_mimetype; }; +Q_DECLARE_SHARED(QMimeMagicRuleMatcher) QT_END_NAMESPACE diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 69f8d72b47..48b94abae5 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -883,16 +883,12 @@ bool QThread::isInterruptionRequested() const Note: don't try to deliver events from the started() signal. */ -static void setThreadDoesNotRequireCoreApplication() -{ - QThreadData::current()->requiresCoreApplication = false; -} - QDaemonThread::QDaemonThread(QObject *parent) : QThread(parent) { // QThread::started() is emitted from the thread we start - connect(this, &QThread::started, setThreadDoesNotRequireCoreApplication); + connect(this, &QThread::started, + [](){ QThreadData::current()->requiresCoreApplication = false; }); } QDaemonThread::~QDaemonThread() diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 280f9ea006..e794da8326 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -975,9 +975,11 @@ bool QDate::setDate(int year, int month, int day) Returns 0 if the date is invalid. + \note In Qt versions prior to 5.7, this function is marked as non-\c{const}. + \sa year(), month(), day(), isValid() */ -void QDate::getDate(int *year, int *month, int *day) +void QDate::getDate(int *year, int *month, int *day) const { ParsedDate pd = { 0, 0, 0 }; if (isValid()) @@ -991,6 +993,17 @@ void QDate::getDate(int *year, int *month, int *day) *day = pd.day; } +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +/*! + \overload + \internal +*/ +void QDate::getDate(int *year, int *month, int *day) +{ + qAsConst(*this).getDate(year, month, day); +} +#endif // < Qt 6 + /*! Returns a QDate object containing a date \a ndays later than the date of this object (or earlier if \a ndays is negative). diff --git a/src/corelib/tools/qdatetime.h b/src/corelib/tools/qdatetime.h index af91ad0fe5..ee3be5553b 100644 --- a/src/corelib/tools/qdatetime.h +++ b/src/corelib/tools/qdatetime.h @@ -99,7 +99,10 @@ QT_DEPRECATED inline bool setYMD(int y, int m, int d) bool setDate(int year, int month, int day); - void getDate(int *year, int *month, int *day); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + void getDate(int *year, int *month, int *day); // ### Qt 6: remove +#endif // < Qt 6 + void getDate(int *year, int *month, int *day) const; QDate addDays(qint64 days) const Q_REQUIRED_RESULT; QDate addMonths(int months) const Q_REQUIRED_RESULT; diff --git a/src/corelib/tools/qdatetimeparser.cpp b/src/corelib/tools/qdatetimeparser.cpp index 9ffef05d78..bbcd64d34f 100644 --- a/src/corelib/tools/qdatetimeparser.cpp +++ b/src/corelib/tools/qdatetimeparser.cpp @@ -131,13 +131,15 @@ bool QDateTimeParser::setDigit(QDateTime &v, int index, int newVal) const } const SectionNode &node = sectionNodes.at(index); - int year = v.date().year(); - int month = v.date().month(); - int day = v.date().day(); - int hour = v.time().hour(); - int minute = v.time().minute(); - int second = v.time().second(); - int msec = v.time().msec(); + const QDate date = v.date(); + const QTime time = v.time(); + int year = date.year(); + int month = date.month(); + int day = date.day(); + int hour = time.hour(); + int minute = time.minute(); + int second = time.second(); + int msec = time.msec(); switch (node.type) { case Hour24Section: case Hour12Section: hour = newVal; break; @@ -887,14 +889,16 @@ QDateTimeParser::StateNode QDateTimeParser::parse(QString &input, int &cursorPos QDTPDEBUG << "parse" << input; { int year, month, day; - currentValue.date().getDate(&year, &month, &day); + QDate currentDate = currentValue.date(); + const QTime currentTime = currentValue.time(); + currentDate.getDate(&year, &month, &day); int year2digits = year % 100; - int hour = currentValue.time().hour(); + int hour = currentTime.hour(); int hour12 = -1; - int minute = currentValue.time().minute(); - int second = currentValue.time().second(); - int msec = currentValue.time().msec(); - int dayofweek = currentValue.date().dayOfWeek(); + int minute = currentTime.minute(); + int second = currentTime.second(); + int msec = currentTime.msec(); + int dayofweek = currentDate.dayOfWeek(); int ampm = -1; Sections isSet = NoSection; @@ -1136,10 +1140,11 @@ end: } case MonthSection: if (sn.count >= 3) { - int tmp = newCurrentValue.date().month(); + const int currentMonth = newCurrentValue.date().month(); + int tmp = currentMonth; // I know the first possible month makes the date too early while ((tmp = findMonth(t, tmp + 1, i)) != -1) { - const QDateTime copy(newCurrentValue.addMonths(tmp - newCurrentValue.date().month())); + const QDateTime copy(newCurrentValue.addMonths(tmp - currentMonth)); if (copy >= minimum && copy <= maximum) break; // break out of while } @@ -1253,7 +1258,8 @@ int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionI QLocale l = locale(); for (int month=startMonth; month<=12; ++month) { - QString str2 = l.monthName(month, type).toLower(); + const QString monthName = l.monthName(month, type); + QString str2 = monthName.toLower(); if (str1.startsWith(str2)) { if (used) { @@ -1261,7 +1267,7 @@ int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionI *used = str2.size(); } if (usedMonth) - *usedMonth = l.monthName(month, type); + *usedMonth = monthName; return month; } @@ -1286,7 +1292,7 @@ int QDateTimeParser::findMonth(const QString &str1, int startMonth, int sectionI if (used) *used = limit; if (usedMonth) - *usedMonth = l.monthName(month, type); + *usedMonth = monthName; return month; } } @@ -1573,7 +1579,8 @@ bool QDateTimeParser::potentialValue(const QString &str, int min, int max, int i int val = (int)locale().toUInt(str); const SectionNode &sn = sectionNode(index); if (sn.type == YearSection2Digits) { - val += currentValue.date().year() - (currentValue.date().year() % 100); + const int year = currentValue.date().year(); + val += year - (year % 100); } if (val >= min && val <= max && str.size() == size) { return true; diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index e843351cf7..479a4283b0 100644 --- a/src/gui/itemmodels/qstandarditemmodel.cpp +++ b/src/gui/itemmodels/qstandarditemmodel.cpp @@ -2168,6 +2168,7 @@ void QStandardItemModel::clear() Q_D(QStandardItemModel); beginResetModel(); d->root.reset(new QStandardItem); + d->root->setFlags(Qt::ItemIsDropEnabled); d->root->d_func()->setModel(this); qDeleteAll(d->columnHeaderItems); d->columnHeaderItems.clear(); diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 14c01085e7..5eb216d58e 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -191,7 +191,7 @@ int QShortcutMap::removeShortcut(int id, QObject *owner, const QKeySequence &key bool allIds = id == 0; // Special case, remove everything - if (allOwners && allKeys && id == 0) { + if (allOwners && allKeys && allIds) { itemsRemoved = d->sequences.size(); d->sequences.clear(); return itemsRemoved; diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index b295fe986a..e184fed275 100644 --- a/src/gui/kernel/qstylehints.cpp +++ b/src/gui/kernel/qstylehints.cpp @@ -76,6 +76,7 @@ public: , m_startDragTime(-1) , m_keyboardInputInterval(-1) , m_cursorFlashTime(-1) + , m_tabFocusBehavior(-1) {} int m_mouseDoubleClickInterval; @@ -84,6 +85,7 @@ public: int m_startDragTime; int m_keyboardInputInterval; int m_cursorFlashTime; + int m_tabFocusBehavior; }; /*! @@ -416,7 +418,25 @@ bool QStyleHints::setFocusOnTouchRelease() const Qt::TabFocusBehavior QStyleHints::tabFocusBehavior() const { - return Qt::TabFocusBehavior(themeableHint(QPlatformTheme::TabFocusBehavior, QPlatformIntegration::TabFocusBehavior).toInt()); + Q_D(const QStyleHints); + return Qt::TabFocusBehavior(d->m_tabFocusBehavior >= 0 ? + d->m_tabFocusBehavior : + themeableHint(QPlatformTheme::TabFocusBehavior, QPlatformIntegration::TabFocusBehavior).toInt()); +} + +/*! + Sets the \a tabFocusBehavior. + \internal + \sa tabFocusBehavior() + \since 5.7 +*/ +void QStyleHints::setTabFocusBehavior(Qt::TabFocusBehavior tabFocusBehavior) +{ + Q_D(QStyleHints); + if (d->m_tabFocusBehavior == tabFocusBehavior) + return; + d->m_tabFocusBehavior = tabFocusBehavior; + emit tabFocusBehaviorChanged(tabFocusBehavior); } /*! diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h index a79fad805a..0b07e60579 100644 --- a/src/gui/kernel/qstylehints.h +++ b/src/gui/kernel/qstylehints.h @@ -67,7 +67,7 @@ class Q_GUI_EXPORT QStyleHints : public QObject Q_PROPERTY(int startDragTime READ startDragTime NOTIFY startDragTimeChanged FINAL) Q_PROPERTY(int startDragVelocity READ startDragVelocity STORED false CONSTANT FINAL) Q_PROPERTY(bool useRtlExtensions READ useRtlExtensions STORED false CONSTANT FINAL) - Q_PROPERTY(Qt::TabFocusBehavior tabFocusBehavior READ tabFocusBehavior STORED false CONSTANT FINAL) + Q_PROPERTY(Qt::TabFocusBehavior tabFocusBehavior READ tabFocusBehavior NOTIFY tabFocusBehaviorChanged FINAL) Q_PROPERTY(bool singleClickActivation READ singleClickActivation STORED false CONSTANT FINAL) public: @@ -93,6 +93,7 @@ public: bool useRtlExtensions() const; bool setFocusOnTouchRelease() const; Qt::TabFocusBehavior tabFocusBehavior() const; + void setTabFocusBehavior(Qt::TabFocusBehavior tabFocusBehavior); bool singleClickActivation() const; Q_SIGNALS: @@ -102,6 +103,7 @@ Q_SIGNALS: void mousePressAndHoldIntervalChanged(int mousePressAndHoldInterval); void startDragDistanceChanged(int startDragDistance); void startDragTimeChanged(int startDragTime); + void tabFocusBehaviorChanged(Qt::TabFocusBehavior tabFocusBehavior); private: friend class QGuiApplication; diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 6110764094..5f5b7f46ec 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -1630,6 +1630,29 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, } /*! + \enum QOpenGLFramebufferObject::FramebufferRestorePolicy + \since 5.7 + + This enum type is used to configure the behavior related to restoring + framebuffer bindings when calling blitFramebuffer(). + + \value DontRestoreFramebufferBinding Do not restore the previous framebuffer binding. + The caller is responsible for tracking and setting + the framebuffer binding as needed. + + \value RestoreFramebufferBindingToDefault After the blit operation, bind the default + framebuffer. + + \value RestoreFrameBufferBinding Restore the previously bound framebuffer. This is + potentially expensive because of the need to + query the currently bound framebuffer. + + \sa blitFramebuffer() +*/ + +/*! + \since 5.7 + Blits from the \a sourceRect rectangle in the \a source framebuffer object to the \a targetRect rectangle in the \a target framebuffer object. @@ -1661,6 +1684,13 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, drawColorAttachmentIndex specify the index of the color attachments in the source and destination framebuffers. + The \a restorePolicy determines if the framebuffer that was bound prior to + calling this function should be restored, or if the default framebuffer + should be bound before returning, of if the caller is responsible for + tracking and setting the bound framebuffer. Restoring the previous + framebuffer can be relatively expensive due to the call to \c{glGetIntegerv} + which on some OpenGL drivers may imply a pipeline stall. + \sa hasOpenGLFramebufferBlit() */ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, @@ -1668,7 +1698,8 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, GLbitfield buffers, GLenum filter, int readColorAttachmentIndex, - int drawColorAttachmentIndex) + int drawColorAttachmentIndex, + QOpenGLFramebufferObject::FramebufferRestorePolicy restorePolicy) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); if (!ctx) @@ -1679,7 +1710,8 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, return; GLuint prevFbo = 0; - ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); + if (restorePolicy == RestoreFrameBufferBinding) + ctx->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &prevFbo); const int sx0 = sourceRect.left(); const int sx1 = sourceRect.left() + sourceRect.width(); @@ -1696,7 +1728,8 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, extensions.glBindFramebuffer(GL_READ_FRAMEBUFFER, source ? source->handle() : defaultFboId); extensions.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target ? target->handle() : defaultFboId); - if (extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) { + const bool supportsMRT = extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets); + if (supportsMRT) { extensions.glReadBuffer(GL_COLOR_ATTACHMENT0 + readColorAttachmentIndex); if (target) { GLenum drawBuf = GL_COLOR_ATTACHMENT0 + drawColorAttachmentIndex; @@ -1708,10 +1741,44 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, tx0, ty0, tx1, ty1, buffers, filter); - if (extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) + if (supportsMRT) extensions.glReadBuffer(GL_COLOR_ATTACHMENT0); - ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW + switch (restorePolicy) { + case RestoreFrameBufferBinding: + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); // sets both READ and DRAW + break; + + case RestoreFramebufferBindingToDefault: + ctx->functions()->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject()); // sets both READ and DRAW + break; + + case DontRestoreFramebufferBinding: + break; + } +} + +/*! + \overload + + Convenience overload to blit between two framebuffer objects and + to restore the previous framebuffer binding. Equivalent to calling + blitFramebuffer(target, targetRect, source, sourceRect, buffers, filter, + readColorAttachmentIndex, drawColorAttachmentIndex, + RestoreFrameBufferBinding). +*/ +void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, + QOpenGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers, + GLenum filter, + int readColorAttachmentIndex, + int drawColorAttachmentIndex) +{ + blitFramebuffer(target, targetRect, source, sourceRect, + buffers, filter, + readColorAttachmentIndex, + drawColorAttachmentIndex, + RestoreFrameBufferBinding); } QT_END_NAMESPACE diff --git a/src/gui/opengl/qopenglframebufferobject.h b/src/gui/opengl/qopenglframebufferobject.h index 25d171e289..b22726cafb 100644 --- a/src/gui/opengl/qopenglframebufferobject.h +++ b/src/gui/opengl/qopenglframebufferobject.h @@ -114,6 +114,20 @@ public: static bool hasOpenGLFramebufferObjects(); static bool hasOpenGLFramebufferBlit(); + + enum FramebufferRestorePolicy { + DontRestoreFramebufferBinding, + RestoreFramebufferBindingToDefault, + RestoreFrameBufferBinding + }; + + static void blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, + QOpenGLFramebufferObject *source, const QRect &sourceRect, + GLbitfield buffers, + GLenum filter, + int readColorAttachmentIndex, + int drawColorAttachmentIndex, + FramebufferRestorePolicy restorePolicy); static void blitFramebuffer(QOpenGLFramebufferObject *target, const QRect &targetRect, QOpenGLFramebufferObject *source, const QRect &sourceRect, GLbitfield buffers, diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 2780de1316..ac6b2df595 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -259,12 +259,21 @@ QSslCipher QSslSocketBackendPrivate::QSslCipher_from_SSL_CIPHER(SSL_CIPHER *ciph return ciph; } +// static +inline QSslErrorEntry QSslErrorEntry::fromStoreContext(X509_STORE_CTX *ctx) { + QSslErrorEntry result = { + q_X509_STORE_CTX_get_error(ctx), + q_X509_STORE_CTX_get_error_depth(ctx) + }; + return result; +} + // ### This list is shared between all threads, and protected by a // mutex. Investigate using thread local storage instead. struct QSslErrorList { QMutex mutex; - QList<QPair<int, int> > errors; + QVector<QSslErrorEntry> errors; }; Q_GLOBAL_STATIC(QSslErrorList, _q_sslErrorList) @@ -272,7 +281,7 @@ int q_X509Callback(int ok, X509_STORE_CTX *ctx) { if (!ok) { // Store the error and at which depth the error was detected. - _q_sslErrorList()->errors << qMakePair<int, int>(q_X509_STORE_CTX_get_error(ctx), q_X509_STORE_CTX_get_error_depth(ctx)); + _q_sslErrorList()->errors << QSslErrorEntry::fromStoreContext(ctx); #ifdef QSSLSOCKET_DEBUG qCDebug(lcSsl) << "verification error: dumping bad certificate"; qCDebug(lcSsl) << QSslCertificatePrivate::QSslCertificate_from_X509(q_X509_STORE_CTX_get_current_cert(ctx)).toPem(); @@ -1040,23 +1049,22 @@ bool QSslSocketBackendPrivate::startHandshake() // Check if the connection has been established. Get all errors from the // verification stage. - _q_sslErrorList()->mutex.lock(); + QMutexLocker locker(&_q_sslErrorList()->mutex); _q_sslErrorList()->errors.clear(); int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); - const QList<QPair<int, int> > &lastErrors = _q_sslErrorList()->errors; + const auto &lastErrors = _q_sslErrorList()->errors; if (!lastErrors.isEmpty()) storePeerCertificates(); - for (int i = 0; i < lastErrors.size(); ++i) { - const QPair<int, int> ¤tError = lastErrors.at(i); - emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.first, - configuration.peerCertificateChain.value(currentError.second))); + for (const auto ¤tError : lastErrors) { + emit q->peerVerifyError(_q_OpenSSL_to_QSslError(currentError.code, + configuration.peerCertificateChain.value(currentError.depth))); if (q->state() != QAbstractSocket::ConnectedState) break; } errorList << lastErrors; - _q_sslErrorList()->mutex.unlock(); + locker.unlock(); // Connection aborted during handshake phase. if (q->state() != QAbstractSocket::ConnectedState) @@ -1133,14 +1141,9 @@ bool QSslSocketBackendPrivate::startHandshake() } // Translate errors from the error list into QSslErrors. - const int numErrors = errorList.size(); - errors.reserve(errors.size() + numErrors); - for (int i = 0; i < numErrors; ++i) { - const QPair<int, int> &errorAndDepth = errorList.at(i); - int err = errorAndDepth.first; - int depth = errorAndDepth.second; - errors << _q_OpenSSL_to_QSslError(err, configuration.peerCertificateChain.value(depth)); - } + errors.reserve(errors.size() + errorList.size()); + for (const auto &error : qAsConst(errorList)) + errors << _q_OpenSSL_to_QSslError(error.code, configuration.peerCertificateChain.value(error.depth)); if (!errors.isEmpty()) { sslErrors = errors; @@ -1692,7 +1695,7 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> & #endif // Now process the errors - const QList<QPair<int, int> > errorList = _q_sslErrorList()->errors; + const auto errorList = std::move(_q_sslErrorList()->errors); _q_sslErrorList()->errors.clear(); sslErrorListMutexLocker.unlock(); @@ -1711,14 +1714,9 @@ QList<QSslError> QSslSocketBackendPrivate::verify(const QList<QSslCertificate> & } // Translate errors from the error list into QSslErrors. - const int numErrors = errorList.size(); - errors.reserve(errors.size() + numErrors); - for (int i = 0; i < numErrors; ++i) { - const QPair<int, int> &errorAndDepth = errorList.at(i); - int err = errorAndDepth.first; - int depth = errorAndDepth.second; - errors << _q_OpenSSL_to_QSslError(err, certificateChain.value(depth)); - } + errors.reserve(errors.size() + errorList.size()); + for (const auto &error : qAsConst(errorList)) + errors << _q_OpenSSL_to_QSslError(error.code, certificateChain.value(error.depth)); q_X509_STORE_free(certStore); diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 890576e378..dc60adda39 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -103,6 +103,14 @@ typedef _STACK STACK; QT_BEGIN_NAMESPACE +struct QSslErrorEntry { + int code; + int depth; + + static QSslErrorEntry fromStoreContext(X509_STORE_CTX *ctx); +}; +Q_DECLARE_TYPEINFO(QSslErrorEntry, Q_PRIMITIVE_TYPE); + class QSslSocketBackendPrivate : public QSslSocketPrivate { Q_DECLARE_PUBLIC(QSslSocket) @@ -117,7 +125,7 @@ public: BIO *readBio; BIO *writeBio; SSL_SESSION *session; - QList<QPair<int, int> > errorList; + QVector<QSslErrorEntry> errorList; #if OPENSSL_VERSION_NUMBER >= 0x10001000L static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate #endif diff --git a/src/plugins/bearer/connman/qconnmanservice_linux_p.h b/src/plugins/bearer/connman/qconnmanservice_linux_p.h index 9a2bf4eb34..e773db9b20 100644 --- a/src/plugins/bearer/connman/qconnmanservice_linux_p.h +++ b/src/plugins/bearer/connman/qconnmanservice_linux_p.h @@ -82,8 +82,9 @@ struct ConnmanMap { QDBusObjectPath objectPath; QVariantMap propertyMap; }; - -typedef QList< ConnmanMap > ConnmanMapList; +Q_DECLARE_TYPEINFO(ConnmanMap, Q_MOVABLE_TYPE); // QDBusObjectPath is movable, but cannot be + // marked as such until Qt 6 +typedef QVector<ConnmanMap> ConnmanMapList; QT_END_NAMESPACE diff --git a/src/plugins/bearer/linux_common/qofonoservice_linux_p.h b/src/plugins/bearer/linux_common/qofonoservice_linux_p.h index 43b9570c99..35614a20f2 100644 --- a/src/plugins/bearer/linux_common/qofonoservice_linux_p.h +++ b/src/plugins/bearer/linux_common/qofonoservice_linux_p.h @@ -83,7 +83,12 @@ struct ObjectPathProperties QDBusObjectPath path; QVariantMap properties; }; -typedef QList<ObjectPathProperties> PathPropertiesList; +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(ObjectPathProperties, Q_MOVABLE_TYPE); // QDBusObjectPath is movable, but cannot be + // marked as such until Qt 6 +QT_END_NAMESPACE + +typedef QVector<ObjectPathProperties> PathPropertiesList; Q_DECLARE_METATYPE(ObjectPathProperties) Q_DECLARE_METATYPE (PathPropertiesList) diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 1eae295724..6340d47c18 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -78,6 +78,7 @@ static AAssetManager *m_assetManager = nullptr; static jobject m_resourcesObj = nullptr; static jobject m_activityObject = nullptr; static jmethodID m_createSurfaceMethodID = nullptr; +static jobject m_serviceObject = nullptr; static jmethodID m_setSurfaceGeometryMethodID = nullptr; static jmethodID m_destroySurfaceMethodID = nullptr; @@ -193,6 +194,11 @@ namespace QtAndroid return m_activityObject; } + jobject service() + { + return m_serviceObject; + } + void showStatusBar() { if (m_statusBarShowing) @@ -534,7 +540,6 @@ static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring para return pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr) == 0; } - static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) { Q_UNUSED(env); @@ -553,6 +558,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/) env->DeleteGlobalRef(m_resourcesObj); if (m_activityObject) env->DeleteGlobalRef(m_activityObject); + if (m_serviceObject) + env->DeleteGlobalRef(m_serviceObject); if (m_bitmapClass) env->DeleteGlobalRef(m_bitmapClass); if (m_ARGB_8888_BitmapConfigValue) @@ -785,20 +792,26 @@ static int registerNatives(JNIEnv *env) jmethodID methodID; GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;"); + jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID); GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); clazz = env->GetObjectClass(m_classLoaderObject); GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + if (serviceObject) + m_serviceObject = env->NewGlobalRef(serviceObject); - if (activityObject) { + if (activityObject) m_activityObject = env->NewGlobalRef(activityObject); + jobject object = activityObject ? activityObject : serviceObject; + if (object) { FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); - m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(activityObject, methodID)); + m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(object, methodID)); GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); - m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(activityObject, methodID)); + m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID)); FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz)); @@ -819,8 +832,6 @@ static int registerNatives(JNIEnv *env) "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); } - - return JNI_TRUE; } diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h index e3c18b2e7a..218e52ccc1 100644 --- a/src/plugins/platforms/android/androidjnimain.h +++ b/src/plugins/platforms/android/androidjnimain.h @@ -83,6 +83,7 @@ namespace QtAndroid AAssetManager *assetManager(); jclass applicationClass(); jobject activity(); + jobject service(); void setApplicationActive(); diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 1f8ee79396..80d7e31aa3 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -66,7 +66,6 @@ #include "qandroidplatformtheme.h" #include "qandroidsystemlocale.h" - QT_BEGIN_NAMESPACE int QAndroidPlatformIntegration::m_defaultGeometryWidth = 320; @@ -87,6 +86,8 @@ void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteA return QtAndroid::javaVM(); if (resource == "QtActivity") return QtAndroid::activity(); + if (resource == "QtService") + return QtAndroid::service(); if (resource == "AndroidStyleData") { if (m_androidStyle) { if (m_androidStyle->m_styleData.isEmpty()) @@ -122,7 +123,6 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ #endif { Q_UNUSED(paramList); - m_androidPlatformNativeInterface = new QAndroidPlatformNativeInterface(); m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -159,6 +159,9 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ #endif // QT_NO_ACCESSIBILITY QJNIObjectPrivate javaActivity(QtAndroid::activity()); + if (!javaActivity.isValid()) + javaActivity = QtAndroid::service(); + if (javaActivity.isValid()) { QJNIObjectPrivate resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); QJNIObjectPrivate configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); @@ -205,13 +208,13 @@ static bool needsBasicRenderloopWorkaround() bool QAndroidPlatformIntegration::hasCapability(Capability cap) const { switch (cap) { - case ThreadedPixmaps: return true; case ApplicationState: return true; - case NativeWidgets: return true; - case OpenGL: return true; - case ForeignWindows: return true; - case ThreadedOpenGL: return !needsBasicRenderloopWorkaround(); - case RasterGLSurface: return true; + case ThreadedPixmaps: return true; + case NativeWidgets: return QtAndroid::activity(); + case OpenGL: return QtAndroid::activity(); + case ForeignWindows: return QtAndroid::activity(); + case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroid::activity(); + case RasterGLSurface: return QtAndroid::activity(); default: return QPlatformIntegration::hasCapability(cap); } @@ -219,11 +222,15 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(QWindow *window) const { + if (!QtAndroid::activity()) + return nullptr; return new QAndroidPlatformBackingStore(window); } QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { + if (!QtAndroid::activity()) + return nullptr; QSurfaceFormat format(context->format()); format.setAlphaBufferSize(8); format.setRedBufferSize(8); @@ -234,6 +241,8 @@ QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const { + if (!QtAndroid::activity()) + return nullptr; QSurfaceFormat format(surface->requestedFormat()); format.setAlphaBufferSize(8); format.setRedBufferSize(8); @@ -245,6 +254,8 @@ QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenS QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const { + if (!QtAndroid::activity()) + return nullptr; if (window->type() == Qt::ForeignWindow) return new QAndroidPlatformForeignWindow(window); else diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index dd29c29bab..aa4fa94f0a 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -294,6 +294,8 @@ int QAndroidPlatformScreen::rasterSurfaces() void QAndroidPlatformScreen::doRedraw() { PROFILE_SCOPE; + if (!QtAndroid::activity()) + return; if (m_dirtyRect.isEmpty()) return; diff --git a/src/plugins/platforms/android/qandroidsystemlocale.cpp b/src/plugins/platforms/android/qandroidsystemlocale.cpp index 1528d90d06..7fe36aa9bc 100644 --- a/src/plugins/platforms/android/qandroidsystemlocale.cpp +++ b/src/plugins/platforms/android/qandroidsystemlocale.cpp @@ -56,6 +56,8 @@ void QAndroidSystemLocale::getLocaleFromJava() const QJNIObjectPrivate javaLocaleObject; QJNIObjectPrivate javaActivity(QtAndroid::activity()); + if (!javaActivity.isValid()) + javaActivity = QtAndroid::service(); if (javaActivity.isValid()) { QJNIObjectPrivate resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); QJNIObjectPrivate configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 22a2922235..464fcc886f 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -376,12 +376,13 @@ static void setWindowOpacity(HWND hwnd, Qt::WindowFlags flags, bool hasAlpha, bo Q_UNUSED(level); #else if (QWindowsWindow::setWindowLayered(hwnd, flags, hasAlpha, level)) { + const BYTE alpha = BYTE(qRound(255.0 * level)); if (hasAlpha && !openGL && (flags & Qt::FramelessWindowHint)) { // Non-GL windows with alpha: Use blend function to update. - BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)(255.0 * level), AC_SRC_ALPHA}; + BLENDFUNCTION blend = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; QWindowsContext::user32dll.updateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL, 0, &blend, ULW_ALPHA); } else { - QWindowsContext::user32dll.setLayeredWindowAttributes(hwnd, 0, (int)(level * 255), LWA_ALPHA); + QWindowsContext::user32dll.setLayeredWindowAttributes(hwnd, 0, alpha, LWA_ALPHA); } } else if (IsWindowVisible(hwnd)) { // Repaint when switching from layered. InvalidateRect(hwnd, NULL, TRUE); @@ -505,7 +506,7 @@ void WindowCreationData::fromWindow(const QWindow *w, const Qt::WindowFlags flag QVariant prop = w->property("_q_embedded_native_parent_handle"); if (prop.isValid()) { embedded = true; - parentHandle = (HWND)prop.value<WId>(); + parentHandle = reinterpret_cast<HWND>(prop.value<WId>()); } if (creationFlags & ForceChild) { @@ -680,10 +681,10 @@ void WindowCreationData::applyWindowFlags(HWND hwnd) const if (newExStyle != oldExStyle) SetWindowLongPtr(hwnd, GWL_EXSTYLE, newExStyle); qCDebug(lcQpaWindows).nospace() << __FUNCTION__ << hwnd << *this - << "\n Style from " << debugWinStyle(oldStyle) << "\n to " - << debugWinStyle(newStyle) << "\n ExStyle from " - << debugWinExStyle(oldExStyle) << " to " - << debugWinExStyle(newExStyle); + << "\n Style from " << debugWinStyle(DWORD(oldStyle)) << "\n to " + << debugWinStyle(DWORD(newStyle)) << "\n ExStyle from " + << debugWinExStyle(DWORD(oldExStyle)) << " to " + << debugWinExStyle(DWORD(newExStyle)); } void WindowCreationData::initialize(const QWindow *w, HWND hwnd, bool frameChange, qreal opacityLevel) const @@ -802,8 +803,8 @@ bool QWindowsGeometryHint::handleCalculateSize(const QMargins &customMargins, co #ifndef Q_OS_WINCE void QWindowsGeometryHint::applyToMinMaxInfo(HWND hwnd, MINMAXINFO *mmi) const { - return applyToMinMaxInfo(GetWindowLong(hwnd, GWL_STYLE), - GetWindowLong(hwnd, GWL_EXSTYLE), mmi); + return applyToMinMaxInfo(DWORD(GetWindowLong(hwnd, GWL_STYLE)), + DWORD(GetWindowLong(hwnd, GWL_EXSTYLE)), mmi); } void QWindowsGeometryHint::applyToMinMaxInfo(DWORD style, DWORD exStyle, MINMAXINFO *mmi) const @@ -1363,7 +1364,7 @@ void QWindowsWindow::updateTransientParent() const if (!tw->testFlag(WithinDestroy)) // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666) newTransientParent = tw->handle(); if (newTransientParent != oldTransientParent) - SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, (LONG_PTR)newTransientParent); + SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, LONG_PTR(newTransientParent)); #endif // !Q_OS_WINCE } @@ -2015,7 +2016,7 @@ QMargins QWindowsWindow::frameMargins() const void QWindowsWindow::setOpacity(qreal level) { qCDebug(lcQpaWindows) << __FUNCTION__ << level; - if (m_opacity != level) { + if (!qFuzzyCompare(m_opacity, level)) { m_opacity = level; if (m_data.hwnd) setWindowOpacity(m_data.hwnd, m_data.flags, @@ -2317,7 +2318,7 @@ void QWindowsWindow::setAlertState(bool enabled) void QWindowsWindow::alertWindow(int durationMs) { - DWORD timeOutMs = GetCaretBlinkTime(); + UINT timeOutMs = GetCaretBlinkTime(); if (!timeOutMs || timeOutMs == INFINITE) timeOutMs = 250; @@ -2326,7 +2327,7 @@ void QWindowsWindow::alertWindow(int durationMs) info.hwnd = m_data.hwnd; info.dwFlags = FLASHW_TRAY; info.dwTimeout = timeOutMs; - info.uCount = durationMs == 0 ? 10 : durationMs / timeOutMs; + info.uCount = durationMs == 0 ? 10 : UINT(durationMs) / timeOutMs; FlashWindowEx(&info); } @@ -2379,11 +2380,11 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) m_iconBig = createHIcon(icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); if (m_iconBig) { - SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)m_iconSmall); - SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, (LPARAM)m_iconBig); + SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall)); + SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconBig)); } else { - SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, (LPARAM)m_iconSmall); - SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, (LPARAM)m_iconSmall); + SendMessage(m_data.hwnd, WM_SETICON, 0 /* ICON_SMALL */, LPARAM(m_iconSmall)); + SendMessage(m_data.hwnd, WM_SETICON, 1 /* ICON_BIG */, LPARAM(m_iconSmall)); } } } @@ -2464,7 +2465,7 @@ void QWindowsWindow::registerTouchWindow(QWindowsWindowFunctions::TouchWindowTou // such as HCBT_CREATEWND if (ret || touchFlags != 0) return; - if (QWindowsContext::user32dll.registerTouchWindow(m_data.hwnd, (ULONG)touchTypes)) + if (QWindowsContext::user32dll.registerTouchWindow(m_data.hwnd, ULONG(touchTypes))) setFlag(TouchRegistered); else qErrnoWarning("RegisterTouchWindow() failed for window '%s'.", qPrintable(window()->objectName())); diff --git a/src/printsupport/dialogs/qprintpreviewdialog.cpp b/src/printsupport/dialogs/qprintpreviewdialog.cpp index 97acbb79a1..435f8869c4 100644 --- a/src/printsupport/dialogs/qprintpreviewdialog.cpp +++ b/src/printsupport/dialogs/qprintpreviewdialog.cpp @@ -250,8 +250,8 @@ void QPrintPreviewDialogPrivate::init(QPrinter *_printer) zoomEditor->setValidator(new ZoomFactorValidator(1, 1000, 1, zoomEditor)); zoomFactor->setLineEdit(zoomEditor); static const short factorsX2[] = { 25, 50, 100, 200, 250, 300, 400, 800, 1600 }; - for (int i = 0; i < int(sizeof(factorsX2) / sizeof(factorsX2[0])); ++i) - zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorsX2[i] / 2.0)); + for (auto factorX2 : factorsX2) + zoomFactor->addItem(QPrintPreviewDialog::tr("%1%").arg(factorX2 / 2.0)); QObject::connect(zoomFactor->lineEdit(), SIGNAL(editingFinished()), q, SLOT(_q_zoomFactorChanged())); QObject::connect(zoomFactor, SIGNAL(currentIndexChanged(int)), diff --git a/src/printsupport/kernel/qpaintengine_alpha.cpp b/src/printsupport/kernel/qpaintengine_alpha.cpp index 4922d210f2..939322cc07 100644 --- a/src/printsupport/kernel/qpaintengine_alpha.cpp +++ b/src/printsupport/kernel/qpaintengine_alpha.cpp @@ -336,8 +336,8 @@ void QAlphaPaintEngine::flushAndInit(bool init) d->resetState(painter()); // fill in the alpha images - for (int i=0; i<rects.size(); ++i) - d->drawAlphaImage(rects.at(i)); + for (const auto &rect : qAsConst(rects)) + d->drawAlphaImage(rect); d->m_alphargn = QRegion(); diff --git a/src/printsupport/kernel/qprintdevice.cpp b/src/printsupport/kernel/qprintdevice.cpp index 29dd0d6529..26799a6f13 100644 --- a/src/printsupport/kernel/qprintdevice.cpp +++ b/src/printsupport/kernel/qprintdevice.cpp @@ -283,10 +283,10 @@ void QPrintDevice::format(QDebug debug) const << ", defaultColorMode="<< defaultColorMode(); # ifndef QT_NO_MIMETYPE const QList<QMimeType> mimeTypes = supportedMimeTypes(); - if (const int mimeTypeCount = mimeTypes.size()) { + if (!mimeTypes.isEmpty()) { debug << ", supportedMimeTypes=("; - for (int i = 0; i < mimeTypeCount; ++i) - debug << " \"" << mimeTypes.at(i).name() << '"'; + for (const auto &mimeType : mimeTypes) + debug << " \"" << mimeType.name() << '"'; debug << ')'; } # endif // !QT_NO_MIMETYPE diff --git a/src/printsupport/kernel/qprinter.cpp b/src/printsupport/kernel/qprinter.cpp index cc4c8244ae..cbab151ea0 100644 --- a/src/printsupport/kernel/qprinter.cpp +++ b/src/printsupport/kernel/qprinter.cpp @@ -1946,13 +1946,12 @@ int QPrinter::winPageSize() const QList<int> QPrinter::supportedResolutions() const { Q_D(const QPrinter); - QList<QVariant> varlist + const QList<QVariant> varlist = d->printEngine->property(QPrintEngine::PPK_SupportedResolutions).toList(); QList<int> intlist; - const int numSupportedResolutions = varlist.size(); - intlist.reserve(numSupportedResolutions); - for (int i = 0; i < numSupportedResolutions; ++i) - intlist << varlist.at(i).toInt(); + intlist.reserve(varlist.size()); + for (auto var : varlist) + intlist << var.toInt(); return intlist; } @@ -2015,10 +2014,11 @@ QList<QPrinter::PaperSource> QPrinter::supportedPaperSources() const Q_D(const QPrinter); QVariant v = d->printEngine->property(QPrintEngine::PPK_PaperSources); - QList<QVariant> variant_list = v.toList(); + const QList<QVariant> variant_list = v.toList(); QList<QPrinter::PaperSource> int_list; - for (int i=0; i<variant_list.size(); ++i) - int_list << (QPrinter::PaperSource) variant_list.at(i).toInt(); + int_list.reserve(variant_list.size()); + for (const auto &variant : variant_list) + int_list << QPrinter::PaperSource(variant.toInt()); return int_list; } diff --git a/src/printsupport/widgets/qprintpreviewwidget.cpp b/src/printsupport/widgets/qprintpreviewwidget.cpp index 2de487e937..fd1cf68587 100644 --- a/src/printsupport/widgets/qprintpreviewwidget.cpp +++ b/src/printsupport/widgets/qprintpreviewwidget.cpp @@ -295,9 +295,9 @@ int QPrintPreviewWidgetPrivate::calcCurrentPage() int maxArea = 0; int newPage = curPage; QRect viewRect = graphicsView->viewport()->rect(); - QList<QGraphicsItem*> items = graphicsView->items(viewRect); - for (int i=0; i<items.size(); ++i) { - PageItem* pg = static_cast<PageItem*>(items.at(i)); + const QList<QGraphicsItem*> items = graphicsView->items(viewRect); + for (auto *item : items) { + PageItem* pg = static_cast<PageItem*>(item); QRect overlap = graphicsView->mapFromScene(pg->sceneBoundingRect()).boundingRect() & viewRect; int area = overlap.width() * overlap.height(); if (area > maxArea) { @@ -335,17 +335,17 @@ void QPrintPreviewWidgetPrivate::init() void QPrintPreviewWidgetPrivate::populateScene() { // remove old pages - for (int i = 0; i < pages.size(); i++) - scene->removeItem(pages.at(i)); + for (auto *page : qAsConst(pages)) + scene->removeItem(page); qDeleteAll(pages); pages.clear(); - int numPages = pictures.count(); QSize paperSize = printer->pageLayout().fullRectPixels(printer->resolution()).size(); QRect pageRect = printer->pageLayout().paintRectPixels(printer->resolution()); - for (int i = 0; i < numPages; i++) { - PageItem* item = new PageItem(i+1, pictures.at(i), paperSize, pageRect); + int page = 1; + for (auto *picture : qAsConst(pictures)) { + PageItem* item = new PageItem(page++, picture, paperSize, pageRect); scene->addItem(item); pages.append(item); } diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp index 14a04d269f..47d6db7ea4 100644 --- a/src/sql/drivers/oci/qsql_oci.cpp +++ b/src/sql/drivers/oci/qsql_oci.cpp @@ -2386,17 +2386,46 @@ bool QOCIDriver::rollbackTransaction() return true; } +enum Expression { + OrExpression, + AndExpression +}; + +static QString make_where_clause(const QString &user, Expression e) +{ + static const char sysUsers[][8] = { + "MDSYS", + "LBACSYS", + "SYS", + "SYSTEM", + "WKSYS", + "CTXSYS", + "WMSYS", + }; + static const char joinC[][4] = { "or" , "and" }; + static Q_CONSTEXPR QLatin1Char bang[] = { QLatin1Char(' '), QLatin1Char('!') }; + + const QLatin1String join(joinC[e], -1); // -1: force strlen call + + QString result; + result.reserve(sizeof sysUsers / sizeof *sysUsers * + // max-sizeof(owner != <sysuser> and ) + (9 + sizeof *sysUsers + 5)); + for (const auto &sysUser : sysUsers) { + const QLatin1String l1(sysUser, -1); // -1: force strlen call + if (l1 != user) + result += QLatin1String("owner ") + bang[e] + QLatin1String("= '") + l1 + QLatin1Char(' ') + join + QLatin1Char(' '); + } + + result.chop(join.size() + 2); // remove final " <join> " + + return result; +} + QStringList QOCIDriver::tables(QSql::TableType type) const { Q_D(const QOCIDriver); QStringList tl; - QStringList sysUsers = QStringList() << QLatin1String("MDSYS") - << QLatin1String("LBACSYS") - << QLatin1String("SYS") - << QLatin1String("SYSTEM") - << QLatin1String("WKSYS") - << QLatin1String("CTXSYS") - << QLatin1String("WMSYS"); QString user = d->user; if ( isIdentifierEscaped(user, QSqlDriver::TableName)) @@ -2404,21 +2433,15 @@ QStringList QOCIDriver::tables(QSql::TableType type) const else user = user.toUpper(); - if(sysUsers.contains(user)) - sysUsers.removeAll(user);; - if (!isOpen()) return tl; QSqlQuery t(createResult()); t.setForwardOnly(true); if (type & QSql::Tables) { - QString query = QLatin1String("select owner, table_name from all_tables where "); - QStringList whereList; - foreach(const QString &sysUserName, sysUsers) - whereList << QLatin1String("owner != '") + sysUserName + QLatin1String("' "); - t.exec(query + whereList.join(QLatin1String(" and "))); - + const QLatin1String tableQuery("select owner, table_name from all_tables where "); + const QString where = make_where_clause(user, AndExpression); + t.exec(tableQuery + where); while (t.next()) { if (t.value(0).toString().toUpper() != user.toUpper()) tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); @@ -2427,8 +2450,8 @@ QStringList QOCIDriver::tables(QSql::TableType type) const } // list all table synonyms as well - query = QLatin1String("select owner, synonym_name from all_synonyms where "); - t.exec(query + whereList.join(QLatin1String(" and "))); + const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); + t.exec(synonymQuery + where); while (t.next()) { if (t.value(0).toString() != d->user) tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); @@ -2437,11 +2460,9 @@ QStringList QOCIDriver::tables(QSql::TableType type) const } } if (type & QSql::Views) { - QString query = QLatin1String("select owner, view_name from all_views where "); - QStringList whereList; - foreach(const QString &sysUserName, sysUsers) - whereList << QLatin1String("owner != '") + sysUserName + QLatin1String("' "); - t.exec(query + whereList.join(QLatin1String(" and "))); + const QLatin1String query("select owner, view_name from all_views where "); + const QString where = make_where_clause(user, AndExpression); + t.exec(query + where); while (t.next()) { if (t.value(0).toString().toUpper() != d->user.toUpper()) tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); @@ -2454,12 +2475,9 @@ QStringList QOCIDriver::tables(QSql::TableType type) const while (t.next()) { tl.append(t.value(0).toString()); } - QString query = QLatin1String("select owner, table_name from all_tables where "); - QStringList whereList; - foreach(const QString &sysUserName, sysUsers) - whereList << QLatin1String("owner = '") + sysUserName + QLatin1String("' "); - t.exec(query + whereList.join(QLatin1String(" or "))); - + const QLatin1String tableQuery("select owner, table_name from all_tables where "); + const QString where = make_where_clause(user, OrExpression); + t.exec(tableQuery + where); while (t.next()) { if (t.value(0).toString().toUpper() != user.toUpper()) tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); @@ -2468,8 +2486,8 @@ QStringList QOCIDriver::tables(QSql::TableType type) const } // list all table synonyms as well - query = QLatin1String("select owner, synonym_name from all_synonyms where "); - t.exec(query + whereList.join(QLatin1String(" or "))); + const QLatin1String synonymQuery("select owner, synonym_name from all_synonyms where "); + t.exec(synonymQuery + where); while (t.next()) { if (t.value(0).toString() != d->user) tl.append(t.value(0).toString() + QLatin1Char('.') + t.value(1).toString()); diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index c1071c9222..2a45b73d14 100644 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -613,13 +613,17 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c bool openReadOnlyOption = false; bool openUriOption = false; - const QStringList opts = QString(conOpts).remove(QLatin1Char(' ')).split(QLatin1Char(';')); - foreach (const QString &option, opts) { - if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT="))) { - bool ok; - const int nt = option.midRef(21).toInt(&ok); - if (ok) - timeOut = nt; + const auto opts = conOpts.splitRef(QLatin1Char(';')); + for (auto option : opts) { + option = option.trimmed(); + if (option.startsWith(QLatin1String("QSQLITE_BUSY_TIMEOUT"))) { + option = option.mid(20).trimmed(); + if (option.startsWith(QLatin1Char('='))) { + bool ok; + const int nt = option.mid(1).trimmed().toInt(&ok); + if (ok) + timeOut = nt; + } } else if (option == QLatin1String("QSQLITE_OPEN_READONLY")) { openReadOnlyOption = true; } else if (option == QLatin1String("QSQLITE_OPEN_URI")) { @@ -657,9 +661,8 @@ void QSQLiteDriver::close() { Q_D(QSQLiteDriver); if (isOpen()) { - foreach (QSQLiteResult *result, d->results) { + for (QSQLiteResult *result : qAsConst(d->results)) result->d_func()->finalize(); - } if (sqlite3_close(d->access) != SQLITE_OK) setLastError(qMakeError(d->access, tr("Error closing database"), diff --git a/src/sql/kernel/qsqlcachedresult.cpp b/src/sql/kernel/qsqlcachedresult.cpp index 3bacffed75..c335d7d8fe 100644 --- a/src/sql/kernel/qsqlcachedresult.cpp +++ b/src/sql/kernel/qsqlcachedresult.cpp @@ -267,7 +267,6 @@ bool QSqlCachedResult::cacheNext() return false; if(isForwardOnly()) { - d->cache.clear(); d->cache.resize(d->colCount); } diff --git a/src/sql/kernel/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp index 5e4c024480..f79c1c71cd 100644 --- a/src/sql/kernel/qsqlresult.cpp +++ b/src/sql/kernel/qsqlresult.cpp @@ -709,8 +709,8 @@ void QSqlResult::bindValue(const QString& placeholder, const QVariant& val, d->binds = NamedBinding; // if the index has already been set when doing emulated named // bindings - don't reset it - QList<int> indexes = d->indexes.value(placeholder); - foreach (int idx, indexes) { + const QList<int> indexes = d->indexes.value(placeholder); + for (int idx : indexes) { if (d->values.count() <= idx) d->values.resize(idx + 1); d->values[idx] = val; diff --git a/src/sql/models/qsqltablemodel.cpp b/src/sql/models/qsqltablemodel.cpp index 3612787fca..0bc86494f9 100644 --- a/src/sql/models/qsqltablemodel.cpp +++ b/src/sql/models/qsqltablemodel.cpp @@ -736,7 +736,8 @@ bool QSqlTableModel::submitAll() bool success = true; - foreach (int row, d->cache.keys()) { + const auto cachedKeys = d->cache.keys(); + for (int row : cachedKeys) { // be sure cache *still* contains the row since overridden selectRow() could have called select() QSqlTableModelPrivate::CacheMap::iterator it = d->cache.find(row); if (it == d->cache.end()) diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 0faa1c7d30..85fb0cd2fe 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -78,8 +78,8 @@ mac { LIBS += -framework Security osx: LIBS += -framework ApplicationServices -framework IOKit - # XCTest support - !lessThan(QMAKE_XCODE_VERSION, "6.0") { + # XCTest support (disabled for now) + false:!lessThan(QMAKE_XCODE_VERSION, "6.0") { OBJECTIVE_SOURCES += qxctestlogger.mm HEADERS += qxctestlogger_p.h diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index 47f2e76faf..9f86bea0ca 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -1909,10 +1909,11 @@ bool QColorDialogPrivate::canBeNativeDialog() const Q_Q(const QColorDialog); if (nativeDialogInUse) return true; - if (q->testAttribute(Qt::WA_DontShowOnScreen)) - return false; - if (q->options() & QColorDialog::DontUseNativeDialog) + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs) + || q->testAttribute(Qt::WA_DontShowOnScreen) + || (q->options() & QColorDialog::DontUseNativeDialog)) { return false; + } QLatin1String staticName(QColorDialog::staticMetaObject.className()); QLatin1String dynamicName(q->metaObject()->className()); diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index af4fd235d5..07422f8811 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -704,10 +704,11 @@ bool QFileDialogPrivate::canBeNativeDialog() const Q_Q(const QFileDialog); if (nativeDialogInUse) return true; - if (q->testAttribute(Qt::WA_DontShowOnScreen)) - return false; - if (q->options() & QFileDialog::DontUseNativeDialog) + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs) + || q->testAttribute(Qt::WA_DontShowOnScreen) + || (q->options() & QFileDialog::DontUseNativeDialog)) { return false; + } QLatin1String staticName(QFileDialog::staticMetaObject.className()); QLatin1String dynamicName(q->metaObject()->className()); diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp index 7dbcf15b73..a0525f6fbd 100644 --- a/src/widgets/dialogs/qfontdialog.cpp +++ b/src/widgets/dialogs/qfontdialog.cpp @@ -1043,10 +1043,11 @@ bool QFontDialogPrivate::canBeNativeDialog() const Q_Q(const QFontDialog); if (nativeDialogInUse) return true; - if (q->testAttribute(Qt::WA_DontShowOnScreen)) - return false; - if (options->options() & QFontDialog::DontUseNativeDialog) + if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs) + || q->testAttribute(Qt::WA_DontShowOnScreen) + || (options->options() & QFontDialog::DontUseNativeDialog)) { return false; + } QLatin1String staticName(QFontDialog::staticMetaObject.className()); QLatin1String dynamicName(q->metaObject()->className()); diff --git a/src/widgets/itemviews/qdirmodel.cpp b/src/widgets/itemviews/qdirmodel.cpp index 37ba5a59f8..dfb1d7619e 100644 --- a/src/widgets/itemviews/qdirmodel.cpp +++ b/src/widgets/itemviews/qdirmodel.cpp @@ -136,11 +136,12 @@ public: QPersistentModelIndexData *data; QPersistentModelIndex index; }; - QList<SavedPersistent> savedPersistent; + QVector<SavedPersistent> savedPersistent; QPersistentModelIndex toBeRefreshed; bool shouldStat; // use the "carefull not to stat directories" mode }; +Q_DECLARE_TYPEINFO(QDirModelPrivate::SavedPersistent, Q_MOVABLE_TYPE); void qt_setDirModelShouldNotStat(QDirModelPrivate *modelPrivate) { @@ -1237,14 +1238,16 @@ void QDirModelPrivate::savePersistentIndexes() { Q_Q(QDirModel); savedPersistent.clear(); + savedPersistent.reserve(persistent.indexes.size()); foreach (QPersistentModelIndexData *data, persistent.indexes) { - SavedPersistent saved; QModelIndex index = data->index; - saved.path = q->filePath(index); - saved.column = index.column(); - saved.data = data; - saved.index = index; - savedPersistent.append(saved); + SavedPersistent saved = { + q->filePath(index), + index.column(), + data, + index, + }; + savedPersistent.push_back(std::move(saved)); } } @@ -1253,11 +1256,9 @@ void QDirModelPrivate::restorePersistentIndexes() Q_Q(QDirModel); bool allow = allowAppendChild; allowAppendChild = false; - for (int i = 0; i < savedPersistent.count(); ++i) { - QPersistentModelIndexData *data = savedPersistent.at(i).data; - QString path = savedPersistent.at(i).path; - int column = savedPersistent.at(i).column; - QModelIndex idx = q->index(path, column); + for (const SavedPersistent &sp : qAsConst(savedPersistent)) { + QPersistentModelIndexData *data = sp.data; + QModelIndex idx = q->index(sp.path, sp.column); if (idx != data->index || data->model == 0) { //data->model may be equal to 0 if the model is getting destroyed persistent.indexes.remove(data->index); diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp index 00e0774012..6fc8cb5e26 100644 --- a/src/widgets/widgets/qdialogbuttonbox.cpp +++ b/src/widgets/widgets/qdialogbuttonbox.cpp @@ -288,13 +288,8 @@ void QDialogButtonBoxPrivate::layoutButtons() } break; case QPlatformDialogHelper::AlternateRole: - { - if (acceptRoleList.size() < 2) - break; - QList<QAbstractButton *> list = acceptRoleList; - list.removeFirst(); - addButtonsToLayout(list, reverse); - } + if (acceptRoleList.size() > 1) + addButtonsToLayout(acceptRoleList.mid(1), reverse); break; case QPlatformDialogHelper::DestructiveRole: { diff --git a/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro b/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro index eef165d9f7..3c12a83f5a 100644 --- a/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro +++ b/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro @@ -4,6 +4,10 @@ TARGET = tst_qstandarditemmodel QT += widgets widgets-private testlib QT += core-private gui-private -SOURCES += tst_qstandarditemmodel.cpp +mtdir = ../../../other/modeltest +INCLUDEPATH += $${mtdir} + +SOURCES += $${mtdir}/modeltest.cpp tst_qstandarditemmodel.cpp +HEADERS += $${mtdir}/modeltest.h diff --git a/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp b/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp index 7e9e5a6e96..dca718a6d8 100644 --- a/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp +++ b/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp @@ -32,6 +32,7 @@ #include <qstandarditemmodel.h> #include <QTreeView> #include <private/qtreeview_p.h> +#include "modeltest.h" class tst_QStandardItemModel : public QObject { @@ -734,6 +735,9 @@ void tst_QStandardItemModel::clear() QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); QSignalSpy layoutChangedSpy(&model, SIGNAL(layoutChanged())); QSignalSpy rowsRemovedSpy(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + ModelTest mt(&model); + model.clear(); QCOMPARE(modelResetSpy.count(), 1); diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 871251df2e..d43f700caa 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -4539,18 +4539,18 @@ bool Configure::showLicense(QString orgLicenseFile) return true; } - bool showLgpl2 = true; + bool showGpl2 = true; QString licenseFile = orgLicenseFile; QString theLicense; if (dictionary["EDITION"] == "OpenSource") { if (platform() != WINDOWS_RT && platform() != WINDOWS_CE && (platform() != ANDROID || dictionary["ANDROID_STYLE_ASSETS"] == "no")) { - theLicense = "GNU Lesser General Public License (LGPL) version 2.1" - "\nor the GNU Lesser General Public License (LGPL) version 3"; + theLicense = "GNU Lesser General Public License (LGPL) version 3\n" + "or the GNU General Public License (GPL) version 2"; } else { theLicense = "GNU Lesser General Public License (LGPL) version 3"; - showLgpl2 = false; + showGpl2 = false; } } else { // the first line of the license file tells us which license it is @@ -4568,9 +4568,9 @@ bool Configure::showLicense(QString orgLicenseFile) << "the " << theLicense << "." << endl << endl; if (dictionary["EDITION"] == "OpenSource") { - cout << "Type '3' to view the Lesser GNU General Public License version 3 (LGPLv3)." << endl; - if (showLgpl2) - cout << "Type 'L' to view the Lesser GNU General Public License version 2.1 (LGPLv2.1)." << endl; + cout << "Type 'L' to view the GNU Lesser General Public License version 3 (LGPLv3)." << endl; + if (showGpl2) + cout << "Type 'G' to view the GNU General Public License version 2 (GPLv2)." << endl; } else { cout << "Type '?' to view the " << theLicense << "." << endl; } @@ -4587,10 +4587,10 @@ bool Configure::showLicense(QString orgLicenseFile) return false; } else { if (dictionary["EDITION"] == "OpenSource") { - if (accept == '3') - licenseFile = orgLicenseFile + "/LICENSE.LGPLv3"; + if (accept == 'L') + licenseFile = orgLicenseFile + "/LICENSE.LGPL3"; else - licenseFile = orgLicenseFile + "/LICENSE.LGPLv21"; + licenseFile = orgLicenseFile + "/LICENSE.GPL2"; } // Get console line height, to fill the screen properly int i = 0, screenHeight = 25; // default @@ -4625,7 +4625,7 @@ void Configure::readLicense() dictionary["LICENSE FILE"] = sourcePath; bool openSource = false; - bool hasOpenSource = QFile::exists(dictionary["LICENSE FILE"] + "/LICENSE.LGPLv3") || QFile::exists(dictionary["LICENSE FILE"] + "/LICENSE.LGPLv21"); + bool hasOpenSource = QFile::exists(dictionary["LICENSE FILE"] + "/LICENSE.LGPL3") || QFile::exists(dictionary["LICENSE FILE"] + "/LICENSE.GPL2"); if (dictionary["BUILDTYPE"] == "commercial") { openSource = false; } else if (dictionary["BUILDTYPE"] == "opensource") { |