diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-12-04 14:41:36 +0100 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2017-12-06 20:46:51 +0000 |
commit | e37c9d5d5f4ba9874808a4e565c36b2953eb96da (patch) | |
tree | 1a7f0a1ae885fccfb6ff8114c4eed9e197bb7908 /src/plugins/platforms/ios | |
parent | 77942a1bdf9fe22d8f076e59ce19fe9a9d7870d7 (diff) | |
download | qtbase-e37c9d5d5f4ba9874808a4e565c36b2953eb96da.tar.gz |
iOS: Harden application state change logic
Make sure we catch application state changes as early as possible,
and deal properly with any changes delivered before we have an app
to send them to.
Change-Id: I6d0ea0398f9fab88fc182342769b075cb144227f
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'src/plugins/platforms/ios')
-rw-r--r-- | src/plugins/platforms/ios/qiosapplicationstate.h | 18 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosapplicationstate.mm | 110 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosintegration.h | 3 |
3 files changed, 65 insertions, 66 deletions
diff --git a/src/plugins/platforms/ios/qiosapplicationstate.h b/src/plugins/platforms/ios/qiosapplicationstate.h index 1c77b26da1..a68147a72a 100644 --- a/src/plugins/platforms/ios/qiosapplicationstate.h +++ b/src/plugins/platforms/ios/qiosapplicationstate.h @@ -40,20 +40,24 @@ #ifndef QIOSAPPLICATIONSTATE_H #define QIOSAPPLICATIONSTATE_H -#include <QtCore/qglobal.h> -#include <QtCore/qvector.h> +#include <QtCore/qobject.h> -Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); +#include <UIKit/UIApplication.h> QT_BEGIN_NAMESPACE -class QIOSApplicationState +class QIOSApplicationState : public QObject { + Q_OBJECT public: QIOSApplicationState(); - ~QIOSApplicationState(); -private: - QVector<NSObject*> m_observers; + + static void handleApplicationStateChanged(UIApplicationState state, const QString &reason); + static Qt::ApplicationState toQtApplicationState(UIApplicationState state); + +Q_SIGNALS: + void applicationStateWillChange(Qt::ApplicationState); + void applicationStateDidChange(Qt::ApplicationState); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosapplicationstate.mm b/src/plugins/platforms/ios/qiosapplicationstate.mm index 7c8e1f9927..fed09999b5 100644 --- a/src/plugins/platforms/ios/qiosapplicationstate.mm +++ b/src/plugins/platforms/ios/qiosapplicationstate.mm @@ -40,82 +40,76 @@ #include "qiosapplicationstate.h" #include "qiosglobal.h" +#include "qiosintegration.h" #include <qpa/qwindowsysteminterface.h> #include <QtCore/qcoreapplication.h> #include <QtGui/private/qguiapplication_p.h> -#import <UIKit/UIKit.h> +QT_BEGIN_NAMESPACE -static Qt::ApplicationState qtApplicationState(UIApplicationState uiApplicationState) +static void qRegisterApplicationStateNotifications() { - switch (uiApplicationState) { - case UIApplicationStateActive: - // The application is visible in front, and receiving events - return Qt::ApplicationActive; - case UIApplicationStateInactive: - // The app is running in the foreground but is not receiving events. This - // typically happens while transitioning to/from active/background, like - // upon app launch or when receiving incoming calls. - return Qt::ApplicationInactive; - case UIApplicationStateBackground: - // Normally the app would enter this state briefly before it gets - // suspeded (you have five seconds, according to Apple). - // You can request more time and start a background task, which would - // normally map closer to Qt::ApplicationHidden. But since we have no - // API for doing that yet, we handle this state as "about to be suspended". - // Note: A screen-shot for the SpringBoard will also be taken after this - // call returns. - return Qt::ApplicationSuspended; + NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; + NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; + + // Map between notifications and corresponding application state. Note that + // there's no separate notification for moving to UIApplicationStateInactive, + // so we use UIApplicationWillResignActiveNotification as an intermediate. + static QMap<NSNotificationName, UIApplicationState> notifications { + { UIApplicationDidBecomeActiveNotification, UIApplicationStateActive }, + { UIApplicationWillResignActiveNotification, UIApplicationStateInactive }, + { UIApplicationDidEnterBackgroundNotification, UIApplicationStateBackground }, + }; + + for (auto i = notifications.constBegin(); i != notifications.constEnd(); ++i) { + [notificationCenter addObserverForName:i.key() object:nil queue:mainQueue + usingBlock:^void(NSNotification *notification) { + NSRange nameRange = NSMakeRange(2, notification.name.length - 14); + QString reason = QString::fromNSString([notification.name substringWithRange:nameRange]); + QIOSApplicationState::handleApplicationStateChanged(i.value(), reason); + }]; } -} -static void handleApplicationStateChanged(UIApplicationState uiApplicationState) -{ - Qt::ApplicationState state = qtApplicationState(uiApplicationState); - qCDebug(lcQpaApplication) << "moved to" << state; - QWindowSystemInterface::handleApplicationStateChanged(state); + // Initialize correct startup state, which may not be the Qt default (inactive) + UIApplicationState startupState = [UIApplication sharedApplication].applicationState; + QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application loaded")); } - -QT_BEGIN_NAMESPACE +Q_CONSTRUCTOR_FUNCTION(qRegisterApplicationStateNotifications) QIOSApplicationState::QIOSApplicationState() { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - - m_observers.push_back([notificationCenter addObserverForName:UIApplicationDidBecomeActiveNotification - object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *) { - handleApplicationStateChanged(UIApplicationStateActive); - } - ]); - - m_observers.push_back([notificationCenter addObserverForName:UIApplicationWillResignActiveNotification - object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *) { - // Note: UIApplication is still UIApplicationStateActive at this point, - // but since there is no separate notification for the inactive state, - // we report UIApplicationStateInactive now. - handleApplicationStateChanged(UIApplicationStateInactive); - } - ]); - - m_observers.push_back([notificationCenter addObserverForName:UIApplicationDidEnterBackgroundNotification - object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *) { - handleApplicationStateChanged(UIApplicationStateBackground); - } - ]); - - // Initialize correct startup state, which may not be the Qt default (inactive) UIApplicationState startupState = [UIApplication sharedApplication].applicationState; - QGuiApplicationPrivate::applicationState = qtApplicationState(startupState); + QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application launched")); } -QIOSApplicationState::~QIOSApplicationState() +void QIOSApplicationState::handleApplicationStateChanged(UIApplicationState uiState, const QString &reason) { - NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; - foreach (const NSObject* observer, m_observers) - [notificationCenter removeObserver:observer]; + Qt::ApplicationState state = toQtApplicationState(uiState); + qCDebug(lcQpaApplication) << qPrintable(reason) + << "- moving from" << QGuiApplication::applicationState() << "to" << state; + + if (QIOSIntegration *integration = QIOSIntegration::instance()) { + emit integration->applicationState.applicationStateWillChange(state); + QWindowSystemInterface::handleApplicationStateChanged(state); + emit integration->applicationState.applicationStateDidChange(state); + qCDebug(lcQpaApplication) << "done moving to" << state; + } else { + qCDebug(lcQpaApplication) << "no platform integration yet, setting state directly"; + QGuiApplicationPrivate::applicationState = state; + } } -QT_END_NAMESPACE +Qt::ApplicationState QIOSApplicationState::toQtApplicationState(UIApplicationState state) +{ + switch (state) { + case UIApplicationStateActive: return Qt::ApplicationActive; + case UIApplicationStateInactive: return Qt::ApplicationInactive; + case UIApplicationStateBackground: return Qt::ApplicationSuspended; + } +} +#include "moc_qiosapplicationstate.cpp" + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 54c1a1dcb7..1d53734096 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -109,6 +109,8 @@ public: QFactoryLoader *optionalPlugins() { return m_optionalPlugins; } + QIOSApplicationState applicationState; + private: QPlatformFontDatabase *m_fontDatabase; #ifndef Q_OS_TVOS @@ -116,7 +118,6 @@ private: #endif QPlatformInputContext *m_inputContext; QTouchDevice *m_touchDevice; - QIOSApplicationState m_applicationState; QIOSServices *m_platformServices; mutable QPlatformAccessibility *m_accessibility; QFactoryLoader *m_optionalPlugins; |