From 48ca7f6220df4b1dc62fda905d5f8ae0b6e6e41c Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Sat, 13 Feb 2016 14:07:14 +0000 Subject: [PATCH 01/30] Provide optimised version of QOpenGLFramebufferObject::blitFramebuffer Profiling shows that the call to glGetIntegerv() in this function that queries for the currently bound framebuffer is very expensive. It's the top hit on the CPU when profiling a Qt3D application using a Scene3D Qt Quick 2 item. The reason it is so expensive is that this call forces the OpenGL driver to propagate the OpenGL state through all of the queued commands in order to answer the query. It may also induce a pipeline stall depending upon the driver implementation. As this function gets called on the hot path every frame whenever using a Scene3D item and may also be called in plain Qt Quick when using ShaderEffect items; the layer property of QQuickItem; or when updating the Qt Quick glyph cache texture on Core profile contexts, it is very much worthy of optimization. This commit adds an overload of the blitFramebuffer() call that allows the caller to provide a policy that can either: * keep the existing behavior of restoring the previous framebuffer binding, * restore the default framebuffer, or * don't restore anything and let the caller be responsible for it. This will allow consumers such as Qt Quick and Qt 3D to use the optimised code path. The existing overloads of blitFramebuffer() retain the current behavior by calling with the policy to restore the previous framebuffer binding. Upon making this change, the cost of blitFramebuffer() is massively reduced. A follow up commit will optimize this further. Change-Id: I417abb7da916ae5088f6817e4eff8ea02c8c5803 Reviewed-by: Laszlo Agocs --- src/gui/opengl/qopenglframebufferobject.cpp | 72 ++++++++++++++++++++- src/gui/opengl/qopenglframebufferobject.h | 14 ++++ 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 61107640949..4ae01c53ef1 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(); @@ -1711,7 +1743,41 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, if (extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) 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 25d171e289c..b22726cafbb 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, From b725f4e144006daac3a13e986e883a928736f6a9 Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Sat, 13 Feb 2016 14:23:48 +0000 Subject: [PATCH 02/30] Cache results of multiple render target support Change-Id: I9e706690d4d2ddaae6c84b0e24de7698704e862d Reviewed-by: Laszlo Agocs --- src/gui/opengl/qopenglframebufferobject.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 4ae01c53ef1..5f5b7f46ec4 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -1728,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; @@ -1740,7 +1741,7 @@ void QOpenGLFramebufferObject::blitFramebuffer(QOpenGLFramebufferObject *target, tx0, ty0, tx1, ty1, buffers, filter); - if (extensions.hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) + if (supportsMRT) extensions.glReadBuffer(GL_COLOR_ATTACHMENT0); switch (restorePolicy) { From 1a358a052e422b72039ace2f803eb80f8a7e43b3 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Mon, 15 Feb 2016 13:39:11 +0200 Subject: [PATCH 03/30] Fix crash when using .NoActionBar theme. m_activity.getActionBar() returns null when a .NoActionBar theme is declared in AndroidManifest.xml file Change-Id: I671891d03913209c3f9f34a6f4dd8894c20de5c2 Reviewed-by: Christian Stromme --- .../jar/src/org/qtproject/qt5/android/QtActivityDelegate.java | 2 ++ 1 file changed, 2 insertions(+) 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 14c7a03a36f..985661bf3c3 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtActivityDelegate.java @@ -1200,6 +1200,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 From 8204a4db0506c3301c5eaeca491344f2d8c1ad32 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 12 Feb 2016 19:09:49 +0100 Subject: [PATCH 04/30] qmake: remove some empty user-defined dtors They prevent move special member functions from being synthesized by the compiler. Change-Id: I90c4a6e286734ef3906ee833826bd3bfbdad3874 Reviewed-by: Lars Knoll --- qmake/generators/win32/msvc_objectmodel.cpp | 4 ---- qmake/generators/win32/msvc_objectmodel.h | 21 ++++++++------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/qmake/generators/win32/msvc_objectmodel.cpp b/qmake/generators/win32/msvc_objectmodel.cpp index cc6e5754ec6..5e655464b71 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 35aa2888871..dfbfaacfa84 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; } From c7c7cf636b07f8467f9028bf1e22ac9b87c1c7a7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 12 Feb 2016 21:46:51 +0100 Subject: [PATCH 05/30] Remove LGPLv2 as a license option in configure Instead show LGPLv3 and GPLv2 as valid options for the open source edition. Change-Id: Id7a203226428031ec873cbaf106dca14a854f155 Reviewed-by: Thiago Macieira Reviewed-by: Oswald Buddenhagen --- configure | 26 +++++++++++++------------- tools/configure/configureapp.cpp | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/configure b/configure index 121031e6faa..41995570023 100755 --- a/configure +++ b/configure @@ -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/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 871251df2e2..d43f700caaa 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") { From a93ff2c2483b7d2f0c678aa3e52495e4cb36e9e1 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Feb 2016 13:42:51 +0100 Subject: [PATCH 06/30] Reduce allocations in QSQLiteDriver::open()'s connection options parsing - Use splitRef() instead of split(), avoiding small temporary QStrings. - Don't remove all spaces before splitting, trim strings at the QStringRef level later, where needed. This will reject nonsense strings like QSQL_ LITE _BUSY_ TI MEOUT= 1 2 3 that were previously (wrongly) accepted. - Use C++11 range-for loop. Change-Id: I875c4cf47b7a283ba55783f70c903bb9947e1cd7 Reviewed-by: Mark Brand --- src/sql/drivers/sqlite/qsql_sqlite.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index c1071c92227..1fff427a666 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")) { From 273114582aaa944b4a22f1782cc5799fdd1e8e7b Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 15 Feb 2016 14:23:28 +0100 Subject: [PATCH 07/30] Add QStyleHints::setTabFocusBehavior() for testing purposes So far we've been dependent on the focus behavior setting in OS X system preferences. This change allows us to start testing both behaviors on any platform. Change-Id: I9ce004f8b9479f8e722a387b795de16edb166a07 Reviewed-by: Mitch Curtis Reviewed-by: Liang Qi --- src/gui/kernel/qstylehints.cpp | 22 +++++++++++++++++++++- src/gui/kernel/qstylehints.h | 4 +++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp index b295fe986a1..e184fed2750 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 a79fad805a0..0b07e605791 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; From 21d19f760c1c9b6c225cb54260ef1bb691ef2e65 Mon Sep 17 00:00:00 2001 From: Eric Lemanissier Date: Mon, 15 Feb 2016 17:33:43 +0100 Subject: [PATCH 08/30] Improve sql allocated cache reuse This change avoids unnecessary allocation of QSqlCachedResult cache. Change-Id: Ief592ab05b50f5e328490c504af088ec74d1938f Reviewed-by: Friedemann Kleint Reviewed-by: Mark Brand --- src/sql/kernel/qsqlcachedresult.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sql/kernel/qsqlcachedresult.cpp b/src/sql/kernel/qsqlcachedresult.cpp index 3bacffed75d..c335d7d8fe7 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); } From 9baf824e4df5e9bc58b4fa1dc86e9a2e0d396ebb Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Feb 2016 12:18:58 +0300 Subject: [PATCH 09/30] QDateTimeParser: de-duplicate calls and cache results Change-Id: I2e95456146b6ce646e244e962082f2967bcaed42 Reviewed-by: Edward Welbourne Reviewed-by: Marc Mutz --- src/corelib/tools/qdatetimeparser.cpp | 45 ++++++++++++++++----------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/corelib/tools/qdatetimeparser.cpp b/src/corelib/tools/qdatetimeparser.cpp index 9ffef05d786..bbcd64d34f6 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; From 56ad625f7843bb66bef2a0ef9b5c97d354fbdb25 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Mon, 15 Feb 2016 14:27:56 +0100 Subject: [PATCH 10/30] Add attribute Qt::AA_DontUseNativeDialogs. The attribute can be set to suppress native dialogs for example for testing purposes. Task-number: QTBUG-51074 Change-Id: I35611e07e00b7a060f22b49d6ab6f3b8627f8aca Reviewed-by: Shawn Rutledge Reviewed-by: J-P Nurmi --- src/corelib/global/qnamespace.h | 1 + src/corelib/global/qnamespace.qdoc | 4 ++++ src/widgets/dialogs/qcolordialog.cpp | 7 ++++--- src/widgets/dialogs/qfiledialog.cpp | 7 ++++--- src/widgets/dialogs/qfontdialog.cpp | 7 ++++--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 25973a3ae4d..b6d8e6fdc88 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 1efcce393d3..b17a1ab48bd 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/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index 47f2e76faf0..9f86bea0ca0 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 288922d740e..ca2b0c08896 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 7dbcf15b73e..a0525f6fbd6 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()); From a44d7862c8e92ad2bc2d0d56e542066e688d5c82 Mon Sep 17 00:00:00 2001 From: Anton Kudryavtsev Date: Tue, 16 Feb 2016 14:56:26 +0300 Subject: [PATCH 11/30] QDate: mark getDate() as const. This method does not modify the object. Can't change the API, so overload and mark the old function for removal in Qt 6. Change-Id: I4aee2bc19209646adc21388375aedd20a09129d0 Reviewed-by: Edward Welbourne Reviewed-by: Marc Mutz --- src/corelib/tools/qdatetime.cpp | 15 ++++++++++++++- src/corelib/tools/qdatetime.h | 5 ++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index 280f9ea006c..e794da8326b 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 af91ad0fe59..ee3be5553b7 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; From bfeb2fdd7999e63fd21045eb7c0ac5dc5e62d726 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Mon, 15 Feb 2016 12:51:25 +0100 Subject: [PATCH 12/30] QStandardItemModel: Reset the flags on clear() Pass modeltest after clear(). Otherwise it fails because more flags than Qt::ItemIsDropEnabled get returned for the QModelIndex(). Change-Id: I8f11515cc7dc9383f528f785312ffb77b3c2699d Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/gui/itemmodels/qstandarditemmodel.cpp | 1 + .../itemmodels/qstandarditemmodel/qstandarditemmodel.pro | 6 +++++- .../qstandarditemmodel/tst_qstandarditemmodel.cpp | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/itemmodels/qstandarditemmodel.cpp b/src/gui/itemmodels/qstandarditemmodel.cpp index e843351cf73..479a4283b01 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/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro b/tests/auto/gui/itemmodels/qstandarditemmodel/qstandarditemmodel.pro index eef165d9f72..3c12a83f5a7 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 7e9e5a6e968..dca718a6d8d 100644 --- a/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp +++ b/tests/auto/gui/itemmodels/qstandarditemmodel/tst_qstandarditemmodel.cpp @@ -32,6 +32,7 @@ #include #include #include +#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); From d6c8073a34e77f8a1afbca913df86f7e70ee9f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 15 Feb 2016 19:41:20 +0100 Subject: [PATCH 13/30] Revert "Fix shared library framework builds of Qt with a platform suffix." MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c4ecb81d6d64a190f7d24222d8cf35d953e73c1e. Hard-coding the library suffix into the linker flags was wrong. The library suffix is handled at runtime with DYLD_IMAGE_SUFFIX, set as part of the Xcode scheme or during debugging in .lldbinit. Change-Id: I11907b2755f7f187fb6fa18202813fde9ada4354 Reviewed-by: Jake Petroules Reviewed-by: Oswald Buddenhagen Reviewed-by: Tor Arne Vestbø --- mkspecs/features/qt.prf | 2 +- mkspecs/features/qt_functions.prf | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index b53617ba2a2..4b40451c966 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 88467e89bad..b2c25078071 100644 --- a/mkspecs/features/qt_functions.prf +++ b/mkspecs/features/qt_functions.prf @@ -13,16 +13,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) { From 4a7ccf74ff85a49e8c701a4d1c40f316b6fec4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 16 Feb 2016 16:39:18 +0100 Subject: [PATCH 14/30] Disable XCTest support in QtTestLib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In recent Xcode versions it doesn't work as expected, and caused build failures in downstream modules, so we disable it until the cause can be investigated further. Change-Id: Ie54c7256a10d73610ec7e481b9d665b75e396365 Reviewed-by: Simon Hausmann Reviewed-by: Tor Arne Vestbø --- src/testlib/testlib.pro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 0faa1c7d30c..85fb0cd2fe5 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 From efcf1dec4992bf7aab5bf1f0f4c0ee8c54030465 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 17 Feb 2016 14:37:50 +0200 Subject: [PATCH 15/30] Say hello to Android Services This changeset enables running a QCoreApplication from within an Android Service. The Android Application running can now have a QtActivity or a QtService, but having both in the same process is not supported. This patch was based on Cory Slep's patch [ChangeLog][Android] Qt can now be used to easily create Android Services. Task-number: QTBUG-37221 Change-Id: I0fd693daaa85b991940ffe9cc41c483022677199 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/android/jar/jar.pri | 3 +- .../qt5/android/QtActivityDelegate.java | 1 + .../org/qtproject/qt5/android/QtNative.java | 121 ++- .../qt5/android/QtNativeLibrariesDir.java | 8 +- .../qt5/android/QtServiceDelegate.java | 182 +++++ .../qt5/android/bindings/QtActivity.java | 701 +----------------- .../android/bindings/QtActivityLoader.java | 193 +++++ .../qt5/android/bindings/QtApplication.java | 16 +- .../qt5/android/bindings/QtLoader.java | 655 ++++++++++++++++ .../qt5/android/bindings/QtService.java | 153 ++++ .../qt5/android/bindings/QtServiceLoader.java | 77 ++ src/android/templates/AndroidManifest.xml | 55 +- src/corelib/io/qstandardpaths_android.cpp | 17 +- src/corelib/kernel/qjnihelpers.cpp | 55 +- src/corelib/kernel/qjnihelpers_p.h | 1 + .../platforms/android/androidjnimain.cpp | 23 +- .../platforms/android/androidjnimain.h | 1 + .../android/qandroidplatformintegration.cpp | 27 +- .../android/qandroidplatformscreen.cpp | 2 + .../android/qandroidsystemlocale.cpp | 2 + 20 files changed, 1533 insertions(+), 760 deletions(-) create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java create mode 100644 src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java diff --git a/src/android/jar/jar.pri b/src/android/jar/jar.pri index b45b353f95a..58caacb8374 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 985661bf3c3..c434cc24b3b 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 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 d9be4c7d9fc..5d4f6e791a7 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 +** Copyright (C) 2016 BogDan Vatra ** 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 8481baec54c..ff3bf193837 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 00000000000..634658eefb2 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt5/android/QtServiceDelegate.java @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 BogDan Vatra +** 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 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 4c2e6122386..678420dae7c 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 + Copyright (c) 2016, BogDan Vatra 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 libs = new ArrayList(); - 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 libs = new ArrayList(); - - 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 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 libraryList = new ArrayList(); - - - String localPrefix = "/data/local/tmp/qt/"; - if (m_activityInfo.metaData.containsKey("android.app.libs_prefix")) - localPrefix = m_activityInfo.metaData.getString("android.app.libs_prefix"); - - 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 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 00000000000..836a7677b3a --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivityLoader.java @@ -0,0 +1,193 @@ +/* + Copyright (c) 2016, BogDan Vatra + 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 c78aeb7f133..2afede6d66c 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 + Copyright (c) 2016, BogDan Vatra 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 delegateMethods = new ArrayList(); 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 + 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 libs = new ArrayList(); + 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 libs = new ArrayList(); + + 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 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 libraryList = new ArrayList(); + + + 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 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 00000000000..c84bdd63c3d --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtService.java @@ -0,0 +1,153 @@ +/* + Copyright (c) 2016, BogDan Vatra + 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 00000000000..e64018f0a8f --- /dev/null +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtServiceLoader.java @@ -0,0 +1,77 @@ +/* + Copyright (c) 2016, BogDan Vatra + 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 2a6d0b6fa3a..02fd0ce23b2 100644 --- a/src/android/templates/AndroidManifest.xml +++ b/src/android/templates/AndroidManifest.xml @@ -10,6 +10,11 @@ + + + + + @@ -32,9 +37,7 @@ - + @@ -49,7 +52,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp index 1b0db648151..2a44daf8b52 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 2324a615d5d..f576ed0d1ce 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(runPendingCppRunnables)}, {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast(dispatchGenericMotionEvent)}, {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast(dispatchKeyEvent)}, + {"setNativeActivity", "(Landroid/app/Activity;)V", reinterpret_cast(setNativeActivity)}, + {"setNativeService", "(Landroid/app/Service;)V", reinterpret_cast(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 1bffd26e08e..34bdbf6c801 100644 --- a/src/corelib/kernel/qjnihelpers_p.h +++ b/src/corelib/kernel/qjnihelpers_p.h @@ -100,6 +100,7 @@ namespace QtAndroidPrivate typedef std::function 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/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 1eae2957247..6340d47c18c 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(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 e3c18b2e7a1..218e52ccc1a 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 1f8ee793962..80d7e31aa3c 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 dd29c29bab5..aa4fa94f0a3 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 1528d90d06d..7fe36aa9bce 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;"); From 66e26430727bc76557b99c8e2b136901c5a38f90 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 8 Jan 2016 00:39:05 +0100 Subject: [PATCH 16/30] QDialogButtonBox: replace a QList copy + pop_front() with mid(1) Simplifies the code and is also more efficient, because it copies less data. Change-Id: I9ad0c372fb4fa6f5818d9d6cb7b7cf35935f8565 Reviewed-by: Friedemann Kleint --- src/widgets/widgets/qdialogbuttonbox.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp index 00e0774012e..6fc8cb5e26e 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 list = acceptRoleList; - list.removeFirst(); - addButtonsToLayout(list, reverse); - } + if (acceptRoleList.size() > 1) + addButtonsToLayout(acceptRoleList.mid(1), reverse); break; case QPlatformDialogHelper::DestructiveRole: { From 4262f31330ea48c384396e40dc50811b97642575 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 18 Dec 2015 14:20:13 +0100 Subject: [PATCH 17/30] QDaemonThread: replace a function pointer with a lambda Saves around 0.5KiB in text size on optimized GCC 5.3 Linux AMD 64 builds. Change-Id: Iaf2664e670a96136031bac67e4012d4f7324eb47 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/thread/qthread.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index 14209d8d8cd..f689ad4d213 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() From 30b0c346ec9bfe3730aa1a5a77199a3b0e61c1f8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 9 Sep 2015 09:29:52 +0200 Subject: [PATCH 18/30] QShortcutMap: re-use an existing bool variable ... instead of re-evaluating its expression. More readable. Change-Id: I18c6ab3bbc4c5a14328f9910fab991f6cad5549d Reviewed-by: Friedemann Kleint --- src/gui/kernel/qshortcutmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 14c01085e7f..5eb216d58e8 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; From 773458ad633c68e9111888b71f8971064cf42fd3 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 25 Jul 2015 12:24:20 +0200 Subject: [PATCH 19/30] Remove QMimeMagicRule's pimpl It didn't make the class implicitly shared, but required an additional heap allocation on construction and copy, as it used 'only' QScopedPointer. As it's private API the pimpl is also not needed for BC reasons. So inline the data members, and some trivial accessors. As a by-product of removing the copy special member functions, we gain nothrow move special member functions. Interestingly, the memory layout of a QList (replacing which is the topic of a future patch) doesn't change due to this change, because the type that formerly fit QList very well now is too large. But copying the type outside QList now no longer allocates memory. Saves more than 2.5KiB in text size on optimized GCC 5.3 Linux AMD64 builds. Change-Id: Ie3588cb5693227da6f1bfa196db924e075a750b3 Reviewed-by: David Faure --- src/corelib/mimetypes/qmimemagicrule.cpp | 212 ++++++++--------------- src/corelib/mimetypes/qmimemagicrule_p.h | 34 ++-- 2 files changed, 95 insertions(+), 151 deletions(-) diff --git a/src/corelib/mimetypes/qmimemagicrule.cpp b/src/corelib/mimetypes/qmimemagicrule.cpp index 1b8acc0f6e2..09473caa789 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 +bool QMimeMagicRule::operator==(const QMimeMagicRule &other) const { -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 -{ - 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 -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(p) & mask) == (value & mask)) return true; @@ -242,105 +223,102 @@ static inline QByteArray makePattern(const QByteArray &value) // // -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; + if (m_number <= quint8(-1)) { + if (m_numberMask == 0) + m_numberMask = quint8(-1); + m_matchFunction = &QMimeMagicRule::matchNumber; } break; case Big16: case Host16: case Little16: - if (d->number <= quint16(-1)) { - d->number = d->type == Little16 ? qFromLittleEndian(d->number) : qFromBigEndian(d->number); - if (d->numberMask == 0) - d->numberMask = quint16(-1); - d->matchFunction = matchNumber; + if (m_number <= quint16(-1)) { + m_number = m_type == Little16 ? qFromLittleEndian(m_number) : qFromBigEndian(m_number); + if (m_numberMask == 0) + m_numberMask = quint16(-1); + m_matchFunction = &QMimeMagicRule::matchNumber; } break; case Big32: case Host32: case Little32: - if (d->number <= quint32(-1)) { - d->number = d->type == Little32 ? qFromLittleEndian(d->number) : qFromBigEndian(d->number); - if (d->numberMask == 0) - d->numberMask = quint32(-1); - d->matchFunction = matchNumber; + if (m_number <= quint32(-1)) { + m_number = m_type == Little32 ? qFromLittleEndian(m_number) : qFromBigEndian(m_number); + if (m_numberMask == 0) + m_numberMask = quint32(-1); + m_matchFunction = &QMimeMagicRule::matchNumber; } 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 84c2fe21747..1a6ca81e110 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,16 @@ 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); 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,7 +89,24 @@ public: static bool matchSubstring(const char *dataPtr, int dataSize, int rangeStart, int rangeLength, int valueLength, const char *valueData, const char *mask); private: - const QScopedPointer 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 + bool matchNumber(const QByteArray &data) const; }; Q_DECLARE_TYPEINFO(QMimeMagicRule, Q_MOVABLE_TYPE); From de8229f37054b195bab4ca8e8a994832a60ae3a0 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sat, 25 Jul 2015 12:25:15 +0200 Subject: [PATCH 20/30] mimetypes: Declare some types as shared It's private API, so we can. As a consequence, had to add nothrow member-swap and, in QMimeGlobPattern, remove the user-defined empty destructor to un-inhibit the (nothrow) move special member functions. Change-Id: If5bb72db3c823c7b0e372f9bec99c7242d11839b Reviewed-by: David Faure Reviewed-by: Lars Knoll --- src/corelib/mimetypes/qmimeglobpattern_p.h | 10 +++++++++- src/corelib/mimetypes/qmimemagicrule_p.h | 15 ++++++++++++++- src/corelib/mimetypes/qmimemagicrulematcher_p.h | 8 ++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/corelib/mimetypes/qmimeglobpattern_p.h b/src/corelib/mimetypes/qmimeglobpattern_p.h index 0088931b5cf..7b4ecd2f6a7 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 { diff --git a/src/corelib/mimetypes/qmimemagicrule_p.h b/src/corelib/mimetypes/qmimemagicrule_p.h index 1a6ca81e110..26f93d96cc7 100644 --- a/src/corelib/mimetypes/qmimemagicrule_p.h +++ b/src/corelib/mimetypes/qmimemagicrule_p.h @@ -69,6 +69,19 @@ public: QMimeMagicRule(const QString &typeStr, const QByteArray &value, const QString &offsets, const QByteArray &mask, QString *errorString); + 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 { return m_type; } @@ -108,7 +121,7 @@ private: template 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 b2e171ec1f7..5e3d5306c05 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 From 88e043a8bd1b49949777c90c75ff36314af7fe0f Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Feb 2016 13:17:21 +0100 Subject: [PATCH 21/30] QOCIDriver: optimize string handling in tables() - Instead of a QStringList, use a const char 2D array to hold the system user names. Don't remove the current user from that list, skip it duing processing. - Extract Method make_where_clause, optimize it and cache its result. Instead of creating a QStringList and join()ing it at the end (sometimes twice), append to the result QString directly. Reserve the max size of the result string, which is statically known. - Keep the query select statements in QLatin1String. They are only used in QStringBuilder expressions. Change-Id: I0593d2812da671a541d49a6136f3ff3d784c63d5 Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/sql/drivers/oci/qsql_oci.cpp | 80 +++++++++++++++++++------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/src/sql/drivers/oci/qsql_oci.cpp b/src/sql/drivers/oci/qsql_oci.cpp index 14a04d269fb..47d6db7ea46 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 != 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 " " + + 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()); From 6f7a5b0ecfa6e0490cd7bed501860ad920c15178 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 15 Feb 2016 13:43:37 +0100 Subject: [PATCH 22/30] QtSql: eradicate remaining Q_FOREACH loops Change-Id: I86afe7104d506b840130517ae8066588fab2d745 Reviewed-by: Lars Knoll Reviewed-by: Mark Brand --- src/sql/drivers/sqlite/qsql_sqlite.cpp | 3 +-- src/sql/kernel/qsqlresult.cpp | 4 ++-- src/sql/models/qsqltablemodel.cpp | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index 1fff427a666..2a45b73d141 100644 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -661,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/qsqlresult.cpp b/src/sql/kernel/qsqlresult.cpp index 5e4c0244800..f79c1c71cd6 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 indexes = d->indexes.value(placeholder); - foreach (int idx, indexes) { + const QList 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 3612787fca8..0bc86494f95 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()) From 8049e9b3b93e855d32021edb6fc4bdcb41ccd512 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 17 Jan 2016 03:08:01 +0100 Subject: [PATCH 23/30] QSslSocket (OpenSSL): use QMutexLocker ... instead of naked QMutex::(un)lock(). Change-Id: I9927e60286231bfc254d99cc88c39301b31df336 Reviewed-by: Lars Knoll --- src/network/ssl/qsslsocket_openssl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index 2780de1316f..ad54e126ef3 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1040,7 +1040,7 @@ 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); @@ -1056,7 +1056,7 @@ bool QSslSocketBackendPrivate::startHandshake() } errorList << lastErrors; - _q_sslErrorList()->mutex.unlock(); + locker.unlock(); // Connection aborted during handshake phase. if (q->state() != QAbstractSocket::ConnectedState) From 7ca90e985cd92c9537fec6d523b93b45c5edfdb5 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 17 Jan 2016 14:16:43 +0100 Subject: [PATCH 24/30] QSslSocket (OpenSSL): replace QList by QVector A QList> is one of the most dangerous ones, because it fundamentally changes memory layout, and therefore performance and invariants, when going from 32-bit platforms (array list) to 64-bit (vector-like). Port to QVector instead, which has a consistent design across all platforms. Also port from QPair to a simple struct { code, depth }, because member names such as 'first' and 'second' have no semantic value and make code using them very hard to understand. Change-Id: I86c95d78dbb2e82ec222d6eae8ba11568e3ff0af Reviewed-by: Lars Knoll --- src/network/ssl/qsslsocket_openssl.cpp | 46 ++++++++++++-------------- src/network/ssl/qsslsocket_openssl_p.h | 10 +++++- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index ad54e126ef3..ac6b2df5951 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 > errors; + QVector 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(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(); @@ -1044,13 +1053,12 @@ bool QSslSocketBackendPrivate::startHandshake() _q_sslErrorList()->errors.clear(); int result = (mode == QSslSocket::SslClientMode) ? q_SSL_connect(ssl) : q_SSL_accept(ssl); - const QList > &lastErrors = _q_sslErrorList()->errors; + const auto &lastErrors = _q_sslErrorList()->errors; if (!lastErrors.isEmpty()) storePeerCertificates(); - for (int i = 0; i < lastErrors.size(); ++i) { - const QPair ¤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; } @@ -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 &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 QSslSocketBackendPrivate::verify(const QList & #endif // Now process the errors - const QList > errorList = _q_sslErrorList()->errors; + const auto errorList = std::move(_q_sslErrorList()->errors); _q_sslErrorList()->errors.clear(); sslErrorListMutexLocker.unlock(); @@ -1711,14 +1714,9 @@ QList QSslSocketBackendPrivate::verify(const QList & } // 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 &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 890576e378e..dc60adda39c 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 > errorList; + QVector errorList; #if OPENSSL_VERSION_NUMBER >= 0x10001000L static int s_indexForSSLExtraData; // index used in SSL_get_ex_data to get the matching QSslSocketBackendPrivate #endif From 24a96e2a3e676e253eac62a2bf50de60751f7d4f Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 29 Dec 2015 11:12:31 +0100 Subject: [PATCH 25/30] QtPrintSupport: convert some index-based into C++11 range-for loops Also reserve a QList in QPrinter::supportedPaperSources(). Change-Id: I94d24b81d7adcad2b28d36efdbef5a8726412f1a Reviewed-by: Lars Knoll --- src/printsupport/dialogs/qprintpreviewdialog.cpp | 4 ++-- src/printsupport/kernel/qpaintengine_alpha.cpp | 4 ++-- src/printsupport/kernel/qprintdevice.cpp | 6 +++--- src/printsupport/kernel/qprinter.cpp | 16 ++++++++-------- src/printsupport/widgets/qprintpreviewwidget.cpp | 16 ++++++++-------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/printsupport/dialogs/qprintpreviewdialog.cpp b/src/printsupport/dialogs/qprintpreviewdialog.cpp index 97acbb79a19..435f8869c40 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 4922d210f23..939322cc077 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; idrawAlphaImage(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 29dd0d65295..26799a6f13f 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 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 cc4c8244aed..cbab151ea0c 100644 --- a/src/printsupport/kernel/qprinter.cpp +++ b/src/printsupport/kernel/qprinter.cpp @@ -1946,13 +1946,12 @@ int QPrinter::winPageSize() const QList QPrinter::supportedResolutions() const { Q_D(const QPrinter); - QList varlist + const QList varlist = d->printEngine->property(QPrintEngine::PPK_SupportedResolutions).toList(); QList 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::supportedPaperSources() const Q_D(const QPrinter); QVariant v = d->printEngine->property(QPrintEngine::PPK_PaperSources); - QList variant_list = v.toList(); + const QList variant_list = v.toList(); QList int_list; - for (int i=0; iviewport()->rect(); - QList items = graphicsView->items(viewRect); - for (int i=0; i(items.at(i)); + const QList items = graphicsView->items(viewRect); + for (auto *item : items) { + PageItem* pg = static_cast(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); } From 47ca3f78139ad51a5018dcdad27fa5caf817f08d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Sun, 5 Jul 2015 22:43:08 +0200 Subject: [PATCH 26/30] bearer plugins: fix uses of inefficient QLists These types are larger than a void*, so holding them in QLists is needlessly inefficient. Worse, the code could come to depend on the fragile property of (inefficient) QLists that references to elements therein never are invalidated. Fix by marking them movable, and holding in a QVector instead. Change-Id: I78774fc78d787241aaadc4f819b6d229e6200f46 Reviewed-by: Lars Knoll --- src/plugins/bearer/connman/qconnmanservice_linux_p.h | 5 +++-- src/plugins/bearer/linux_common/qofonoservice_linux_p.h | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/plugins/bearer/connman/qconnmanservice_linux_p.h b/src/plugins/bearer/connman/qconnmanservice_linux_p.h index 9a2bf4eb342..e773db9b209 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 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 43b9570c995..35614a20f2a 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 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 PathPropertiesList; Q_DECLARE_METATYPE(ObjectPathProperties) Q_DECLARE_METATYPE (PathPropertiesList) From aae362e2fe3c0a71dd6d01eaf7f20085506ef7f4 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 24 Jun 2015 02:04:09 +0200 Subject: [PATCH 27/30] QDirModel: replace an inefficient QList with QVector SavedPersistent is larger than a void*, so holding it in QLists is needlessly inefficient. Worse, the code could come to depend on the fragile property of (inefficient) QLists that references to elements therein never are invalidated. Fix by marking it movable and holding it in a QVector instead. Take advantage of rvalue-enabled QVector::push_back(); optimize element construction by using aggregate initialization. Change-Id: I4fd88879aa13e6536d59d164b3c33fbc2fead77f Reviewed-by: Lars Knoll --- src/widgets/itemviews/qdirmodel.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/widgets/itemviews/qdirmodel.cpp b/src/widgets/itemviews/qdirmodel.cpp index 37ba5a59f82..dfb1d7619e1 100644 --- a/src/widgets/itemviews/qdirmodel.cpp +++ b/src/widgets/itemviews/qdirmodel.cpp @@ -136,11 +136,12 @@ public: QPersistentModelIndexData *data; QPersistentModelIndex index; }; - QList savedPersistent; + QVector 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); From d562c7761e90dcd3eb9b6270ac5977327e04c924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20K=C3=BCmmel?= Date: Mon, 15 Feb 2016 22:29:44 +0100 Subject: [PATCH 28/30] Fix typos in qOverload documentation Change-Id: I92f99623f659543934c52961307b0b53c8520948 Reviewed-by: Marc Mutz Reviewed-by: Olivier Goffart (Woboq GmbH) --- src/corelib/global/qglobal.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index e4fa0f73918..3ea121fcc22 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 \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 \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 \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 From c1da6347e8d3ba73de20ab8fb3e50ec3359b75ac Mon Sep 17 00:00:00 2001 From: Jake Petroules Date: Tue, 16 Feb 2016 20:44:06 -0800 Subject: [PATCH 29/30] xcodeSDKInfo: return the correct path based on the specified SDK. This fixes a regression introduced in 9daeb6fe9d35b10ed739ea0a0566533524ffd532. Change-Id: I3100b307bb65c90bdc023be4993afaea666e409d Reviewed-by: Oswald Buddenhagen --- mkspecs/features/mac/sdk.prf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkspecs/features/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index 46ed51deb49..ab37b1740f8 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) From 9b49b375e3d753fa3cd8166da266724a85cfe389 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 12 Feb 2016 17:04:48 +0100 Subject: [PATCH 30/30] Windows QPA: Fix warnings as shown by Qt Creator's Clang based code model. Fix warnings in file qwindowswindow.cpp. Remaining part of a refactoring done in the 5.6 branch. Task-number: QTBUG-50804 Change-Id: I4d7b423e0802ac39109c30c0de615664d3ee9216 Reviewed-by: Joerg Bornemann --- .../platforms/windows/qwindowswindow.cpp | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 22a2922235c..464fcc886fd 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(); + parentHandle = reinterpret_cast(prop.value()); } 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()));