on xenial help sign in
Delay tutorials if a notification or dialog is present
4a2274eMichael Terry
qml/Components/Dialogs.qml+2
@@ -27,6 +27,8 @@ import "../Greeter"
27Item {
27Item {
28 id: root
28 id: root
29
29
30+ readonly property alias hasActiveDialog: dialogLoader.active
31+
30 // to be set from outside, useful mostly for testing purposes
32 // to be set from outside, useful mostly for testing purposes
31 property var unitySessionService: DBusUnitySessionService
33 property var unitySessionService: DBusUnitySessionService
32 property var closeAllApps: function() {
34 property var closeAllApps: function() {
qml/Shell.qml+2−1
@@ -617,7 +617,8 @@ Item {
617 anchors.fill: parent
617 anchors.fill: parent
618
618
619 paused: callManager.hasCalls || greeter.shown
619 paused: callManager.hasCalls || greeter.shown
620+ delayed: dialogs.hasActiveDialog || notifications.count > 0 ||
620− keyboardVisible: inputMethod.state === "shown"
621+ inputMethod.state === "shown"
621 usageScenario: shell.usageScenario
622 usageScenario: shell.usageScenario
622 lastInputTimestamp: inputFilter.lastInputTimestamp
623 lastInputTimestamp: inputFilter.lastInputTimestamp
623 launcher: launcher
624 launcher: launcher
qml/Tutorial/Tutorial.qml+4−4
@@ -32,8 +32,8 @@ Item {
32 property Item panel
32 property Item panel
33 property Item stage
33 property Item stage
34 property string usageScenario
34 property string usageScenario
35− property bool paused
35+ property bool paused // hide any existing tutorial and don't show new ones
36− property bool keyboardVisible
36+ property bool delayed // don't show new tutorials
37 property int lastInputTimestamp
37 property int lastInputTimestamp
38
38
39 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
39 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
@@ -86,8 +86,8 @@ Item {
86
86
87 Binding {
87 Binding {
88 target: loader.item
88 target: loader.item
89− property: "keyboardVisible"
89+ property: "delayed"
90− value: root.keyboardVisible
90+ value: root.delayed
91 }
91 }
92
92
93 Binding {
93 Binding {
qml/Tutorial/TutorialContent.qml+5−5
@@ -27,7 +27,7 @@ Item {
27 property Item stage
27 property Item stage
28 property string usageScenario
28 property string usageScenario
29 property bool paused
29 property bool paused
30− property bool keyboardVisible
30+ property bool delayed
31 property var lastInputTimestamp
31 property var lastInputTimestamp
32
32
33 readonly property bool launcherEnabled: !running
33 readonly property bool launcherEnabled: !running
@@ -85,7 +85,7 @@ Item {
85 hides: [launcher, panel.indicators]
85 hides: [launcher, panel.indicators]
86 paused: root.paused
86 paused: root.paused
87
87
88− isReady: !tutorialLeftLoader.skipped && !paused && !keyboardVisible
88+ isReady: !tutorialLeftLoader.skipped && !paused && !delayed
89
89
90 InactivityTimer {
90 InactivityTimer {
91 id: tutorialLeftTimer
91 id: tutorialLeftTimer
@@ -119,7 +119,7 @@ Item {
119 paused: root.paused
119 paused: root.paused
120
120
121 skipped: tutorialTopLoader.skipped
121 skipped: tutorialTopLoader.skipped
122− isReady: tutorialLeftLoader.skipped && !skipped && !paused && !keyboardVisible
122+ isReady: tutorialLeftLoader.skipped && !skipped && !paused && !delayed
123
123
124 InactivityTimer {
124 InactivityTimer {
125 id: tutorialTopTimer
125 id: tutorialTopTimer
@@ -153,7 +153,7 @@ Item {
153 paused: root.paused
153 paused: root.paused
154
154
155 skipped: tutorialLeftLongLoader.skipped
155 skipped: tutorialLeftLongLoader.skipped
156− isReady: tutorialTopLoader.skipped && !skipped && !paused && !keyboardVisible &&
156+ isReady: tutorialTopLoader.skipped && !skipped && !paused && !delayed &&
157 ApplicationManager.focusedApplicationId != "unity8-dash"
157 ApplicationManager.focusedApplicationId != "unity8-dash"
158
158
159 InactivityTimer {
159 InactivityTimer {
@@ -189,7 +189,7 @@ Item {
189 paused: root.paused
189 paused: root.paused
190
190
191 skipped: tutorialRightLoader.skipped
191 skipped: tutorialRightLoader.skipped
192− isReady: tutorialTopLoader.skipped && !skipped && !paused && !keyboardVisible &&
192+ isReady: tutorialTopLoader.skipped && !skipped && !paused && !delayed &&
193 ApplicationManager.count >= 4
193 ApplicationManager.count >= 4
194
194
195 InactivityTimer {
195 InactivityTimer {
tests/qmltests/Tutorial/tst_Tutorial.qml+77−13
@@ -22,6 +22,7 @@ import Ubuntu.Components 1.3
22import Ubuntu.Components.ListItems 1.3
22import Ubuntu.Components.ListItems 1.3
23import Ubuntu.Telephony 0.1 as Telephony
23import Ubuntu.Telephony 0.1 as Telephony
24import Unity.Application 0.1
24import Unity.Application 0.1
25+import Unity.Notifications 1.0
25import Unity.Test 0.1 as UT
26import Unity.Test 0.1 as UT
26import Utils 0.1
27import Utils 0.1
27
28
@@ -55,6 +56,33 @@ Rectangle {
55 phoneNumber: "+447812221111"
56 phoneNumber: "+447812221111"
56 }
57 }
57
58
59+ Component {
60+ id: mockNotification
61+ QtObject {}
62+ }
63+
64+ ListModel {
65+ id: mockNotificationsModel
66+
67+ function getRaw(id) {
68+ return mockNotification.createObject(mockNotificationsModel)
69+ }
70+ }
71+
72+ function addNotification() {
73+ var n = {
74+ type: Notification.Confirmation,
75+ hints: {},
76+ summary: "",
77+ body: "",
78+ icon: "",
79+ secondaryIcon: "",
80+ actions: []
81+ };
82+
83+ mockNotificationsModel.append(n);
84+ }
85+
58 Component.onCompleted: {
86 Component.onCompleted: {
59 // must set the mock mode before loading the Shell
87 // must set the mock mode before loading the Shell
60 LightDM.Greeter.mockMode = "single-pin";
88 LightDM.Greeter.mockMode = "single-pin";
@@ -262,6 +290,7 @@ Rectangle {
262 // reload our test subject to get it in a fresh state once again
290 // reload our test subject to get it in a fresh state once again
263 shellLoader.active = true;
291 shellLoader.active = true;
264
292
293+ mockNotificationsModel.clear();
265 tryCompare(shellLoader, "status", Loader.Ready);
294 tryCompare(shellLoader, "status", Loader.Ready);
266 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
295 removeTimeConstraintsFromDirectionalDragAreas(shellLoader.item);
267 }
296 }
@@ -690,24 +719,59 @@ Rectangle {
690 verify(tutorialLeft.shown);
719 verify(tutorialLeft.shown);
691 }
720 }
692
721
693− function test_oskPreventsTutorial() {
722+ function test_oskDelaysTutorial() {
694− var surface = SurfaceManager.inputMethodSurface;
723+ var tutorial = findChild(shell, "tutorial");
695− var inputMethod = findInvisibleChild(shell, "inputMethod");
724+ verify(!tutorial.delayed);
696
725
697− AccountsService.demoEdges = false;
698− surface.setState(Mir.RestoredState);
726+ SurfaceManager.inputMethodSurface.setState(Mir.RestoredState);
699− tryCompare(inputMethod, "state", "shown");
700
727
728+ tryCompare(tutorial, "delayed", true);
729+ }
730+
731+ function test_notificationDelaysTutorial() {
701 var tutorial = findChild(shell, "tutorial");
732 var tutorial = findChild(shell, "tutorial");
702− tryCompare(tutorial, "keyboardVisible", true);
733+ verify(!tutorial.delayed);
703
734
704− AccountsService.demoEdges = true;
735+ var notifications = findChild(shell, "notificationList");
705− var tutorialLeft = findChild(shell, "tutorialLeft");
736+ notifications.model = mockNotificationsModel;
706− verify(!tutorialLeft.shown);
737+ addNotification();
707
738
708− surface.setState(Mir.MinimizedState);
739+ tryCompare(tutorial, "delayed", true);
709− tryCompare(inputMethod, "state", "hidden");
740+ }
710− tryCompare(tutorialLeft, "shown", false);
741+
742+ function test_dialogDelaysTutorial() {
743+ var tutorial = findChild(shell, "tutorial");
744+ verify(!tutorial.delayed);
745+
746+ var dialogs = findChild(shell, "dialogs");
747+ dialogs.showPowerDialog();
748+
749+ verify(tutorial.delayed);
750+ }
751+
752+ function test_delayedTutorial() {
753+ var tutorial = findChild(shell, "tutorial");
754+ var tutorialTop = findChild(tutorial, "tutorialTop");
755+ var tutorialTopTimer = findChild(tutorialTop, "tutorialTopTimer");
756+
757+ // Get top tutorial ready
758+ AccountsService.demoEdgesCompleted = ["left"];
759+ verify(tutorialTop.isReady);
760+ verify(tutorialTopTimer.running);
761+
762+ // Confirm that we become unready when delayed, but timer still goes
763+ tutorial.delayed = true;
764+ verify(!tutorialTop.isReady);
765+ verify(tutorialTopTimer.running);
766+
767+ // Confirm that we don't open the tutorial when delayed
768+ tutorialTopTimer.interval = 1;
769+ wait(5);
770+ verify(!tutorialTop.shown);
771+
772+ // Confirm we go back to normal when undelayed
773+ tutorial.delayed = false;
774+ tryCompare(tutorialTop, "shown", true);
711 }
775 }
712
776
713 function test_accountsServiceSettings() {
777 function test_accountsServiceSettings() {