on xenial help sign in
Handle incoming calls, the greeter, and the OSK
commit5e2b709a4c500ff4359d8ce133963c7d7bc4be3e authorMichael Terry <michael.terry@canonical.com> dateTue 20 Oct 2015, 17:27 (11 years ago) parentda65b64
7 files changed+264−36
qml/Shell.qml+4−2 qml/Tutorial/Tutorial.qml+14 qml/Tutorial/TutorialContent.qml+25−18 qml/Tutorial/TutorialPage.qml+23−1 qml/Tutorial/TutorialRight.qml+1 tests/mocks/Unity/Application/SurfaceManager.cpp+1 tests/qmltests/Tutorial/tst_Tutorial.qml+196−15
qml/Shell.qml+4−2
@@ -376,7 +376,7 @@ Item {
376376 id: inputMethod
377377 objectName: "inputMethod"
378378 anchors { fill: parent; topMargin: panel.panelHeight }
379− z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
379+ z: notifications.useModal || panel.indicators.shown || wizard.active || tutorial.running ? overlay.z + 1 : overlay.z - 1
380380 }
381381
382382 Connections {
@@ -407,7 +407,7 @@ Item {
407407 hides: [launcher, panel.indicators]
408408 tabletMode: shell.usageScenario != "phone"
409409 launcherOffset: launcher.progress
410− forcedUnlock: wizard.active || tutorial.running
410+ forcedUnlock: wizard.active
411411 background: shell.background
412412
413413 // avoid overlapping with Launcher's edge drag area
@@ -570,6 +570,8 @@ Item {
570570 objectName: "tutorial"
571571 anchors.fill: parent
572572
573+ paused: callManager.hasCalls || greeter.shown
574+ keyboardVisible: inputMethod.state === "shown"
573575 usageScenario: shell.usageScenario
574576 launcher: launcher
575577 panel: panel
qml/Tutorial/Tutorial.qml+14
@@ -32,6 +32,8 @@ Item {
3232 property Item panel
3333 property Item stage
3434 property string usageScenario
35+ property bool paused
36+ property bool keyboardVisible
3537
3638 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
3739 readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
@@ -74,6 +76,18 @@ Item {
7476 value: root.usageScenario
7577 }
7678
79+ Binding {
80+ target: loader.item
81+ property: "paused"
82+ value: root.paused
83+ }
84+
85+ Binding {
86+ target: loader.item
87+ property: "keyboardVisible"
88+ value: root.keyboardVisible
89+ }
90+
7791 Connections {
7892 target: loader.item
7993 onFinished: AccountsService.demoEdges = false
qml/Tutorial/TutorialContent.qml+25−18
@@ -26,6 +26,8 @@ Item {
2626 property Item panel
2727 property Item stage
2828 property string usageScenario
29+ property bool paused
30+ property bool keyboardVisible
2931
3032 readonly property bool launcherEnabled: !running || tutorialLeft.shown
3133 readonly property bool spreadEnabled: !running || tutorialRight.shown
@@ -49,14 +51,15 @@ Item {
4951 anchors.fill: parent
5052 launcher: root.launcher
5153 hides: [launcher, panel.indicators]
54+ paused: root.paused
5255
5356 readonly property bool skipped: (root.usageScenario !== "phone"
5457 && root.usageScenario !== "tablet")
5558 || AccountsService.demoEdgesCompleted.indexOf("left") != -1
56− readonly property bool isReady: !skipped
59+ isReady: !skipped && !paused && !keyboardVisible
5760
5861 onSkippedChanged: if (skipped && shown) hide()
59− onIsReadyChanged: if (isReady) show()
62+ onIsReadyChanged: if (isReady && !shown) show()
6063 onFinished: AccountsService.markDemoEdgeCompleted("left")
6164 }
6265
@@ -66,11 +69,12 @@ Item {
6669 anchors.fill: parent
6770 panel: root.panel
6871 hides: [launcher, panel.indicators]
72+ paused: root.paused
6973
7074 readonly property bool skipped: (root.usageScenario !== "phone"
7175 && root.usageScenario !== "tablet")
7276 || AccountsService.demoEdgesCompleted.indexOf("top") != -1
73− readonly property bool isReady: tutorialLeft.skipped && !skipped
77+ isReady: tutorialLeft.skipped && !skipped && !paused && !keyboardVisible
7478
7579 Timer {
7680 id: tutorialTopTimer
@@ -85,7 +89,7 @@ Item {
8589 }
8690
8791 onSkippedChanged: if (skipped && shown) hide()
88− onIsReadyChanged: if (isReady) tutorialTopTimer.start()
92+ onIsReadyChanged: if (isReady && !shown) tutorialTopTimer.start()
8993 onFinished: AccountsService.markDemoEdgeCompleted("top")
9094 }
9195
@@ -96,10 +100,10 @@ Item {
96100 stage: root.stage
97101 usageScenario: root.usageScenario
98102 hides: [launcher, panel.indicators]
103+ paused: root.paused
99104
100105 readonly property bool skipped: AccountsService.demoEdgesCompleted.indexOf("right") != -1
101− readonly property bool isReady: tutorialTop.skipped && !skipped
102− && ApplicationManager.count >= 3
106+ isReady: tutorialTop.skipped && !skipped && !paused && !keyboardVisible && ApplicationManager.count >= 3
103107
104108 Timer {
105109 id: tutorialRightTimer
@@ -114,7 +118,7 @@ Item {
114118 }
115119
116120 onSkippedChanged: if (skipped && shown) hide()
117− onIsReadyChanged: if (isReady) tutorialRightTimer.start()
121+ onIsReadyChanged: if (isReady && !shown) tutorialRightTimer.start()
118122 onFinished: AccountsService.markDemoEdgeCompleted("right")
119123 }
120124
@@ -123,17 +127,20 @@ Item {
123127 objectName: "tutorialBottom"
124128 anchors.fill: parent
125129 hides: [launcher, panel.indicators]
130+ paused: root.paused
126131
127− readonly property bool skipped: AccountsService.demoEdgesCompleted.indexOf("bottom") != -1
128− readonly property bool isReady: tutorialRight.skipped && !skipped &&
129− // focused app is an app known to have a bottom edge
130− (ApplicationManager.focusedApplicationId == "dialer-app" ||
131− ApplicationManager.focusedApplicationId == "webbrowser-app" ||
132− ApplicationManager.focusedApplicationId == "messaging-app" ||
133− ApplicationManager.focusedApplicationId == "address-book-app" ||
134− ApplicationManager.focusedApplicationId == "camera-app" ||
135− ApplicationManager.focusedApplicationId == "calculator-app" ||
136− ApplicationManager.focusedApplicationId == "ubuntu-clock-app")
132+ readonly property bool skipped: (root.usageScenario !== "phone"
133+ && root.usageScenario !== "tablet")
134+ || AccountsService.demoEdgesCompleted.indexOf("bottom") != -1
135+ isReady: tutorialRight.skipped && !skipped && !paused && !keyboardVisible &&
136+ // focused app is an app known to have a bottom edge
137+ (ApplicationManager.focusedApplicationId == "dialer-app" ||
138+ ApplicationManager.focusedApplicationId == "webbrowser-app" ||
139+ ApplicationManager.focusedApplicationId == "messaging-app" ||
140+ ApplicationManager.focusedApplicationId == "address-book-app" ||
141+ ApplicationManager.focusedApplicationId == "camera-app" ||
142+ ApplicationManager.focusedApplicationId == "ubuntu-calculator-app" ||
143+ ApplicationManager.focusedApplicationId == "ubuntu-clock-app")
137144
138145 Timer {
139146 id: tutorialBottomTimer
@@ -148,7 +155,7 @@ Item {
148155 }
149156
150157 onSkippedChanged: if (skipped && shown) hide()
151− onIsReadyChanged: if (isReady) tutorialBottomTimer.start()
158+ onIsReadyChanged: if (isReady && !shown) tutorialBottomTimer.start()
152159 onFinished: {
153160 AccountsService.markDemoEdgeCompleted("bottom");
154161 root.finish();
qml/Tutorial/TutorialPage.qml+23−1
@@ -26,11 +26,18 @@ Showable {
2626 property alias background: background
2727 property alias mouseArea: mouseArea
2828 property real opacityOverride: 1
29+ property bool paused
30+ property bool isReady
2931
3032 signal finished()
3133
3234 ////
3335
36+ QtObject {
37+ id: d
38+ property bool showOnUnpause
39+ }
40+
3441 visible: false
3542 shown: false
3643
@@ -38,6 +45,17 @@ Showable {
3845 onOpacityOverrideChanged: if (opacityOverride <= 0) hide()
3946 property real _showOpacity: 0
4047
48+ onPausedChanged: {
49+ if (paused && shown) {
50+ hide();
51+ } else if (!paused && d.showOnUnpause) {
52+ d.showOnUnpause = false;
53+ if (isReady) {
54+ show();
55+ }
56+ }
57+ }
58+
4159 showAnimation: StandardAnimation {
4260 property: "_showOpacity"
4361 from: 0
@@ -52,7 +70,11 @@ Showable {
5270 duration: UbuntuAnimation.BriskDuration
5371 onStopped: {
5472 root.visible = false;
55− root.finished();
73+ if (root.paused) {
74+ d.showOnUnpause = true;
75+ } else {
76+ root.finished();
77+ }
5678 }
5779 }
5880
qml/Tutorial/TutorialRight.qml+1
@@ -30,6 +30,7 @@ TutorialPage {
3030 // Else on desktop, fade out when the spread is shown
3131 Connections {
3232 target: usageScenario === "desktop" ? stage : null
33+ ignoreUnknownSignals: true
3334 onSpreadShownChanged: if (stage.spreadShown && root.shown) root.hide()
3435 }
3536
tests/mocks/Unity/Application/SurfaceManager.cpp+1
@@ -56,6 +56,7 @@ MirSurface *SurfaceManager::inputMethodSurface()
5656 m_virtualKeyboard = new VirtualKeyboard;
5757 connect(m_virtualKeyboard, &QObject::destroyed, this, [this](QObject *obj) {
5858 MirSurface* surface = qobject_cast<MirSurface*>(obj);
59+ m_virtualKeyboard = nullptr;
5960 Q_EMIT surfaceDestroyed(surface);
6061 });
6162 Q_EMIT surfaceCreated(m_virtualKeyboard);
tests/qmltests/Tutorial/tst_Tutorial.qml+196−15
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import AccountsService 0.1
2020import IntegratedLightDM 0.1 as LightDM
2121import Ubuntu.Components 1.1
22+import Ubuntu.Telephony 0.1 as Telephony
2223import Unity.Application 0.1
2324import Unity.Test 0.1 as UT
2425
@@ -45,6 +46,11 @@ Item {
4546 }
4647 }
4748
49+ Telephony.CallEntry {
50+ id: phoneCall
51+ phoneNumber: "+447812221111"
52+ }
53+
4854 Component.onCompleted: {
4955 // must set the mock mode before loading the Shell
5056 LightDM.Greeter.mockMode = "single-pin";
@@ -77,13 +83,29 @@ Item {
7783
7884 Rectangle {
7985 id: buttons
80− color: "white"
86+ color: "black"
8187 width: units.gu(30)
8288 height: shellLoader.height
8389
8490 Column {
8591 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
8692 spacing: units.gu(1)
93+ Row {
94+ anchors { left: parent.left; right: parent.right }
95+ Button {
96+ text: "Hide Greeter"
97+ onClicked: {
98+ if (shellLoader.status !== Loader.Ready)
99+ return;
100+
101+ var greeter = testCase.findChild(shellLoader.item, "greeter");
102+ if (greeter.shown) {
103+ greeter.hide();
104+ }
105+ }
106+ }
107+ }
108+
87109 Row {
88110 anchors { left: parent.left; right: parent.right }
89111 Button {
@@ -98,6 +120,62 @@ Item {
98120 }
99121 }
100122 }
123+
124+ Row {
125+ anchors { left: parent.left; right: parent.right }
126+ CheckBox {
127+ onCheckedChanged: {
128+ if (checked) {
129+ callManager.foregroundCall = phoneCall;
130+ } else {
131+ callManager.foregroundCall = null;
132+ }
133+ }
134+ }
135+ Label {
136+ text: "Active Call"
137+ anchors.verticalCenter: parent.verticalCenter
138+ }
139+ }
140+
141+ Row {
142+ anchors { left: parent.left; right: parent.right }
143+ CheckBox {
144+ activeFocusOnPress: false
145+ onCheckedChanged: {
146+ var surface = SurfaceManager.inputMethodSurface();
147+ if (checked) {
148+ surface.setState(Mir.RestoredState);
149+ } else {
150+ surface.setState(Mir.MinimizedState);
151+ }
152+ }
153+ }
154+ Label {
155+ text: "Input Method"
156+ anchors.verticalCenter: parent.verticalCenter
157+ }
158+ }
159+
160+ Row {
161+ anchors { left: parent.left; right: parent.right }
162+ CheckBox {
163+ onCheckedChanged: {
164+ if (shellLoader.status !== Loader.Ready)
165+ return;
166+
167+ if (checked) {
168+ shellLoader.item.usageScenario = "desktop";
169+ } else {
170+ shellLoader.item.usageScenario = "phone";
171+ }
172+ }
173+ }
174+ Label {
175+ text: "Desktop Mode"
176+ anchors.verticalCenter: parent.verticalCenter
177+ }
178+ }
101179 }
102180 }
103181 }
@@ -114,14 +192,16 @@ Item {
114192 function init() {
115193 tryCompare(shell, "enabled", true); // enabled by greeter when ready
116194
117− var greeter = findChild(shell, "greeter");
118− tryCompare(greeter, "shown", true);
119−
195+ shell.usageScenario = "phone";
196+ callManager.foregroundCall = null;
120197 AccountsService.demoEdges = false;
121198 AccountsService.demoEdgesCompleted = [];
122199 AccountsService.demoEdges = true;
123200
124− tryCompare(greeter, "shown", false);
201+ LightDM.Greeter.hideGreeter();
202+
203+ var tutorialLeft = findChild(shell, "tutorialLeft");
204+ tryCompare(tutorialLeft, "opacity", 1);
125205 }
126206
127207 function cleanup() {
@@ -485,20 +565,121 @@ Item {
485565 tryCompare(tutorialBottom, "shown", true);
486566 }
487567
488− function disabled_test_skipOnDesktop() {
489− var tutorial = findChild(shell, "tutorial");
490− tryCompare(tutorial, "active", true);
491− tryCompare(tutorial, "running", true);
568+ function test_activeCallInterruptsTutorial() {
569+ var tutorialLeft = findChild(shell, "tutorialLeft");
570+ verify(tutorialLeft.shown);
571+ verify(!tutorialLeft.paused);
492572
493− shell.usageScenario = "desktop";
494− tryCompare(tutorial, "active", false);
495− tryCompare(tutorial, "running", false);
573+ callManager.foregroundCall = phoneCall;
574+ verify(!tutorialLeft.shown);
575+ verify(tutorialLeft.paused);
576+ tryCompare(tutorialLeft, "visible", false);
577+
578+ callManager.foregroundCall = null;
579+ verify(tutorialLeft.shown);
580+ verify(!tutorialLeft.paused);
581+ }
582+
583+ function test_greeterInterruptsTutorial() {
584+ var tutorialLeft = findChild(shell, "tutorialLeft");
585+ verify(tutorialLeft.shown);
586+ verify(!tutorialLeft.paused);
587+
588+ LightDM.Greeter.showGreeter();
589+ verify(!tutorialLeft.shown);
590+ verify(tutorialLeft.paused);
591+ tryCompare(tutorialLeft, "visible", false);
592+
593+ LightDM.Greeter.hideGreeter();
594+ tryCompare(tutorialLeft, "shown", true);
595+ verify(!tutorialLeft.paused);
596+ }
597+
598+ function test_interruptionChecksReadyStateWhenDone() {
599+ // If we're done with an interruption (like active call), make sure
600+ // that we don't blindly resume the tutorial -- our trigger
601+ // conditions still need to be met. For example, there need to be
602+ // enough apps open for the right edge tutorial.
603+
604+ openTutorialRight();
605+
606+ var tutorialRight = findChild(shell, "tutorialRight");
607+ verify(tutorialRight.isReady);
608+ verify(tutorialRight.shown);
609+ verify(!tutorialRight.paused);
610+
611+ callManager.foregroundCall = phoneCall;
612+ killApps();
613+ callManager.foregroundCall = null;
614+
615+ verify(!tutorialRight.isReady);
616+ verify(!tutorialRight.shown);
617+ verify(!tutorialRight.paused);
618+ compare(AccountsService.demoEdgesCompleted, ["left", "top"]);
496619 }
497620
498− function disabled_test_interrupted() {
499− goToPage("tutorialLeft");
621+ function test_desktopOnlyShowsTutorialRight() {
622+ shell.usageScenario = "desktop";
623+
624+ var tutorialLeft = findChild(shell, "tutorialLeft");
625+ var tutorialTop = findChild(shell, "tutorialTop");
626+ var tutorialRight = findChild(shell, "tutorialRight");
627+ var tutorialBottom = findChild(shell, "tutorialBottom");
628+ verify(tutorialLeft.skipped);
629+ verify(tutorialTop.skipped);
630+ verify(!tutorialRight.skipped);
631+ verify(tutorialBottom.skipped);
632+ compare(AccountsService.demoEdgesCompleted, []);
633+
500634 ApplicationManager.startApplication("dialer-app");
501− checkFinished();
635+ ApplicationManager.startApplication("camera-app");
636+ ApplicationManager.startApplication("messaging-app");
637+ tryCompare(tutorialRight, "isReady", true);
638+ tryCompare(tutorialRight, "shown", true);
639+ }
640+
641+ function test_oskDoesNotHideTutorial() {
642+ var tutorialLeft = findChild(shell, "tutorialLeft");
643+ verify(tutorialLeft.shown);
644+
645+ var surface = SurfaceManager.inputMethodSurface();
646+ surface.setState(Mir.RestoredState);
647+
648+ var inputMethod = findInvisibleChild(shell, "inputMethod");
649+ tryCompare(inputMethod, "state", "shown");
650+
651+ verify(tutorialLeft.shown);
652+ }
653+
654+ function test_oskPreventsTutorial() {
655+ var surface = SurfaceManager.inputMethodSurface();
656+ var inputMethod = findInvisibleChild(shell, "inputMethod");
657+
658+ AccountsService.demoEdges = false;
659+ surface.setState(Mir.RestoredState);
660+ tryCompare(inputMethod, "state", "shown");
661+
662+ AccountsService.demoEdges = true;
663+ var tutorialLeft = findChild(shell, "tutorialLeft");
664+ verify(!tutorialLeft.shown);
665+
666+ surface.setState(Mir.MinimizedState);
667+ tryCompare(inputMethod, "state", "hidden");
668+ verify(tutorialLeft.shown);
669+ }
670+
671+ function test_accountsServiceSettings() {
672+ var tutorialLeft = findChild(shell, "tutorialLeft");
673+ verify(tutorialLeft != null);
674+ verify(tutorialLeft.shown);
675+
676+ AccountsService.demoEdges = false;
677+ verify(findChild(shell, "tutorialLeft") == null);
678+
679+ AccountsService.demoEdges = true;
680+ tutorialLeft = findChild(shell, "tutorialLeft");
681+ verify(tutorialLeft != null);
682+ verify(tutorialLeft.shown);
502683 }
503684 }
504685}