on xenial help sign in
First pass at dialer-above-greeter
commite50ae73085691460fbde1fd27a8546b95fa7da61 authorMichael Terry <michael.terry@canonical.com> dateTue 24 Jun 2014, 19:52 (12 years ago) parentfd2a831
8 files changed+284−45
qml/Components/Lockscreen.qml+7−5 qml/Shell.qml+69−25 qml/Stages/PhoneStage.qml+13−1 tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+1−9 tests/qmltests/CMakeLists.txt+2−2 tests/qmltests/Greeter/tst_Lockscreen.qml+3−3 tests/qmltests/tst_Shell.qml+18 tests/qmltests/tst_ShellWithPin.qml+171
qml/Components/Lockscreen.qml+7−5
@@ -186,6 +186,8 @@ Showable {
186186 }
187187
188188 Column {
189+ id: emergencyCallColumn
190+
189191 anchors {
190192 left: parent.left
191193 bottom: parent.bottom
@@ -203,11 +205,6 @@ Showable {
203205 name: "phone-app-call-symbolic"
204206 color: "#f3f3e7"
205207 opacity: 0.6
206−
207− MouseArea {
208− anchors.fill: parent
209− onClicked: root.emergencyCall()
210− }
211208 }
212209
213210 Label {
@@ -219,6 +216,11 @@ Showable {
219216 }
220217 }
221218
219+ MouseArea {
220+ anchors.fill: emergencyCallColumn
221+ onClicked: root.emergencyCall()
222+ }
223+
222224 Component {
223225 id: infoPopupComponent
224226 Dialog {
qml/Shell.qml+69−25
@@ -135,7 +135,7 @@ FocusScope {
135135 id: dash
136136 objectName: "dash"
137137
138− available: !greeter.shown && !lockscreen.shown
138+ available: !LightDM.Greeter.active
139139 hides: [stages, launcher, panel.indicators]
140140 shown: disappearingAnimationProgress !== 1.0 && greeterWrapper.showProgress !== 1.0
141141 enabled: disappearingAnimationProgress === 0.0 && greeterWrapper.showProgress === 0.0 && edgeDemo.dashEnabled
@@ -198,7 +198,7 @@ FocusScope {
198198
199199 x: {
200200 if (shown) {
201− if (overlayMode || locked) {
201+ if (overlayMode || locked || greeter.fakeActiveForApp !== "") {
202202 return 0;
203203 }
204204 return launcher.progress
@@ -243,10 +243,17 @@ FocusScope {
243243 target: ApplicationManager
244244
245245 onFocusRequested: {
246+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
247+ lockscreen.show();
248+ }
249+ greeter.hide();
246250 stages.show(true);
247251 }
248252
249253 onFocusedApplicationIdChanged: {
254+ if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== ApplicationManager.focusedApplicationId) {
255+ lockscreen.show();
256+ }
250257 if (ApplicationManager.focusedApplicationId.length > 0) {
251258 stages.show(false);
252259 } else {
@@ -288,6 +295,11 @@ FocusScope {
288295 property: "dragAreaWidth"
289296 value: shell.edgeSize
290297 }
298+ Binding {
299+ target: applicationsDisplayLoader.item
300+ property: "spreadEnabled"
301+ value: greeter.fakeActiveForApp === "" // to support emergency dialer hack
302+ }
291303 }
292304 }
293305
@@ -312,6 +324,13 @@ FocusScope {
312324
313325 onEntered: LightDM.Greeter.respond(passphrase);
314326 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 = ""
315334
316335 Component.onCompleted: {
317336 if (LightDM.Users.count == 1) {
@@ -342,6 +361,7 @@ FocusScope {
342361 }
343362 if (LightDM.Greeter.authenticated) {
344363 lockscreen.hide();
364+ greeter.login();
345365 } else {
346366 lockscreen.clear(true);
347367 }
@@ -351,7 +371,7 @@ FocusScope {
351371 Binding {
352372 target: LightDM.Greeter
353373 property: "active"
354− value: greeter.shown || lockscreen.shown
374+ value: greeter.shown || lockscreen.shown || greeter.fakeActiveForApp != ""
355375 }
356376
357377 Rectangle {
@@ -374,11 +394,16 @@ FocusScope {
374394 }
375395
376396 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
397+ onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
377398
378399 Greeter {
379400 id: greeter
380401 objectName: "greeter"
381402
403+ signal sessionStarted() // helpful for tests
404+
405+ property string fakeActiveForApp: ""
406+
382407 available: true
383408 hides: [launcher, panel.indicators]
384409 shown: true
@@ -390,6 +415,16 @@ FocusScope {
390415
391416 dragHandleWidth: shell.edgeSize
392417
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+
393428 onShownChanged: {
394429 if (shown) {
395430 lockscreen.reset();
@@ -398,6 +433,7 @@ FocusScope {
398433 if (LightDM.Users.count == 1) {
399434 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
400435 }
436+ greeter.fakeActiveForApp = "";
401437 greeter.forceActiveFocus();
402438 }
403439 }
@@ -447,12 +483,36 @@ FocusScope {
447483 }
448484
449485 function showHome() {
450− var animate = !greeter.shown && !stages.shown
451− greeter.hide()
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+
495+ var animate = !LightDM.Greeter.active && !stages.shown
452496 dash.setCurrentScope("clickscope", animate, false)
453497 stages.hide()
454498 }
455499
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+
456516 function hideIndicatorMenu(delay) {
457517 panel.hideIndicatorMenu(delay);
458518 }
@@ -473,8 +533,8 @@ FocusScope {
473533 }
474534 property string focusedAppId: ApplicationManager.focusedApplicationId
475535 property var focusedApplication: ApplicationManager.findApplication(focusedAppId)
476− fullscreenMode: focusedApplication && stages.fullscreen && !greeter.shown && !lockscreen.shown
477− searchVisible: !greeter.shown && !lockscreen.shown && dash.shown && dash.searchable
536+ fullscreenMode: focusedApplication && stages.fullscreen && !LightDM.Greeter.active
537+ searchVisible: !LightDM.Greeter.active && dash.shown && dash.searchable
478538
479539 InputFilterArea {
480540 anchors {
@@ -508,24 +568,8 @@ FocusScope {
508568 dragAreaWidth: shell.edgeSize
509569 available: edgeDemo.launcherEnabled
510570
511− onShowDashHome: {
512− if (edgeDemo.running)
513− return;
514−
515− showHome()
516− }
517− onDash: {
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− }
571+ onShowDashHome: showHome()
572+ onDash: showDash()
529573 onDashSwipeChanged: if (dashSwipe && stages.shown) dash.setCurrentScope("clickscope", false, true)
530574 onLauncherApplicationSelected: {
531575 if (!edgeDemo.running)
qml/Stages/PhoneStage.qml+13−1
@@ -18,6 +18,7 @@ import QtQuick 2.0
1818import Ubuntu.Components 0.1
1919import Ubuntu.Gestures 0.1
2020import Unity.Application 0.1
21+import LightDM 0.1 as LightDM
2122import Utils 0.1
2223import "../Components"
2324
@@ -33,6 +34,7 @@ Item {
3334 readonly property bool painting: mainScreenshotImage.visible || fadeInScreenshotImage.visible || appSplash.visible || spreadView.visible
3435 property bool fullscreen: priv.focusedApplication ? priv.focusedApplication.fullscreen : false
3536 property bool locked: spreadView.visible
37+ property bool spreadEnabled: true
3638
3739 // Not used for PhoneStage, only useful for SideStage and similar
3840 property bool overlayMode: false
@@ -131,6 +133,11 @@ Item {
131133 mainScreenshotImage.visible = true;
132134 } else if (priv.secondApplicationStarting && priv.waitingForScreenshot) {
133135 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+ }
134141 }
135142 waitingForScreenshot = false;
136143 }
@@ -145,6 +152,11 @@ Item {
145152 priv.newFocusedAppId = appId;
146153 root.fullscreen = ApplicationManager.findApplication(appId).fullscreen;
147154 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+ }
148160 } else {
149161 ApplicationManager.focusApplication(appId);
150162 }
@@ -244,7 +256,7 @@ Item {
244256 EdgeDragArea {
245257 id: spreadDragArea
246258 direction: Direction.Leftwards
247− enabled: ApplicationManager.count > 1 && spreadView.phase != 2
259+ enabled: ApplicationManager.count > 1 && spreadView.phase != 2 && root.spreadEnabled
248260
249261 anchors { top: parent.top; right: parent.right; bottom: parent.bottom }
250262 width: root.dragAreaWidth
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp+1−9
@@ -19,8 +19,6 @@
1919#include "../Greeter.h"
2020#include "../GreeterPrivate.h"
2121
22−#include <QDebug>
23−
2422namespace QLightDM
2523{
2624
@@ -34,19 +32,13 @@ GreeterPrivate::GreeterPrivate(Greeter* parent)
3432void GreeterPrivate::handleAuthenticate()
3533{
3634 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);
4136}
4237
4338void GreeterPrivate::handleRespond(const QString &response)
4439{
4540 Q_Q(Greeter);
46−
47−
4841 authenticated = (response == "1234");
49− qDebug() << "responding" << response << authenticated;
5042 Q_EMIT q->authenticationComplete();
5143}
5244
tests/qmltests/CMakeLists.txt+2−2
@@ -19,8 +19,8 @@ set(qmltest_DEFAULT_TARGETS qmluitests)
1919set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
2020set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
2121
22−add_qml_test(. Shell IMPORT_PATHS ${CMAKE_BINARY_DIR}/plugins ${qmltest_DEFAULT_IMPORT_PATHS} ${CMAKE_BINARY_DIR}/tests/mocks
23− ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
22+add_qml_test(. Shell 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")
2424add_qml_test(Components Carousel)
2525add_qml_test(Components DraggingArea)
2626add_qml_test(Components EdgeDemoOverlay)
tests/qmltests/Greeter/tst_Lockscreen.qml+3−3
@@ -57,10 +57,10 @@ Rectangle {
5757 target: LightDM.Greeter
5858
5959 onShowPrompt: {
60− if (text.indexOf("PIN") >= 0) {
61− pinPadCheckBox.checked = false
62− } else {
60+ if (text == "PIN") {
6361 pinPadCheckBox.checked = true
62+ } else {
63+ pinPadCheckBox.checked = false
6464 }
6565 lockscreen.placeholderText = text;
6666 }
tests/qmltests/tst_Shell.qml+18
@@ -50,6 +50,11 @@ Item {
5050 id: shell
5151 }
5252
53+ SignalSpy {
54+ id: sessionSpy
55+ signalName: "sessionStarted"
56+ }
57+
5358 UT.UnityTestCase {
5459 name: "Shell"
5560 when: windowShown
@@ -91,6 +96,9 @@ Item {
9196 verify(ok);
9297
9398 swipeAwayGreeter();
99+
100+ sessionSpy.target = findChild(shell, "greeter")
101+ sessionSpy.clear()
94102 }
95103
96104 function cleanup() {
@@ -556,5 +564,15 @@ Item {
556564 tryCompare(greeter, "showProgress", 0)
557565 waitUntilApplicationWindowIsFullyVisible()
558566 }
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+ }
559577 }
560578}
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+}