Merge lp:~dandrader/unity8/app-state-handling into lp:unity8

Proposed by Daniel d'Andrada on 2015-05-08
Status: Merged
Approved by: Albert Astals Cid on 2015-08-04
Approved revision: 1829
Merged at revision: 1901
Proposed branch: lp:~dandrader/unity8/app-state-handling
Merge into: lp:unity8
Prerequisite: lp:~dandrader/unity8/autoInstallTouchRegistry
Diff against target: 1115 lines (+468/-121)
22 files modified
CMakeLists.txt (+1/-1)
debian/control (+1/-1)
plugins/Greeter/Unity/Launcher/CMakeLists.txt (+0/-1)
plugins/Unity/Launcher/CMakeLists.txt (+0/-1)
qml/Components/Unity8Settings.qml (+50/-0)
qml/OrientedShell.qml (+3/-3)
qml/Shell.qml (+10/-12)
qml/Stages/DesktopStage.qml (+13/-0)
qml/Stages/PhoneStage.qml (+10/-0)
qml/Stages/TabletStage.qml (+14/-0)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+40/-6)
tests/mocks/Unity/Application/ApplicationInfo.h (+7/-0)
tests/mocks/Unity/Application/ApplicationManager.cpp (+2/-49)
tests/mocks/Unity/Application/ApplicationManager.h (+0/-7)
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt (+0/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+3/-4)
tests/qmltests/Stages/ApplicationCheckBox.qml (+21/-0)
tests/qmltests/Stages/tst_DesktopStage.qml (+2/-2)
tests/qmltests/Stages/tst_PhoneStage.qml (+75/-3)
tests/qmltests/Stages/tst_TabletStage.qml (+161/-1)
tests/qmltests/tst_OrientedShell.qml (+3/-3)
tests/qmltests/tst_Shell.qml (+52/-26)
To merge this branch: bzr merge lp:~dandrader/unity8/app-state-handling
Reviewer Review Type Date Requested Status
Albert Astals Cid (community) Abstain on 2015-08-04
PS Jenkins bot (community) continuous-integration Needs Fixing on 2015-08-03
Gerry Boland 2015-05-08 Approve on 2015-07-22
Review via email: mp+258653@code.launchpad.net

Commit message

Stages now control the "requestedState" property of applications

We now decide whether a application is suspended or not via the new Application.requestedState property.

Previously that was controlled by qtmir and tied to Application.focus. We don't want that anymore since it does not apply to a windowed usage mode scenario, for instance.

Description of the change

This branch contains lp:~dandrader/unity8/fixOrientedShellTests and lp:~dandrader/unity8/fixShellTests since I cannot set multiple prerequisites

* Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~dandrader/unity-api/app-state-handling/+merge/258643
https://code.launchpad.net/~dandrader/qtmir/detach-state-from-focus/+merge/258648

This also has https://code.launchpad.net/~nick-dedekind/unity8/desktop-app-focus/+merge/256287 as prerequisite (in addition to lp:~dandrader/unity8/autoInstallTouchRegistry) but launchpad doesn't allow multiple prerequisites. So launchpad's diff will show more than the changes from this branch.

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

* Did you make sure that your branch does not contain spurious tags?
Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable

* If you changed the UI, has there been a design review?
Not applicable

To post a comment you must log in.
Albert Astals Cid (aacid) wrote :

Text conflict in plugins/Ubuntu/Gestures/TouchGate.cpp
Text conflict in plugins/Ubuntu/Gestures/TouchGate.h
2 conflicts encountered.

Daniel d'Andrada (dandrader) wrote :

> Text conflict in plugins/Ubuntu/Gestures/TouchGate.cpp
> Text conflict in plugins/Ubuntu/Gestures/TouchGate.h
> 2 conflicts encountered.

Fixed.

Gerry Boland (gerboland) wrote :

Please update commit message to refer to "requestedState" instead of the old "active" application property.

Daniel d'Andrada (dandrader) wrote :

> Please update commit message to refer to "requestedState" instead of the old
> "active" application property.

Done.

Gerry Boland (gerboland) wrote :

