on xenial help sign in
First pass at dialer-above-greeter
e50ae73Michael Terry
qml/Components/Lockscreen.qml+7−5
@@ -186,6 +186,8 @@ Showable {
186 }
186 }
187
187
188 Column {
188 Column {
189+ id: emergencyCallColumn
190+
189 anchors {
191 anchors {
190 left: parent.left
192 left: parent.left
191 bottom: parent.bottom
193 bottom: parent.bottom
@@ -203,11 +205,6 @@ Showable {
203 name: "phone-app-call-symbolic"
205 name: "phone-app-call-symbolic"
204 color: "#f3f3e7"
206 color: "#f3f3e7"
205 opacity: 0.6
207 opacity: 0.6
206−
207− MouseArea {
208− anchors.fill: parent
209− onClicked: root.emergencyCall()
210− }
211 }
208 }
212
209
213 Label {
210 Label {
@@ -219,6 +216,11 @@ Showable {
219 }
216 }
220 }
217 }
221
218
219+ MouseArea {
220+ anchors.fill: emergencyCallColumn
221+ onClicked: root.emergencyCall()
222+ }
223+
222 Component {
224 Component {
223 id: infoPopupComponent
225 id: infoPopupComponent
224 Dialog {
226 Dialog {
qml/Shell.qml+69−25
@@ -135,7 +135,7 @@ FocusScope {
135 id: dash
135 id: dash
136 objectName: "dash"
136 objectName: "dash"
137
137
138− available: !greeter.shown && !lockscreen.shown
138+ available: !LightDM.Greeter.active
139 hides: [stages, launcher, panel.indicators]
139 hides: [stages, launcher, panel.indicators]
140 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
140 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
141 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
141 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
@@ -198,7 +198,7 @@ FocusScope {
198
198
199 x: {
199 x: {
200 if (shown) {
200 if (shown) {
201− if (overlayMode || locked) {
201+ if (overlayMode || locked || greeter.fakeActiveForApp !== "") {
202 return 0;
202 return 0;
203 }
203 }
204 return launcher.progress
204 return launcher.progress
@@ -243,10 +243,17 @@ FocusScope {
243 target: ApplicationManager
243 target: ApplicationManager
244
244
245 onFocusRequested: {
245 onFocusRequested: {
246+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
247+ lockscreen.show();
248+ }
249+ greeter.hide();
246 stages.show(true);
250 stages.show(true);
247 }
251 }
248
252
249 onFocusedApplicationIdChanged: {
253 onFocusedApplicationIdChanged: {
254+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
255+ lockscreen.show();
256+ }
250 if (ApplicationManager.focusedApplicationId.length > 0) {
257 if (ApplicationManager.focusedApplicationId.length > 0) {
251 stages.show(false);
258 stages.show(false);
252 } else {
259 } else {
@@ -288,6 +295,11 @@ FocusScope {
288 property: "dragAreaWidth"
295 property: "dragAreaWidth"
289 value: shell.edgeSize
296 value: shell.edgeSize
290 }
297 }
298+ Binding {
299+ target: applicationsDisplayLoader.item
300+ property: "spreadEnabled"
301+ value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
302+ }
291 }
303 }
292 }
304 }
293
305
@@ -312,6 +324,13 @@ FocusScope {
312
324
313 onEntered: LightDM.Greeter.respond(passphrase);
325 onEntered: LightDM.Greeter.respond(passphrase);
314 onCancel: greeter.show()
326 onCancel: greeter.show()
327+ onEmergencyCall: {
328+ greeter.fakeActiveForApp = "dialer-app"
329+ shell.activateApplication("dialer-app")
330+ lockscreen.hide()
331+ }
332+
333+ onShownChanged: if (shown) greeter.fakeActiveForApp = ""
315
334
316 Component.onCompleted: {
335 Component.onCompleted: {
317 if (LightDM.Users.count == 1) {
336 if (LightDM.Users.count == 1) {
@@ -342,6 +361,7 @@ FocusScope {
342 }
361 }
343 if (LightDM.Greeter.authenticated) {
362 if (LightDM.Greeter.authenticated) {
344 lockscreen.hide();
363 lockscreen.hide();
364+ greeter.login();
345 } else {
365 } else {
346 lockscreen.clear(true);
366 lockscreen.clear(true);
347 }
367 }
@@ -351,7 +371,7 @@ FocusScope {
351 Binding {
371 Binding {
352 target: LightDM.Greeter
372 target: LightDM.Greeter
353 property: "active"
373 property: "active"
354− value: greeter.shown || lockscreen.shown
374+ value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
355 }
375 }
356
376
357 Rectangle {
377 Rectangle {
@@ -374,11 +394,16 @@ FocusScope {
374 }
394 }
375
395
376 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
396 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
397+ onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
377
398
378 Greeter {
399 Greeter {
379 id: greeter
400 id: greeter
380 objectName: "greeter"
401 objectName: "greeter"
381
402
403+ signal sessionStarted() // helpful for tests
404+
405+ property string fakeActiveForApp: ""
406+
382 available: true
407 available: true
383 hides: [launcher, panel.indicators]
408 hides: [launcher, panel.indicators]
384 shown: true
409 shown: true
@@ -390,6 +415,16 @@ FocusScope {
390
415
391 dragHandleWidth: shell.edgeSize
416 dragHandleWidth: shell.edgeSize
392
417
418+ function login() {
419+ enabled = false;
420+ LightDM.Greeter.startSessionSync();
421+ sessionStarted();
422+ greeter.hide();
423+ lockscreen.hide();
424+ launcher.hide();
425+ enabled = true;
426+ }
427+
393 onShownChanged: {
428 onShownChanged: {
394 if (shown) {
429 if (shown) {
395 lockscreen.reset();
430 lockscreen.reset();
@@ -398,6 +433,7 @@ FocusScope {
398 if (LightDM.Users.count == 1) {
433 if (LightDM.Users.count == 1) {
399 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
434 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
400 }
435 }
436+ greeter.fakeActiveForApp = "";
401 greeter.forceActiveFocus();
437 greeter.forceActiveFocus();
402 }
438 }
403 }
439 }
@@ -447,12 +483,36 @@ FocusScope {
447 }
483 }
448
484
449 function showHome() {
485 function showHome() {
486+ if (edgeDemo.running)
487+ return
488+
489+ if (LightDM.Greeter.active) {
490+ if (!LightDM.Greeter.promptless)
491+ lockscreen.show()
492+ greeter.hide()
493+ }
494+
450− var animate = !greeter.shown && !stages.shown
495+ var animate = !LightDM.Greeter.active && !stages.shown
451− greeter.hide()
452 dash.setCurrentScope("clickscope", animate, false)
496 dash.setCurrentScope("clickscope", animate, false)
453 stages.hide()
497 stages.hide()
454 }
498 }
455
499
500+ function showDash() {
501+ if (LightDM.Greeter.active && !LightDM.Greeter.promptless)
502+ return;
503+
504+ if (stages.shown && !stages.overlayMode) {
505+ if (!stages.locked) {
506+ stages.hide();
507+ launcher.hide();
508+ }
509+ }
510+ if (greeter.shown) {
511+ greeter.hideRight();
512+ launcher.hide();
513+ }
514+ }
515+
456 function hideIndicatorMenu(delay) {
516 function hideIndicatorMenu(delay) {
457 panel.hideIndicatorMenu(delay);
517 panel.hideIndicatorMenu(delay);
458 }
518 }
@@ -473,8 +533,8 @@ FocusScope {
473 }
533 }
474 property string focusedAppId: ApplicationManager.focusedApplicationId
534 property string focusedAppId: ApplicationManager.focusedApplicationId
475 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
535 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
476− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
536+ fullscreenMode: focusedApplication && stages.fullscreen && !LightDM.Greeter.active
477− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
537+ searchVisible: !LightDM.Greeter.active && dash.shown && dash.searchable
478
538
479 InputFilterArea {
539 InputFilterArea {
480 anchors {
540 anchors {
@@ -508,24 +568,8 @@ FocusScope {
508 dragAreaWidth: shell.edgeSize
568 dragAreaWidth: shell.edgeSize
509 available: edgeDemo.launcherEnabled
569 available: edgeDemo.launcherEnabled
510
570
511− onShowDashHome: {
571+ onShowDashHome: showHome()
512− if (edgeDemo.running)
513− return;
514−
515− showHome()
516− }
517− onDash: {
572+ onDash: showDash()
518− if (stages.shown && !stages.overlayMode) {
519− if (!stages.locked) {
520− stages.hide();
521− launcher.hide();
522− }
523− }
524− if (greeter.shown) {
525− greeter.hideRight();
526− launcher.hide();
527− }
528− }
529 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
573 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
530 onLauncherApplicationSelected: {
574 onLauncherApplicationSelected: {
531 if (!edgeDemo.running)
575 if (!edgeDemo.running)
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+2−2
@@ -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)
tests/qmltests/Greeter/tst_Lockscreen.qml+3−3
@@ -57,10 +57,10 @@ 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
62− } else {
63 pinPadCheckBox.checked = true
61 pinPadCheckBox.checked = true
62+ } else {
63+ pinPadCheckBox.checked = false
64 }
64 }
65 lockscreen.placeholderText = text;
65 lockscreen.placeholderText = text;
66 }
66 }
tests/qmltests/tst_Shell.qml+18
@@ -50,6 +50,11 @@ Item {
50 id: shell
50 id: shell
51 }
51 }
52
52
53+ SignalSpy {
54+ id: sessionSpy
55+ signalName: "sessionStarted"
56+ }
57+
53 UT.UnityTestCase {
58 UT.UnityTestCase {
54 name: "Shell"
59 name: "Shell"
55 when: windowShown
60 when: windowShown
@@ -91,6 +96,9 @@ Item {
91 verify(ok);
96 verify(ok);
92
97
93 swipeAwayGreeter();
98 swipeAwayGreeter();
99+
100+ sessionSpy.target = findChild(shell, "greeter")
101+ sessionSpy.clear()
94 }
102 }
95
103
96 function cleanup() {
104 function cleanup() {
@@ -556,5 +564,15 @@ Item {
556 tryCompare(greeter, "showProgress", 0)
564 tryCompare(greeter, "showProgress", 0)
557 waitUntilApplicationWindowIsFullyVisible()
565 waitUntilApplicationWindowIsFullyVisible()
558 }
566 }
567+
568+ function test_login() {
569+ var greeter = findChild(shell, "greeter")
570+ greeter.show()
571+ tryCompare(greeter, "showProgress", 1)
572+
573+ tryCompare(sessionSpy, "count", 0)
574+ swipeAwayGreeter()
575+ tryCompare(sessionSpy, "count", 1)
576+ }
559 }
577 }
560}
578}
tests/qmltests/tst_ShellWithPin.qmlnew file+171
@@ -0,0 +1,171 @@
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+ swipeAwayGreeter();
97+
98+ sessionSpy.target = findChild(shell, "greeter")
99+ sessionSpy.clear()
100+ }
101+
102+ function swipeAwayGreeter() {
103+ var greeter = findChild(shell, "greeter");
104+ tryCompare(greeter, "showProgress", 1);
105+
106+ var touchX = shell.width - (shell.edgeSize / 2);
107+ var touchY = shell.height / 2;
108+ touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
109+
110+ // wait until the animation has finished
111+ tryCompare(greeter, "showProgress", 0);
112+
113+ // and for pin to be ready
114+ var lockscreen = findChild(shell, "lockscreen");
115+ var pinPadLoader = findChild(lockscreen, "pinPadLoader");
116+ tryCompare(pinPadLoader, "status", Loader.Ready)
117+ waitForRendering(lockscreen)
118+ }
119+
120+ function enterPin(pin) {
121+ var inputField = findChild(shell, "pinentryField")
122+ for (var i = 0; i < pin.length; ++i) {
123+ var character = pin.charAt(i)
124+ var button = findChild(shell, "pinPadButton" + character)
125+ mouseClick(button, units.gu(1), units.gu(1))
126+ }
127+ }
128+
129+ function dragLauncherIntoView() {
130+ var launcherPanel = findChild(shell, "launcherPanel");
131+ verify(launcherPanel.x = - launcherPanel.width);
132+
133+ var touchStartX = 2;
134+ var touchStartY = shell.height / 2;
135+ touchFlick(shell, touchStartX, touchStartY, launcherPanel.width + units.gu(1), touchStartY);
136+
137+ tryCompare(launcherPanel, "x", 0);
138+ }
139+
140+ function tapOnAppIconInLauncher() {
141+ var launcherPanel = findChild(shell, "launcherPanel");
142+
143+ // pick the first icon, the one at the bottom.
144+ var appIcon = findChild(launcherPanel, "launcherDelegate0")
145+
146+ // Swipe upwards over the launcher to ensure that this icon
147+ // at the bottom is not folded and faded away.
148+ var touchStartX = launcherPanel.width / 2;
149+ var touchStartY = launcherPanel.height / 2;
150+ touchFlick(launcherPanel, touchStartX, touchStartY, touchStartX, 0);
151+ tryCompare(launcherPanel, "moving", false);
152+
153+ // NB tapping (i.e., using touch events) doesn't activate the icon... go figure...
154+ mouseClick(appIcon, appIcon.width / 2, appIcon.height / 2);
155+ }
156+
157+ function test_login() {
158+ tryCompare(sessionSpy, "count", 0)
159+ enterPin("1234")
160+ tryCompare(sessionSpy, "count", 1)
161+ }
162+
163+ function test_emergencyCall() {
164+ dragLauncherIntoView()
165+ tapOnAppIconInLauncher()
166+
167+ var lockscreen = findChild(shell, "lockscreen")
168+ tryCompare(lockscreen, "shown", true)
169+ }
170+ }
171+}