on xenial help sign in
Allow running the dialer-app in emergency mode when the screen is locked.
27c1c3cMichael Terry
qml/Components/Lockscreen.qml+17−5
@@ -186,6 +186,17 @@ Showable {
186 }
186 }
187
187
188 Column {
188 Column {
189+ id: emergencyCallColumn
190+
191+ // FIXME: We *should* show emergency dialer if there is a SIM present,
192+ // regardless of whether the side stage is enabled. But right now,
193+ // the assumption is that narrow screens are phones which have SIMs
194+ // and wider screens are tablets which don't. When we do allow this
195+ // on devices with a side stage and a SIM, work should be done to
196+ // ensure that the main stage is disabled while the dialer is present
197+ // in the side stage.
198+ visible: !shell.sideStageEnabled
199+
189 anchors {
200 anchors {
190 left: parent.left
201 left: parent.left
191 bottom: parent.bottom
202 bottom: parent.bottom
@@ -203,11 +214,6 @@ Showable {
203 name: "phone-app-call-symbolic"
214 name: "phone-app-call-symbolic"
204 color: "#f3f3e7"
215 color: "#f3f3e7"
205 opacity: 0.6
216 opacity: 0.6
206−
207− MouseArea {
208− anchors.fill: parent
209− onClicked: root.emergencyCall()
210− }
211 }
217 }
212
218
213 Label {
219 Label {
@@ -219,6 +225,12 @@ Showable {
219 }
225 }
220 }
226 }
221
227
228+ MouseArea {
229+ anchors.fill: emergencyCallColumn
230+ onClicked: root.emergencyCall()
231+ enabled: emergencyCallColumn.visible
232+ }
233+
222 Component {
234 Component {
223 id: infoPopupComponent
235 id: infoPopupComponent
224 Dialog {
236 Dialog {
qml/Greeter/Greeter.qml+1−1
@@ -70,7 +70,7 @@ Showable {
70 DraggingArea {
70 DraggingArea {
71 id: dragHandle
71 id: dragHandle
72 anchors.fill: parent
72 anchors.fill: parent
73− enabled: greeter.narrowMode || !greeter.locked
73+ enabled: (greeter.narrowMode || !greeter.locked) && greeter.enabled
74 orientation: Qt.Horizontal
74 orientation: Qt.Horizontal
75 propagateComposedEvents: true
75 propagateComposedEvents: true
76
76
qml/Shell.qml+84−27
@@ -137,7 +137,7 @@ FocusScope {
137 id: dash
137 id: dash
138 objectName: "dash"
138 objectName: "dash"
139
139
140− available: !greeter.shown && !lockscreen.shown
140+ available: !LightDM.Greeter.active
141 hides: [stages, launcher, panel.indicators]
141 hides: [stages, launcher, panel.indicators]
142 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
142 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
143 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
143 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
@@ -200,7 +200,7 @@ FocusScope {
200
200
201 x: {
201 x: {
202 if (shown) {
202 if (shown) {
203− if (overlayMode || locked) {
203+ if (overlayMode || locked || greeter.fakeActiveForApp !== "") {
204 return 0;
204 return 0;
205 }
205 }
206 return launcher.progress
206 return launcher.progress
@@ -245,10 +245,17 @@ FocusScope {
245 target: ApplicationManager
245 target: ApplicationManager
246
246
247 onFocusRequested: {
247 onFocusRequested: {
248+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
249+ lockscreen.show();
250+ }
251+ greeter.hide();
248 stages.show(true);
252 stages.show(true);
249 }
253 }
250
254
251 onFocusedApplicationIdChanged: {
255 onFocusedApplicationIdChanged: {
256+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
257+ lockscreen.show();
258+ }
252 if (ApplicationManager.focusedApplicationId.length > 0) {
259 if (ApplicationManager.focusedApplicationId.length > 0) {
253 stages.show(false);
260 stages.show(false);
254 } else {
261 } else {
@@ -439,6 +446,11 @@ FocusScope {
439
446
440 source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
447 source: shell.sideStageEnabled ? "Stages/StageWithSideStage.qml" : "Stages/PhoneStage.qml"
441
448
449+ Binding {
450+ target: applicationsDisplayLoader.item
451+ property: "objectName"
452+ value: "stage"
453+ }
442 Binding {
454 Binding {
443 target: applicationsDisplayLoader.item
455 target: applicationsDisplayLoader.item
444 property: "moving"
456 property: "moving"
@@ -454,6 +466,11 @@ FocusScope {
454 property: "dragAreaWidth"
466 property: "dragAreaWidth"
455 value: shell.edgeSize
467 value: shell.edgeSize
456 }
468 }
469+ Binding {
470+ target: applicationsDisplayLoader.item
471+ property: "spreadEnabled"
472+ value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
473+ }
457 }
474 }
458 }
475 }
459
476
@@ -478,6 +495,13 @@ FocusScope {
478
495
479 onEntered: LightDM.Greeter.respond(passphrase);
496 onEntered: LightDM.Greeter.respond(passphrase);
480 onCancel: greeter.show()
497 onCancel: greeter.show()
498+ onEmergencyCall: {
499+ greeter.fakeActiveForApp = "dialer-app"
500+ shell.activateApplication("dialer-app")
501+ lockscreen.hide()
502+ }
503+
504+ onShownChanged: if (shown) greeter.fakeActiveForApp = ""
481
505
482 Component.onCompleted: {
506 Component.onCompleted: {
483 if (LightDM.Users.count == 1) {
507 if (LightDM.Users.count == 1) {
@@ -510,6 +534,7 @@ FocusScope {
510 }
534 }
511 if (LightDM.Greeter.authenticated) {
535 if (LightDM.Greeter.authenticated) {
512 lockscreen.hide();
536 lockscreen.hide();
537+ greeter.login();
513 } else {
538 } else {
514 lockscreen.clear(true);
539 lockscreen.clear(true);
515 }
540 }
@@ -519,7 +544,7 @@ FocusScope {
519 Binding {
544 Binding {
520 target: LightDM.Greeter
545 target: LightDM.Greeter
521 property: "active"
546 property: "active"
522− value: greeter.shown || lockscreen.shown
547+ value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
523 }
548 }
524
549
525 Rectangle {
550 Rectangle {
@@ -542,11 +567,16 @@ FocusScope {
542 }
567 }
543
568
544 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
569 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
570+ onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
545
571
546 Greeter {
572 Greeter {
547 id: greeter
573 id: greeter
548 objectName: "greeter"
574 objectName: "greeter"
549
575
576+ signal sessionStarted() // helpful for tests
577+
578+ property string fakeActiveForApp: ""
579+
550 available: true
580 available: true
551 hides: [launcher, panel.indicators]
581 hides: [launcher, panel.indicators]
552 shown: true
582 shown: true
@@ -558,6 +588,16 @@ FocusScope {
558
588
559 dragHandleWidth: shell.edgeSize
589 dragHandleWidth: shell.edgeSize
560
590
591+ function login() {
592+ enabled = false;
593+ LightDM.Greeter.startSessionSync();
594+ sessionStarted();
595+ greeter.hide();
596+ lockscreen.hide();
597+ launcher.hide();
598+ enabled = true;
599+ }
600+
561 onShownChanged: {
601 onShownChanged: {
562 if (shown) {
602 if (shown) {
563 lockscreen.reset();
603 lockscreen.reset();
@@ -569,6 +609,7 @@ FocusScope {
569 if (LightDM.Users.count == 1) {
609 if (LightDM.Users.count == 1) {
570 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
610 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
571 }
611 }
612+ greeter.fakeActiveForApp = "";
572 greeter.forceActiveFocus();
613 greeter.forceActiveFocus();
573 }
614 }
574 }
615 }
@@ -618,12 +659,40 @@ FocusScope {
618 }
659 }
619
660
620 function showHome() {
661 function showHome() {
662+ if (edgeDemo.running) {
663+ return
664+ }
665+
666+ if (LightDM.Greeter.active) {
667+ if (!LightDM.Greeter.promptless) {
668+ lockscreen.show()
669+ }
670+ greeter.hide()
671+ }
672+
621− var animate = !greeter.shown && !stages.shown
673+ var animate = !LightDM.Greeter.active && !stages.shown
622− greeter.hide()
623 dash.setCurrentScope("clickscope", animate, false)
674 dash.setCurrentScope("clickscope", animate, false)
624 stages.hide()
675 stages.hide()
625 }
676 }
626
677
678+ function showDash() {
679+ if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
680+ return;
681+ }
682+
683+ if (stages.shown && !stages.overlayMode && !stages.locked) {
684+ stages.hide();
685+ launcher.fadeOut();
686+ } else {
687+ launcher.switchToNextState("visible");
688+ }
689+
690+ if (greeter.shown) {
691+ greeter.hideRight();
692+ launcher.fadeOut();
693+ }
694+ }
695+
627 Item {
696 Item {
628 id: overlay
697 id: overlay
629
698
@@ -631,17 +700,18 @@ FocusScope {
631
700
632 Panel {
701 Panel {
633 id: panel
702 id: panel
703+ objectName: "panel"
634 anchors.fill: parent //because this draws indicator menus
704 anchors.fill: parent //because this draws indicator menus
635 indicators {
705 indicators {
636 hides: [launcher]
706 hides: [launcher]
637− available: edgeDemo.panelEnabled
707+ available: edgeDemo.panelEnabled && greeter.fakeActiveForApp === ""
638 contentEnabled: edgeDemo.panelContentEnabled
708 contentEnabled: edgeDemo.panelContentEnabled
639 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
709 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
640 panelHeight: units.gu(3)
710 panelHeight: units.gu(3)
641 }
711 }
642 property string focusedAppId: ApplicationManager.focusedApplicationId
712 property string focusedAppId: ApplicationManager.focusedApplicationId
643 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
713 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
644− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
714+ fullscreenMode: (focusedApplication && stages.fullscreen && !LightDM.Greeter.active) || greeter.fakeActiveForApp !== ""
645
715
646 InputFilterArea {
716 InputFilterArea {
647 anchors {
717 anchors {
@@ -666,6 +736,7 @@ FocusScope {
666
736
667 Launcher {
737 Launcher {
668 id: launcher
738 id: launcher
739+ objectName: "launcher"
669
740
670 readonly property bool dashSwipe: progress > 0
741 readonly property bool dashSwipe: progress > 0
671
742
@@ -673,29 +744,15 @@ FocusScope {
673 anchors.bottom: parent.bottom
744 anchors.bottom: parent.bottom
674 width: parent.width
745 width: parent.width
675 dragAreaWidth: shell.edgeSize
746 dragAreaWidth: shell.edgeSize
676− available: edgeDemo.launcherEnabled
747+ available: edgeDemo.launcherEnabled && greeter.fakeActiveForApp === ""
677
748
678− onShowDashHome: {
749+ onShowDashHome: showHome()
679− if (edgeDemo.running)
680− return;
681−
682− showHome()
683− }
684− onDash: {
750+ onDash: showDash()
685− if (stages.shown && !stages.overlayMode && !stages.locked) {
686− stages.hide();
687− launcher.fadeOut();
688− } else {
689− launcher.switchToNextState("visible");
690− }
691−
692− if (greeter.shown) {
693− greeter.hideRight();
694− launcher.fadeOut();
695− }
696− }
697 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
751 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
698 onLauncherApplicationSelected: {
752 onLauncherApplicationSelected: {
753+ if (greeter.fakeActiveForApp !== "") {
754+ lockscreen.show()
755+ }
699 if (!edgeDemo.running)
756 if (!edgeDemo.running)
700 shell.activateApplication(appId)
757 shell.activateApplication(appId)
701 }
758 }
qml/Stages/PhoneStage.qml+13−1
@@ -18,6 +18,7 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Ubuntu.Gestures 0.1
19import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
20import Unity.Application 0.1
21+import LightDM 0.1 as LightDM
21import Utils 0.1
22import Utils 0.1
22import "../Components"
23import "../Components"
23
24
@@ -33,6 +34,7 @@ Item {
33 readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
34 readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
34 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
35 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
35 property bool locked: spreadView.visible
36 property bool locked: spreadView.visible
37+ property bool spreadEnabled: true
36
38
37 // Not used for PhoneStage, only useful for SideStage and similar
39 // Not used for PhoneStage, only useful for SideStage and similar
38 property bool overlayMode: false
40 property bool overlayMode: false
@@ -131,6 +133,11 @@ Item {
131 mainScreenshotImage.visible = true;
133 mainScreenshotImage.visible = true;
132 } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
134 } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
133 applicationSwitchingAnimation.start();
135 applicationSwitchingAnimation.start();
136+ if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
137+ // When greeter is up, no fancy animations, since user may
138+ // glimpse an app they shouldn't
139+ applicationSwitchingAnimation.complete();
140+ }
134 }
141 }
135 waitingForScreenshot = false;
142 waitingForScreenshot = false;
136 }
143 }
@@ -145,6 +152,11 @@ Item {
145 priv.newFocusedAppId = appId;
152 priv.newFocusedAppId = appId;
146 root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
153 root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
147 applicationSwitchingAnimation.start();
154 applicationSwitchingAnimation.start();
155+ if (LightDM.Greeter.active && !LightDM.Greeter.promptless) {
156+ // When greeter is up, no fancy animations, since user may
157+ // glimpse an app they shouldn't
158+ applicationSwitchingAnimation.complete();
159+ }
148 } else {
160 } else {
149 ApplicationManager.focusApplication(appId);
161 ApplicationManager.focusApplication(appId);
150 }
162 }
@@ -244,7 +256,7 @@ Item {
244 EdgeDragArea {
256 EdgeDragArea {
245 id: spreadDragArea
257 id: spreadDragArea
246 direction: Direction.Leftwards
258 direction: Direction.Leftwards
247− enabled: ApplicationManager.count > 1 && spreadView.phase != 2
259+ enabled: ApplicationManager.count > 1 && spreadView.phase != 2 && root.spreadEnabled
248
260
249 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
261 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
250 width: root.dragAreaWidth
262 width: root.dragAreaWidth
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+1−9
@@ -19,8 +19,6 @@
19#include "../Greeter.h"
19#include "../Greeter.h"
20#include "../GreeterPrivate.h"
20#include "../GreeterPrivate.h"
21
21
22−#include <QDebug>
23−
24namespace QLightDM
22namespace QLightDM
25{
23{
26
24
@@ -34,19 +32,13 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
34void GreeterPrivate::handleAuthenticate()
32void GreeterPrivate::handleAuthenticate()
35{
33{
36 Q_Q(Greeter);
34 Q_Q(Greeter);
37−
38− qDebug() << "handleAuthentication called!" << authenticationUser;
39−
40− Q_EMIT q->showPrompt("PIN:", Greeter::PromptTypeSecret);
35+ Q_EMIT q->showPrompt("PIN", Greeter::PromptTypeSecret);
41}
36}
42
37
43void GreeterPrivate::handleRespond(const QString &response)
38void GreeterPrivate::handleRespond(const QString &response)
44{
39{
45 Q_Q(Greeter);
40 Q_Q(Greeter);
46−
47−
48 authenticated = (response == "1234");
41 authenticated = (response == "1234");
49− qDebug() << "responding" << response << authenticated;
50 Q_EMIT q->authenticationComplete();
42 Q_EMIT q->authenticationComplete();
51}
43}
52
44
tests/qmltests/CMakeLists.txt+3−3
@@ -19,8 +19,8 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
21
21
22−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
22+add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
23− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
23+add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
24add_qml_test(Components Carousel)
24add_qml_test(Components Carousel)
25add_qml_test(Components DraggingArea)
25add_qml_test(Components DraggingArea)
26add_qml_test(Components EdgeDemoOverlay)
26add_qml_test(Components EdgeDemoOverlay)
@@ -75,4 +75,4 @@ add_qml_test(Panel/Indicators DefaultIndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=
75# These MenuItemFactory tests need the test/mocks/ to come before plugins/
75# These MenuItemFactory tests need the test/mocks/ to come before plugins/
76add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
76add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
77add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
77add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
78−add_qml_test(Stages PhoneStage)
78+add_qml_test(Stages PhoneStage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
tests/qmltests/Greeter/tst_Lockscreen.qml+1−1
@@ -57,7 +57,7 @@ Rectangle {
57 target: LightDM.Greeter
57 target: LightDM.Greeter
58
58
59 onShowPrompt: {
59 onShowPrompt: {
60− if (text.indexOf("PIN") >= 0) {
60+ if (text === "PIN") {
61 pinPadCheckBox.checked = false
61 pinPadCheckBox.checked = false
62 } else {
62 } else {
63 pinPadCheckBox.checked = true
63 pinPadCheckBox.checked = true
tests/qmltests/tst_Shell.qml+19
@@ -51,6 +51,11 @@ Item {
51 id: shell
51 id: shell
52 }
52 }
53
53
54+ SignalSpy {
55+ id: sessionSpy
56+ signalName: "sessionStarted"
57+ }
58+
54 UT.UnityTestCase {
59 UT.UnityTestCase {
55 name: "Shell"
60 name: "Shell"
56 when: windowShown
61 when: windowShown
@@ -92,6 +97,8 @@ Item {
92 verify(ok);
97 verify(ok);
93
98
94 swipeAwayGreeter();
99 swipeAwayGreeter();
100+
101+ sessionSpy.target = findChild(shell, "greeter")
95 }
102 }
96
103
97 function cleanup() {
104 function cleanup() {
@@ -528,5 +535,17 @@ Item {
528 LightDM.Greeter.showGreeter()
535 LightDM.Greeter.showGreeter()
529 tryCompare(greeter, "showProgress", 1)
536 tryCompare(greeter, "showProgress", 1)
530 }
537 }
538+
539+ function test_login() {
540+ sessionSpy.clear()
541+
542+ var greeter = findChild(shell, "greeter")
543+ greeter.show()
544+ tryCompare(greeter, "showProgress", 1)
545+
546+ tryCompare(sessionSpy, "count", 0)
547+ swipeAwayGreeter()
548+ tryCompare(sessionSpy, "count", 1)
549+ }
531 }
550 }
532}
551}
tests/qmltests/tst_ShellWithPin.qmlnew file+203
@@ -0,0 +1,203 @@
1+/*
2+ * Copyright (C) 2014 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.0
18+import QtTest 1.0
19+import GSettings 1.0
20+import LightDM 0.1 as LightDM
21+import Unity.Application 0.1
22+import Unity.Test 0.1 as UT
23+import Powerd 0.1
24+
25+import "../../qml"
26+
27+Item {
28+ width: shell.width
29+ height: shell.height
30+
31+ QtObject {
32+ id: applicationArguments
33+
34+ function hasGeometry() {
35+ return false;
36+ }
37+
38+ function width() {
39+ return 0;
40+ }
41+
42+ function height() {
43+ return 0;
44+ }
45+ }
46+
47+ Shell {
48+ id: shell
49+ }
50+
51+ SignalSpy {
52+ id: sessionSpy
53+ signalName: "sessionStarted"
54+ }
55+
56+ UT.UnityTestCase {
57+ name: "ShellWithPin"
58+ when: windowShown
59+
60+ function initTestCase() {
61+ var ok = false;
62+ var attempts = 0;
63+ var maxAttempts = 1000;
64+
65+ // Qt loads a qml scene asynchronously. So early on, some findChild() calls made in
66+ // tests may fail because the desired child item wasn't loaded yet.
67+ // Thus here we try to ensure the scene has been fully loaded before proceeding with the tests.
68+ // As I couldn't find an API in QQuickView & friends to tell me that the scene is 100% loaded
69+ // (all items instantiated, etc), I resort to checking the existence of some key items until
70+ // repeatedly until they're all there.
71+ do {
72+ var dashContentList = findChild(shell, "dashContentList");
73+ waitForRendering(dashContentList);
74+ var homeLoader = findChild(dashContentList, "clickscope loader");
75+ ok = homeLoader !== null
76+ && homeLoader.item !== undefined;
77+
78+ var greeter = findChild(shell, "greeter");
79+ ok &= greeter !== null;
80+
81+ var launcherPanel = findChild(shell, "launcherPanel");
82+ ok &= launcherPanel !== null;
83+
84+ attempts++;
85+ if (!ok) {
86+ console.log("Attempt " + attempts + " failed. Waiting a bit before trying again.");
87+ // wait a bit before retrying
88+ wait(100);
89+ } else {
90+ console.log("All seem fine after " + attempts + " attempts.");
91+ }
92+ } while (!ok && attempts <= maxAttempts);
93+
94+ verify(ok);
95+
96+ sessionSpy.target = findChild(shell, "greeter")
97+ }
98+
99+ function init() {
100+ swipeAwayGreeter()
101+ }
102+
103+ function cleanup() {
104+ LightDM.Greeter.showGreeter()
105+ var greeter = findChild(shell, "greeter")
106+ tryCompare(greeter, "showProgress", 1)
107+
108+ // kill all (fake) running apps
109+ killApps()
110+ }
111+
112+ function killApps() {
113+ while (ApplicationManager.count > 0) {
114+ ApplicationManager.stopApplication(ApplicationManager.get(0).appId)
115+ }
116+ compare(ApplicationManager.count, 0)
117+ }
118+
119+ function swipeAwayGreeter() {
120+ var greeter = findChild(shell, "greeter");
121+ tryCompare(greeter, "showProgress", 1);
122+
123+ var touchX = shell.width - (shell.edgeSize / 2);
124+ var touchY = shell.height / 2;
125+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
126+
127+ // wait until the animation has finished
128+ tryCompare(greeter, "showProgress", 0);
129+
130+ // and for pin to be ready
131+ var lockscreen = findChild(shell, "lockscreen");
132+ var pinPadLoader = findChild(lockscreen, "pinPadLoader");
133+ tryCompare(pinPadLoader, "status", Loader.Ready)
134+ waitForRendering(lockscreen)
135+ }
136+
137+ function enterPin(pin) {
138+ var inputField = findChild(shell, "pinentryField")
139+ for (var i = 0; i < pin.length; ++i) {
140+ var character = pin.charAt(i)
141+ var button = findChild(shell, "pinPadButton" + character)
142+ mouseClick(button, units.gu(1), units.gu(1))
143+ }
144+ }
145+
146+ function test_login() {
147+ tryCompare(sessionSpy, "count", 0)
148+ enterPin("1234")
149+ tryCompare(sessionSpy, "count", 1)
150+ }
151+
152+ function test_emergencyCall() {
153+ var greeter = findChild(shell, "greeter")
154+ var lockscreen = findChild(shell, "lockscreen")
155+ var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
156+ var panel = findChild(shell, "panel")
157+ var indicators = findChild(shell, "indicators")
158+ var launcher = findChild(shell, "launcher")
159+ var stage = findChild(shell, "stage")
160+
161+ mouseClick(emergencyButton, units.gu(1), units.gu(1))
162+
163+ tryCompare(greeter, "fakeActiveForApp", "dialer-app")
164+ tryCompare(lockscreen, "shown", false)
165+ tryCompare(panel, "fullscreenMode", true)
166+ tryCompare(indicators, "available", false)
167+ tryCompare(launcher, "available", false)
168+ tryCompare(stage, "spreadEnabled", false)
169+
170+ // Cancel emergency mode, and go back to normal
171+ waitForRendering(greeter)
172+ LightDM.Greeter.showGreeter()
173+
174+ tryCompare(greeter, "shown", true)
175+ tryCompare(greeter, "fakeActiveForApp", "")
176+ tryCompare(lockscreen, "shown", true)
177+ tryCompare(panel, "fullscreenMode", false)
178+ tryCompare(indicators, "available", true)
179+ tryCompare(launcher, "available", true)
180+ tryCompare(stage, "spreadEnabled", true)
181+ }
182+
183+ function test_emergencyCallCrash() {
184+ var lockscreen = findChild(shell, "lockscreen")
185+ var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
186+ mouseClick(emergencyButton, units.gu(1), units.gu(1))
187+
188+ tryCompare(lockscreen, "shown", false)
189+ killApps() // kill dialer-app, as if it crashed
190+ tryCompare(lockscreen, "shown", true)
191+ }
192+
193+ function test_emergencyCallAppLaunch() {
194+ var lockscreen = findChild(shell, "lockscreen")
195+ var emergencyButton = findChild(lockscreen, "emergencyCallIcon")
196+ mouseClick(emergencyButton, units.gu(1), units.gu(1))
197+
198+ tryCompare(lockscreen, "shown", false)
199+ ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
200+ tryCompare(lockscreen, "shown", true)
201+ }
202+ }
203+}