1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qiosapplicationstate.h"
41
42#include "qiosglobal.h"
43#include "qiosintegration.h"
44
45#include <qpa/qwindowsysteminterface.h>
46#include <QtCore/qcoreapplication.h>
47#include <QtCore/private/qcore_mac_p.h>
48
49#include <QtGui/private/qguiapplication_p.h>
50
51QT_BEGIN_NAMESPACE
52
53static void qRegisterApplicationStateNotifications()
54{
55    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
56    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
57
58    // Map between notifications and corresponding application state. Note that
59    // there's no separate notification for moving to UIApplicationStateInactive,
60    // so we use UIApplicationWillResignActiveNotification as an intermediate.
61    using NotificationMap = QMap<NSNotificationName, UIApplicationState>;
62    static auto notifications = qt_apple_isApplicationExtension() ? NotificationMap{
63        { NSExtensionHostWillEnterForegroundNotification, UIApplicationStateInactive },
64        { NSExtensionHostDidBecomeActiveNotification, UIApplicationStateActive },
65        { NSExtensionHostWillResignActiveNotification, UIApplicationStateInactive },
66        { NSExtensionHostDidEnterBackgroundNotification, UIApplicationStateBackground },
67    } : NotificationMap{
68        { UIApplicationWillEnterForegroundNotification, UIApplicationStateInactive },
69        { UIApplicationDidBecomeActiveNotification, UIApplicationStateActive },
70        { UIApplicationWillResignActiveNotification, UIApplicationStateInactive },
71        { UIApplicationDidEnterBackgroundNotification, UIApplicationStateBackground },
72    };
73
74    for (auto i = notifications.constBegin(); i != notifications.constEnd(); ++i) {
75        [notificationCenter addObserverForName:i.key() object:nil queue:mainQueue
76            usingBlock:^void(NSNotification *notification) {
77                NSRange nameRange = NSMakeRange(2, notification.name.length - 14);
78                QString reason = QString::fromNSString([notification.name substringWithRange:nameRange]);
79                QIOSApplicationState::handleApplicationStateChanged(i.value(), reason);
80        }];
81    }
82
83    if (qt_apple_isApplicationExtension()) {
84        // Extensions are not allowed to access UIApplication, so we assume the state is active
85        QIOSApplicationState::handleApplicationStateChanged(UIApplicationStateActive,
86            QLatin1String("Extension loaded, assuming state is active"));
87    } else {
88        // Initialize correct startup state, which may not be the Qt default (inactive)
89        UIApplicationState startupState = qt_apple_sharedApplication().applicationState;
90        QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application loaded"));
91    }
92}
93Q_CONSTRUCTOR_FUNCTION(qRegisterApplicationStateNotifications)
94
95QIOSApplicationState::QIOSApplicationState()
96{
97    if (!qt_apple_isApplicationExtension()) {
98        UIApplicationState startupState = qt_apple_sharedApplication().applicationState;
99        QIOSApplicationState::handleApplicationStateChanged(startupState, QLatin1String("Application launched"));
100    }
101}
102
103void QIOSApplicationState::handleApplicationStateChanged(UIApplicationState uiState, const QString &reason)
104{
105    Qt::ApplicationState oldState = QGuiApplication::applicationState();
106    Qt::ApplicationState newState = toQtApplicationState(uiState);
107    qCDebug(lcQpaApplication) << qPrintable(reason) << "- moving from" << oldState << "to" << newState;
108
109    if (QIOSIntegration *integration = QIOSIntegration::instance()) {
110        emit integration->applicationState.applicationStateWillChange(oldState, newState);
111        QWindowSystemInterface::handleApplicationStateChanged(newState);
112        emit integration->applicationState.applicationStateDidChange(oldState, newState);
113        qCDebug(lcQpaApplication) << "done moving to" << newState;
114    } else {
115        qCDebug(lcQpaApplication) << "no platform integration yet, setting state directly";
116        QGuiApplicationPrivate::applicationState = newState;
117    }
118}
119
120Qt::ApplicationState QIOSApplicationState::toQtApplicationState(UIApplicationState state)
121{
122    switch (state) {
123    case UIApplicationStateActive: return Qt::ApplicationActive;
124    case UIApplicationStateInactive: return Qt::ApplicationInactive;
125    case UIApplicationStateBackground: return Qt::ApplicationSuspended;
126    }
127}
128
129#include "moc_qiosapplicationstate.cpp"
130
131QT_END_NAMESPACE
132