Merge lp:~mterry/unity8/no-touch-no-lifecycle into lp:unity8

Proposed by Michael Terry
Status: Merged
Approved by: Gerry Boland
Approved revision: 2001
Merged at revision: 2031
Proposed branch: lp:~mterry/unity8/no-touch-no-lifecycle
Merge into: lp:unity8
Prerequisite: lp:~unity-team/unity8/new_fix_upsidedown
Diff against target: 651 lines (+218/-86)
15 files modified
CMakeLists.txt (+1/-1)
debian/control (+2/-2)
qml/Stages/AbstractStage.qml (+63/-0)
qml/Stages/DesktopStage.qml (+4/-20)
qml/Stages/PhoneStage.qml (+18/-29)
qml/Stages/ShimStage.qml (+1/-1)
qml/Stages/TabletStage.qml (+14/-32)
tests/mocks/GSettings.1.0/fake_gsettings.cpp (+31/-0)
tests/mocks/GSettings.1.0/fake_gsettings.h (+10/-0)
tests/mocks/Unity/Application/ApplicationInfo.cpp (+12/-0)
tests/mocks/Unity/Application/ApplicationInfo.h (+4/-0)
tests/mocks/Unity/Application/ApplicationManager.cpp (+9/-0)
tests/mocks/Unity/Application/ApplicationManager.h (+1/-1)
tests/plugins/Unity/Launcher/launchermodeltest.cpp (+1/-0)
tests/qmltests/tst_Shell.qml (+47/-0)
To merge this branch: bzr merge lp:~mterry/unity8/no-touch-no-lifecycle
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada (community) Abstain
Andrea Cimitan Pending
Review via email: mp+275919@code.launchpad.net

This proposal supersedes a proposal from 2015-09-29.

Commit message

Handle lifecycle policy exceptions ourselves, instead of letting qtmir do it for us and allow non-Touch apps to opt-out of the Touch lifecycle.

This requires using the new isTouchApp unity-api property to ApplicationInfoInterface.

Now that qtmir won't decide policy for suspending exemptions anymore, we take over the interpretation of the lifecycleException GSettings key. Since the GSettings key for that was registered under the qtmir namespace (and there's no technical reason to migrate settings), I left the schema in qtmir itself. We merely consume it.

Description of the change

== Checklist ==

* Are there any related MPs required for this MP to build/function as expected? Please list.
 - https://code.launchpad.net/~mterry/unity-api/no-touch-no-lifecycle/+merge/272829
 - https://code.launchpad.net/~mterry/qtmir/no-touch-no-lifecycle/+merge/272791

 * 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?
 I'm a member of that team.

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

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Andrea Cimitan (cimi) : Posted in a previous version of this proposal
review: Needs Fixing
Revision history for this message
Michael Terry (mterry) wrote : Posted in a previous version of this proposal

> 1.2 for now

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

=== added file 'qml/graphics/applicationIcons/libreoffice@18.png'
Binary files qml/graphics/applicationIcons/libreoffice@18.png 1970-01-01 00:00:00 +0000 and qml/graphics/applicationIcons/libreoffice@18.png 2015-10-08 19:48:39 +0000 differ
Why did that change?

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

=== added file 'qml/Stages/AbstractStage.qml'
Instead of destroying and re-creating the GSettings instance when Stage changes, could this file be a Singleton shared by all Stages?