Bug:
via launcher, rapidly launch camera, webbrowser and contacts. When contacts finally appears, open spread: camera app is showing live preview, it should be suspended. Browser is also running. If you return to dash, the wakelock is not released (as other apps considered "running"

review: Needs Fixing
Gerry Boland (gerboland) wrote :

Bug: open camera app. Then open gallery. Tap the camera icon in the Gallery header. It should cause focus to switch to the camera app, but actually nothing happens

Gerry Boland (gerboland) wrote :

Bug (probably same as above, but reporting for completeness), if Settings app running, another app open and focused, open indicators and select "Battery Settings" menu option - nothing happens. Indicators should close and Settings app brought to front.

Gerry Boland (gerboland) wrote :

Bug: open system settings. Switch to dash. Via ssh, kill the system-settings process. Then switch to the system settings app in unity8. It should cause the process to be respawned, but it appears to do nothing.

review: Needs Fixing
Gerry Boland (gerboland) wrote :

Bug: launch camera, then immediately hit the power key to turn off the display. Camera app is not suspended. Wakelock is held.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 12:45, Gerry Boland wrote:
> Review: Needs Fixing
>
> Bug:
> via launcher, rapidly launch camera, webbrowser and contacts. When contacts finally appears, open spread: camera app is showing live preview, it should be suspended. Browser is also running. If you return to dash, the wakelock is not released (as other apps considered "running"
unity8 logic is sound and working fine in that case. Problem lies in
qtmir. Just realized how messy the relationship between application
state, session state and surface creation is. Man, this is going to take
some refactoring...

Albert Astals Cid (aacid) wrote :

Text conflict in tests/plugins/Unity/Launcher/launchermodeltest.cpp
Text conflict in tests/qmltests/tst_Shell.qml
2 conflicts encountered.

Daniel d'Andrada (dandrader) wrote :

On 21/05/2015 09:04, Albert Astals Cid wrote:
> Text conflict in tests/plugins/Unity/Launcher/launchermodeltest.cpp
> Text conflict in tests/qmltests/tst_Shell.qml
> 2 conflicts encountered.
>

Fixed.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 12:45, Gerry Boland wrote:
> Review: Needs Fixing
>
> Bug:
> via launcher, rapidly launch camera, webbrowser and contacts. When contacts finally appears, open spread: camera app is showing live preview, it should be suspended. Browser is also running. If you return to dash, the wakelock is not released (as other apps considered "running"
Fixed.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 12:49, Gerry Boland wrote:
> Bug: open camera app. Then open gallery. Tap the camera icon in the Gallery header. It should cause focus to switch to the camera app, but actually nothing happens
Fixed in qtmir.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 13:11, Gerry Boland wrote:
> Bug (probably same as above, but reporting for completeness), if Settings app running, another app open and focused, open indicators and select "Battery Settings" menu option - nothing happens. Indicators should close and Settings app brought to front.
It is. Fixed in qtmir.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 13:16, Gerry Boland wrote:
> Review: Needs Fixing
>
> Bug: open system settings. Switch to dash. Via ssh, kill the system-settings process. Then switch to the system settings app in unity8. It should cause the process to be respawned, but it appears to do nothing.
Fixed.

Daniel d'Andrada (dandrader) wrote :

On 13/05/15 13:21, Gerry Boland wrote:
> Bug: launch camera, then immediately hit the power key to turn off the display. Camera app is not suspended. Wakelock is held.
Fixed.

Daniel d'Andrada (dandrader) wrote :

On 27/05/15 18:15, Daniel d'Andrada wrote:
> On 13/05/15 12:45, Gerry Boland wrote:
>> Review: Needs Fixing
>>
>> Bug:
>> via launcher, rapidly launch camera, webbrowser and contacts. When contacts finally appears, open spread: camera app is showing live preview, it should be suspended. Browser is also running. If you return to dash, the wakelock is not released (as other apps considered "running"
> Fixed.
>
Fixed in qtmir, to be more clear.

Gerry Boland (gerboland) wrote :

Pass 2 testing. Things much improved! Am still testing, but found this small issue:

Start dialer-app, let it load. Switch to Dash. Wait until dialer-app suspended. Kill it from command line. Then open spread and select Dialer app. There is a nasty flicker between the old snapshot disappearing & the resumed app's first frame.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

On 29/05/15 19:29, Gerry Boland wrote:
> Review: Needs Fixing
>
> Pass 2 testing. Things much improved! Am still testing, but found this small issue:
>
> Start dialer-app, let it load. Switch to Dash. Wait until dialer-app suspended. Kill it from command line. Then open spread and select Dialer app. There is a nasty flicker between the old snapshot disappearing & the resumed app's first frame.
>
>
Fixed in qtmir.

The flicker is the whole SpreadDelegate disappearing for a split second.
After spending 1.5 workdays on it I still don't know exactly how or why
it was happening. But at least I found that a simple and harmless change
in qtmir makes it go away (deferring respawning of the app to the next
event loop by using a queued connection instead of a direct one).

It was also pretty curious that a short right-edge swipe didn't exhibit
this issue.

Lukáš Tinkl (lukas-kde) wrote :

Added a clarification/comment on the current GSettingsQt situation

Daniel d'Andrada (dandrader) wrote :

> Added a clarification/comment on the current GSettingsQt situation

Updated the FIXME comment. Thanks.

Albert Astals Cid (aacid) wrote :

Text conflict in tests/qmltests/tst_Shell.qml
1 conflicts encountered.

Daniel d'Andrada (dandrader) wrote :

On 23/06/15 04:34, Albert Astals Cid wrote:
> Text conflict in tests/qmltests/tst_Shell.qml
> 1 conflicts encountered.

Fixed.

Gerry Boland (gerboland) wrote :

Looking good on desktop

review: Needs Fixing
Gerry Boland (gerboland) wrote :

Ok, tested on phone, tablet & desktop. Unable to find any regression, nice one.

review: Approve (functional)
Gerry Boland (gerboland) wrote :

I can't fault the code. Functional testing works well. AP tests pass here. Approve!

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Y
 * Did CI run pass? If not, please explain why.
Depends on qtmir/unity-api change
 * Did you make sure that the branch does not contain spurious tags?
Y

review: Approve
Albert Astals Cid (aacid) wrote :

Setting as needs fixing because of
  Text conflict in qml/OrientedShell.qml
  Text conflict in tests/qmltests/tst_OrientedShell.qml
  2 conflicts encountered.

Should top-approve again once re-merged

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

On 06/07/15 04:20, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> Setting as needs fixing because of
> Text conflict in qml/OrientedShell.qml
> Text conflict in tests/qmltests/tst_OrientedShell.qml
> 2 conflicts encountered.
>
> Should top-approve again once re-merged

Fixed.

Gerry Boland (gerboland) wrote :

Need to depend on unity-api 7.98

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

> Need to depend on unity-api 7.98

Done.

Gerry Boland (gerboland) :
review: Approve
Gerry Boland (gerboland) wrote :

Please merge trunk, some conflicts have appeared

review: Needs Fixing
Gerry Boland (gerboland) wrote :

My fault, merges ok

review: Approve
Albert Astals Cid (aacid) wrote :

Text conflict in plugins/Greeter/Unity/Launcher/CMakeLists.txt
Text conflict in plugins/Unity/Launcher/CMakeLists.txt
Text conflict in qml/Shell.qml
Text conflict in qml/Stages/DesktopStage.qml
Text conflict in tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt
Text conflict in tests/qmltests/tst_Shell.qml

Was top approved before

review: Needs Fixing
1827. By Daniel d'Andrada on 2015-08-03

Merge trunk

[ CI Train Bot ]
* Resync trunk.
* allow opening the manage dash area by clicking with a mouse on the
  arrow label (LP: #1431564)
* TouchRegistry: remove null candidates from list of candidates (LP:
  #1473492)
[ Lukáš Tinkl ]
* Fix power dialogs on desktop
* Provide DBUS compatibility with various session services
  (suspend/hibernate, lock/unlock, screensaver, etc)
* React on PrtScr keyboard shortcut for taking screenshots on desktop
  (LP: #1474149)
* launcher parity: close apps from quicklist (LP: #1457201)
[ Michael Zanetti ]
* Implement first edition for a desktop Alt+Tab spread
* drop the gcc-4.9 dependency (LP: #1452348)
[ Mirco Müller ]
* Added corresponding tests and visual tweaks to a launcher-item's
  progress-overlay.
* Added corresponding tests and visual tweaks to a launcher-item's
  progress-overlay.
* Implemented alert/wiggle feature for launcher-icons.
* Implemented alert/wiggle feature for launcher-icons.
[ handsome_feng ]
* makes left swip reset the search string. (LP: #1413791)
[ handsome_feng<email address hidden> ]
* Don't expand indicators when tap to return to call. (LP: #1453217)
* makes left swip reset the search string. (LP: #1413791)

1828. By Daniel d'Andrada on 2015-08-03

Remove stuff

1829. By Daniel d'Andrada on 2015-08-03

Update debian/control

Daniel d'Andrada (dandrader) wrote :

On 03/08/15 10:20, Albert Astals Cid wrote:
> Review: Needs Fixing
>
> Text conflict in plugins/Greeter/Unity/Launcher/CMakeLists.txt
> Text conflict in plugins/Unity/Launcher/CMakeLists.txt
> Text conflict in qml/Shell.qml
> Text conflict in qml/Stages/DesktopStage.qml
> Text conflict in tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt
> Text conflict in tests/qmltests/tst_Shell.qml
>
> Was top approved before

Fixed.

Albert Astals Cid (aacid) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-06-17 12:14:27 +0000
3+++ CMakeLists.txt 2015-08-03 14:21:12 +0000
4@@ -56,7 +56,7 @@
5 find_package(Qt5Concurrent 5.2 REQUIRED)
6 find_package(Qt5Sql 5.2 REQUIRED)
7
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
10
11 # Standard install paths
12 include(GNUInstallDirs)
13
14=== modified file 'debian/control'
15--- debian/control 2015-07-29 12:38:53 +0000
16+++ debian/control 2015-08-03 14:21:12 +0000
17@@ -28,7 +28,7 @@
18 libqt5xmlpatterns5-dev,
19 libsystemsettings-dev,
20 libudev-dev,
21- libunity-api-dev (>= 7.98),
22+ libunity-api-dev (>= 7.99),
23 libusermetricsoutput1-dev,
24 libxcb1-dev,
25 pkg-config,
26
27=== modified file 'plugins/Greeter/Unity/Launcher/CMakeLists.txt'
28--- plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
29+++ plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
30@@ -1,5 +1,4 @@
31 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
32-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
33 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
34
35 add_definitions(-DSM_BUSNAME=systemBus)
36
37=== modified file 'plugins/Unity/Launcher/CMakeLists.txt'
38--- plugins/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
39+++ plugins/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
40@@ -1,5 +1,4 @@
41 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
42-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
43 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
44
45 add_definitions(-DSM_BUSNAME=systemBus)
46
47=== added file 'qml/Components/Unity8Settings.qml'
48--- qml/Components/Unity8Settings.qml 1970-01-01 00:00:00 +0000
49+++ qml/Components/Unity8Settings.qml 2015-08-03 14:21:12 +0000
50@@ -0,0 +1,50 @@
51+/*
52+ * Copyright (C) 2015 Canonical, Ltd.
53+ *
54+ * This program is free software; you can redistribute it and/or modify
55+ * it under the terms of the GNU General Public License as published by
56+ * the Free Software Foundation; version 3.
57+ *
58+ * This program is distributed in the hope that it will be useful,
59+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
60+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
61+ * GNU General Public License for more details.
62+ *
63+ * You should have received a copy of the GNU General Public License
64+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
65+ */
66+
67+import QtQuick 2.0
68+import GSettings 1.0
69+
70+QtObject {
71+ id: root
72+
73+ property string usageMode: "Staged"
74+
75+ // FIXME: Works around a bug where if we change a Loader's source in response to a GSettings
76+ // property change in the same event loop, the Loader's previously loaded component does not
77+ // get destroyed and its bindings continue to operate!
78+ //
79+ // Shouldn't be needed after
80+ // https://code.launchpad.net/~lukas-kde/gsettings-qt/queued-processing/+merge/259883 gets
81+ // merged.
82+ property var timer: Timer {
83+ interval: 1
84+ onTriggered: { root.usageMode = root.wrapped.usageMode; }
85+ }
86+ property var wrappedConnections: Connections {
87+ target: root.wrapped
88+ ignoreUnknownSignals: true // don't spam us
89+ onUsageModeChanged: { root.timer.start(); }
90+ }
91+ property var wrapped: GSettings {
92+ schema.id: "com.canonical.Unity8"
93+ Component.onCompleted: {
94+ // init the value. it's a dynamic prop, so we have to check first
95+ if (root.usageMode) {
96+ root.usageMode = root.wrapped.usageMode;
97+ }
98+ }
99+ }
100+}
101
102=== modified file 'qml/OrientedShell.qml'
103--- qml/OrientedShell.qml 2015-07-01 17:52:34 +0000
104+++ qml/OrientedShell.qml 2015-08-03 14:21:12 +0000
105@@ -43,7 +43,7 @@
106
107
108 // to be overwritten by tests
109- property var usageModeSettings: GSettings { schema.id: "com.canonical.Unity8" }
110+ property var unity8Settings: Unity8Settings {}
111 property var oskSettings: GSettings { schema.id: "com.canonical.keyboard.maliit" }
112
113 property int physicalOrientation: Screen.orientation
114@@ -160,9 +160,9 @@
115 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
116 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
117 usageScenario: {
118- if (root.usageModeSettings.usageMode === "Windowed") {
119+ if (root.unity8Settings.usageMode === "Windowed") {
120 return "desktop";
121- } else if (root.usageModeSettings.usageMode === "Staged") {
122+ } else if (root.unity8Settings.usageMode === "Staged") {
123 if (deviceConfiguration.category === "phone") {
124 return "phone";
125 } else {
126
127=== modified file 'qml/Shell.qml'
128--- qml/Shell.qml 2015-07-29 12:38:53 +0000
129+++ qml/Shell.qml 2015-08-03 14:21:12 +0000
130@@ -191,12 +191,6 @@
131 z: dialogs.z + 10
132 }
133
134- Binding {
135- target: ApplicationManager
136- property: "forceDashActive"
137- value: launcher.shown || launcher.dashSwipe
138- }
139-
140 WindowKeysFilter {
141 Keys.onPressed: physicalKeysMapper.onKeyPressed(event);
142 Keys.onReleased: physicalKeysMapper.onKeyReleased(event);
143@@ -354,6 +348,16 @@
144 }
145 Binding {
146 target: applicationsDisplayLoader.item
147+ property: "keepDashRunning"
148+ value: launcher.shown || launcher.dashSwipe
149+ }
150+ Binding {
151+ target: applicationsDisplayLoader.item
152+ property: "suspended"
153+ value: greeter.shown
154+ }
155+ Binding {
156+ target: applicationsDisplayLoader.item
157 property: "altTabPressed"
158 value: physicalKeysMapper.altTabPressed
159 }
160@@ -459,12 +463,6 @@
161 }
162
163 onEmergencyCall: startLockedApp("dialer-app")
164-
165- Binding {
166- target: ApplicationManager
167- property: "suspended"
168- value: greeter.shown
169- }
170 }
171 }
172
173
174=== modified file 'qml/Stages/DesktopStage.qml'
175--- qml/Stages/DesktopStage.qml 2015-07-16 15:26:26 +0000
176+++ qml/Stages/DesktopStage.qml 2015-08-03 14:21:12 +0000
177@@ -39,6 +39,8 @@
178 property int shellPrimaryOrientation
179 property int nativeOrientation
180 property bool beingResized: false
181+ property bool keepDashRunning: true
182+ property bool suspended: false
183
184 // functions to be called from outside
185 function updateFocusedAppOrientation() { /* TODO */ }
186@@ -215,6 +217,17 @@
187 }
188 }
189
190+ Binding {
191+ target: ApplicationManager.get(index)
192+ property: "requestedState"
193+ // TODO: figure out some lifecycle policy, like suspending minimized apps
194+ // if running on a tablet or something.
195+ // TODO: If the device has a dozen suspended apps because it was running
196+ // in staged mode, when it switches to Windowed mode it will suddenly
197+ // resume all those apps at once. We might want to avoid that.
198+ value: ApplicationInfoInterface.RequestedRunning // Always running for now
199+ }
200+
201 function maximize() {
202 minimized = false;
203 maximized = true;
204
205=== modified file 'qml/Stages/PhoneStage.qml'
206--- qml/Stages/PhoneStage.qml 2015-07-14 08:09:58 +0000
207+++ qml/Stages/PhoneStage.qml 2015-08-03 14:21:12 +0000
208@@ -36,6 +36,8 @@
209 property bool altTabEnabled: true
210 property real startScale: 1.1
211 property real endScale: 0.7
212+ property bool keepDashRunning: true
213+ property bool suspended: false
214 property int shellOrientationAngle: 0
215 property int shellOrientation
216 property int shellPrimaryOrientation
217@@ -428,6 +430,14 @@
218 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
219 focusFirstApp: root.focusFirstApp
220
221+ Binding {
222+ target: appDelegate.application
223+ property: "requestedState"
224+ value: (isDash && root.keepDashRunning) || (!root.suspended && appDelegate.focus)
225+ ? ApplicationInfoInterface.RequestedRunning
226+ : ApplicationInfoInterface.RequestedSuspended
227+ }
228+
229 readonly property bool isDash: model.appId == "unity8-dash"
230
231 z: isDash && !spreadView.active ? -1 : behavioredIndex
232
233=== modified file 'qml/Stages/TabletStage.qml'
234--- qml/Stages/TabletStage.qml 2015-07-06 09:31:43 +0000
235+++ qml/Stages/TabletStage.qml 2015-08-03 14:21:12 +0000
236@@ -36,6 +36,8 @@
237 property bool spreadEnabled: true // If false, animations and right edge will be disabled
238
239 property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
240+ property bool keepDashRunning: true
241+ property bool suspended: false
242 property int shellOrientationAngle: 0
243 property int shellOrientation
244 property int shellPrimaryOrientation
245@@ -117,6 +119,7 @@
246
247 QtObject {
248 id: priv
249+ objectName: "stagesPriv"
250
251 property string focusedAppId: ApplicationManager.focusedApplicationId
252 readonly property var focusedAppDelegate: {
253@@ -626,6 +629,17 @@
254
255 readonly property bool isDash: model.appId == "unity8-dash"
256
257+ Binding {
258+ target: spreadTile.application
259+ property: "requestedState"
260+ value: (spreadTile.isDash && root.keepDashRunning)
261+ ||
262+ (!root.suspended && (model.appId == priv.mainStageAppId
263+ || model.appId == priv.sideStageAppId))
264+ ? ApplicationInfoInterface.RequestedRunning
265+ : ApplicationInfoInterface.RequestedSuspended
266+ }
267+
268 // FIXME: A regular binding doesn't update any more after closing an app.
269 // Using a Binding for now.
270 Binding {
271
272=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
273--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-03-06 04:44:11 +0000
274+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-08-03 14:21:12 +0000
275@@ -30,7 +30,7 @@
276 : ApplicationInfoInterface(appId, parent)
277 , m_appId(appId)
278 , m_stage(MainStage)
279- , m_state(Starting)
280+ , m_state(Stopped)
281 , m_focused(false)
282 , m_fullscreen(false)
283 , m_session(0)
284@@ -39,6 +39,7 @@
285 Qt::InvertedPortraitOrientation |
286 Qt::InvertedLandscapeOrientation)
287 , m_rotatesWindowContents(false)
288+ , m_requestedState(RequestedRunning)
289 , m_manualSurfaceCreation(false)
290 {
291 }
292@@ -46,7 +47,7 @@
293 ApplicationInfo::ApplicationInfo(QObject *parent)
294 : ApplicationInfoInterface(QString(), parent)
295 , m_stage(MainStage)
296- , m_state(Starting)
297+ , m_state(Stopped)
298 , m_focused(false)
299 , m_fullscreen(false)
300 , m_session(0)
301@@ -55,6 +56,7 @@
302 Qt::InvertedPortraitOrientation |
303 Qt::InvertedLandscapeOrientation)
304 , m_rotatesWindowContents(false)
305+ , m_requestedState(RequestedRunning)
306 , m_manualSurfaceCreation(false)
307 {
308 }
309@@ -74,7 +76,6 @@
310
311 void ApplicationInfo::setSession(Session* session)
312 {
313- qDebug() << "Application::setSession - appId=" << appId() << "session=" << session;
314 if (m_session == session)
315 return;
316
317@@ -91,6 +92,8 @@
318 m_session->setApplication(this);
319 m_session->setParent(this);
320 SessionManager::singleton()->registerSession(m_session);
321+ connect(m_session, &Session::surfaceChanged,
322+ this, &ApplicationInfo::onSessionSurfaceChanged);
323
324 if (!m_manualSurfaceCreation) {
325 QTimer::singleShot(500, m_session, SLOT(createSurface()));
326@@ -161,11 +164,12 @@
327 m_state = value;
328 Q_EMIT stateChanged(value);
329
330- if (!m_manualSurfaceCreation && m_state == ApplicationInfo::Running) {
331+ if (!m_manualSurfaceCreation && !m_session && m_state == ApplicationInfo::Starting) {
332 QTimer::singleShot(500, this, SLOT(createSession()));
333 } else if (m_state == ApplicationInfo::Stopped) {
334- delete m_session;
335- m_session = nullptr;
336+ Session *session = m_session;
337+ setSession(nullptr);
338+ delete session;
339 }
340 }
341 }
342@@ -213,3 +217,33 @@
343 {
344 m_rotatesWindowContents = value;
345 }
346+
347+ApplicationInfo::RequestedState ApplicationInfo::requestedState() const
348+{
349+ return m_requestedState;
350+}
351+
352+void ApplicationInfo::setRequestedState(RequestedState value)
353+{
354+ if (m_requestedState != value) {
355+ m_requestedState = value;
356+ Q_EMIT requestedStateChanged(m_requestedState);
357+
358+ if (m_requestedState == RequestedRunning && m_state == Suspended) {
359+ setState(Running);
360+ } else if (m_requestedState == RequestedSuspended && m_state == Running) {
361+ setState(Suspended);
362+ }
363+ }
364+}
365+
366+void ApplicationInfo::onSessionSurfaceChanged(MirSurfaceItem* surface)
367+{
368+ if (surface != nullptr && m_state == Starting) {
369+ if (m_requestedState == RequestedRunning) {
370+ setState(Running);
371+ } else {
372+ setState(Suspended);
373+ }
374+ }
375+}
376
377=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
378--- tests/mocks/Unity/Application/ApplicationInfo.h 2015-03-06 04:44:11 +0000
379+++ tests/mocks/Unity/Application/ApplicationInfo.h 2015-08-03 14:21:12 +0000
380@@ -46,6 +46,9 @@
381 ApplicationInfo(const QString &appId, QObject *parent = nullptr);
382 ~ApplicationInfo();
383
384+ RequestedState requestedState() const override;
385+ void setRequestedState(RequestedState) override;
386+
387 void setIconId(const QString &iconId);
388 void setScreenshotId(const QString &screenshotId);
389
390@@ -101,6 +104,9 @@
391 public Q_SLOTS:
392 Q_INVOKABLE void createSession();
393
394+private Q_SLOTS:
395+ void onSessionSurfaceChanged(MirSurfaceItem*);
396+
397 private:
398 void setIcon(const QUrl &value);
399
400@@ -116,6 +122,7 @@
401 Session* m_session;
402 Qt::ScreenOrientations m_supportedOrientations;
403 bool m_rotatesWindowContents;
404+ RequestedState m_requestedState;
405
406 bool m_manualSurfaceCreation;
407 };
408
409=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
410--- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-06-24 11:41:09 +0000
411+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2015-08-03 14:21:12 +0000
412@@ -49,8 +49,6 @@
413
414 ApplicationManager::ApplicationManager(QObject *parent)
415 : ApplicationManagerInterface(parent)
416- , m_suspended(false)
417- , m_forceDashActive(false)
418 {
419 m_roleNames.insert(RoleSession, "session");
420 m_roleNames.insert(RoleFullscreen, "fullscreen");
421@@ -217,7 +215,7 @@
422 && application->stage() == ApplicationInfo::SideStage) {
423 application->setStage(ApplicationInfo::MainStage);
424 }
425- application->setState(ApplicationInfo::Running);
426+ application->setState(ApplicationInfo::Starting);
427
428 return application;
429 }
430@@ -262,50 +260,6 @@
431 return QString();
432 }
433
434-bool ApplicationManager::suspended() const
435-{
436- return m_suspended;
437-}
438-
439-void ApplicationManager::setSuspended(bool suspended)
440-{
441- ApplicationInfo *focusedApp = findApplication(focusedApplicationId());
442- if (focusedApp) {
443- if (suspended) {
444- focusedApp->setState(ApplicationInfo::Suspended);
445- } else {
446- focusedApp->setState(ApplicationInfo::Running);
447- }
448- }
449- m_suspended = suspended;
450- Q_EMIT suspendedChanged();
451-}
452-
453-bool ApplicationManager::forceDashActive() const
454-{
455- return m_forceDashActive;
456-}
457-
458-void ApplicationManager::setForceDashActive(bool forceDashActive)
459-{
460- if (m_forceDashActive == forceDashActive) {
461- return;
462- }
463-
464- ApplicationInfo *dash = findApplication("unity8-dash");
465- if (dash) {
466- if (forceDashActive) {
467- dash->setState(ApplicationInfo::Running);
468- } else {
469- if (!dash->focused()) {
470- dash->setState(ApplicationInfo::Suspended);
471- }
472- }
473- }
474- m_forceDashActive = forceDashActive;
475- Q_EMIT forceDashActiveChanged();
476-}
477-
478 bool ApplicationManager::focusApplication(const QString &appId)
479 {
480 ApplicationInfo *application = findApplication(appId);
481@@ -316,13 +270,11 @@
482 for (ApplicationInfo *app : m_runningApplications) {
483 if (app->focused()) {
484 app->setFocused(false);
485- app->setState(ApplicationInfo::Suspended);
486 }
487 }
488
489 // focus this app
490 application->setFocused(true);
491- application->setState(ApplicationInfo::Running);
492
493 // move app to top of stack
494 move(m_runningApplications.indexOf(application), 0);
495@@ -394,6 +346,7 @@
496 application->setScreenshotId("gallery");
497 application->setIconId("gallery");
498 application->setFullscreen(true);
499+ application->setStage(ApplicationInfo::MainStage);
500 m_availableApplications.append(application);
501
502 application = new ApplicationInfo(this);
503
504=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
505--- tests/mocks/Unity/Application/ApplicationManager.h 2015-04-01 11:25:54 +0000
506+++ tests/mocks/Unity/Application/ApplicationManager.h 2015-08-03 14:21:12 +0000
507@@ -69,11 +69,6 @@
508 Q_INVOKABLE bool stopApplication(const QString &appId) override;
509
510 QString focusedApplicationId() const override;
511- bool suspended() const override;
512- void setSuspended(bool suspended) override;
513-
514- bool forceDashActive() const override;
515- void setForceDashActive(bool forceDashActive) override;
516
517 // Only for testing
518 QStringList availableApplications();
519@@ -96,8 +91,6 @@
520 void remove(ApplicationInfo* application);
521 void buildListOfAvailableApplications();
522 void onWindowCreated();
523- bool m_suspended;
524- bool m_forceDashActive;
525 QList<ApplicationInfo*> m_runningApplications;
526 QList<ApplicationInfo*> m_availableApplications;
527 QTimer m_windowCreatedTimer;
528
529=== modified file 'tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt'
530--- tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-07-23 10:31:56 +0000
531+++ tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt 2015-08-03 14:21:12 +0000
532@@ -1,5 +1,4 @@
533 pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
534-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
535
536 include_directories(
537 ${CMAKE_CURRENT_SOURCE_DIR}
538
539=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
540--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-07-23 14:13:57 +0000
541+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-08-03 14:21:12 +0000
542@@ -39,6 +39,9 @@
543 Q_OBJECT
544 public:
545 MockApp(const QString &appId, QObject *parent = 0): ApplicationInfoInterface(appId, parent), m_appId(appId), m_focused(false) { }
546+
547+ RequestedState requestedState() const override { return RequestedRunning; }
548+ void setRequestedState(RequestedState) override {}
549 QString appId() const override { return m_appId; }
550 QString name() const override { return "mock"; }
551 QString comment() const override { return "this is a mock"; }
552@@ -117,10 +120,6 @@
553 endRemoveRows();
554 }
555 bool requestFocusApplication(const QString &appId) override { Q_UNUSED(appId); return true; }
556- bool suspended() const override { return false; }
557- void setSuspended(bool) override {}
558- bool forceDashActive() const override { return false; }
559- void setForceDashActive(bool) override {}
560
561 private:
562 QList<MockApp*> m_list;
563
564=== modified file 'tests/qmltests/Stages/ApplicationCheckBox.qml'
565--- tests/qmltests/Stages/ApplicationCheckBox.qml 2015-06-12 16:07:43 +0000
566+++ tests/qmltests/Stages/ApplicationCheckBox.qml 2015-08-03 14:21:12 +0000
567@@ -86,8 +86,29 @@
568 }
569 }
570 Label {
571+ id: appIdLabel
572 text: root.appId
573 color: "white"
574 anchors.verticalCenter: parent.verticalCenter
575 }
576+ Rectangle {
577+ color: {
578+ if (d.application) {
579+ if (d.application.state === ApplicationInfoInterface.Starting) {
580+ return "yellow";
581+ } else if (d.application.state === ApplicationInfoInterface.Running) {
582+ return "green";
583+ } else if (d.application.state === ApplicationInfoInterface.Suspended) {
584+ return "blue";
585+ } else {
586+ return "darkred";
587+ }
588+ } else {
589+ return "darkred";
590+ }
591+ }
592+ width: height
593+ height: appIdLabel.height * 0.7
594+ anchors.verticalCenter: parent.verticalCenter
595+ }
596 }
597
598=== modified file 'tests/qmltests/Stages/tst_DesktopStage.qml'
599--- tests/qmltests/Stages/tst_DesktopStage.qml 2015-05-01 13:21:30 +0000
600+++ tests/qmltests/Stages/tst_DesktopStage.qml 2015-08-03 14:21:12 +0000
601@@ -24,9 +24,8 @@
602
603 import "../../../qml/Stages"
604
605-Rectangle {
606+Item {
607 id: root
608- color: "darkblue"
609 width: desktopStageLoader.width + controls.width
610 height: desktopStageLoader.height
611
612@@ -57,6 +56,7 @@
613 property bool itemDestroyed: false
614 sourceComponent: Component {
615 DesktopStage {
616+ color: "darkblue"
617 anchors.fill: parent
618 Component.onDestruction: {
619 desktopStageLoader.itemDestroyed = true;
620
621=== modified file 'tests/qmltests/Stages/tst_PhoneStage.qml'
622--- tests/qmltests/Stages/tst_PhoneStage.qml 2015-06-23 14:20:23 +0000
623+++ tests/qmltests/Stages/tst_PhoneStage.qml 2015-08-03 14:21:12 +0000
624@@ -301,16 +301,16 @@
625 addApps(2);
626
627 var secondApp = ApplicationManager.get(0);
628- tryCompare(secondApp, "state", ApplicationInfoInterface.Running);
629+ tryCompare(secondApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
630 tryCompare(secondApp, "focused", true);
631
632 var firstApp = ApplicationManager.get(1);
633- tryCompare(firstApp, "state", ApplicationInfoInterface.Suspended);
634+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
635 tryCompare(firstApp, "focused", false);
636
637 ApplicationManager.stopApplication(secondApp.appId);
638
639- tryCompare(firstApp, "state", ApplicationInfoInterface.Running);
640+ tryCompare(firstApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
641 tryCompare(firstApp, "focused", true);
642 }
643
644@@ -388,5 +388,77 @@
645
646 tryCompare(focusedDelegate, "x", 0);
647 }
648+
649+ function test_focusedAppIsTheOnlyRunningApp() {
650+ addApps(2);
651+
652+ var delegateA = findChild(phoneStage, "appDelegate0");
653+ verify(delegateA);
654+ var delegateB = findChild(phoneStage, "appDelegate1");
655+ verify(delegateB);
656+
657+ // A is focused and running, B is unfocused and suspended
658+ compare(delegateA.focus, true);
659+ compare(delegateA.application.requestedState, ApplicationInfoInterface.RequestedRunning);
660+ compare(delegateB.focus, false);
661+ compare(delegateB.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
662+
663+ // Switch foreground/focused appp from A to B
664+ goToSpread();
665+ phoneStage.select(delegateB.application.appId);
666+
667+ // Now it's the other way round
668+ // A is unfocused and suspended, B is focused and running
669+ tryCompare(delegateA, "focus", false);
670+ tryCompare(delegateA.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
671+ tryCompare(delegateB, "focus", true);
672+ tryCompare(delegateB.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
673+ }
674+
675+ function test_dashRemainsRunningIfStageIsToldSo() {
676+ addApps(1);
677+
678+ var delegateDash = findChild(phoneStage, "appDelegate1");
679+ verify(delegateDash);
680+ compare(delegateDash.application.appId, "unity8-dash");
681+
682+ var delegateOther = findChild(phoneStage, "appDelegate0");
683+ verify(delegateOther);
684+
685+ goToSpread();
686+ phoneStage.select("unity8-dash");
687+
688+ tryCompare(delegateDash, "focus", true);
689+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
690+ compare(delegateOther.focus, false);
691+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedSuspended);
692+
693+ goToSpread();
694+ phoneStage.select(delegateOther.application.appId);
695+
696+ // The other app gets focused and running but dash is kept running despite being unfocused
697+ tryCompare(delegateDash, "focus", false);
698+ tryCompare(delegateDash.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
699+ compare(delegateOther.focus, true);
700+ compare(delegateOther.application.requestedState, ApplicationInfoInterface.RequestedRunning);
701+ }
702+
703+ function test_foregroundAppIsSuspendedWhenStageIsSuspended() {
704+ addApps(1);
705+
706+ var delegate = findChild(phoneStage, "appDelegate0");
707+ verify(delegate);
708+
709+ compare(delegate.focus, true);
710+ compare(delegate.application.requestedState, ApplicationInfoInterface.RequestedRunning);
711+
712+ phoneStage.suspended = true;
713+
714+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedSuspended);
715+
716+ phoneStage.suspended = false;
717+
718+ tryCompare(delegate.application, "requestedState", ApplicationInfoInterface.RequestedRunning);
719+ }
720 }
721 }
722
723=== modified file 'tests/qmltests/Stages/tst_TabletStage.qml'
724--- tests/qmltests/Stages/tst_TabletStage.qml 2015-06-12 16:07:43 +0000
725+++ tests/qmltests/Stages/tst_TabletStage.qml 2015-08-03 14:21:12 +0000
726@@ -77,9 +77,17 @@
727 appId: "webbrowser-app"
728 }
729 ApplicationCheckBox {
730+ id: galleryCheckBox
731+ appId: "gallery-app"
732+ }
733+ ApplicationCheckBox {
734 id: dialerCheckBox
735 appId: "dialer-app"
736 }
737+ ApplicationCheckBox {
738+ id: facebookCheckBox
739+ appId: "facebook-webapp"
740+ }
741 }
742 }
743
744@@ -93,6 +101,19 @@
745 function init() {
746 tabletStageLoader.active = true;
747 tryCompare(tabletStageLoader, "status", Loader.Ready);
748+
749+ // this is very strange, but sometimes the test starts without
750+ // TabletStage components having finished loading themselves
751+ var appWindow = null;
752+ while (!appWindow) {
753+ appWindow = findChild(tabletStage, "appWindow_unity8-dash");
754+ if (!appWindow) {
755+ console.log("didn't find appWindow_unity8-dash in " + tabletStage + ". Trying again...");
756+ wait(50);
757+ }
758+ }
759+
760+ waitUntilAppSurfaceShowsUp("unity8-dash");
761 }
762
763 function cleanup() {
764@@ -109,7 +130,9 @@
765
766 // kill all (fake) running apps
767 webbrowserCheckBox.checked = false;
768+ galleryCheckBox.checked = false;
769 dialerCheckBox.checked = false;
770+ facebookCheckBox.checked = false;
771 }
772
773 function waitUntilAppSurfaceShowsUp(appId) {
774@@ -120,6 +143,34 @@
775 tryCompare(appWindowStates, "state", "surface");
776 }
777
778+ function switchToApp(targetAppId) {
779+ touchFlick(tabletStage,
780+ tabletStage.width - (tabletStage.dragAreaWidth / 2), tabletStage.height / 2,
781+ tabletStage.x + 1, tabletStage.height / 2);
782+
783+ var spreadView = findChild(tabletStage, "spreadView");
784+ verify(spreadView);
785+ tryCompare(spreadView, "phase", 2);
786+ tryCompare(spreadView, "flicking", false);
787+ tryCompare(spreadView, "moving", false);
788+
789+ waitUntilAppDelegateStopsMoving(targetAppId);
790+
791+ var targetAppWindow = findChild(tabletStage, "appWindow_" + targetAppId);
792+ tap(targetAppWindow, 10, 10);
793+ }
794+
795+ function waitUntilAppDelegateStopsMoving(targetAppId)
796+ {
797+ var targetAppDelegate = findChild(tabletStage, "tabletSpreadDelegate_" + targetAppId);
798+ verify(targetAppDelegate);
799+ var lastValue = undefined;
800+ do {
801+ lastValue = targetAppDelegate.animatedProgress;
802+ wait(30);
803+ } while (lastValue != targetAppDelegate.animatedProgress);
804+ }
805+
806 function test_tappingSwitchesFocusBetweenStages() {
807 webbrowserCheckBox.checked = true;
808 waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId);
809@@ -155,7 +206,6 @@
810 tryCompare(webbrowserApp.session.surface, "activeFocus", false);
811 }
812
813-
814 function test_closeAppInSideStage() {
815 dialerCheckBox.checked = true;
816 waitUntilAppSurfaceShowsUp(dialerCheckBox.appId);
817@@ -200,6 +250,116 @@
818 appWindow.width / 2, appWindow.height / 2,
819 appWindow.width / 2, -appWindow.height / 2);
820 }
821+
822+ function test_suspendsAndResumesAppsInMainStage() {
823+ webbrowserCheckBox.checked = true;
824+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
825+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
826+
827+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
828+
829+ galleryCheckBox.checked = true;
830+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
831+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
832+
833+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
834+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
835+
836+ switchToApp(webbrowserApp.appId);
837+
838+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
839+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
840+
841+ switchToApp(galleryApp.appId);
842+
843+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
844+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
845+ }
846+
847+
848+ function test_foregroundMainAndSideStageAppsAreKeptRunning() {
849+
850+ var stagesPriv = findInvisibleChild(tabletStage, "stagesPriv");
851+ verify(stagesPriv);
852+
853+ // launch two main stage apps
854+ // gallery will be on foreground and webbrowser on background
855+
856+ webbrowserCheckBox.checked = true;
857+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
858+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
859+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
860+
861+ galleryCheckBox.checked = true;
862+ waitUntilAppSurfaceShowsUp(galleryCheckBox.appId)
863+ var galleryApp = ApplicationManager.findApplication(galleryCheckBox.appId);
864+ compare(galleryApp.stage, ApplicationInfoInterface.MainStage);
865+
866+ compare(stagesPriv.mainStageAppId, galleryCheckBox.appId);
867+
868+ // then launch two side stage apps
869+ // facebook will be on foreground and dialer on background
870+
871+ dialerCheckBox.checked = true;
872+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
873+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
874+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
875+
876+ facebookCheckBox.checked = true;
877+ waitUntilAppSurfaceShowsUp(facebookCheckBox.appId)
878+ var facebookApp = ApplicationManager.findApplication(facebookCheckBox.appId);
879+ compare(facebookApp.stage, ApplicationInfoInterface.SideStage);
880+
881+ compare(stagesPriv.sideStageAppId, facebookCheckBox.appId);
882+
883+ // Now check that the foreground apps are running and that the background ones
884+ // are suspended
885+
886+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
887+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
888+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Running);
889+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Suspended);
890+
891+ switchToApp(dialerCheckBox.appId);
892+
893+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Running);
894+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Suspended);
895+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
896+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
897+
898+ switchToApp(webbrowserCheckBox.appId);
899+
900+ tryCompare(galleryApp, "state", ApplicationInfoInterface.Suspended);
901+ tryCompare(webbrowserApp, "state", ApplicationInfoInterface.Running);
902+ tryCompare(facebookApp, "state", ApplicationInfoInterface.Suspended);
903+ tryCompare(dialerApp, "state", ApplicationInfoInterface.Running);
904+ }
905+
906+ function test_foregroundAppsAreSuspendedWhenStageIsSuspended() {
907+ webbrowserCheckBox.checked = true;
908+ waitUntilAppSurfaceShowsUp(webbrowserCheckBox.appId)
909+ var webbrowserApp = ApplicationManager.findApplication(webbrowserCheckBox.appId);
910+ compare(webbrowserApp.stage, ApplicationInfoInterface.MainStage);
911+
912+ dialerCheckBox.checked = true;
913+ waitUntilAppSurfaceShowsUp(dialerCheckBox.appId)
914+ var dialerApp = ApplicationManager.findApplication(dialerCheckBox.appId);
915+ compare(dialerApp.stage, ApplicationInfoInterface.SideStage);
916+
917+
918+ compare(webbrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
919+ compare(dialerApp.requestedState, ApplicationInfoInterface.RequestedRunning);
920+
921+ tabletStage.suspended = true;
922+
923+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
924+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
925+
926+ tabletStage.suspended = false;
927+
928+ tryCompare(webbrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
929+ tryCompare(dialerApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
930+ }
931 }
932
933 }
934
935=== modified file 'tests/qmltests/tst_OrientedShell.qml'
936--- tests/qmltests/tst_OrientedShell.qml 2015-07-10 14:44:55 +0000
937+++ tests/qmltests/tst_OrientedShell.qml 2015-08-03 14:21:12 +0000
938@@ -46,7 +46,7 @@
939 }
940
941 QtObject {
942- id: mockUsageModeSettings
943+ id: mockUnity8Settings
944 property string usageMode: usageModeSelector.model[usageModeSelector.selectedIndex]
945 }
946
947@@ -125,7 +125,7 @@
948 sourceComponent: Component {
949 OrientedShell {
950 anchors.fill: parent
951- usageModeSettings: mockUsageModeSettings
952+ unity8Settings: mockUnity8Settings
953 oskSettings: mockOskSettings
954 physicalOrientation: root.physicalOrientation0
955 orientationLocked: orientationLockedCheckBox.checked
956@@ -926,7 +926,7 @@
957
958 function test_attachRemoveInputDevices() {
959 usageModeSelector.selectedIndex = 2;
960- tryCompare(mockUsageModeSettings, "usageMode", "Automatic")
961+ tryCompare(mockUnity8Settings, "usageMode", "Automatic")
962
963 loadShell("mako")
964 var shell = findChild(orientedShell, "shell");
965
966=== modified file 'tests/qmltests/tst_Shell.qml'
967--- tests/qmltests/tst_Shell.qml 2015-07-29 12:38:53 +0000
968+++ tests/qmltests/tst_Shell.qml 2015-08-03 14:21:12 +0000
969@@ -67,11 +67,6 @@
970 width: units.gu(40)
971 height: units.gu(71)
972 }
973- StateChangeScript {
974- script: {
975- GSettingsController.setUsageMode("Staged")
976- }
977- }
978 },
979 State {
980 name: "tablet"
981@@ -80,11 +75,6 @@
982 width: units.gu(100)
983 height: units.gu(71)
984 }
985- StateChangeScript {
986- script: {
987- GSettingsController.setUsageMode("Staged")
988- }
989- }
990 },
991 State {
992 name: "desktop"
993@@ -97,11 +87,6 @@
994 target: mouseEmulation
995 checked: false
996 }
997- StateChangeScript {
998- script: {
999- GSettingsController.setUsageMode("Windowed")
1000- }
1001- }
1002 }
1003 ]
1004
1005@@ -317,7 +302,6 @@
1006 mouseEmulation.checked = true;
1007 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
1008 tearDown();
1009- GSettingsController.setUsageMode("Staged");
1010 }
1011
1012 function loadShell(formFactor) {
1013@@ -602,9 +586,9 @@
1014 verify(mainAppId != "");
1015 var mainApp = ApplicationManager.findApplication(mainAppId);
1016 verify(mainApp);
1017- tryCompare(mainApp, "state", ApplicationInfoInterface.Running);
1018+ tryCompare(mainApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1019
1020- // Suspend while call is active...
1021+ // Display off while call is active...
1022 callManager.foregroundCall = phoneCall;
1023 Powerd.status = Powerd.Off;
1024 tryCompare(greeter, "shown", false);
1025@@ -615,8 +599,7 @@
1026 Powerd.status = Powerd.Off;
1027 tryCompare(greeter, "fullyShown", true);
1028
1029- tryCompare(ApplicationManager, "suspended", true);
1030- compare(mainApp.state, ApplicationInfoInterface.Suspended);
1031+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1032
1033 // And wake up
1034 Powerd.status = Powerd.On;
1035@@ -632,8 +615,7 @@
1036 removeTimeConstraintsFromDirectionalDragAreas(greeter);
1037 swipeAwayGreeter();
1038
1039- tryCompare(ApplicationManager, "suspended", false);
1040- compare(mainApp.state, ApplicationInfoInterface.Running);
1041+ compare(mainApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1042 tryCompare(ApplicationManager, "focusedApplicationId", mainAppId);
1043 }
1044
1045@@ -804,15 +786,15 @@
1046
1047 function test_launchedAppHasActiveFocus_data() {
1048 return [
1049- {tag: "phone", formFactor: "phone", usageMode: "Staged"},
1050- {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
1051- {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
1052+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
1053+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"},
1054+ {tag: "desktop", formFactor: "tablet", usageScenario: "desktop"}
1055 ]
1056 }
1057
1058 function test_launchedAppHasActiveFocus(data) {
1059- GSettingsController.setUsageMode(data.usageMode);
1060 loadShell(data.formFactor);
1061+ shell.usageScenario = data.usageScenario;
1062 swipeAwayGreeter();
1063
1064 var webApp = ApplicationManager.startApplication("webbrowser-app");
1065@@ -1347,6 +1329,50 @@
1066 compare(launcher.inverted, data.launcherInverted);
1067 }
1068
1069+ function test_unfocusedAppsGetSuspendedAfterEnteringStagedMode() {
1070+ loadShell("tablet");
1071+ shell.usageScenario = "desktop";
1072+
1073+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1074+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1075+
1076+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1077+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1078+
1079+ ApplicationManager.requestFocusApplication("unity8-dash");
1080+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1081+
1082+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1083+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedRunning);
1084+
1085+ shell.usageScenario = "tablet";
1086+
1087+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1088+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedSuspended);
1089+ }
1090+
1091+ function test_unfocusedAppsAreResumedWhenEnteringWindowedMode() {
1092+ loadShell("tablet");
1093+ shell.usageScenario = "tablet";
1094+
1095+ var webBrowserApp = ApplicationManager.startApplication("webbrowser-app");
1096+ waitUntilAppWindowIsFullyLoaded(webBrowserApp);
1097+
1098+ var galleryApp = ApplicationManager.startApplication("gallery-app");
1099+ waitUntilAppWindowIsFullyLoaded(galleryApp);
1100+
1101+ ApplicationManager.requestFocusApplication("unity8-dash");
1102+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
1103+
1104+ compare(webBrowserApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1105+ compare(galleryApp.requestedState, ApplicationInfoInterface.RequestedSuspended);
1106+
1107+ shell.usageScenario = "desktop";
1108+
1109+ tryCompare(webBrowserApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1110+ tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1111+ }
1112+
1113 function test_altTabSwitchesFocus() {
1114 loadShell("desktop");
1115 shell.usageScenario = "desktop"

Subscribers

People subscribed via source and target branches