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>bb10
parent
77942a1bdf
commit
e37c9d5d5f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
static Qt::ApplicationState qtApplicationState(UIApplicationState uiApplicationState)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleApplicationStateChanged(UIApplicationState uiApplicationState)
|
||||
{
|
||||
Qt::ApplicationState state = qtApplicationState(uiApplicationState);
|
||||
qCDebug(lcQpaApplication) << "moved to" << state;
|
||||
QWindowSystemInterface::handleApplicationStateChanged(state);
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QIOSApplicationState::QIOSApplicationState()
|
||||
static void qRegisterApplicationStateNotifications()
|
||||
{
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
|
||||
|
||||
m_observers.push_back([notificationCenter addObserverForName:UIApplicationDidBecomeActiveNotification
|
||||
object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *) {
|
||||
handleApplicationStateChanged(UIApplicationStateActive);
|
||||
}
|
||||
]);
|
||||
// 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 },
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
]);
|
||||
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);
|
||||
}];
|
||||
}
|
||||
|
||||
// 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 loaded"));
|
||||
}
|
||||
Q_CONSTRUCTOR_FUNCTION(qRegisterApplicationStateNotifications)
|
||||
|
||||
QIOSApplicationState::QIOSApplicationState()
|
||||
{
|
||||
UIApplicationState startupState = [UIApplication sharedApplication].applicationState;
|
||||
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::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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue