on xenial help sign in
Switch from lockscreen PIN pad to a passcode entry box that uses the OSK.
commit80ab351bdbb8e9ac980a62c534eb1c95beb2e497 authorMichael Terry <michael.terry@canonical.com> dateTue 30 Aug 2016, 09:58 (10 years ago) parentdd6ea60
17 files changed+351−238
qml/Components/Lockscreen.qml−23 qml/Components/PassphraseLockscreen.qml−4 qml/Components/PinLockscreen.qml−4 qml/Components/DelayedLockscreen.qml → qml/Greeter/DelayedLockscreen.qml+6−15 qml/Greeter/Greeter.qml+34−21 qml/Greeter/GreeterPrompt.qml+21−7 qml/Greeter/LoginList.qml+9−3 qml/Greeter/NarrowView.qml+147−61 qml/Greeter/WideView.qml+11−5 tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.cpp+27 tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.h+22 tests/qmltests/Components/tst_Lockscreen.qml−38 tests/qmltests/Greeter/TestView.qml+3−3 tests/qmltests/Greeter/tst_Greeter.qml+1 tests/qmltests/Greeter/tst_NarrowView.qml+34−11 tests/qmltests/Greeter/tst_WideView.qml+8−4 tests/qmltests/tst_ShellWithPin.qml+28−39
qml/Components/Lockscreen.qml−23
@@ -41,9 +41,6 @@ Showable {
4141 // The text to be displayed in case the login failed
4242 property string errorText: ""
4343
44− // If > 0, a forced delay is happening
45− property int delayMinutes: 0
46−
4744 // Set those to a value greater 0 to restrict the pin length.
4845 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
4946 // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
@@ -91,14 +88,6 @@ Showable {
9188 popup.z = Number.MAX_VALUE
9289 }
9390
94− function showText(text) {
95− // Programmatically enters the given text into the lockscreen
96− if (pinPadLoader.item) {
97− pinPadLoader.item.showText(text);
98− pinPadLoader.waiting = true;
99− }
100− }
101−
10291 Rectangle {
10392 // In case background fails to load
10493 id: backgroundBackup
@@ -141,8 +130,6 @@ Showable {
141130 source: {
142131 if (resetting || !root.required) {
143132 return ""
144− } else if (root.delayMinutes > 0) {
145− return "DelayedLockscreen.qml"
146133 } else if (root.alphaNumeric) {
147134 return "PassphraseLockscreen.qml"
148135 } else {
@@ -197,16 +184,6 @@ Showable {
197184 property: "entryEnabled"
198185 value: !pinPadLoader.waiting
199186 }
200− Binding {
201− target: pinPadLoader.item
202− property: "alphaNumeric"
203− value: root.alphaNumeric
204− }
205− Binding {
206− target: pinPadLoader.item
207− property: "delayMinutes"
208− value: root.delayMinutes
209− }
210187 Binding {
211188 target: pinPadLoader.item
212189 property: "showCancelButton"
qml/Components/PassphraseLockscreen.qml−4
@@ -43,10 +43,6 @@ FocusScope {
4343 }
4444 }
4545
46− function showText(text) {
47− pinentryField.text = text;
48− }
49−
5046 Column {
5147 id: shakeContainer
5248 anchors.horizontalCenter: parent.horizontalCenter
qml/Components/PinLockscreen.qml−4
@@ -46,10 +46,6 @@ FocusScope {
4646 }
4747 }
4848
49− function showText(text) {
50− pinentryField.text = text;
51− }
52−
5349 Keys.onPressed: {
5450 if (pinentryField.text.length == root.maxPinLength)
5551 return;
qml/Components/DelayedLockscreen.qml → qml/Greeter/DelayedLockscreen.qmlrenamed+6−15
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013,2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19−import "../Components"
2019
2120Item {
2221 id: root
@@ -24,12 +23,6 @@ Item {
2423 property int delayMinutes
2524 property bool alphaNumeric
2625
27− signal entered(string passphrase) // unused
28− signal cancel() // unused
29−
30− function clear(playAnimation) {}
31− function showText(text) {}
32−
3326 Column {
3427 anchors.left: parent.left
3528 anchors.right: parent.right
@@ -46,7 +39,7 @@ Item {
4639 horizontalAlignment: Text.AlignHCenter
4740 wrapMode: Text.Wrap
4841 fontSize: "x-large"
49− color: "#f3f3e7"
42+ color: "white"
5043 text: i18n.tr("Device Locked")
5144 }
5245
@@ -60,7 +53,7 @@ Item {
6053 anchors.right: parent.right
6154 horizontalAlignment: Text.AlignHCenter
6255 wrapMode: Text.Wrap
63− color: "#f3f3e7"
56+ color: "white"
6457 text: alphaNumeric ?
6558 i18n.tr("You have been locked out due to too many failed passphrase attempts.") :
6659 i18n.tr("You have been locked out due to too many failed passcode attempts.")
@@ -71,7 +64,7 @@ Item {
7164 anchors.right: parent.right
7265 horizontalAlignment: Text.AlignHCenter
7366 wrapMode: Text.Wrap
74− color: "#f3f3e7"
67+ color: "white"
7568 text: i18n.tr("Please wait %1 minute and then try again…",
7669 "Please wait %1 minutes and then try again…",
7770 root.delayMinutes).arg(root.delayMinutes)
@@ -83,10 +76,8 @@ Item {
8376 }
8477
8578 Icon {
86− // It would be nice to use a less network-specific name,
87− // but this is the only lock icon we have.
88− name: "network-secure"
89− color: "#f3f3e7"
79+ name: "lock"
80+ color: "white"
9081 height: units.gu(4)
9182 width: units.gu(4)
9283 anchors.horizontalCenter: parent.horizontalCenter
qml/Greeter/Greeter.qml+34−21
@@ -72,9 +72,12 @@ Showable {
7272 }
7373 forcedUnlock = false;
7474 if (required) {
75+ if (loader.item) {
76+ loader.item.reset(true /* forceShow */);
77+ }
7578 // Normally loader.onLoaded will select a user, but if we're
7679 // already shown, do it manually.
77− d.selectUser(d.currentIndex, true);
80+ d.selectUser(d.currentIndex, false);
7881 }
7982
8083 // Even though we may already be shown, we want to call show() for its
@@ -194,7 +197,7 @@ Showable {
194197 return;
195198 d.waiting = true;
196199 if (reset) {
197− loader.item.reset();
200+ loader.item.reset(false /* forceShow */);
198201 }
199202 currentIndex = index;
200203 var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);
@@ -238,6 +241,29 @@ Showable {
238241 }
239242 }
240243 }
244+
245+ function showPromptMessage(text, isError) {
246+ // inefficient, but we only rarely deal with messages
247+ var html = text.replace(/&/g, "&amp;")
248+ .replace(/</g, "&lt;")
249+ .replace(/>/g, "&gt;")
250+ .replace(/\n/g, "<br>");
251+ if (isError) {
252+ html = "<font color=\"#df382c\">" + html + "</font>";
253+ }
254+
255+ if (loader.item) {
256+ loader.item.showMessage(html);
257+ }
258+ }
259+
260+ function showFingerprintMessage(msg) {
261+ if (loader.item) {
262+ loader.item.reset(false /* forceShow */);
263+ loader.item.showErrorMessage(msg);
264+ }
265+ showPromptMessage(msg, true);
266+ }
241267 }
242268
243269 onLauncherOffsetChanged: {
@@ -452,20 +478,7 @@ Showable {
452478 onShowGreeter: root.forceShow()
453479 onHideGreeter: root.forcedUnlock = true
454480
455− onShowMessage: {
456− // inefficient, but we only rarely deal with messages
457− var html = text.replace(/&/g, "&amp;")
458− .replace(/</g, "&lt;")
459− .replace(/>/g, "&gt;")
460− .replace(/\n/g, "<br>");
461− if (isError) {
462− html = "<font color=\"#df382c\">" + html + "</font>";
463− }
464−
465− if (loader.item) {
466− loader.item.showMessage(html);
467− }
468− }
481+ onShowMessage: d.showPromptMessage(text, isError)
469482
470483 onShowPrompt: {
471484 if (loader.item) {
@@ -573,10 +586,8 @@ Showable {
573586 if (!d.secureFingerprint) {
574587 d.startUnlock(false /* toTheRight */); // use normal login instead
575588 }
576− if (loader.item) {
577− var msg = d.secureFingerprint ? i18n.tr("Try again") : "";
578− loader.item.showErrorMessage(msg);
579− }
589+ var msg = d.secureFingerprint ? i18n.tr("Try again") : "";
590+ d.showFingerprintMessage(msg);
580591 }
581592
582593 Component.onCompleted: restartOperation()
@@ -594,8 +605,10 @@ Showable {
594605 return;
595606 }
596607 console.log("Identified user by fingerprint:", result);
597− if (loader.item)
608+ if (loader.item) {
609+ loader.item.enabled = false;
598610 loader.item.notifyAuthenticationSucceeded(true /* showFakePassword */);
611+ }
599612 if (root.active)
600613 root.forcedUnlock = true;
601614 }
qml/Greeter/GreeterPrompt.qml+21−7
@@ -38,6 +38,16 @@ FocusScope {
3838 d.enabled = true;
3939 }
4040
41+ function showFakePassword() {
42+ // Just a silly hack for looking like 4 pin numbers got entered, if
43+ // a fingerprint was used and we happen to be using a pin. This was
44+ // a request from Design.
45+ if (isSecret && isPrompt && !isAlphanumeric) {
46+ d.enabled = false;
47+ text = "...."; // actual text doesn't matter
48+ }
49+ }
50+
4151 StyledItem {
4252 id: d
4353
@@ -50,6 +60,12 @@ FocusScope {
5060 : theme.palette.disabled.raisedSecondaryText
5161 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
5262 : theme.palette.disabled.negative
63+
64+ onEnabledChanged: {
65+ if (!enabled) {
66+ fakeLabel.text = passwordInput.displayText;
67+ }
68+ }
5369 }
5470
5571 Rectangle {
@@ -142,15 +158,13 @@ FocusScope {
142158 }
143159 }
144160
145− onAccepted: {
146− if (d.enabled)
147− respond();
148− }
161+ onAccepted: respond()
149162
150163 function respond() {
151− d.enabled = false;
152− fakeLabel.text = displayText;
153− root.responded(text);
164+ if (d.enabled) {
165+ d.enabled = false;
166+ root.responded(text);
167+ }
154168 }
155169
156170 Keys.onEscapePressed: {
qml/Greeter/LoginList.qml+9−3
@@ -29,6 +29,7 @@ StyledItem {
2929 property int currentIndex
3030 property bool locked
3131 property bool waiting
32+ property alias boxVerticalOffset: highlightItem.y
3233
3334 readonly property alias passwordInput: passwordInput
3435 readonly property int numAboveBelow: 4
@@ -84,6 +85,10 @@ StyledItem {
8485 root.resetAuthentication();
8586 }
8687
88+ function showFakePassword() {
89+ passwordInput.showFakePassword();
90+ }
91+
8792 QtObject {
8893 id: d
8994
@@ -123,12 +128,12 @@ StyledItem {
123128
124129 LoginAreaContainer {
125130 id: highlightItem
131+ objectName: "highlightItem"
126132 anchors {
127133 left: parent.left
128134 leftMargin: units.gu(2)
129135 right: parent.right
130136 rightMargin: units.gu(2)
131− verticalCenter: parent.verticalCenter
132137 }
133138
134139 height: root.highlightedHeight
@@ -142,8 +147,8 @@ StyledItem {
142147 anchors.leftMargin: units.gu(2)
143148 anchors.rightMargin: units.gu(2)
144149
145− preferredHighlightBegin: userList.height / 2 - root.highlightedHeight / 2
146− preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
150+ preferredHighlightBegin: highlightItem.y
151+ preferredHighlightEnd: highlightItem.y
147152 highlightRangeMode: ListView.StrictlyEnforceRange
148153 highlightMoveDuration: root.moveDuration
149154 flickDeceleration: 10000
@@ -352,6 +357,7 @@ StyledItem {
352357
353358 WrongPasswordAnimation {
354359 id: wrongPasswordAnimation
360+ objectName: "wrongPasswordAnimation"
355361 target: passwordInput
356362 }
357363 }
qml/Greeter/NarrowView.qml+147−61
@@ -15,7 +15,9 @@
1515 */
1616
1717import QtQuick 2.4
18+import QtQuick.Window 2.2
1819import Ubuntu.Components 1.3
20+import Ubuntu.Telephony 0.1 as Telephony
1921import "../Components"
2022
2123FocusScope {
@@ -23,13 +25,13 @@ FocusScope {
2325
2426 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
2527 property alias launcherOffset: coverPage.launcherOffset
26− property int currentIndex // unused
27− property alias delayMinutes: lockscreen.delayMinutes
28+ property alias currentIndex: loginList.currentIndex
29+ property alias delayMinutes: delayedLockscreen.delayMinutes
2830 property alias backgroundTopMargin: coverPage.backgroundTopMargin
2931 property url background
3032 property bool locked
31− property bool alphanumeric
32− property var userModel // unused
33+ property alias alphanumeric: loginList.alphanumeric
34+ property alias userModel: loginList.model
3335 property alias infographicModel: coverPage.infographicModel
3436 property string sessionToStart
3537 property bool waiting
@@ -37,21 +39,25 @@ FocusScope {
3739 readonly property bool required: coverPage.required || lockscreen.required
3840 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
3941
40− signal selected(int index) // unused
42+ // so that it can be replaced in tests with a mock object
43+ property var inputMethod: Qt.inputMethod
44+
45+ signal selected(int index)
4146 signal responded(string response)
4247 signal tease()
4348 signal emergencyCall()
4449
4550 function showMessage(html) {
46− // TODO
51+ loginList.showMessage(html);
4752 }
4853
4954 function showPrompt(text, isSecret, isDefaultPrompt) {
50− lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
51− lockscreen.maybeShow();
55+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
5256 }
5357
5458 function showLastChance() {
59+ /* TODO: when we finish support for resetting device after too many
60+ failed logins, we should re-add this popup.
5561 var title = lockscreen.alphaNumeric ?
5662 i18n.tr("Sorry, incorrect passphrase.") :
5763 i18n.tr("Sorry, incorrect passcode.");
@@ -60,6 +66,7 @@ FocusScope {
6066 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
6167 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."));
6268 lockscreen.showInfoPopup(title, text);
69+ */
6370 }
6471
6572 function hide() {
@@ -68,27 +75,24 @@ FocusScope {
6875 }
6976
7077 function notifyAuthenticationSucceeded(showFakePassword) {
71− // When using an alternate log in mechanism like fingerprints, the
72− // design is it looks like the user entered a passcode.
73− if (!alphanumeric && showFakePassword) {
74− lockscreen.showText("...."); // actual text doesn't matter, we show bullets
78+ if (showFakePassword) {
79+ loginList.showFakePassword();
7580 }
7681 }
7782
7883 function notifyAuthenticationFailed() {
79− lockscreen.customError = "";
80− lockscreen.clear(true);
84+ loginList.showError();
8185 }
8286
8387 function showErrorMessage(msg) {
8488 coverPage.showErrorMessage(msg);
85− lockscreen.customError = msg ? msg : " "; // avoid default message
86− lockscreen.clear(true);
8789 }
8890
89− function reset() {
90− lockscreen.customError = "";
91− coverPage.show();
91+ function reset(forceShow) {
92+ loginList.reset();
93+ if (forceShow) {
94+ coverPage.show();
95+ }
9296 }
9397
9498 function tryToUnlock(toTheRight) {
@@ -110,48 +114,62 @@ FocusScope {
110114 }
111115 }
112116
113− Lockscreen {
117+ Showable {
114118 id: lockscreen
115119 objectName: "lockscreen"
116−
120+ anchors.fill: parent
117121 shown: false
122+
118123 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
119124 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
120− anchors.fill: parent
121− visible: required
122− enabled: !coverPage.shown
123− background: root.background
124− darkenBackground: 0.4
125− alphaNumeric: root.alphanumeric
126− minPinLength: 4
127− maxPinLength: 4
128−
129− property string promptText
130− infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
131− alphaNumeric ? i18n.tr("Enter passphrase") :
132− i18n.tr("Enter passcode")
133−
134− property string customError
135− errorText: customError !== "" ? customError :
136− promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
137− alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
138− i18n.ctr("passphrase", "Please re-enter") :
139− i18n.tr("Sorry, incorrect passcode")
140−
141− onEntered: root.responded(passphrase)
142− onCancel: coverPage.show()
143− onEmergencyCall: root.emergencyCall()
144−
145− onEnabledChanged: {
146− if (enabled) {
147− lockscreen.forceActiveFocus();
125+
126+ CrossFadeImage {
127+ id: lockscreenBackground
128+ objectName: "lockscreenBackground"
129+ anchors {
130+ fill: parent
131+ topMargin: root.backgroundTopMargin
148132 }
133+ source: root.background
134+ fillMode: Image.PreserveAspectCrop
135+ // Limit how much memory we'll reserve for this image
136+ sourceSize.height: Math.max(Screen.width, Screen.height)
137+ sourceSize.width: 0
149138 }
150139
151− onVisibleChanged: {
152− if (visible) {
153− lockscreen.forceActiveFocus();
140+ // Darken background to match CoverPage
141+ Rectangle {
142+ anchors.fill: parent
143+ color: "black"
144+ opacity: 0.4
145+ }
146+
147+ LoginList {
148+ id: loginList
149+ objectName: "loginList"
150+
151+ anchors {
152+ horizontalCenter: parent.horizontalCenter
153+ top: parent.top
154+ bottom: parent.bottom
154155 }
156+ width: units.gu(40)
157+ boxVerticalOffset: units.gu(14)
158+ enabled: !coverPage.shown && visible
159+ visible: !delayedLockscreen.visible
160+
161+ locked: root.locked
162+
163+ onSelected: if (enabled) root.selected(index)
164+ onResponded: root.responded(response)
165+ }
166+
167+ DelayedLockscreen {
168+ id: delayedLockscreen
169+ objectName: "delayedLockscreen"
170+ anchors.fill: parent
171+ visible: delayMinutes > 0
172+ alphaNumeric: loginList.alphanumeric
155173 }
156174
157175 function maybeShow() {
@@ -179,15 +197,9 @@ FocusScope {
179197
180198 onShowProgressChanged: {
181199 if (showProgress === 1) {
182− lockscreen.reset();
183− }
184−
185− if (showProgress === 0) {
186− if (root.locked) {
187− lockscreen.clear(false); // to reset focus if necessary
188− } else {
189− root.responded("");
190− }
200+ loginList.reset();
201+ } else if (showProgress === 0) {
202+ loginList.tryToUnlock();
191203 }
192204 }
193205
@@ -199,4 +211,78 @@ FocusScope {
199211 }
200212 }
201213 }
214+
215+ StyledItem {
216+ id: bottomBar
217+ visible: lockscreen.shown
218+ height: units.gu(4)
219+
220+ anchors.left: parent.left
221+ anchors.right: parent.right
222+ anchors.top: parent.bottom
223+ anchors.topMargin: - height * (1 - coverPage.showProgress)
224+ - (inputMethod && inputMethod.visible ?
225+ inputMethod.keyboardRectangle.height : 0)
226+
227+ Rectangle {
228+ color: UbuntuColors.porcelain // matches OSK background
229+ anchors.fill: parent
230+ }
231+
232+ Label {
233+ text: i18n.tr("Cancel")
234+ anchors.left: parent.left
235+ anchors.leftMargin: units.gu(2)
236+ anchors.top: parent.top
237+ anchors.bottom: parent.bottom
238+ verticalAlignment: Text.AlignVCenter
239+ font.weight: Font.Light
240+ fontSize: "small"
241+ color: UbuntuColors.slate
242+
243+ AbstractButton {
244+ anchors.fill: parent
245+ anchors.leftMargin: -units.gu(2)
246+ anchors.rightMargin: -units.gu(2)
247+ onClicked: coverPage.show()
248+ }
249+ }
250+
251+ Label {
252+ objectName: "emergencyCallLabel"
253+ text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
254+ anchors.right: parent.right
255+ anchors.rightMargin: units.gu(2)
256+ anchors.top: parent.top
257+ anchors.bottom: parent.bottom
258+ verticalAlignment: Text.AlignVCenter
259+ font.weight: Font.Light
260+ fontSize: "small"
261+ color: UbuntuColors.slate
262+ // TODO: uncomment once bug 1616538 is fixed
263+ // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
264+ enabled: visible
265+
266+ AbstractButton {
267+ anchors.fill: parent
268+ anchors.leftMargin: -units.gu(2)
269+ anchors.rightMargin: -units.gu(2)
270+ onClicked: root.emergencyCall()
271+ }
272+ }
273+ }
274+
275+ // FIXME: It's difficult to keep something tied closely to the OSK (bug
276+ // 1616163). But as a hack to avoid the background peeking out,
277+ // we add an extra Rectangle that just serves to hide the background
278+ // during OSK animations.
279+ Rectangle {
280+ visible: bottomBar.visible
281+ height: inputMethod && inputMethod.visible ?
282+ inputMethod.keyboardRectangle.height : 0
283+ anchors.bottom: parent.bottom
284+ anchors.left: parent.left
285+ anchors.right: parent.right
286+ color: UbuntuColors.porcelain
287+ }
202288}
qml/Greeter/WideView.qml+11−5
@@ -51,7 +51,7 @@ FocusScope {
5151 loginList.showError();
5252 }
5353
54− function reset() {
54+ function reset(forceShow) {
5555 loginList.reset();
5656 }
5757
@@ -83,8 +83,10 @@ FocusScope {
8383 coverPage.hide();
8484 }
8585
86− function notifyAuthenticationSucceeded() {
87− // Nothing needed
86+ function notifyAuthenticationSucceeded(showFakePassword) {
87+ if (showFakePassword) {
88+ loginList.showFakePassword();
89+ }
8890 }
8991
9092 function showLastChance() {
@@ -122,15 +124,19 @@ FocusScope {
122124 id: loginList
123125 objectName: "loginList"
124126
125− height: inputMethod && inputMethod.visible ?
126− parent.height - inputMethod.keyboardRectangle.height : parent.height
127127 width: units.gu(40)
128128 anchors {
129129 left: parent.left
130130 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
131131 top: parent.top
132+ bottom: parent.bottom
132133 }
133134
135+ boxVerticalOffset: (height - highlightedHeight -
136+ (inputMethod && inputMethod.visible ?
137+ inputMethod.keyboardRectangle.height : 0)) / 2
138+ Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
139+
134140 model: root.userModel
135141 currentSession: LightDMService.greeter.defaultSession
136142 onResponded: root.responded(response)
tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.cpp+27
@@ -20,6 +20,8 @@
2020
2121MockTelepathyHelper::MockTelepathyHelper(QObject *parent)
2222 : QObject(parent)
23+ , m_ready(true)
24+ , m_emergencyCallsAvailable(true)
2325{
2426}
2527
@@ -33,3 +35,28 @@ void MockTelepathyHelper::registerChannelObserver(const QString& name)
3335{
3436 Q_UNUSED(name);
3537}
38+
39+bool MockTelepathyHelper::ready() const
40+{
41+ return m_ready;
42+}
43+
44+void MockTelepathyHelper::setReady(bool value)
45+{
46+ if (m_ready != value) {
47+ m_ready = value;
48+ Q_EMIT readyChanged();
49+ }
50+}
51+bool MockTelepathyHelper::emergencyCallsAvailable() const
52+{
53+ return m_emergencyCallsAvailable;
54+}
55+
56+void MockTelepathyHelper::setEmergencyCallsAvailable(bool value)
57+{
58+ if (m_emergencyCallsAvailable != value) {
59+ m_emergencyCallsAvailable = value;
60+ Q_EMIT emergencyCallsAvailableChanged();
61+ }
62+}
tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.h+22
@@ -24,6 +24,14 @@
2424class MockTelepathyHelper : public QObject
2525{
2626 Q_OBJECT
27+ Q_PROPERTY(bool ready
28+ READ ready
29+ WRITE setReady // only in mock
30+ NOTIFY readyChanged)
31+ Q_PROPERTY(bool emergencyCallsAvailable
32+ READ emergencyCallsAvailable
33+ WRITE setEmergencyCallsAvailable // only in mock
34+ NOTIFY emergencyCallsAvailableChanged)
2735 Q_DISABLE_COPY(MockTelepathyHelper)
2836public:
2937 MockTelepathyHelper(QObject *parent = 0);
@@ -31,6 +39,20 @@ public:
3139 static MockTelepathyHelper *instance();
3240
3341 Q_INVOKABLE void registerChannelObserver(const QString& name);
42+
43+ bool ready() const;
44+ void setReady(bool value);
45+
46+ bool emergencyCallsAvailable() const;
47+ void setEmergencyCallsAvailable(bool value);
48+
49+Q_SIGNALS:
50+ void readyChanged();
51+ void emergencyCallsAvailableChanged();
52+
53+private:
54+ bool m_ready;
55+ bool m_emergencyCallsAvailable;
3456};
3557
3658#endif // MOCKCONTACTWATCHER_H
tests/qmltests/Components/tst_Lockscreen.qml−38
@@ -37,7 +37,6 @@ Rectangle {
3737 alphaNumeric: pinPadCheckBox.checked
3838 minPinLength: minPinLengthTextField.text
3939 maxPinLength: maxPinLengthTextField.text
40− delayMinutes: delayMinutesTextField.text
4140 background: "../../../qml/graphics/phone_background.jpg"
4241 }
4342
@@ -109,16 +108,6 @@ Rectangle {
109108 text: "Retries left"
110109 }
111110 }
112− Row {
113− TextField {
114− id: delayMinutesTextField
115− width: units.gu(7)
116− text: "0"
117− }
118− Label {
119− text: "Delay Minutes"
120− }
121− }
122111 Label {
123112 id: pinLabel
124113 anchors.verticalCenter: parent.verticalCenter
@@ -172,7 +161,6 @@ Rectangle {
172161
173162 function cleanup() {
174163 lockscreen.clear(false);
175− delayMinutesTextField.text = "0"
176164 enteredLabel.text = "";
177165
178166 // Reset sizing
@@ -455,32 +443,6 @@ Rectangle {
455443 compare(label.text, data.text);
456444 }
457445
458− function test_delayMinutes() {
459− delayMinutesTextField.text = "4"
460− waitForLockscreenReady()
461− var label = findChild(lockscreen, "deviceLockedLabel")
462− compare(label.text, "Device Locked")
463− }
464−
465− function test_showText_data() {
466− return [
467− { tag: "alphanumeric", alphanumeric: true },
468− { tag: "pinPad", alphanumeric: false },
469− ]
470− }
471−
472− function test_showText(data) {
473− pinPadCheckBox.checked = data.alphanumeric;
474− waitForLockscreenReady();
475−
476− lockscreen.showText("test");
477−
478− var pinPadLoader = findChild(lockscreen, "pinPadLoader");
479− verify(pinPadLoader.waiting);
480− compare(enteredLabel.text, ""); // no entered signal should occur
481− compare(lockscreen.passphrase, "test");
482− }
483−
484446 function test_resize_data() {
485447 return [
486448 { tag: "small", width: units.gu(40), height: units.gu(76) },
tests/qmltests/Greeter/TestView.qml+3−3
@@ -51,7 +51,7 @@ Item {
5151 signal _notifyAuthenticationSucceededCalled(bool showFakePassword)
5252 signal _notifyAuthenticationFailedCalled()
5353 signal _showErrorMessageCalled(string msg)
54− signal _resetCalled()
54+ signal _resetCalled(bool forceShow)
5555 signal _tryToUnlockCalled(bool toTheRight)
5656
5757 function showMessage(html) {
@@ -84,8 +84,8 @@ Item {
8484 _showErrorMessageCalled(msg);
8585 }
8686
87− function reset() {
88− _resetCalled();
87+ function reset(forceShow) {
88+ _resetCalled(forceShow);
8989 }
9090
9191 function tryToUnlock(toTheRight) {
tests/qmltests/Greeter/tst_Greeter.qml+1
@@ -559,6 +559,7 @@ Item {
559559
560560 LightDM.Greeter.showGreeter();
561561 compare(viewResetSpy.count, 1);
562+ compare(viewResetSpy.signalArguments[0][0], true);
562563 tryCompare(viewShowPromptSpy, "count", 1);
563564 }
564565
tests/qmltests/Greeter/tst_NarrowView.qml+34−11
@@ -20,6 +20,7 @@ import ".."
2020import "../../../qml/Greeter"
2121import LightDM.IntegratedLightDM 0.1 as LightDM
2222import Ubuntu.Components 1.3
23+import Ubuntu.Telephony 0.1 as Telephony
2324import Unity.Test 0.1 as UT
2425
2526Item {
@@ -293,6 +294,8 @@ Item {
293294 function init() {
294295 view.currentIndex = 0; // break binding with text field
295296
297+ telepathyHelper.ready = true;
298+ telepathyHelper.emergencyCallsAvailable = true;
296299 selectedSpy.clear();
297300 respondedSpy.clear();
298301 teaseSpy.clear();
@@ -328,14 +331,6 @@ Item {
328331 waitForRendering(view);
329332 }
330333
331− function enterPin(pin) {
332− for (var i = 0; i < pin.length; ++i) {
333− var character = pin.charAt(i);
334− var button = findChild(view, "pinPadButton" + character);
335− tap(button);
336− }
337− }
338−
339334 function test_tease_data() {
340335 return [
341336 {tag: "left", x: 0, offset: 0, count: 1},
@@ -352,8 +347,9 @@ Item {
352347
353348 function test_respondedWithPin() {
354349 view.locked = true;
350+ view.showPrompt("", true, true);
355351 swipeAwayCover();
356− enterPin("1234");
352+ typeString("1234");
357353 compare(respondedSpy.count, 1);
358354 compare(respondedSpy.signalArguments[0][0], "1234");
359355 }
@@ -361,6 +357,7 @@ Item {
361357 function test_respondedWithPassphrase() {
362358 view.locked = true;
363359 view.alphanumeric = true;
360+ view.showPrompt("", true, true);
364361 swipeAwayCover();
365362 typeString("test");
366363 keyClick(Qt.Key_Enter);
@@ -382,12 +379,38 @@ Item {
382379 compare(respondedSpy.signalArguments[0][0], "");
383380 }
384381
385− function test_emergencyCall() {
382+ function test_emergencyCall_data() {
383+ return [
384+ {tag: "phone", available: true},
385+ {tag: "desktop", available: false},
386+ ];
387+ }
388+ function test_emergencyCall(data) {
389+ telepathyHelper.emergencyCallsAvailable = data.available;
386390 view.locked = true;
387391 swipeAwayCover();
388392 var emergencyCallLabel = findChild(view, "emergencyCallLabel");
389393 tap(emergencyCallLabel);
390− compare(emergencySpy.count, 1);
394+ if (!data.available) {
395+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
396+ }
397+ compare(emergencyCallLabel.visible, data.available ? true : false);
398+ compare(emergencySpy.count, data.available ? 1 : 0);
399+ }
400+
401+ function test_emergencyCallAvailability() {
402+ view.locked = true;
403+ var emergencyCallLabel = findChild(view, "emergencyCallLabel");
404+ verify(emergencyCallLabel.visible);
405+
406+ telepathyHelper.emergencyCallsAvailable = false;
407+ telepathyHelper.ready = true;
408+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
409+ verify(!emergencyCallLabel.visible);
410+
411+ telepathyHelper.emergencyCallsAvailable = true;
412+ telepathyHelper.ready = false;
413+ verify(!emergencyCallLabel.visible);
391414 }
392415
393416 function test_fullyShown() {
tests/qmltests/Greeter/tst_WideView.qml+8−4
@@ -641,17 +641,21 @@ StyledItem {
641641
642642 keyboardVisibleCheckBox.checked = true;
643643
644− tryCompare(loginList, "height", view.height - view.inputMethod.keyboardRectangle.height);
644+ var halfway = (view.height - loginList.highlightedHeight) / 2;
645+ var halfwayWithOsk = halfway - view.inputMethod.keyboardRectangle.height / 2;
646+ tryCompare(loginList, "boxVerticalOffset", halfwayWithOsk);
647+
648+ var highlightItem = findChild(loginList, "highlightItem");
645649 tryCompareFunction( function() {
646− var loginListRect = loginList.mapToItem(view, 0, 0, loginList.width, loginList.height);
647− return loginListRect.y + loginListRect.height <= view.inputMethod.keyboardRectangle.y;
650+ var highlightRect = highlightItem.mapToItem(view, 0, 0, highlightItem.width, highlightItem.height);
651+ return highlightRect.y + highlightRect.height <= view.inputMethod.keyboardRectangle.y;
648652 }, true);
649653
650654 // once the vkb goes away, loginList goes back to its full height
651655
652656 keyboardVisibleCheckBox.checked = false;
653657
654− tryCompare(loginList, "height", view.height);
658+ tryCompare(loginList, "boxVerticalOffset", halfway);
655659 }
656660
657661 function test_passphrase() {
tests/qmltests/tst_ShellWithPin.qml+28−39
@@ -154,7 +154,6 @@ Item {
154154 var greeter = findChild(shell, "greeter");
155155 sessionSpy.target = greeter;
156156 swipeAwayGreeter(true);
157− waitForLockscreen()
158157 greeter.failedLoginsDelayAttempts = -1;
159158 greeter.maxFailedLogins = -1;
160159
@@ -214,20 +213,16 @@ Item {
214213 }
215214 }
216215
217− function waitForLockscreen() {
218− var lockscreen = findChild(shell, "lockscreen");
219− var pinPadLoader = findChild(lockscreen, "pinPadLoader");
220− tryCompare(pinPadLoader, "status", Loader.Ready)
221− waitForRendering(lockscreen)
216+ function enterPin(pin) {
217+ typeString(pin);
222218 }
223219
224− function enterPin(pin) {
225− for (var i = 0; i < pin.length; ++i) {
226− var character = pin.charAt(i)
227− var button = findChild(shell, "pinPadButton" + character)
228− tryCompare(button, "enabled", true);
229− tap(button)
230− }
220+ function showGreeter() {
221+ LightDM.Greeter.showGreeter();
222+ tryCompare(shell, "waitingOnGreeter", false);
223+ var coverPage = findChild(shell, "coverPage");
224+ tryCompare(coverPage, "showProgress", 1);
225+ removeTimeConstraintsFromSwipeAreas(shell);
231226 }
232227
233228 function confirmLockedApp(app) {
@@ -248,7 +243,7 @@ Item {
248243 LightDM.Greeter.hideGreeter();
249244 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
250245
251− LightDM.Greeter.showGreeter();
246+ showGreeter();
252247 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
253248
254249 LightDM.Greeter.hideGreeter();
@@ -293,7 +288,7 @@ Item {
293288
294289 // Cancel emergency mode, and go back to normal
295290 waitForRendering(greeter)
296− LightDM.Greeter.showGreeter()
291+ showGreeter()
297292
298293 tryCompare(greeter, "shown", true)
299294 tryCompare(greeter, "lockedApp", "")
@@ -336,7 +331,7 @@ Item {
336331 enterPin("1234");
337332 tryCompare(tutorial, "paused", false);
338333
339− LightDM.Greeter.showGreeter();
334+ showGreeter();
340335 verify(tutorial.paused);
341336
342337 swipeAwayGreeter(true);
@@ -358,22 +353,23 @@ Item {
358353 var greeter = findChild(shell, "greeter");
359354 greeter.failedLoginsDelayAttempts = 3;
360355
361− var placeHolder = findChild(shell, "wrongNoticeLabel")
362− tryCompare(placeHolder, "text", "")
356+ var wrongPasswordAnimation = findInvisibleChild(shell, "wrongPasswordAnimation");
363357
364358 enterPin("1111")
365− tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
359+ verify(wrongPasswordAnimation.running);
366360
367361 enterPin("1111")
368− tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
362+ verify(wrongPasswordAnimation.running);
369363
370− var lockscreen = findChild(shell, "lockscreen")
371− tryCompare(lockscreen, "delayMinutes", 0)
364+ var delayedLockscreen = findChild(shell, "delayedLockscreen");
365+ tryCompare(delayedLockscreen, "delayMinutes", 0);
372366 enterPin("1111")
373− tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
367+ tryCompare(delayedLockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
374368 }
375369
376370 function test_factoryReset() {
371+ skip("Factory reset support is not finished")
372+
377373 maxRetriesTextField.text = "3"
378374 resetSpy.clear()
379375
@@ -404,8 +400,7 @@ Item {
404400 compare(shell.usageScenario, "phone");
405401 compare(applicationsDisplayLoader.usageScenario, "phone");
406402
407− var lockscreen = findChild(shell, "lockscreen")
408− lockscreen.emergencyCall()
403+ tap(findChild(shell, "emergencyCallLabel"));
409404 confirmLockedApp("dialer-app")
410405
411406 // OK, we're in. Now try (but fail) to switch to tablet mode
@@ -432,16 +427,13 @@ Item {
432427 // - Should be back in emergency mode dialer
433428
434429 var greeter = findChild(shell, "greeter");
435− var lockscreen = findChild(shell, "lockscreen");
436− verify(lockscreen);
437430
438− lockscreen.emergencyCall();
431+ tap(findChild(shell, "emergencyCallLabel"));
439432 confirmLockedApp("dialer-app");
440433 callManager.foregroundCall = phoneCall;
441434
442− LightDM.Greeter.showGreeter();
443− lockscreen = findChild(shell, "lockscreen");
444− verify(lockscreen);
435+ showGreeter();
436+ var lockscreen = findChild(shell, "lockscreen");
445437 tryCompare(lockscreen, "shown", true);
446438 tryCompare(greeter, "hasLockedApp", false);
447439
@@ -484,7 +476,7 @@ Item {
484476 }
485477
486478 function test_focusRequestedHidesCoverPage() {
487− LightDM.Greeter.showGreeter();
479+ showGreeter();
488480
489481 var app = ApplicationManager.startApplication("gallery-app");
490482 // wait until the app is fully loaded (ie, real surface replaces splash screen)
@@ -496,8 +488,7 @@ Item {
496488 tryCompare(coverPage, "showProgress", 0);
497489 tryCompare(greeter, "fullyShown", true);
498490
499− LightDM.Greeter.showGreeter();
500− tryCompare(coverPage, "showProgress", 1);
491+ showGreeter();
501492
502493 // Make sure focusing same app triggers same behavior
503494 ApplicationManager.requestFocusApplication("gallery-app");
@@ -571,9 +562,7 @@ Item {
571562 only after its idle timeout is triggered.
572563 */
573564 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
574− LightDM.Greeter.showGreeter();
575− var coverPage = testCase.findChild(shell, "coverPage");
576− tryCompare(coverPage, "showProgress", 1);
565+ showGreeter();
577566
578567 var launcher = testCase.findChild(shell, "launcher")
579568 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
@@ -589,6 +578,7 @@ Item {
589578 tryCompare(launcherPanel, "x", 0);
590579
591580 // Greeter should be displaced
581+ var coverPage = findChild(shell, "coverPage");
592582 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
593583
594584 touchRelease(shell, toX, shell.height * 0.5);
@@ -611,8 +601,7 @@ Item {
611601 tryCompare(shell, "mainApp", galleryApp);
612602
613603 // Show greeter
614− LightDM.Greeter.showGreeter();
615− tryCompare(coverPage, "showProgress", 1);
604+ showGreeter();
616605
617606 // Swipe cover page away
618607 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);