on xenial help sign in
Implement latest visual designs for passphrase lockscreen.

The visual specs are here: https://drive.google.com/a/canonical.com/folderview?id=0B8I8ZVKH-8Ssb2Zhcm9kUE9TLVE This branch does a few things: - Total redesign for passphrase lockscreen. It now has no visible text box nor says "Hello". - Total redesign of how we inform the user that they have attempted to log in too many times. Now instead of saying something like "please wait" in the same lockscreen, we show a custom lockscreen with a longer message and a lock icon. - Tweak the spacing on the passcode lockscreen. - Adds a phone icon to the "Emergency Call" label on the bottom -- and makes that label move up if the OSK covers it. - Changes the "Swipe to Unlock" text to just "Unlock" with arrow brackets on either side. Approved by: Michael Zanetti

commita281d95f19d1e92928f891498f72cd8c3c8d696b authorMichael Terry <michael.terry@canonical.com> dateTue 30 Sep 2014, 16:56 (12 years ago) parent7ae0699596a275
10 files changed+379−208
qml/Components/DelayedLockscreen.qml+92 qml/Components/Lockscreen.qml+115−96 qml/Components/PassphraseLockscreen.qml+96−43 qml/Components/PinLockscreen.qml+7−5 qml/Greeter/Greeter.qml+1−1 qml/Shell.qml+30−9 tests/autopilot/unity8/shell/tests/test_lock_screen.py+3−1 tests/qmltests/CMakeLists.txt+1−1 tests/qmltests/Greeter/tst_Lockscreen.qml → tests/qmltests/Components/tst_Lockscreen.qml+31−51 tests/qmltests/tst_ShellWithPin.qml+3−1
qml/Components/DelayedLockscreen.qmlnew file+92
@@ -0,0 +1,92 @@
1+/*
2+ * Copyright (C) 2013,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 Ubuntu.Components 0.1
19+import "../Components"
20+
21+Item {
22+ id: root
23+ anchors.fill: parent
24+
25+ property int delayMinutes
26+ property bool alphaNumeric
27+
28+ function clear(playAnimation) {}
29+
30+ Column {
31+ anchors.left: parent.left
32+ anchors.right: parent.right
33+ anchors.leftMargin: units.gu(4)
34+ anchors.rightMargin: units.gu(4)
35+ anchors.verticalCenter: parent.verticalCenter
36+ spacing: units.gu(2)
37+
38+ Label {
39+ id: deviceLockedLabel
40+ objectName: "deviceLockedLabel"
41+ anchors.left: parent.left
42+ anchors.right: parent.right
43+ horizontalAlignment: Text.AlignHCenter
44+ wrapMode: Text.Wrap
45+ fontSize: "x-large"
46+ color: "#f3f3e7"
47+ text: i18n.tr("Device Locked")
48+ }
49+
50+ Item { // spacer
51+ width: units.gu(1)
52+ height: units.gu(1)
53+ }
54+
55+ Label {
56+ anchors.left: parent.left
57+ anchors.right: parent.right
58+ horizontalAlignment: Text.AlignHCenter
59+ wrapMode: Text.Wrap
60+ color: "#f3f3e7"
61+ text: alphaNumeric ?
62+ i18n.tr("You have been locked out due to too many failed passphrase attempts.") :
63+ i18n.tr("You have been locked out due to too many failed passcode attempts.")
64+ }
65+
66+ Label {
67+ anchors.left: parent.left
68+ anchors.right: parent.right
69+ horizontalAlignment: Text.AlignHCenter
70+ wrapMode: Text.Wrap
71+ color: "#f3f3e7"
72+ text: i18n.tr("Please wait %1 minute and then try again…",
73+ "Please wait %1 minutes and then try again…",
74+ root.delayMinutes).arg(root.delayMinutes)
75+ }
76+
77+ Item { // spacer
78+ width: units.gu(1)
79+ height: units.gu(1)
80+ }
81+
82+ Icon {
83+ // It would be nice to use a less network-specific name,
84+ // but this is the only lock icon we have.
85+ name: "network-secure"
86+ color: "#f3f3e7"
87+ height: units.gu(4)
88+ width: units.gu(4)
89+ anchors.horizontalCenter: parent.horizontalCenter
90+ }
91+ }
92+}
qml/Components/Lockscreen.qml+115−96
@@ -28,13 +28,14 @@ Showable {
2828 property string infoText: ""
2929
3030 // Retries text (e.g. 3 retries left)
31+ // (This is not currently used, but will be necessary for SIM unlock screen)
3132 property string retryText: ""
3233
3334 // The text to be displayed in case the login failed
3435 property string errorText: ""
3536
36− // In case the Lockscreen can show a greeter message, this is the username
37− property string username: ""
37+ // If > 0, a forced delay is happening
38+ property int delayMinutes: 0
3839
3940 // Set those to a value greater 0 to restrict the pin length.
4041 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
@@ -54,16 +55,7 @@ Showable {
5455 signal emergencyCall()
5556 signal infoPopupConfirmed()
5657
57− onRequiredChanged: {
58− if (required && pinPadLoader.item) {
59− clear(false)
60− }
61− }
62−
63− function forceDelay(delay) {
64− forcedDelayTimer.interval = delay
65− forcedDelayTimer.start()
66− }
58+ onActiveFocusChanged: if (activeFocus && pinPadLoader.item) pinPadLoader.item.forceActiveFocus()
6759
6860 function reset() {
6961 // This causes the loader below to destry and recreate the source
@@ -83,11 +75,6 @@ Showable {
8375 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
8476 }
8577
86− Timer {
87− id: forcedDelayTimer
88− onTriggered: pinPadLoader.showWrongText = false
89− }
90−
9178 Rectangle {
9279 // In case background fails to load or is undefined
9380 id: backgroundBackup
@@ -107,86 +94,100 @@ Showable {
10794
10895 MouseArea {
10996 anchors.fill: root
97+ onClicked: {
98+ if (pinPadLoader.item)
99+ pinPadLoader.item.forceActiveFocus()
100+ }
110101 }
111102
112− Loader {
113− id: pinPadLoader
114− objectName: "pinPadLoader"
115− anchors {
116− left: parent.left
117− right: parent.right
118− verticalCenter: parent.verticalCenter
119− verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : 0
120− }
121− property bool resetting: false
122− property bool waiting: false
123− property bool showWrongText: false
124−
125− source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
126− onSourceChanged: {
127− waiting = false
128− showWrongText = false
129− }
130− onLoaded: {
131− if (forcedDelayTimer.running) {
132− pinPadLoader.item.clear(true)
103+ FocusScope {
104+ id: loaderScope
105+ anchors.fill: parent
106+
107+ Loader {
108+ id: pinPadLoader
109+ objectName: "pinPadLoader"
110+ anchors.fill: parent
111+ property bool resetting: false
112+ property bool waiting: false
113+ property bool showWrongText: false
114+
115+ source: {
116+ if (resetting || !root.required) {
117+ return ""
118+ } else if (root.delayMinutes > 0) {
119+ return "DelayedLockscreen.qml"
120+ } else if (root.alphaNumeric) {
121+ return "PassphraseLockscreen.qml"
122+ } else {
123+ return "PinLockscreen.qml"
124+ }
125+ }
126+ onSourceChanged: {
127+ waiting = false
128+ showWrongText = false
129+ if (loaderScope.activeFocus && pinPadLoader.item)
130+ pinPadLoader.item.forceActiveFocus()
133131 }
134− }
135132
136− Connections {
137− target: pinPadLoader.item
133+ Connections {
134+ target: pinPadLoader.item
138135
139− onEntered: {
140− pinPadLoader.waiting = true
141− root.entered(passphrase);
142− }
136+ onEntered: {
137+ pinPadLoader.waiting = true
138+ root.entered(passphrase);
139+ }
143140
144− onCancel: {
145− root.cancel()
141+ onCancel: {
142+ root.cancel()
143+ }
146144 }
147− }
148145
149− Binding {
150− target: pinPadLoader.item
151− property: "minPinLength"
152− value: root.minPinLength
153− }
154− Binding {
155− target: pinPadLoader.item
156− property: "maxPinLength"
157− value: root.maxPinLength
158− }
159− Binding {
160− target: pinPadLoader.item
161− property: "infoText"
162− value: root.infoText
163− }
164− Binding {
165− target: pinPadLoader.item
166− property: "retryText"
167− value: forcedDelayTimer.running ? i18n.tr("Please wait") : root.retryText
168− }
169− Binding {
170− target: pinPadLoader.item
171− property: "errorText"
172− value: forcedDelayTimer.running ? i18n.tr("Too many incorrect attempts") :
173− (pinPadLoader.showWrongText ? root.errorText : "")
174− }
175− Binding {
176− target: pinPadLoader.item
177− property: "username"
178− value: root.username
179− }
180− Binding {
181− target: pinPadLoader.item
182− property: "entryEnabled"
183− value: !pinPadLoader.waiting && !forcedDelayTimer.running
146+ Binding {
147+ target: pinPadLoader.item
148+ property: "minPinLength"
149+ value: root.minPinLength
150+ }
151+ Binding {
152+ target: pinPadLoader.item
153+ property: "maxPinLength"
154+ value: root.maxPinLength
155+ }
156+ Binding {
157+ target: pinPadLoader.item
158+ property: "infoText"
159+ value: root.infoText
160+ }
161+ Binding {
162+ target: pinPadLoader.item
163+ property: "retryText"
164+ value: root.retryText
165+ }
166+ Binding {
167+ target: pinPadLoader.item
168+ property: "errorText"
169+ value: pinPadLoader.showWrongText ? root.errorText : ""
170+ }
171+ Binding {
172+ target: pinPadLoader.item
173+ property: "entryEnabled"
174+ value: !pinPadLoader.waiting
175+ }
176+ Binding {
177+ target: pinPadLoader.item
178+ property: "alphaNumeric"
179+ value: root.alphaNumeric
180+ }
181+ Binding {
182+ target: pinPadLoader.item
183+ property: "delayMinutes"
184+ value: root.delayMinutes
185+ }
184186 }
185187 }
186188
187− Label {
188− id: emergencyCallLabel
189− objectName: "emergencyCallLabel"
189+ Item {
190+ id: emergencyCallRow
190191
191192 // FIXME: We *should* show emergency dialer if there is a SIM present,
192193 // regardless of whether the side stage is enabled. But right now,
@@ -199,19 +200,37 @@ Showable {
199200
200201 anchors {
201202 bottom: parent.bottom
202− bottomMargin: units.gu(4)
203− horizontalCenter: parent.horizontalCenter
203+ bottomMargin: units.gu(7) + (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0)
204+ left: parent.left
205+ right: parent.right
204206 }
205207
206− text: i18n.tr("Emergency Call")
207− color: "#f3f3e7"
208− opacity: 0.6
209− }
208+ Label {
209+ id: emergencyCallLabel
210+ objectName: "emergencyCallLabel"
211+ anchors.horizontalCenter: parent.horizontalCenter
210212
211− MouseArea {
212− anchors.fill: emergencyCallLabel
213− onClicked: root.emergencyCall()
214− enabled: emergencyCallLabel.visible
213+ text: i18n.tr("Emergency Call")
214+ color: "#f3f3e7"
215+ }
216+
217+ Icon {
218+ id: emergencyCallIcon
219+ anchors.left: emergencyCallLabel.right
220+ anchors.leftMargin: units.gu(1)
221+ width: emergencyCallLabel.height
222+ height: emergencyCallLabel.height
223+ name: "call-start"
224+ color: "#f3f3e7"
225+ }
226+
227+ MouseArea {
228+ anchors.top: emergencyCallLabel.top
229+ anchors.bottom: emergencyCallLabel.bottom
230+ anchors.left: emergencyCallLabel.left
231+ anchors.right: emergencyCallIcon.right
232+ onClicked: root.emergencyCall()
233+ }
215234 }
216235
217236 Component {
qml/Components/PassphraseLockscreen.qml+96−43
@@ -20,10 +20,12 @@ import "../Components"
2020
2121Item {
2222 id: root
23− height: highlightItem.height
23+ anchors.top: parent.top
24+ anchors.topMargin: units.gu(4)
25+ height: shakeContainer.height
2426
2527 property string infoText
26− property string username: ""
28+ property string errorText
2729 property bool entryEnabled: true
2830
2931 signal entered(string passphrase)
@@ -31,64 +33,115 @@ Item {
3133
3234 function clear(playAnimation) {
3335 pinentryField.text = "";
36+ pinentryField.incorrectOverride = false;
37+ pinentryField.forceActiveFocus();
3438 if (playAnimation) {
3539 wrongPasswordAnimation.start();
36− pinentryField.forceActiveFocus();
37− } else {
38− pinentryField.focus = false
3940 }
4041 }
4142
42− Rectangle {
43− id: highlightItem
44− width: units.gu(32)
45− height: units.gu(10)
46− anchors.centerIn: parent
47− color: Qt.rgba(0.1, 0.1, 0.1, 0.4)
48− border.color: Qt.rgba(0.4, 0.4, 0.4, 0.4)
49− border.width: units.dp(1)
50− radius: units.gu(1.5)
51− antialiasing: true
43+ onActiveFocusChanged: if (activeFocus) pinentryField.forceActiveFocus()
44+
45+ Column {
46+ id: shakeContainer
47+ anchors.horizontalCenter: parent.horizontalCenter
48+ width: parent.width
49+ spacing: units.gu(2)
5250
5351 Label {
54− objectName: "greeterLabel"
55− anchors {
56− left: parent.left
57− top: parent.top
58− right: parent.right
59− margins: units.gu(1.5)
60− }
61− text: root.username.length > 0 ? i18n.tr("Hello %1").arg(root.username) : i18n.tr("Hello")
62− color: "white"
52+ id: infoField
53+ objectName: "infoTextLabel"
54+ fontSize: "x-large"
55+ color: "#f3f3e7"
56+ anchors.horizontalCenter: parent.horizontalCenter
57+ text: root.infoText
6358 }
6459
65− TextField {
66− id: pinentryField
67− objectName: "pinentryField"
68− anchors {
69− horizontalCenter: parent.horizontalCenter
70− bottom: parent.bottom
71− margins: units.gu(1)
60+ Item {
61+ id: entryContainer
62+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
63+ height: units.gu(4)
64+
65+ TextInput {
66+ id: pinentryField
67+ objectName: "pinentryField"
68+
69+ property bool incorrectOverride: false
70+
71+ anchors {
72+ top: parent.top
73+ left: parent.left
74+ right: parent.right
75+ }
76+ horizontalAlignment: Text.AlignHCenter
77+ font.pixelSize: FontUtils.sizeToPixels("large")
78+ echoMode: TextInput.Password
79+ inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData |
80+ Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
81+ color: "#f3f3e7"
82+ cursorDelegate: Item {} // disable cursor
83+ onCursorPositionChanged: {
84+ // And because we don't show the cursor, always position the
85+ // cursor at the end of the string (so backspace works like
86+ // the user expects, even if they've clicked on us and
87+ // thus accidentally moved the cursor)
88+ if (cursorPosition !== length) {
89+ cursorPosition = length
90+ }
91+ }
92+ clip: true
93+
94+ // This is so that we can draw our own dots, for we want
95+ // complete control over the pixel sizes. (The ubuntu font
96+ // has oddly sized password characters that don't scale right)
97+ opacity: 0
98+
99+ // simulate being disabled, but without removing OSK focus
100+ maximumLength: root.entryEnabled ? 32767 : length
101+
102+ onTextChanged: incorrectOverride = true
103+
104+ onAccepted: {
105+ if (pinentryField.text) {
106+ root.entered(pinentryField.text);
107+ }
108+ }
72109 }
73− height: units.gu(4.5)
74− width: parent.width - units.gu(2)
75− echoMode: TextInput.Password
76− opacity: 0.9
77− hasClearButton: false
78− enabled: root.entryEnabled
79− placeholderText: root.infoText
80−
81− onAccepted: {
82− if (pinentryField.text) {
83− root.entered(pinentryField.text);
110+
111+ Row {
112+ id: dotRow
113+ anchors.centerIn: entryContainer
114+
115+ property real dotSize: Math.min(units.gu(2), entryContainer.width / pinentryField.length)
116+ spacing: Math.min(units.gu(2), Math.max(0, (entryContainer.width / pinentryField.length) - dotSize))
117+
118+ Repeater {
119+ model: pinentryField.length
120+ delegate: Rectangle {
121+ color: "#f3f3e7"
122+ width: dotRow.dotSize
123+ height: width
124+ radius: width / 2
125+ }
84126 }
85127 }
128+
129+ Label {
130+ id: wrongNoticeLabel
131+ objectName: "wrongNoticeLabel"
132+ fontSize: "large"
133+ color: "#f3f3e7"
134+ anchors.horizontalCenter: parent.horizontalCenter
135+ horizontalAlignment: Text.AlignHCenter
136+ text: root.errorText
137+ visible: pinentryField.text.length == 0 && !pinentryField.incorrectOverride
138+ }
86139 }
87140 }
88141
89142 WrongPasswordAnimation {
90143 id: wrongPasswordAnimation
91144 objectName: "wrongPasswordAnimation"
92− target: pinentryField
145+ target: shakeContainer
93146 }
94147}
qml/Components/PinLockscreen.qml+7−5
@@ -21,7 +21,9 @@ import "../Components"
2121
2222Column {
2323 id: root
24− anchors.centerIn: parent
24+ anchors.top: parent.top
25+ anchors.topMargin: units.gu(4)
26+ anchors.horizontalCenter: parent.horizontalCenter
2527 spacing: units.gu(2)
2628
2729 property string infoText
@@ -49,7 +51,7 @@ Column {
4951 id: shakeContainer
5052 anchors.horizontalCenter: parent.horizontalCenter
5153 width: parent.width
52− spacing: units.gu(2)
54+ spacing: units.gu(1)
5355
5456 Label {
5557 id: infoField
@@ -193,19 +195,19 @@ Column {
193195 }
194196 PinPadButton {
195197 iconName: "close"
196− height: numbersGrid.buttonHeight
198+ height: units.gu(5) // visual spec has this row a little closer in
197199 width: numbersGrid.buttonWidth
198200
199201 onClicked: root.cancel()
200202 }
201203 Item {
202− height: numbersGrid.buttonHeight
204+ height: units.gu(5)
203205 width: numbersGrid.buttonWidth
204206 }
205207 PinPadButton {
206208 iconName: "tick"
207209 objectName: "confirmButton"
208− height: numbersGrid.buttonHeight
210+ height: units.gu(5)
209211 width: numbersGrid.buttonWidth
210212 enabled: root.enabled && pinentryField.text.length >= root.minPinLength
211213 visible: root.minPinLength == -1 || root.minPinLength !== root.maxPinLength
qml/Greeter/Greeter.qml+1−1
@@ -154,7 +154,7 @@ Showable {
154154 anchors.horizontalCenter: parent.horizontalCenter
155155 anchors.bottom: parent.bottom
156156 anchors.bottomMargin: units.gu(5)
157− text: i18n.tr("Swipe to unlock")
157+ text: "《 " + i18n.tr("Unlock") + " 》"
158158 color: "white"
159159 font.weight: Font.Light
160160
qml/Shell.qml+30−9
@@ -60,7 +60,7 @@ Item {
6060
6161 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
6262 property int failedLoginsDelayAttempts: 7 // number of failed logins
63− property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
63+ property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
6464
6565 property int orientation
6666 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
@@ -278,8 +278,6 @@ Item {
278278 id: lockscreen
279279 objectName: "lockscreen"
280280
281− readonly property int backgroundTopMargin: -panel.panelHeight
282−
283281 hides: [launcher, panel.indicators]
284282 shown: false
285283 enabled: true
@@ -300,6 +298,19 @@ Item {
300298
301299 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
302300
301+ Timer {
302+ id: forcedDelayTimer
303+ interval: 1000 * 60
304+ onTriggered: {
305+ if (lockscreen.delayMinutes > 0) {
306+ lockscreen.delayMinutes -= 1
307+ if (lockscreen.delayMinutes > 0) {
308+ start() // go again
309+ }
310+ }
311+ }
312+ }
313+
303314 Component.onCompleted: {
304315 if (greeter.narrowMode) {
305316 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -317,14 +328,15 @@ Item {
317328 if (greeter.narrowMode) {
318329 if (isDefaultPrompt) {
319330 if (lockscreen.alphaNumeric) {
320− lockscreen.infoText = i18n.tr("Enter your passphrase")
321− lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
331+ lockscreen.infoText = i18n.tr("Enter passphrase")
332+ lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
333+ i18n.tr("Please re-enter")
322334 } else {
323− lockscreen.infoText = i18n.tr("Enter your passcode")
335+ lockscreen.infoText = i18n.tr("Enter passcode")
324336 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
325337 }
326338 } else {
327− lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
339+ lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
328340 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
329341 }
330342
@@ -372,7 +384,8 @@ Item {
372384 }
373385 }
374386 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
375− lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
387+ lockscreen.delayMinutes = failedLoginsDelayMinutes
388+ forcedDelayTimer.start()
376389 }
377390
378391 lockscreen.clear(true);
@@ -418,7 +431,15 @@ Item {
418431 }
419432
420433 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
421− onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
434+ onShowProgressChanged: {
435+ if (showProgress === 0) {
436+ if (LightDM.Greeter.authenticated) {
437+ greeter.login()
438+ } else if (greeter.narrowMode) {
439+ lockscreen.clear(false) // to reset focus if necessary
440+ }
441+ }
442+ }
422443
423444 Greeter {
424445 id: greeter
tests/autopilot/unity8/shell/tests/test_lock_screen.py+3−1
@@ -132,8 +132,10 @@ class TestLockscreen(UnityTestCase):
132132 )
133133
134134 pin_entry_field = self.main_window.get_pinentryField()
135− pin_entry_field.write(passphrase)
135+ # pinentryField should automatically have focus
136+ self.keyboard.type(passphrase)
136137 logger.debug("Typed passphrase: %s", pin_entry_field.text)
138+ self.assertEqual(pin_entry_field.text, passphrase)
137139 self.keyboard.type("\n")
138140
139141 def _enter_prompt_passphrase(self, passphrase):
tests/qmltests/CMakeLists.txt+1−1
@@ -26,6 +26,7 @@ add_qml_test(Components Carousel)
2626add_qml_test(Components DraggingArea)
2727add_qml_test(Components EdgeDemoOverlay)
2828add_qml_test(Components LazyImage)
29+add_qml_test(Components Lockscreen)
2930add_qml_test(Components Rating)
3031add_qml_test(Components ResponsiveFlowView)
3132add_qml_test(Components ResponsiveGridView)
@@ -60,7 +61,6 @@ add_qml_test(Dash/ScopeSettings ScopeSettingList)
6061add_qml_test(Dash/ScopeSettings ScopeSettingNumber)
6162add_qml_test(Dash/ScopeSettings ScopeSettingString)
6263add_qml_test(Dash/ScopeSettings ScopeSettingsWidgetFactory)
63−add_qml_test(Greeter Lockscreen ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
6464add_qml_test(Greeter MultiGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
6565add_qml_test(Greeter SingleGreeter ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
6666add_qml_test(Greeter Infographics ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics:${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
tests/qmltests/Greeter/tst_Lockscreen.qml → tests/qmltests/Components/tst_Lockscreen.qmlrenamed+31−51
@@ -18,15 +18,13 @@ import QtQuick 2.0
1818import QtTest 1.0
1919import ".."
2020import "../../../qml/Components"
21−import AccountsService 0.1
22−import LightDM 0.1 as LightDM
2321import Ubuntu.Components 0.1
2422import Unity.Test 0.1 as UT
2523
2624Rectangle {
2725 id: root
2826 width: units.gu(80)
29− height: units.gu(70)
27+ height: units.gu(76)
3028 color: "orange"
3129
3230 Lockscreen {
@@ -39,7 +37,7 @@ Rectangle {
3937 alphaNumeric: pinPadCheckBox.checked
4038 minPinLength: minPinLengthTextField.text
4139 maxPinLength: maxPinLengthTextField.text
42− username: "Lola"
40+ delayMinutes: delayMinutesTextField.text
4341 background: "../../../qml/graphics/phone_background.jpg"
4442 }
4543
@@ -53,24 +51,6 @@ Rectangle {
5351 }
5452 }
5553
56− function setUser(username) {
57− AccountsService.user = username
58− LightDM.Greeter.authenticate(username)
59− }
60−
61− Connections {
62− target: LightDM.Greeter
63−
64− onShowPrompt: {
65− if (AccountsService.passwordDisplayHint === AccountsService.Numeric) {
66− pinPadCheckBox.checked = false
67− } else {
68− pinPadCheckBox.checked = true
69− }
70− lockscreen.infoText = text;
71− }
72− }
73−
7454 Rectangle {
7555 anchors.fill: parent
7656 anchors.leftMargin: lockscreen.width
@@ -129,6 +109,16 @@ Rectangle {
129109 text: "Retries left"
130110 }
131111 }
112+ Row {
113+ TextField {
114+ id: delayMinutesTextField
115+ width: units.gu(7)
116+ text: "0"
117+ }
118+ Label {
119+ text: "Delay Minutes"
120+ }
121+ }
132122 Label {
133123 id: pinLabel
134124 anchors.verticalCenter: parent.verticalCenter
@@ -141,17 +131,6 @@ Rectangle {
141131 id: enteredLabel
142132 }
143133 }
144− Button {
145− text: "start auth (1234)"
146− width: parent.width
147− onClicked: setUser("has-pin")
148− }
149− Button {
150− text: "start auth (password)"
151− width: parent.width
152− onClicked: setUser("has-password")
153− }
154−
155134 TextField {
156135 id: infoTextTextField
157136 width: parent.width
@@ -193,6 +172,7 @@ Rectangle {
193172
194173 function cleanup() {
195174 lockscreen.clear(false);
175+ delayMinutesTextField.text = "0"
196176 }
197177
198178 function waitForLockscreenReady() {
@@ -237,8 +217,8 @@ Rectangle {
237217
238218 function test_labels_data() {
239219 return [
240− {tag: "numeric", alphanumeric: false, infoText: "Please enter your PIN", username: "foobar" },
241− {tag: "alphanumeric", alphanumeric: true, infoText: "Please enter your password", username: "Lola" }
220+ {tag: "numeric", alphanumeric: false, infoText: "Please enter your PIN" },
221+ {tag: "alphanumeric", alphanumeric: true, infoText: "Please enter your password" }
242222 ]
243223 }
244224
@@ -246,22 +226,16 @@ Rectangle {
246226 pinPadCheckBox.checked = data.alphanumeric
247227 lockscreen.infoText = data.infoText
248228 waitForLockscreenReady();
249− if (data.alphanumeric) {
250− compare(findChild(lockscreen, "pinentryField").placeholderText, data.infoText, "Placeholdertext is not what it should be")
251− compare(findChild(lockscreen, "greeterLabel").text, "Hello " + data.username, "Greeter is not set correctly")
252− } else {
253− compare(findChild(lockscreen, "infoTextLabel").text, data.infoText, "Placeholdertext is not what it should be")
254− }
229+ compare(findChild(lockscreen, "infoTextLabel").text, data.infoText, "Placeholdertext is not what it should be")
255230 }
256231
257−
258232 function test_unlock_data() {
259233 return [
260− {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
261− {tag: "alphanumeric", alphanumeric: true, username: "has-password", password: "password", minPinLength: -1, maxPinLength: -1},
262− {tag: "numeric (wrong)", alphanumeric: false, username: "has-pin", password: "4321", minPinLength: 4, maxPinLength: 4},
263− {tag: "alphanumeric (wrong)", alphanumeric: true, username: "has-password", password: "drowssap", minPinLength: -1, maxPinLength: -1},
264− {tag: "flexible length", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: -1, maxPinLength: -1},
234+ {tag: "numeric", alphanumeric: false, password: "1234", minPinLength: 4, maxPinLength: 4},
235+ {tag: "alphanumeric", alphanumeric: true, password: "password", minPinLength: -1, maxPinLength: -1},
236+ {tag: "numeric (wrong)", alphanumeric: false, password: "4321", minPinLength: 4, maxPinLength: 4},
237+ {tag: "alphanumeric (wrong)", alphanumeric: true, password: "drowssap", minPinLength: -1, maxPinLength: -1},
238+ {tag: "flexible length", alphanumeric: false, password: "1234", minPinLength: -1, maxPinLength: -1},
265239 ]
266240 }
267241
@@ -269,13 +243,13 @@ Rectangle {
269243 enteredLabel.text = ""
270244 minPinLengthTextField.text = data.minPinLength
271245 maxPinLengthTextField.text = data.maxPinLength
272− setUser(data.username)
246+ pinPadCheckBox.checked = data.alphanumeric
247+ infoTextTextField.text = "Enter " + (data.alphanumeric ? "passphrase" : "passcode")
273248 waitForLockscreenReady();
274249
275250 var inputField = findChild(lockscreen, "pinentryField")
276251 if (data.alphanumeric) {
277− mouseClick(inputField, units.gu(1), units.gu(1))
278− tryCompare(inputField, "focus", true);
252+ tryCompare(inputField, "activeFocus", true);
279253 typeString(data.password)
280254 keyClick(Qt.Key_Enter)
281255 } else {
@@ -305,7 +279,6 @@ Rectangle {
305279
306280 var inputField = findChild(lockscreen, "pinentryField")
307281 if (data.alphanumeric) {
308− mouseClick(inputField, units.gu(1), units.gu(1))
309282 tryCompare(inputField, "activeFocus", true);
310283 typeString("1")
311284 } else {
@@ -470,6 +443,13 @@ Rectangle {
470443 var label = findChild(lockscreen, "infoTextLabel")
471444 compare(label.text, data.text);
472445 }
446+
447+ function test_delayMinutes() {
448+ delayMinutesTextField.text = "4"
449+ waitForLockscreenReady()
450+ var label = findChild(lockscreen, "deviceLockedLabel")
451+ compare(label.text, "Device Locked")
452+ }
473453 }
474454
475455 SignalSpy {
tests/qmltests/tst_ShellWithPin.qml+3−1
@@ -230,8 +230,10 @@ Item {
230230 enterPin("1111")
231231 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
232232
233+ var lockscreen = findChild(shell, "lockscreen")
234+ tryCompare(lockscreen, "delayMinutes", 0)
233235 enterPin("1111")
234− tryCompare(placeHolder, "text", "Too many incorrect attempts")
236+ tryCompare(lockscreen, "delayMinutes", shell.failedLoginsDelayMinutes)
235237 }
236238
237239 function test_factoryReset() {