=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-06-17 12:14:27 +0000
+++ CMakeLists.txt 2015-08-03 14:21:12 +0000
@@ -56,7 +56,7 @@
find_package(Qt5Concurrent 5.2 REQUIRED)
find_package(Qt5Sql 5.2 REQUIRED)
-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
# Standard install paths
include(GNUInstallDirs)
=== modified file 'debian/control'
--- debian/control 2015-07-29 12:38:53 +0000
+++ debian/control 2015-08-03 14:21:12 +0000
@@ -28,7 +28,7 @@
libqt5xmlpatterns5-dev,
libsystemsettings-dev,
libudev-dev,
- libunity-api-dev (>= 7.98),
+ libunity-api-dev (>= 7.99),
libusermetricsoutput1-dev,
libxcb1-dev,
pkg-config,
=== modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt'
--- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
+++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
@@ -1,5 +1,4 @@
pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
add_definitions(-DSM_BUSNAME=systemBus)
=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
--- plugins/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
+++ plugins/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
@@ -1,5 +1,4 @@
pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
add_definitions(-DSM_BUSNAME=systemBus)
=== added file 'qml/Components/Unity8Settings.qml'
--- qml/Components/Unity8Settings.qml 1970-01-01 00:00:00 +0000
+++ qml/Components/Unity8Settings.qml 2015-08-03 14:21:12 +0000
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 Canonical, Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import QtQuick 2.0
+import GSettings 1.0
+
+QtObject {
+ id: root
+
+ property string usageMode: "Staged"
+
+ // FIXME: Works around a bug where if we change a Loader's source in response to a GSettings
+ // property change in the same event loop, the Loader's previously loaded component does not
+ // get destroyed and its bindings continue to operate!
+ //
+ // Shouldn't be needed after
+ // https://code.launchpad.net/~lukas-kde/gsettings-qt/queued-processing/+merge/259883 gets
+ // merged.
+ property var timer: Timer {
+ interval: 1
+ onTriggered: { root.usageMode = root.wrapped.usageMode; }
+ }
+ property var wrappedConnections: Connections {
+ target: root.wrapped
+ ignoreUnknownSignals: true // don't spam us
+ onUsageModeChanged: { root.timer.start(); }
+ }
+ property var wrapped: GSettings {
+ schema.id: "com.canonical.Unity8"
+ Component.onCompleted: {
+ // init the value. it's a dynamic prop, so we have to check first
+ if (root.usageMode) {
+ root.usageMode = root.wrapped.usageMode;
+ }
+ }
+ }
+}
=== modified file 'qml/OrientedShell.qml'
--- qml/OrientedShell.qml 2015-07-01 17:52:34 +0000
+++ qml/OrientedShell.qml 2015-08-03 14:21:12 +0000
@@ -43,7 +43,7 @@
// to be overwritten by tests
- property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
+ property var unity8Settings: Unity8Settings {}
property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
property int physicalOrientation: Screen.orientation
@@ -160,9 +160,9 @@
// TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
// what's the output device (eg: big TV, desktop monitor, phone display), etc.
usageScenario: {
- if (root.usageModeSettings.usageMode === "Windowed") {
+ if (root.unity8Settings.usageMode === "Windowed") {
return "desktop";
- } else if (root.usageModeSettings.usageMode === "Staged") {
+ } else if (root.unity8Settings.usageMode === "Staged") {
if (deviceConfiguration.category === "phone") {
return "phone";
} else {
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2015-07-29 12:38:53 +0000
+++ qml/Shell.qml 2015-08-03 14:21:12 +0000
@@ -191,12 +191,6 @@
z: dialogs.z + 10
}
- Binding {
- target: ApplicationManager
- property: "forceDashActive"
- value: launcher.shown || launcher.dashSwipe
- }
-
WindowKeysFilter {
Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
@@ -354,6 +348,16 @@
}
Binding {
target: applicationsDisplayLoader.item
+ property: "keepDashRunning"
+ value: launcher.shown || launcher.dashSwipe
+ }
+ Binding {
+ target: applicationsDisplayLoader.item
+ property: "suspended"
+ value: greeter.shown
+ }
+ Binding {
+ target: applicationsDisplayLoader.item
property: "altTabPressed"
value: physicalKeysMapper.altTabPressed
}
@@ -459,12 +463,6 @@
}
onEmergencyCall: startLockedApp("dialer-app")
-
- Binding {
- target: ApplicationManager
- property: "suspended"
- value: greeter.shown
- }
}
}
=== modified file 'qml/Stages/DesktopStage.qml'
--- qml/Stages/DesktopStage.qml 2015-07-16 15:26:26 +0000
+++ qml/Stages/DesktopStage.qml 2015-08-03 14:21:12 +0000
@@ -39,6 +39,8 @@
property int shellPrimaryOrientation
property int nativeOrientation
property bool beingResized: false
+ property bool keepDashRunning: true
+ property bool suspended: false
// functions to be called from outside
function updateFocusedAppOrientation() { /* TODO */ }
@@ -215,6 +217,17 @@
}
}
+ Binding {
+ target: ApplicationManager.get(index)
+ property: "requestedState"
+ // TODO: figure out some lifecycle policy, like suspending minimized apps
+ // if running on a tablet or something.
+ // TODO: If the device has a dozen suspended apps because it was running
+ // in staged mode, when it switches to Windowed mode it will suddenly
+ // resume all those apps at once. We might want to avoid that.
+ value: ApplicationInfoInterface.RequestedRunning // Always running for now
+ }
+
function maximize() {
minimized = false;
maximized = true;
=== modified file 'qml/Stages/PhoneStage.qml'
--- qml/Stages/PhoneStage.qml 2015-07-14 08:09:58 +0000
+++ qml/Stages/PhoneStage.qml 2015-08-03 14:21:12 +0000
@@ -36,6 +36,8 @@
property bool altTabEnabled: true
property real startScale: 1.1
property real endScale: 0.7
+ property bool keepDashRunning: true
+ property bool suspended: false
property int shellOrientationAngle: 0
property int shellOrientation
property int shellPrimaryOrientation
@@ -428,6 +430,14 @@
dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
focusFirstApp: root.focusFirstApp
+ Binding {
+ target: appDelegate.application
+ property: "requestedState"
+ value: (isDash && root.keepDashRunning) || (!root.suspended && appDelegate.focus)
+ ? ApplicationInfoInterface.RequestedRunning
+ : ApplicationInfoInterface.RequestedSuspended
+ }
+
readonly property bool isDash: model.appId == "unity8-dash"
z: isDash && !spreadView.active ? -1 : behavioredIndex
=== modified file 'qml/Stages/TabletStage.qml'
--- qml/Stages/TabletStage.qml 2015-07-06 09:31:43 +0000
+++ qml/Stages/TabletStage.qml 2015-08-03 14:21:12 +0000
@@ -36,6 +36,8 @@
property bool spreadEnabled: true // If false, animations and right edge will be disabled
property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
+ property bool keepDashRunning: true
+ property bool suspended: false
property int shellOrientationAngle: 0
property int shellOrientation
property int shellPrimaryOrientation
@@ -117,6 +119,7 @@
QtObject {
id: priv
+ objectName: "stagesPriv"
property string focusedAppId: ApplicationManager.focusedApplicationId
readonly property var focusedAppDelegate: {
@@ -626,6 +629,17 @@
readonly property bool isDash: model.appId == "unity8-dash"
+ Binding {
+ target: spreadTile.application
+ property: "requestedState"
+ value: (spreadTile.isDash && root.keepDashRunning)
+ ||
+ (!root.suspended && (model.appId == priv.mainStageAppId
+ || model.appId == priv.sideStageAppId))
+ ? ApplicationInfoInterface.RequestedRunning
+ : ApplicationInfoInterface.RequestedSuspended
+ }
+
// FIXME: A regular binding doesn't update any more after closing an app.
// Using a Binding for now.
Binding {
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-03-06 04:44:11 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-08-03 14:21:12 +0000
@@ -30,7 +30,7 @@
: ApplicationInfoInterface(appId, parent)
, m_appId(appId)
, m_stage(MainStage)
- , m_state(Starting)
+ , m_state(Stopped)
, m_focused(false)
, m_fullscreen(false)
, m_session(0)
@@ -39,6 +39,7 @@
Qt::InvertedPortraitOrientation |
Qt::InvertedLandscapeOrientation)
, m_rotatesWindowContents(false)
+ , m_requestedState(RequestedRunning)
, m_manualSurfaceCreation(false)
{
}
@@ -46,7 +47,7 @@
ApplicationInfo::ApplicationInfo(QObject *parent)
: ApplicationInfoInterface(QString(), parent)
, m_stage(MainStage)
- , m_state(Starting)
+ , m_state(Stopped)
, m_focused(false)
, m_fullscreen(false)
, m_session(0)
@@ -55,6 +56,7 @@
Qt::InvertedPortraitOrientation |
Qt::InvertedLandscapeOrientation)
, m_rotatesWindowContents(false)
+ , m_requestedState(RequestedRunning)
, m_manualSurfaceCreation(false)
{
}
@@ -74,7 +76,6 @@
void ApplicationInfo::setSession(Session* session)
{
- qDebug() << "Application::setSession - appId=" << appId() << "session=" << session;
if (m_session == session)
return;
@@ -91,6 +92,8 @@
m_session->setApplication(this);
m_session->setParent(this);
SessionManager::singleton()->registerSession(m_session);
+ connect(m_session, &Session::surfaceChanged,
+ this, &ApplicationInfo::onSessionSurfaceChanged);
if (!m_manualSurfaceCreation) {
QTimer::singleShot(500, m_session, SLOT(createSurface()));
@@ -161,11 +164,12 @@
m_state = value;
Q_EMIT stateChanged(value);
- if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
+ if (!m_manualSurfaceCreation && !m_session && m_state == ApplicationInfo::Starting) {
QTimer::singleShot(500, this, SLOT(createSession()));
} else if (m_state == ApplicationInfo::Stopped) {
- delete m_session;
- m_session = nullptr;
+ Session *session = m_session;
+ setSession(nullptr);
+ delete session;
}
}
}
@@ -213,3 +217,33 @@
{
m_rotatesWindowContents = value;
}
+
+ApplicationInfo::RequestedState ApplicationInfo::requestedState() const
+{
+ return m_requestedState;
+}
+
+void ApplicationInfo::setRequestedState(RequestedState value)
+{
+ if (m_requestedState != value) {
+ m_requestedState = value;
+ Q_EMIT requestedStateChanged(m_requestedState);
+
+ if (m_requestedState == RequestedRunning && m_state == Suspended) {
+ setState(Running);
+ } else if (m_requestedState == RequestedSuspended && m_state == Running) {
+ setState(Suspended);
+ }
+ }
+}
+
+void ApplicationInfo::onSessionSurfaceChanged(MirSurfaceItem* surface)
+{
+ if (surface != nullptr && m_state == Starting) {
+ if (m_requestedState == RequestedRunning) {
+ setState(Running);
+ } else {
+ setState(Suspended);
+ }
+ }
+}
=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
--- tests/mocks/Unity/Application/ApplicationInfo.h 2015-03-06 04:44:11 +0000
+++ tests/mocks/Unity/Application/ApplicationInfo.h 2015-08-03 14:21:12 +0000
@@ -46,6 +46,9 @@
ApplicationInfo(const QString &appId, QObject *parent = nullptr);
~ApplicationInfo();
+ RequestedState requestedState() const override;
+ void setRequestedState(RequestedState) override;
+
void setIconId(const QString &iconId);
void setScreenshotId(const QString &screenshotId);
@@ -101,6 +104,9 @@
public Q_SLOTS:
Q_INVOKABLE void createSession();
+private Q_SLOTS:
+ void onSessionSurfaceChanged(MirSurfaceItem*);
+
private:
void setIcon(const QUrl &value);
@@ -116,6 +122,7 @@
Session* m_session;
Qt::ScreenOrientations m_supportedOrientations;
bool m_rotatesWindowContents;
+ RequestedState m_requestedState;
bool m_manualSurfaceCreation;
};
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
--- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-06-24 11:41:09 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2015-08-03 14:21:12 +0000
@@ -49,8 +49,6 @@
ApplicationManager::ApplicationManager(QObject *parent)
: ApplicationManagerInterface(parent)
- , m_suspended(false)
- , m_forceDashActive(false)
{
m_roleNames.insert(RoleSession, "session");
m_roleNames.insert(RoleFullscreen, "fullscreen");
@@ -217,7 +215,7 @@
&& application->stage() == ApplicationInfo::SideStage) {
application->setStage(ApplicationInfo::MainStage);
}
- application->setState(ApplicationInfo::Running);
+ application->setState(ApplicationInfo::Starting);
return application;
}
@@ -262,50 +260,6 @@
return QString();
}
-bool ApplicationManager::suspended() const
-{
- return m_suspended;
-}
-
-void ApplicationManager::setSuspended(bool suspended)
-{
- ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
- if (focusedApp) {
- if (suspended) {
- focusedApp->setState(ApplicationInfo::Suspended);
- } else {
- focusedApp->setState(ApplicationInfo::Running);
- }
- }
- m_suspended = suspended;
- Q_EMIT suspendedChanged();
-}
-
-bool ApplicationManager::forceDashActive() const
-{
- return m_forceDashActive;
-}
-
-void ApplicationManager::setForceDashActive(bool forceDashActive)
-{
- if (m_forceDashActive == forceDashActive) {
- return;
- }
-
- ApplicationInfo *dash = findApplication("unity8-dash");
- if (dash) {
- if (forceDashActive) {
- dash->setState(ApplicationInfo::Running);
- } else {
- if (!dash->focused()) {
- dash->setState(ApplicationInfo::Suspended);
- }
- }
- }
- m_forceDashActive = forceDashActive;
- Q_EMIT forceDashActiveChanged();
-}
-
bool ApplicationManager::focusApplication(const QString &appId)
{
ApplicationInfo *application = findApplication(appId);
@@ -316,13 +270,11 @@
for (ApplicationInfo *app : m_runningApplications) {
if (app->focused()) {
app->setFocused(false);
- app->setState(ApplicationInfo::Suspended);
}
}
// focus this app
application->setFocused(true);
- application->setState(ApplicationInfo::Running);
// move app to top of stack
move(m_runningApplications.indexOf(application), 0);
@@ -394,6 +346,7 @@
application->setScreenshotId("gallery");
application->setIconId("gallery");
application->setFullscreen(true);
+ application->setStage(ApplicationInfo::MainStage);
m_availableApplications.append(application);
application = new ApplicationInfo(this);
=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
--- tests/mocks/Unity/Application/ApplicationManager.h 2015-04-01 11:25:54 +0000
+++ tests/mocks/Unity/Application/ApplicationManager.h 2015-08-03 14:21:12 +0000
@@ -69,11 +69,6 @@
Q_INVOKABLE bool stopApplication(const QString &appId) override;
QString focusedApplicationId() const override;
- bool suspended() const override;
- void setSuspended(bool suspended) override;
-
- bool forceDashActive() const override;
- void setForceDashActive(bool forceDashActive) override;
// Only for testing
QStringList availableApplications();
@@ -96,8 +91,6 @@
void remove(ApplicationInfo* application);
void buildListOfAvailableApplications();
void onWindowCreated();
- bool m_suspended;
- bool m_forceDashActive;
QList m_runningApplications;
QList m_availableApplications;
QTimer m_windowCreatedTimer;
=== modified file 'tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt'
--- tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
+++ tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
@@ -1,5 +1,4 @@
pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-07-23 14:13:57 +0000
+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-08-03 14:21:12 +0000
@@ -39,6 +39,9 @@
Q_OBJECT
public:
MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
+
+ RequestedState requestedState() const override { return RequestedRunning; }
+ void setRequestedState(RequestedState) override {}
QString appId() const override { return m_appId; }
QString name() const override { return "mock"; }
QString comment() const override { return "this is a mock"; }
@@ -117,10 +120,6 @@
endRemoveRows();
}
bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
- bool suspended() const override { return false; }
- void setSuspended(bool) override {}
- bool forceDashActive() const override { return false; }
- void setForceDashActive(bool) override {}
private:
QList m_list;
=== modified file 'tests/qmltests/Stages/ApplicationCheckBox.qml'
--- tests/qmltests/Stages/ApplicationCheckBox.qml 2015-06-12 16:07:43 +0000
+++ tests/qmltests/Stages/ApplicationCheckBox.qml 2015-08-03 14:21:12 +0000
@@ -86,8 +86,29 @@
}
}
Label {
+ id: appIdLabel
text: root.appId
color: "white"
anchors.verticalCenter: parent.verticalCenter
}
+ Rectangle {
+ color: {
+ if (d.application) {
+ if (d.application.state === ApplicationInfoInterface.Starting) {
+ return "yellow";
+ } else if (d.application.state === ApplicationInfoInterface.Running) {
+ return "green";
+ } else if (d.application.state === ApplicationInfoInterface.Suspended) {
+ return "blue";
+ } else {
+ return "darkred";
+ }
+ } else {
+ return "darkred";
+ }
+ }
+ width: height
+ height: appIdLabel.height * 0.7
+ anchors.verticalCenter: parent.verticalCenter
+ }
}
=== modified file 'tests/qmltests/Stages/tst_DesktopStage.qml'
--- tests/qmltests/Stages/tst_DesktopStage.qml 2015-05-01 13:21:30 +0000
+++ tests/qmltests/Stages/tst_DesktopStage.qml 2015-08-03 14:21:12 +0000
@@ -24,9 +24,8 @@
import "../../../qml/Stages"
-Rectangle {
+Item {
id: root
- color: "darkblue"
width: desktopStageLoader.width + controls.width
height: desktopStageLoader.height
@@ -57,6 +56,7 @@
property bool itemDestroyed: false
sourceComponent: Component {
DesktopStage {
+ color: "darkblue"
anchors.fill: parent
Component.onDestruction: {
desktopStageLoader.itemDestroyed = true;
=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
--- tests/qmltests/Stages/tst_PhoneStage.qml 2015-06-23 14:20:23 +0000
+++ tests/qmltests/Stages/tst_PhoneStage.qml 2015-08-03 14:21:12 +0000
@@ -301,16 +301,16 @@
addApps(2);
var secondApp = ApplicationManager.get(0);
- tryCompare(secondApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(secondApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
tryCompare(secondApp, "focused", true);
var firstApp = ApplicationManager.get(1);
- tryCompare(firstApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
tryCompare(firstApp, "focused", false);
ApplicationManager.stopApplication(secondApp.appId);
- tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
tryCompare(firstApp, "focused", true);
}
@@ -388,5 +388,77 @@
tryCompare(focusedDelegate, "x", 0);
}
+
+ function test_focusedAppIsTheOnlyRunningApp() {
+ addApps(2);
+
+ var delegateA = findChild(phoneStage, "appDelegate0");
+ verify(delegateA);
+ var delegateB = findChild(phoneStage, "appDelegate1");
+ verify(delegateB);
+
+ // A is focused and running, B is unfocused and suspended
+ compare(delegateA.focus, true);
+ compare(delegateA.application.requestedState, ApplicationInfoInterface.RequestedRunning);
+ compare(delegateB.focus, false);
+ compare(delegateB.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
+
+ // Switch foreground/focused appp from A to B
+ goToSpread();
+ phoneStage.select(delegateB.application.appId);
+
+ // Now it's the other way round
+ // A is unfocused and suspended, B is focused and running
+ tryCompare(delegateA, "focus", false);
+ tryCompare(delegateA.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+ tryCompare(delegateB, "focus", true);
+ tryCompare(delegateB.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ }
+
+ function test_dashRemainsRunningIfStageIsToldSo() {
+ addApps(1);
+
+ var delegateDash = findChild(phoneStage, "appDelegate1");
+ verify(delegateDash);
+ compare(delegateDash.application.appId, "unity8-dash");
+
+ var delegateOther = findChild(phoneStage, "appDelegate0");
+ verify(delegateOther);
+
+ goToSpread();
+ phoneStage.select("unity8-dash");
+
+ tryCompare(delegateDash, "focus", true);
+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ compare(delegateOther.focus, false);
+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
+
+ goToSpread();
+ phoneStage.select(delegateOther.application.appId);
+
+ // The other app gets focused and running but dash is kept running despite being unfocused
+ tryCompare(delegateDash, "focus", false);
+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ compare(delegateOther.focus, true);
+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
+ }
+
+ function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
+ addApps(1);
+
+ var delegate = findChild(phoneStage, "appDelegate0");
+ verify(delegate);
+
+ compare(delegate.focus, true);
+ compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
+
+ phoneStage.suspended = true;
+
+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+
+ phoneStage.suspended = false;
+
+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ }
}
}
=== modified file 'tests/qmltests/Stages/tst_TabletStage.qml'
--- tests/qmltests/Stages/tst_TabletStage.qml 2015-06-12 16:07:43 +0000
+++ tests/qmltests/Stages/tst_TabletStage.qml 2015-08-03 14:21:12 +0000
@@ -77,9 +77,17 @@
appId: "webbrowser-app"
}
ApplicationCheckBox {
+ id: galleryCheckBox
+ appId: "gallery-app"
+ }
+ ApplicationCheckBox {
id: dialerCheckBox
appId: "dialer-app"
}
+ ApplicationCheckBox {
+ id: facebookCheckBox
+ appId: "facebook-webapp"
+ }
}
}
@@ -93,6 +101,19 @@
function init() {
tabletStageLoader.active = true;
tryCompare(tabletStageLoader, "status", Loader.Ready);
+
+ // this is very strange, but sometimes the test starts without
+ // TabletStage components having finished loading themselves
+ var appWindow = null;
+ while (!appWindow) {
+ appWindow = findChild(tabletStage, "appWindow_unity8-dash");
+ if (!appWindow) {
+ console.log("didn't find appWindow_unity8-dash in " + tabletStage + ". Trying again...");
+ wait(50);
+ }
+ }
+
+ waitUntilAppSurfaceShowsUp("unity8-dash");
}
function cleanup() {
@@ -109,7 +130,9 @@
// kill all (fake) running apps
webbrowserCheckBox.checked = false;
+ galleryCheckBox.checked = false;
dialerCheckBox.checked = false;
+ facebookCheckBox.checked = false;
}
function waitUntilAppSurfaceShowsUp(appId) {
@@ -120,6 +143,34 @@
tryCompare(appWindowStates, "state", "surface");
}
+ function switchToApp(targetAppId) {
+ touchFlick(tabletStage,
+ tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
+ tabletStage.x + 1, tabletStage.height / 2);
+
+ var spreadView = findChild(tabletStage, "spreadView");
+ verify(spreadView);
+ tryCompare(spreadView, "phase", 2);
+ tryCompare(spreadView, "flicking", false);
+ tryCompare(spreadView, "moving", false);
+
+ waitUntilAppDelegateStopsMoving(targetAppId);
+
+ var targetAppWindow = findChild(tabletStage, "appWindow_" + targetAppId);
+ tap(targetAppWindow, 10, 10);
+ }
+
+ function waitUntilAppDelegateStopsMoving(targetAppId)
+ {
+ var targetAppDelegate = findChild(tabletStage, "tabletSpreadDelegate_" + targetAppId);
+ verify(targetAppDelegate);
+ var lastValue = undefined;
+ do {
+ lastValue = targetAppDelegate.animatedProgress;
+ wait(30);
+ } while (lastValue != targetAppDelegate.animatedProgress);
+ }
+
function test_tappingSwitchesFocusBetweenStages() {
webbrowserCheckBox.checked = true;
waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
@@ -155,7 +206,6 @@
tryCompare(webbrowserApp.session.surface, "activeFocus", false);
}
-
function test_closeAppInSideStage() {
dialerCheckBox.checked = true;
waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
@@ -200,6 +250,116 @@
appWindow.width / 2, appWindow.height / 2,
appWindow.width / 2, -appWindow.height / 2);
}
+
+ function test_suspendsAndResumesAppsInMainStage() {
+ webbrowserCheckBox.checked = true;
+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
+
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
+
+ galleryCheckBox.checked = true;
+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
+
+ switchToApp(webbrowserApp.appId);
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
+
+ switchToApp(galleryApp.appId);
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
+ }
+
+
+ function test_foregroundMainAndSideStageAppsAreKeptRunning() {
+
+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
+ verify(stagesPriv);
+
+ // launch two main stage apps
+ // gallery will be on foreground and webbrowser on background
+
+ webbrowserCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
+
+ galleryCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(galleryCheckBox.appId)
+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
+
+ compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
+
+ // then launch two side stage apps
+ // facebook will be on foreground and dialer on background
+
+ dialerCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
+
+ facebookCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(facebookCheckBox.appId)
+ var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
+ compare(facebookApp.stage, ApplicationInfoInterface.SideStage);
+
+ compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
+
+ // Now check that the foreground apps are running and that the background ones
+ // are suspended
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Suspended);
+
+ switchToApp(dialerCheckBox.appId);
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
+
+ switchToApp(webbrowserCheckBox.appId);
+
+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
+ }
+
+ function test_foregroundAppsAreSuspendedWhenStageIsSuspended() {
+ webbrowserCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
+
+ dialerCheckBox.checked = true;
+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
+
+
+ compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
+ compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
+
+ tabletStage.suspended = true;
+
+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+
+ tabletStage.suspended = false;
+
+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ }
}
}
=== modified file 'tests/qmltests/tst_OrientedShell.qml'
--- tests/qmltests/tst_OrientedShell.qml 2015-07-10 14:44:55 +0000
+++ tests/qmltests/tst_OrientedShell.qml 2015-08-03 14:21:12 +0000
@@ -46,7 +46,7 @@
}
QtObject {
- id: mockUsageModeSettings
+ id: mockUnity8Settings
property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
}
@@ -125,7 +125,7 @@
sourceComponent: Component {
OrientedShell {
anchors.fill: parent
- usageModeSettings: mockUsageModeSettings
+ unity8Settings: mockUnity8Settings
oskSettings: mockOskSettings
physicalOrientation: root.physicalOrientation0
orientationLocked: orientationLockedCheckBox.checked
@@ -926,7 +926,7 @@
function test_attachRemoveInputDevices() {
usageModeSelector.selectedIndex = 2;
- tryCompare(mockUsageModeSettings, "usageMode", "Automatic")
+ tryCompare(mockUnity8Settings, "usageMode", "Automatic")
loadShell("mako")
var shell = findChild(orientedShell, "shell");
=== modified file 'tests/qmltests/tst_Shell.qml'
--- tests/qmltests/tst_Shell.qml 2015-07-29 12:38:53 +0000
+++ tests/qmltests/tst_Shell.qml 2015-08-03 14:21:12 +0000
@@ -67,11 +67,6 @@
width: units.gu(40)
height: units.gu(71)
}
- StateChangeScript {
- script: {
- GSettingsController.setUsageMode("Staged")
- }
- }
},
State {
name: "tablet"
@@ -80,11 +75,6 @@
width: units.gu(100)
height: units.gu(71)
}
- StateChangeScript {
- script: {
- GSettingsController.setUsageMode("Staged")
- }
- }
},
State {
name: "desktop"
@@ -97,11 +87,6 @@
target: mouseEmulation
checked: false
}
- StateChangeScript {
- script: {
- GSettingsController.setUsageMode("Windowed")
- }
- }
}
]
@@ -317,7 +302,6 @@
mouseEmulation.checked = true;
tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
tearDown();
- GSettingsController.setUsageMode("Staged");
}
function loadShell(formFactor) {
@@ -602,9 +586,9 @@
verify(mainAppId != "");
var mainApp = ApplicationManager.findApplication(mainAppId);
verify(mainApp);
- tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
+ tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
- // Suspend while call is active...
+ // Display off while call is active...
callManager.foregroundCall = phoneCall;
Powerd.status = Powerd.Off;
tryCompare(greeter, "shown", false);
@@ -615,8 +599,7 @@
Powerd.status = Powerd.Off;
tryCompare(greeter, "fullyShown", true);
- tryCompare(ApplicationManager, "suspended", true);
- compare(mainApp.state, ApplicationInfoInterface.Suspended);
+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
// And wake up
Powerd.status = Powerd.On;
@@ -632,8 +615,7 @@
removeTimeConstraintsFromDirectionalDragAreas(greeter);
swipeAwayGreeter();
- tryCompare(ApplicationManager, "suspended", false);
- compare(mainApp.state, ApplicationInfoInterface.Running);
+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
}
@@ -804,15 +786,15 @@
function test_launchedAppHasActiveFocus_data() {
return [
- {tag: "phone", formFactor: "phone", usageMode: "Staged"},
- {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
- {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
]
}
function test_launchedAppHasActiveFocus(data) {
- GSettingsController.setUsageMode(data.usageMode);
loadShell(data.formFactor);
+ shell.usageScenario = data.usageScenario;
swipeAwayGreeter();
var webApp = ApplicationManager.startApplication("webbrowser-app");
@@ -1347,6 +1329,50 @@
compare(launcher.inverted, data.launcherInverted);
}
+ function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
+ loadShell("tablet");
+ shell.usageScenario = "desktop";
+
+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
+
+ var galleryApp = ApplicationManager.startApplication("gallery-app");
+ waitUntilAppWindowIsFullyLoaded(galleryApp);
+
+ ApplicationManager.requestFocusApplication("unity8-dash");
+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
+
+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
+
+ shell.usageScenario = "tablet";
+
+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
+ }
+
+ function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
+ loadShell("tablet");
+ shell.usageScenario = "tablet";
+
+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
+
+ var galleryApp = ApplicationManager.startApplication("gallery-app");
+ waitUntilAppWindowIsFullyLoaded(galleryApp);
+
+ ApplicationManager.requestFocusApplication("unity8-dash");
+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
+
+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
+
+ shell.usageScenario = "desktop";
+
+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
+ }
+
function test_altTabSwitchesFocus() {
loadShell("desktop");
shell.usageScenario = "desktop"