+Rectangle {
This looks like a useless rectangle, no color is set. It could be a QtObject, if you weren't inheriting from it.

Oh, I see why the LibreOffice icon. Please ignore above comment

review: Needs Fixing
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

+ !model.isTouchApp
+ || isExemptFromLifecycle(model.appId)
I think it would improve readability to make a quick boolean property :
canSuspend: model.isTouchApp && !isExemptFromLifecycle(model.appId)

Revision history for this message
Michael Terry (mterry) wrote : Posted in a previous version of this proposal

> === added file 'qml/Stages/AbstractStage.qml'
> Instead of destroying and re-creating the GSettings instance when
> Stage changes, could this file be a Singleton shared by all Stages?

It could be, sure, but I wasn't in the mindset of optimizing for Stage changes. What about the hundred other objects that get destroyed & created in that case? Are GSettings instances particularly expensive (honest question)?

> +Rectangle {
> This looks like a useless rectangle, no color is set. It could be
> a QtObject, if you weren't inheriting from it.

Fair point. I was trying to avoid unrelated changes by not merging common Stage code. I've done that now, and consolidated the color: settings as well as the public API for stages (which various of the Stage implementations didn't fully implement!).

> + !model.isTouchApp
> + || isExemptFromLifecycle(model.appId)
> I think it would improve readability to make a quick boolean property :
> canSuspend: model.isTouchApp && !isExemptFromLifecycle(model.appId)

Good point. Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
1994. By Michael Terry

Go back to using 1.3

1995. By Michael Terry

Merge new_fix_upsidedown branch

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

"Set lifecycle policy ourselves, instead of letting qtmir do it for us and allow non-Touch apps to opt-out of the Touch lifecycle."

This commit message is not correct. Unity 8 is *already* controlling the lifecycle policy.

The only thing missing in trunk is the "allow non-Touch apps to opt-out of the Touch lifecycle" part.

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

test_lifecyclePolicy is testing again ground already covered by test_unfocusedAppsAreResumedWhenEnteringWindowedMode and test_unfocusedAppsGetSuspendedAfterEnteringStagedMode.

It's too comprehensive. You can't and should not expect a single test to cover all angles and aspects of lifecycle policy as that catch-all name (test_lifecyclePolicy) implies. You should write a test specifically for this new Application.isTouchApp property. Ie, how its value affects the Application.requestedState value set by unity8. Eg: tst_touchAppsAreNeverSuspended.

review: Needs Fixing
Revision history for this message
Michael Terry (mterry) wrote :

> This commit message is not correct. Unity 8 is *already* controlling the lifecycle policy.

Sort of. Qtmir was overriding unity8's requests based on its own handling of the exception list. This branch moves that handling into unity8, so that unity8 is wholly responsible for policy. I've made the commit message clearer.

> test_lifecyclePolicy is testing again ground already covered by test_unfocusedAppsAreResumedWhenEnteringWindowedMode and test_unfocusedAppsGetSuspendedAfterEnteringStagedMode.

Sure, OK. I'll split the test in two (exception list and isTouchApp) and remove the sanity checks that duplicate checks in those tests. New commit coming.

Revision history for this message
Michael Terry (mterry) wrote :

Alright. I made the requested test change. I still like having "control group" checks, but I understand the argument that those other test methods are the control group.

1996. By Michael Terry

Break test apart

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Thanks for splitting it into smaller tests.

For the sake of clarity it's worth checking that libreoffice and webbrowser-app are not focused before the final compare() calls. Afterall, the only reason you launched a second app after them was to get them unfocused. A check (and/or comment) would make your intent clear.

1997. By Michael Terry

Also confirm that app no longer has focus

Revision history for this message
Michael Terry (mterry) wrote :

> For the sake of clarity it's worth checking that libreoffice
> and webbrowser-app are not focused before the final compare() calls.

Done.

1998. By Michael Terry

Add quick clarifying comment

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Thanks!

review: Abstain
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1999. By Michael Terry

Bump shell application version

2000. By Michael Terry

Bump shell application version harder

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2001. By Michael Terry

Whoops, remove testing changes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Tested on device, unable to detect any regression, looks good!

review: Approve

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-10-21 11:51:38 +0000
3+++ CMakeLists.txt 2015-11-03 20:18:21 +0000
4@@ -57,7 +57,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=9)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=10)
10
11 # Standard install paths
12 include(GNUInstallDirs)
13
14=== modified file 'debian/control'
15--- debian/control 2015-10-21 11:51:11 +0000
16+++ debian/control 2015-11-03 20:18:21 +0000
17@@ -127,7 +127,7 @@
18 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
19 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
20 ubuntu-thumbnailer-impl-0,
21- unity-application-impl-9,
22+ unity-application-impl-10,
23 unity-notifications-impl-3,
24 unity-plugin-scopes | unity-scopes-impl,
25 unity-scopes-impl-7,
26@@ -173,7 +173,7 @@
27 Depends: ${misc:Depends},
28 ${shlibs:Depends},
29 Provides: unity-application-impl,
30- unity-application-impl-9,
31+ unity-application-impl-10,
32 Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
33 Description: Fake environment for running Unity 8 shell
34 Provides fake implementations of some QML modules used by Unity 8 shell
35
36=== added file 'qml/Stages/AbstractStage.qml'
37--- qml/Stages/AbstractStage.qml 1970-01-01 00:00:00 +0000
38+++ qml/Stages/AbstractStage.qml 2015-11-03 20:18:21 +0000
39@@ -0,0 +1,63 @@
40+/*
41+ * Copyright (C) 2015 Canonical, Ltd.
42+ *
43+ * This program is free software; you can redistribute it and/or modify
44+ * it under the terms of the GNU General Public License as published by
45+ * the Free Software Foundation; version 3.
46+ *
47+ * This program is distributed in the hope that it will be useful,
48+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
49+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
50+ * GNU General Public License for more details.
51+ *
52+ * You should have received a copy of the GNU General Public License
53+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
54+ */
55+
56+import QtQuick 2.4
57+import Ubuntu.Components 1.3
58+import GSettings 1.0
59+
60+Rectangle {
61+ id: root
62+
63+ color: "#111111"
64+
65+ // Controls to be set from outside
66+ property bool altTabPressed
67+ property url background
68+ property bool beingResized
69+ property int dragAreaWidth
70+ property bool interactive
71+ property real inverseProgress // This is the progress for left edge drags, in pixels.
72+ property bool keepDashRunning: true
73+ property real maximizedAppTopMargin
74+ property real nativeHeight
75+ property real nativeWidth
76+ property QtObject orientations
77+ property int shellOrientation
78+ property int shellOrientationAngle
79+ property bool spreadEnabled: true // If false, animations and right edge will be disabled
80+ property bool suspended
81+
82+ // To be read from outside
83+ property var mainApp: null
84+ property int mainAppWindowOrientationAngle
85+ property bool orientationChangesEnabled
86+
87+ // Shared code for use in stage implementations
88+ GSettings {
89+ id: lifecycleExceptions
90+ schema.id: "com.canonical.qtmir"
91+ }
92+
93+ function isExemptFromLifecycle(appId) {
94+ var shortAppId = appId.split('_')[0];
95+ for (var i = 0; i < lifecycleExceptions.lifecycleExemptAppids.length; i++) {
96+ if (shortAppId === lifecycleExceptions.lifecycleExemptAppids[i]) {
97+ return true;
98+ }
99+ }
100+ return false;
101+ }
102+}
103
104=== modified file 'qml/Stages/DesktopStage.qml'
105--- qml/Stages/DesktopStage.qml 2015-11-03 20:18:21 +0000
106+++ qml/Stages/DesktopStage.qml 2015-11-03 20:18:21 +0000
107@@ -26,35 +26,17 @@
108 import Utils 0.1
109 import Ubuntu.Gestures 0.1
110
111-Rectangle {
112+AbstractStage {
113 id: root
114 anchors.fill: parent
115
116- // Controls to be set from outside
117- property int dragAreaWidth // just to comply with the interface shared between stages
118- property real maximizedAppTopMargin
119- property bool interactive
120- property bool spreadEnabled // just to comply with the interface shared between stages
121- property real inverseProgress: 0 // just to comply with the interface shared between stages
122- property int shellOrientationAngle: 0
123- property int shellOrientation
124- property QtObject orientations
125- property bool beingResized: false
126- property bool keepDashRunning: true
127- property bool suspended: false
128- property alias background: wallpaper.source
129- property alias altTabPressed: spread.altTabPressed
130-
131 // functions to be called from outside
132 function updateFocusedAppOrientation() { /* TODO */ }
133 function updateFocusedAppOrientationAnimated() { /* TODO */}
134
135- // To be read from outside
136- readonly property var mainApp: ApplicationManager.focusedApplicationId
137+ mainApp: ApplicationManager.focusedApplicationId
138 ? ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
139 : null
140- property int mainAppWindowOrientationAngle: 0
141- readonly property bool orientationChangesEnabled: false
142
143 Connections {
144 target: ApplicationManager
145@@ -122,6 +104,7 @@
146 CrossFadeImage {
147 id: wallpaper
148 anchors.fill: parent
149+ source: root.background
150 sourceSize { height: root.height; width: root.width }
151 fillMode: Image.PreserveAspectCrop
152 }
153@@ -275,5 +258,6 @@
154 anchors.fill: parent
155 workspace: appContainer
156 focus: state == "altTab"
157+ altTabPressed: root.altTabPressed
158 }
159 }
160
161=== modified file 'qml/Stages/PhoneStage.qml'
162--- qml/Stages/PhoneStage.qml 2015-11-03 20:18:21 +0000
163+++ qml/Stages/PhoneStage.qml 2015-11-03 20:18:21 +0000
164@@ -22,29 +22,15 @@
165 import Utils 0.1
166 import "../Components"
167
168-Rectangle {
169+AbstractStage {
170 id: root
171
172- // Controls to be set from outside
173- property int dragAreaWidth
174- property real maximizedAppTopMargin
175- property bool interactive
176- property bool spreadEnabled: true // If false, animations and right edge will be disabled
177- property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
178 property QtObject applicationManager: ApplicationManager
179 property bool focusFirstApp: true // If false, focused app will appear on right edge like other apps
180 property bool altTabEnabled: true
181 property real startScale: 1.1
182 property real endScale: 0.7
183- property bool keepDashRunning: true
184- property bool suspended: false
185- property int shellOrientationAngle: 0
186
187- property int shellOrientation
188- property QtObject orientations
189- property real nativeWidth
190- property real nativeHeight
191- property bool beingResized: false
192 onBeingResizedChanged: {
193 if (beingResized) {
194 // Brace yourselves for impact!
195@@ -56,6 +42,8 @@
196 priv.reset();
197 }
198 }
199+
200+ // Functions to be called from outside
201 function updateFocusedAppOrientation() {
202 if (spreadRepeater.count > 0) {
203 spreadRepeater.itemAt(0).matchShellOrientation();
204@@ -85,16 +73,14 @@
205 }
206 }
207
208- // To be read from outside
209- readonly property var mainApp: applicationManager.focusedApplicationId
210+ mainApp: applicationManager.focusedApplicationId
211 ? applicationManager.findApplication(applicationManager.focusedApplicationId)
212 : null
213
214- property int mainAppWindowOrientationAngle: 0
215- readonly property bool orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
216- && !priv.focusedAppDelegateIsDislocated
217- && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
218- && spreadView.phase === 0
219+ orientationChangesEnabled: priv.focusedAppOrientationChangesEnabled
220+ && !priv.focusedAppDelegateIsDislocated
221+ && !(priv.focusedAppDelegate && priv.focusedAppDelegate.xBehavior.running)
222+ && spreadView.phase === 0
223
224 // How far left the stage has been dragged
225 readonly property real dragProgress: spreadRepeater.count > 0 ? -spreadRepeater.itemAt(0).xTranslate : 0
226@@ -106,8 +92,6 @@
227
228 signal opened()
229
230- color: "#111111"
231-
232 function select(appId) {
233 spreadView.snapTo(priv.indexOf(appId));
234 }
235@@ -438,16 +422,21 @@
236 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
237 focusFirstApp: root.focusFirstApp
238
239+ readonly property bool isDash: model.appId == "unity8-dash"
240+
241+ readonly property bool canSuspend: model.isTouchApp
242+ && !isExemptFromLifecycle(model.appId)
243+
244 Binding {
245 target: appDelegate.application
246 property: "requestedState"
247- value: (isDash && root.keepDashRunning) || (!root.suspended && appDelegate.focus)
248- ? ApplicationInfoInterface.RequestedRunning
249- : ApplicationInfoInterface.RequestedSuspended
250+ value: !canSuspend
251+ || (isDash && root.keepDashRunning)
252+ || (!root.suspended && appDelegate.focus)
253+ ? ApplicationInfoInterface.RequestedRunning
254+ : ApplicationInfoInterface.RequestedSuspended
255 }
256
257- readonly property bool isDash: model.appId == "unity8-dash"
258-
259 z: isDash && !spreadView.active ? -1 : behavioredIndex
260
261 x: {
262
263=== modified file 'qml/Stages/ShimStage.qml'
264--- qml/Stages/ShimStage.qml 2015-04-21 19:43:25 +0000
265+++ qml/Stages/ShimStage.qml 2015-11-03 20:18:21 +0000
266@@ -17,7 +17,7 @@
267 import QtQuick 2.3
268 import Ubuntu.Components 0.1
269
270-Rectangle {
271+AbstractStage {
272 id: shimStage
273
274 anchors.fill: parent
275
276=== modified file 'qml/Stages/TabletStage.qml'
277--- qml/Stages/TabletStage.qml 2015-11-03 20:18:21 +0000
278+++ qml/Stages/TabletStage.qml 2015-11-03 20:18:21 +0000
279@@ -21,30 +21,12 @@
280 import Utils 0.1
281 import "../Components"
282
283-Rectangle {
284+AbstractStage {
285 id: root
286 objectName: "stages"
287 anchors.fill: parent
288- color: "#111111"
289-
290- // Controls to be set from outside
291- property int dragAreaWidth
292- property real maximizedAppTopMargin
293- property bool interactive
294- property alias beingResized: spreadView.beingResized
295-
296- property bool spreadEnabled: true // If false, animations and right edge will be disabled
297-
298- property real inverseProgress: 0 // This is the progress for left edge drags, in pixels.
299- property bool keepDashRunning: true
300- property bool suspended: false
301- property int shellOrientationAngle: 0
302-
303- property int shellOrientation
304- property QtObject orientations
305- property real nativeWidth
306- property real nativeHeight
307-
308+
309+ // Functions to be called from outside
310 function updateFocusedAppOrientation() {
311 var mainStageAppIndex = priv.indexOf(priv.mainStageAppId);
312 if (mainStageAppIndex >= 0 && mainStageAppIndex < spreadRepeater.count) {
313@@ -87,10 +69,7 @@
314 }
315 }
316
317- // To be read from outside
318- property var mainApp: null
319- property int mainAppWindowOrientationAngle: 0
320- readonly property bool orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
321+ orientationChangesEnabled: priv.mainAppOrientationChangesEnabled
322
323 onWidthChanged: {
324 spreadView.selectedIndex = -1;
325@@ -303,7 +282,7 @@
326 }
327
328 property bool animateX: true
329- property bool beingResized: false
330+ property bool beingResized: root.beingResized
331 onBeingResizedChanged: {
332 if (beingResized) {
333 // Brace yourselves for impact!
334@@ -630,15 +609,18 @@
335
336 readonly property bool isDash: model.appId == "unity8-dash"
337
338+ readonly property bool canSuspend: model.isTouchApp
339+ && !isExemptFromLifecycle(model.appId)
340+
341 Binding {
342 target: spreadTile.application
343 property: "requestedState"
344- value: (spreadTile.isDash && root.keepDashRunning)
345- ||
346- (!root.suspended && (model.appId == priv.mainStageAppId
347- || model.appId == priv.sideStageAppId))
348- ? ApplicationInfoInterface.RequestedRunning
349- : ApplicationInfoInterface.RequestedSuspended
350+ value: !canSuspend
351+ || (isDash && root.keepDashRunning)
352+ || (!root.suspended && (model.appId == priv.mainStageAppId
353+ || model.appId == priv.sideStageAppId))
354+ ? ApplicationInfoInterface.RequestedRunning
355+ : ApplicationInfoInterface.RequestedSuspended
356 }
357
358 // FIXME: A regular binding doesn't update any more after closing an app.
359
360=== added file 'qml/graphics/applicationIcons/libreoffice@18.png'
361Binary files qml/graphics/applicationIcons/libreoffice@18.png 1970-01-01 00:00:00 +0000 and qml/graphics/applicationIcons/libreoffice@18.png 2015-11-03 20:18:21 +0000 differ
362=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.cpp'
363--- tests/mocks/GSettings.1.0/fake_gsettings.cpp 2015-09-02 13:06:56 +0000
364+++ tests/mocks/GSettings.1.0/fake_gsettings.cpp 2015-11-03 20:18:21 +0000
365@@ -75,6 +75,19 @@
366 }
367 }
368
369+QStringList GSettingsControllerQml::lifecycleExemptAppids() const
370+{
371+ return m_lifecycleExemptAppids;
372+}
373+
374+void GSettingsControllerQml::setLifecycleExemptAppids(const QStringList &appIds)
375+{
376+ if (m_lifecycleExemptAppids != appIds) {
377+ m_lifecycleExemptAppids = appIds;
378+ Q_EMIT lifecycleExemptAppidsChanged(m_lifecycleExemptAppids);
379+ }
380+}
381+
382 GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
383 }
384
385@@ -114,6 +127,8 @@
386 this, &GSettingsQml::usageModeChanged);
387 connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::lockedOutTimeChanged,
388 this, &GSettingsQml::lockedOutTimeChanged);
389+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::lifecycleExemptAppidsChanged,
390+ this, &GSettingsQml::lifecycleExemptAppidsChanged);
391 }
392
393 GSettingsSchemaQml * GSettingsQml::schema() const {
394@@ -167,3 +182,19 @@
395 GSettingsControllerQml::instance()->setLockedOutTime(timestamp);
396 }
397 }
398+
399+QStringList GSettingsQml::lifecycleExemptAppids() const
400+{
401+ if (m_schema->id() == "com.canonical.qtmir") {
402+ return GSettingsControllerQml::instance()->lifecycleExemptAppids();
403+ } else {
404+ return QStringList();
405+ }
406+}
407+
408+void GSettingsQml::setLifecycleExemptAppids(const QStringList &appIds)
409+{
410+ if (m_schema->id() == "com.canonical.qtmir") {
411+ GSettingsControllerQml::instance()->setLifecycleExemptAppids(appIds);
412+ }
413+}
414
415=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.h'
416--- tests/mocks/GSettings.1.0/fake_gsettings.h 2015-08-19 23:41:18 +0000
417+++ tests/mocks/GSettings.1.0/fake_gsettings.h 2015-11-03 20:18:21 +0000
418@@ -19,6 +19,7 @@
419
420 #include <QList>
421 #include <QObject>
422+#include <QStringList>
423
424 class GSettingsSchemaQml: public QObject
425 {
426@@ -48,6 +49,7 @@
427 Q_PROPERTY(QString pictureUri READ pictureUri WRITE setPictureUri NOTIFY pictureUriChanged)
428 Q_PROPERTY(QString usageMode READ usageMode WRITE setUsageMode NOTIFY usageModeChanged)
429 Q_PROPERTY(qint64 lockedOutTime READ lockedOutTime WRITE setLockedOutTime NOTIFY lockedOutTimeChanged)
430+ Q_PROPERTY(QStringList lifecycleExemptAppids READ lifecycleExemptAppids WRITE setLifecycleExemptAppids NOTIFY lifecycleExemptAppidsChanged)
431
432 public:
433 GSettingsQml(QObject *parent = nullptr);
434@@ -56,16 +58,19 @@
435 QString pictureUri() const;
436 QString usageMode() const;
437 qint64 lockedOutTime() const;
438+ QStringList lifecycleExemptAppids() const;
439
440 void setPictureUri(const QString &str);
441 void setUsageMode(const QString &usageMode);
442 void setLockedOutTime(qint64 timestamp);
443+ void setLifecycleExemptAppids(const QStringList &appIds);
444
445 Q_SIGNALS:
446 void schemaChanged();
447 void pictureUriChanged(const QString&);
448 void usageModeChanged(const QString&);
449 void lockedOutTimeChanged(qint64);
450+ void lifecycleExemptAppidsChanged(const QStringList &);
451
452 private:
453 GSettingsSchemaQml* m_schema;
454@@ -90,10 +95,14 @@
455 qint64 lockedOutTime() const;
456 Q_INVOKABLE void setLockedOutTime(qint64 timestamp);
457
458+ QStringList lifecycleExemptAppids() const;
459+ Q_INVOKABLE void setLifecycleExemptAppids(const QStringList &appIds);
460+
461 Q_SIGNALS:
462 void pictureUriChanged(const QString&);
463 void usageModeChanged(const QString&);
464 void lockedOutTimeChanged(qint64 timestamp);
465+ void lifecycleExemptAppidsChanged(const QStringList&);
466
467 private:
468 GSettingsControllerQml();
469@@ -101,6 +110,7 @@
470 QString m_pictureUri;
471 QString m_usageMode;
472 qint64 m_lockedOutTime;
473+ QStringList m_lifecycleExemptAppids;
474
475 static GSettingsControllerQml* s_controllerInstance;
476 QList<GSettingsQml *> m_registeredGSettings;
477
478=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.cpp'
479--- tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-08-31 10:25:07 +0000
480+++ tests/mocks/Unity/Application/ApplicationInfo.cpp 2015-11-03 20:18:21 +0000
481@@ -41,6 +41,7 @@
482 Qt::InvertedLandscapeOrientation)
483 , m_rotatesWindowContents(false)
484 , m_requestedState(RequestedRunning)
485+ , m_isTouchApp(true)
486 , m_manualSurfaceCreation(false)
487 {
488 }
489@@ -58,6 +59,7 @@
490 Qt::InvertedLandscapeOrientation)
491 , m_rotatesWindowContents(false)
492 , m_requestedState(RequestedRunning)
493+ , m_isTouchApp(true)
494 , m_manualSurfaceCreation(false)
495 {
496 }
497@@ -245,6 +247,16 @@
498 }
499 }
500
501+bool ApplicationInfo::isTouchApp() const
502+{
503+ return m_isTouchApp;
504+}
505+
506+void ApplicationInfo::setIsTouchApp(bool isTouchApp)
507+{
508+ m_isTouchApp = isTouchApp;
509+}
510+
511 void ApplicationInfo::onSessionSurfaceChanged(MirSurface* surface)
512 {
513 if (surface != nullptr && m_state == Starting) {
514
515=== modified file 'tests/mocks/Unity/Application/ApplicationInfo.h'
516--- tests/mocks/Unity/Application/ApplicationInfo.h 2015-08-03 15:00:47 +0000
517+++ tests/mocks/Unity/Application/ApplicationInfo.h 2015-11-03 20:18:21 +0000
518@@ -89,6 +89,9 @@
519 bool manualSurfaceCreation() const { return m_manualSurfaceCreation; }
520 void setManualSurfaceCreation(bool value);
521
522+ bool isTouchApp() const override;
523+ void setIsTouchApp(bool isTouchApp); // only in mock
524+
525 public:
526 void setSession(Session* session);
527 Session* session() const { return m_session; }
528@@ -121,6 +124,7 @@
529 Qt::ScreenOrientations m_supportedOrientations;
530 bool m_rotatesWindowContents;
531 RequestedState m_requestedState;
532+ bool m_isTouchApp;
533
534 bool m_manualSurfaceCreation;
535 };
536
537=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
538--- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-08-03 13:47:44 +0000
539+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2015-11-03 20:18:21 +0000
540@@ -105,6 +105,8 @@
541 return app->state();
542 case RoleFocused:
543 return app->focused();
544+ case RoleIsTouchApp:
545+ return app->isTouchApp();
546 case RoleSession:
547 return QVariant::fromValue(app->session());
548 case RoleFullscreen:
549@@ -458,6 +460,13 @@
550 application->setName("YouTube");
551 application->setIconId("youtube");
552 m_availableApplications.append(application);
553+
554+ application = new ApplicationInfo(this);
555+ application->setAppId("libreoffice");
556+ application->setName("LibreOffice");
557+ application->setIconId("libreoffice");
558+ application->setIsTouchApp(false);
559+ m_availableApplications.append(application);
560 }
561
562
563
564=== modified file 'tests/mocks/Unity/Application/ApplicationManager.h'
565--- tests/mocks/Unity/Application/ApplicationManager.h 2015-06-19 18:19:33 +0000
566+++ tests/mocks/Unity/Application/ApplicationManager.h 2015-11-03 20:18:21 +0000
567@@ -43,7 +43,7 @@
568 static ApplicationManager *singleton();
569
570 enum MoreRoles {
571- RoleSession = RoleFocused+1,
572+ RoleSession = RoleIsTouchApp+1,
573 RoleFullscreen,
574 };
575 enum Flag {
576
577=== modified file 'tests/plugins/Unity/Launcher/launchermodeltest.cpp'
578--- tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-08-19 14:24:07 +0000
579+++ tests/plugins/Unity/Launcher/launchermodeltest.cpp 2015-11-03 20:18:21 +0000
580@@ -57,6 +57,7 @@
581 QColor splashColorFooter() const override { return QColor(0,0,0,0); }
582 Qt::ScreenOrientations supportedOrientations() const override { return Qt::PortraitOrientation; }
583 bool rotatesWindowContents() const override { return false; }
584+ bool isTouchApp() const override { return true; }
585
586 // Methods used for mocking (not in the interface)
587 void setFocused(bool focused) { m_focused = focused; Q_EMIT focusedChanged(focused); }
588
589=== modified file 'tests/qmltests/tst_Shell.qml'
590--- tests/qmltests/tst_Shell.qml 2015-11-03 20:18:21 +0000
591+++ tests/qmltests/tst_Shell.qml 2015-11-03 20:18:21 +0000
592@@ -380,6 +380,8 @@
593 LightDM.Greeter.authenticate(""); // reset greeter
594
595 sessionSpy.clear();
596+
597+ GSettingsController.setLifecycleExemptAppids([]);
598 }
599
600 function killApps() {
601@@ -1780,5 +1782,50 @@
602 waitForRendering(shell);
603 tryCompare(panelButtons, "visible", true);
604 }
605+
606+ function test_lifecyclePolicyForNonTouchApp_data() {
607+ return [
608+ {tag: "phone", formFactor: "phone", usageScenario: "phone"},
609+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet"}
610+ ]
611+ }
612+
613+ function test_lifecyclePolicyForNonTouchApp(data) {
614+ loadShell(data.formFactor);
615+ shell.usageScenario = data.usageScenario;
616+
617+ var app1 = ApplicationManager.startApplication("libreoffice");
618+ waitUntilAppWindowIsFullyLoaded(app1);
619+ var app2 = ApplicationManager.startApplication("dialer-app");
620+ waitUntilAppWindowIsFullyLoaded(app2);
621+
622+ // Make sure app1 is unfocused but still running
623+ compare(app1.session.surface.activeFocus, false);
624+ compare(app1.isTouchApp, false); // sanity check our mock, which sets this for us
625+ compare(app1.requestedState, ApplicationInfoInterface.RequestedRunning);
626+ }
627+
628+ function test_lifecyclePolicyExemption_data() {
629+ return [
630+ {tag: "phone", formFactor: "phone", usageScenario: "phone", suspendsApps: true},
631+ {tag: "tablet", formFactor: "tablet", usageScenario: "tablet", suspendsApps: true}
632+ ]
633+ }
634+
635+ function test_lifecyclePolicyExemption(data) {
636+ loadShell(data.formFactor);
637+ shell.usageScenario = data.usageScenario;
638+
639+ GSettingsController.setLifecycleExemptAppids(["webbrowser-app"]);
640+
641+ var app1 = ApplicationManager.startApplication("webbrowser-app");
642+ waitUntilAppWindowIsFullyLoaded(app1);
643+ var app2 = ApplicationManager.startApplication("dialer-app");
644+ waitUntilAppWindowIsFullyLoaded(app2);
645+
646+ // Make sure app1 is unfocused but still running
647+ compare(app1.session.surface.activeFocus, false);
648+ compare(app1.requestedState, ApplicationInfoInterface.RequestedRunning);
649+ }
650 }
651 }

Subscribers

People subscribed via source and target branches