on xenial help sign in
Merge in passphrase-ui-tweaks
4b5bd6cMichael Terry
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+57−50
@@ -34,13 +34,14 @@ Showable {
34 property string infoText: ""
34 property string infoText: ""
35
35
36 // Retries text (e.g. 3 retries left)
36 // Retries text (e.g. 3 retries left)
37+ // (This is not currently used, but will be necessary for SIM unlock screen)
37 property string retryText: ""
38 property string retryText: ""
38
39
39 // The text to be displayed in case the login failed
40 // The text to be displayed in case the login failed
40 property string errorText: ""
41 property string errorText: ""
41
42
42− // In case the Lockscreen can show a greeter message, this is the username
43+ // If > 0, a forced delay is happening
43− property string username: ""
44+ property int delayMinutes: 0
44
45
45 // Set those to a value greater 0 to restrict the pin length.
46 // Set those to a value greater 0 to restrict the pin length.
46 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
47 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
@@ -60,17 +61,6 @@ Showable {
60 signal emergencyCall()
61 signal emergencyCall()
61 signal infoPopupConfirmed()
62 signal infoPopupConfirmed()
62
63
63− onRequiredChanged: {
64− if (required && pinPadLoader.item) {
65− clear(false)
66− }
67− }
68−
69− function forceDelay(delay) {
70− forcedDelayTimer.interval = delay
71− forcedDelayTimer.start()
72− }
73−
74 function reset() {
64 function reset() {
75 // This causes the loader below to destry and recreate the source
65 // This causes the loader below to destry and recreate the source
76 pinPadLoader.resetting = true;
66 pinPadLoader.resetting = true;
@@ -89,11 +79,6 @@ Showable {
89 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
79 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
90 }
80 }
91
81
92− Timer {
93− id: forcedDelayTimer
94− onTriggered: pinPadLoader.showWrongText = false
95− }
96−
97 Rectangle {
82 Rectangle {
98 // In case background fails to load
83 // In case background fails to load
99 id: backgroundBackup
84 id: backgroundBackup
@@ -119,25 +104,26 @@ Showable {
119 Loader {
104 Loader {
120 id: pinPadLoader
105 id: pinPadLoader
121 objectName: "pinPadLoader"
106 objectName: "pinPadLoader"
122− anchors {
107+ anchors.fill: parent
123− left: parent.left
124− right: parent.right
125− verticalCenter: parent.verticalCenter
126− verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : 0
127− }
128 property bool resetting: false
108 property bool resetting: false
129 property bool waiting: false
109 property bool waiting: false
130 property bool showWrongText: false
110 property bool showWrongText: false
131
111
112+ source: {
113+ if (resetting || !root.required) {
114+ return ""
115+ } else if (root.delayMinutes > 0) {
116+ return "DelayedLockscreen.qml"
117+ } else if (root.alphaNumeric) {
132− source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
118+ return "PassphraseLockscreen.qml"
119+ } else {
120+ return "PinLockscreen.qml"
121+ }
122+ }
133 onSourceChanged: {
123 onSourceChanged: {
134 waiting = false
124 waiting = false
135 showWrongText = false
125 showWrongText = false
136− }
126+ clear(false)
137− onLoaded: {
138− if (forcedDelayTimer.running) {
139− pinPadLoader.item.clear(true)
140− }
141 }
127 }
142
128
143 Connections {
129 Connections {
@@ -171,23 +157,27 @@ Showable {
171 Binding {
157 Binding {
172 target: pinPadLoader.item
158 target: pinPadLoader.item
173 property: "retryText"
159 property: "retryText"
174− value: forcedDelayTimer.running ? i18n.tr("Please wait") : root.retryText
160+ value: root.retryText
175 }
161 }
176 Binding {
162 Binding {
177 target: pinPadLoader.item
163 target: pinPadLoader.item
178 property: "errorText"
164 property: "errorText"
179− value: forcedDelayTimer.running ? i18n.tr("Too many incorrect attempts") :
180− (pinPadLoader.showWrongText ? root.errorText : "")
165+ value: pinPadLoader.showWrongText ? root.errorText : ""
181 }
166 }
182 Binding {
167 Binding {
183 target: pinPadLoader.item
168 target: pinPadLoader.item
184− property: "username"
169+ property: "entryEnabled"
185− value: root.username
170+ value: !pinPadLoader.waiting
186 }
171 }
187 Binding {
172 Binding {
188 target: pinPadLoader.item
173 target: pinPadLoader.item
189− property: "entryEnabled"
174+ property: "alphaNumeric"
190− value: !pinPadLoader.waiting && !forcedDelayTimer.running
175+ value: root.alphaNumeric
176+ }
177+ Binding {
178+ target: pinPadLoader.item
179+ property: "delayMinutes"
180+ value: root.delayMinutes
191 }
181 }
192 Binding {
182 Binding {
193 target: pinPadLoader.item
183 target: pinPadLoader.item
@@ -196,27 +186,44 @@ Showable {
196 }
186 }
197 }
187 }
198
188
199− Label {
189+ Item {
200− id: emergencyCallLabel
190+ id: emergencyCallRow
201− objectName: "emergencyCallLabel"
202
191
203 visible: showEmergencyCallButton
192 visible: showEmergencyCallButton
204
193
205 anchors {
194 anchors {
206 bottom: parent.bottom
195 bottom: parent.bottom
207− bottomMargin: units.gu(4)
196+ bottomMargin: units.gu(7) + (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0)
208− horizontalCenter: parent.horizontalCenter
197+ left: parent.left
198+ right: parent.right
209 }
199 }
210
200
211− text: i18n.tr("Emergency Call")
201+ Label {
212− color: "#f3f3e7"
202+ id: emergencyCallLabel
213− opacity: 0.6
203+ objectName: "emergencyCallLabel"
214− }
204+ anchors.horizontalCenter: parent.horizontalCenter
215
205
206+ text: i18n.tr("Emergency Call")
207+ color: "#f3f3e7"
208+ }
209+
210+ Icon {
211+ id: emergencyCallIcon
212+ anchors.left: emergencyCallLabel.right
213+ anchors.leftMargin: units.gu(1)
214+ width: emergencyCallLabel.height
215+ height: emergencyCallLabel.height
216+ name: "call-start"
217+ color: "#f3f3e7"
218+ }
219+
216− MouseArea {
220+ MouseArea {
221+ anchors.top: emergencyCallLabel.top
222+ anchors.bottom: emergencyCallLabel.bottom
217− anchors.fill: emergencyCallLabel
223+ anchors.left: emergencyCallLabel.left
224+ anchors.right: emergencyCallIcon.right
218− onClicked: root.emergencyCall()
225+ onClicked: root.emergencyCall()
219− enabled: emergencyCallLabel.visible
226+ }
220 }
227 }
221
228
222 Component {
229 Component {
qml/Components/PassphraseLockscreen.qml+73−44
@@ -20,10 +20,12 @@ import "../Components"
20
20
21Item {
21Item {
22 id: root
22 id: root
23+ anchors.top: parent.top
24+ anchors.topMargin: units.gu(4)
23− height: highlightItem.height
25+ height: shakeContainer.height
24
26
25 property string infoText
27 property string infoText
26− property string username: ""
28+ property string errorText
27 property bool entryEnabled: true
29 property bool entryEnabled: true
28
30
29 signal entered(string passphrase)
31 signal entered(string passphrase)
@@ -31,57 +33,84 @@ Item {
31
33
32 function clear(playAnimation) {
34 function clear(playAnimation) {
33 pinentryField.text = "";
35 pinentryField.text = "";
36+ pinentryField.incorrectOverride = false;
37+ pinentryField.forceActiveFocus();
34 if (playAnimation) {
38 if (playAnimation) {
35 wrongPasswordAnimation.start();
39 wrongPasswordAnimation.start();
36− pinentryField.forceActiveFocus();
37− } else {
38− pinentryField.focus = false
39 }
40 }
40 }
41 }
41
42
42− Rectangle {
43+ Column {
43− id: highlightItem
44+ id: shakeContainer
44− width: units.gu(32)
45+ anchors.horizontalCenter: parent.horizontalCenter
45− height: units.gu(10)
46+ width: parent.width
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)
47+ spacing: units.gu(2)
51− antialiasing: true
52
48
53 Label {
49 Label {
50+ id: infoField
54− objectName: "greeterLabel"
51+ objectName: "infoTextLabel"
55− anchors {
52+ fontSize: "x-large"
56− left: parent.left
53+ color: "#f3f3e7"
57− top: parent.top
54+ anchors.horizontalCenter: parent.horizontalCenter
58− right: parent.right
55+ text: root.infoText
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"
63 }
56 }
64
57
65− TextField {
58+ Item {
66− id: pinentryField
59+ id: entryContainer
67− objectName: "pinentryField"
68− anchors {
69− horizontalCenter: parent.horizontalCenter
70− bottom: parent.bottom
71− margins: units.gu(1)
60+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
72− }
73− height: units.gu(4.5)
61+ height: units.gu(4)
74− width: parent.width - units.gu(2)
62+
63+ TextInput {
64+ id: pinentryField
65+ objectName: "pinentryField"
66+
67+ property bool incorrectOverride: false
68+
69+ anchors {
70+ top: parent.top
71+ left: parent.left
72+ right: parent.right
73+ }
74+ horizontalAlignment: Text.AlignHCenter
75+ font.pixelSize: FontUtils.sizeToPixels("large")
75− echoMode: TextInput.Password
76+ echoMode: TextInput.Password
76− opacity: 0.9
77+ inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData |
77− hasClearButton: false
78+ Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
78− enabled: root.entryEnabled
79+ color: "#f3f3e7"
79− placeholderText: root.infoText
80+ cursorDelegate: Item {} // disable cursor
80−
81+ onCursorPositionChanged: {
81− onAccepted: {
82+ // And because we don't show the cursor, always position the
82− if (pinentryField.text) {
83+ // cursor at the end of the string (so backspace works like
83− root.entered(pinentryField.text);
84+ // the user expects, even if they've clicked on us and
85+ // thus accidentally moved the cursor)
86+ if (cursorPosition !== length) {
87+ cursorPosition = length
88+ }
84 }
89 }
90+ enabled: root.entryEnabled
91+ clip: true
92+
93+ onTextChanged: incorrectOverride = true
94+
95+ onActiveFocusChanged: if (!activeFocus) pinentryField.forceActiveFocus()
96+ onEnabledChanged: if (enabled) pinentryField.forceActiveFocus()
97+
98+ onAccepted: {
99+ if (pinentryField.text) {
100+ root.entered(pinentryField.text);
101+ }
102+ }
103+ }
104+
105+ Label {
106+ id: wrongNoticeLabel
107+ objectName: "wrongNoticeLabel"
108+ fontSize: "large"
109+ color: "#f3f3e7"
110+ anchors.horizontalCenter: parent.horizontalCenter
111+ horizontalAlignment: Text.AlignHCenter
112+ text: root.errorText
113+ visible: pinentryField.text.length == 0 && !pinentryField.incorrectOverride
85 }
114 }
86 }
115 }
87 }
116 }
@@ -89,6 +118,6 @@ Item {
89 WrongPasswordAnimation {
118 WrongPasswordAnimation {
90 id: wrongPasswordAnimation
119 id: wrongPasswordAnimation
91 objectName: "wrongPasswordAnimation"
120 objectName: "wrongPasswordAnimation"
92− target: pinentryField
121+ target: shakeContainer
93 }
122 }
94}
123}
qml/Components/PinLockscreen.qml+7−5
@@ -21,7 +21,9 @@ import "../Components"
21
21
22Column {
22Column {
23 id: root
23 id: root
24− anchors.centerIn: parent
24+ anchors.top: parent.top
25+ anchors.topMargin: units.gu(4)
26+ anchors.horizontalCenter: parent.horizontalCenter
25 spacing: units.gu(2)
27 spacing: units.gu(2)
26
28
27 property string infoText
29 property string infoText
@@ -50,7 +52,7 @@ Column {
50 id: shakeContainer
52 id: shakeContainer
51 anchors.horizontalCenter: parent.horizontalCenter
53 anchors.horizontalCenter: parent.horizontalCenter
52 width: parent.width
54 width: parent.width
53− spacing: units.gu(2)
55+ spacing: units.gu(1)
54
56
55 Label {
57 Label {
56 id: infoField
58 id: infoField
@@ -195,20 +197,20 @@ Column {
195 }
197 }
196 PinPadButton {
198 PinPadButton {
197 iconName: "close"
199 iconName: "close"
198− height: numbersGrid.buttonHeight
200+ height: units.gu(5) // visual spec has this row a little closer in
199 width: numbersGrid.buttonWidth
201 width: numbersGrid.buttonWidth
200
202
201 onClicked: root.cancel()
203 onClicked: root.cancel()
202 visible: root.showCancelButton
204 visible: root.showCancelButton
203 }
205 }
204 Item {
206 Item {
205− height: numbersGrid.buttonHeight
207+ height: units.gu(5)
206 width: numbersGrid.buttonWidth
208 width: numbersGrid.buttonWidth
207 }
209 }
208 PinPadButton {
210 PinPadButton {
209 iconName: "tick"
211 iconName: "tick"
210 objectName: "confirmButton"
212 objectName: "confirmButton"
211− height: numbersGrid.buttonHeight
213+ height: units.gu(5)
212 width: numbersGrid.buttonWidth
214 width: numbersGrid.buttonWidth
213 enabled: root.enabled && pinentryField.text.length >= root.minPinLength
215 enabled: root.enabled && pinentryField.text.length >= root.minPinLength
214 visible: root.minPinLength == -1 || root.minPinLength !== root.maxPinLength
216 visible: root.minPinLength == -1 || root.minPinLength !== root.maxPinLength
qml/Greeter/Greeter.qml+1−1
@@ -154,7 +154,7 @@ Showable {
154 anchors.horizontalCenter: parent.horizontalCenter
154 anchors.horizontalCenter: parent.horizontalCenter
155 anchors.bottom: parent.bottom
155 anchors.bottom: parent.bottom
156 anchors.bottomMargin: units.gu(5)
156 anchors.bottomMargin: units.gu(5)
157− text: i18n.tr("Swipe to unlock")
157+ text: "《 " + i18n.tr("Unlock") + " 》"
158 color: "white"
158 color: "white"
159 font.weight: Font.Light
159 font.weight: Font.Light
160
160
qml/Shell.qml+30−9
@@ -60,7 +60,7 @@ Item {
60
60
61 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
61 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
62 property int failedLoginsDelayAttempts: 7 // number of failed logins
62 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
64
64
65 property int orientation
65 property int orientation
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
66 readonly property int deviceOrientationAngle: Screen.angleBetween(Screen.primaryOrientation, Screen.orientation)
@@ -278,8 +278,6 @@ Item {
278 id: lockscreen
278 id: lockscreen
279 objectName: "lockscreen"
279 objectName: "lockscreen"
280
280
281− readonly property int backgroundTopMargin: -panel.panelHeight
282−
283 hides: [launcher, panel.indicators]
281 hides: [launcher, panel.indicators]
284 shown: false
282 shown: false
285 enabled: true
283 enabled: true
@@ -309,6 +307,19 @@ Item {
309
307
310 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
308 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
311
309
310+ Timer {
311+ id: forcedDelayTimer
312+ interval: 1000 * 60
313+ onTriggered: {
314+ if (lockscreen.delayMinutes > 0) {
315+ lockscreen.delayMinutes -= 1
316+ if (lockscreen.delayMinutes > 0) {
317+ start() // go again
318+ }
319+ }
320+ }
321+ }
322+
312 Component.onCompleted: {
323 Component.onCompleted: {
313 if (greeter.narrowMode) {
324 if (greeter.narrowMode) {
314 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
325 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -326,14 +337,15 @@ Item {
326 if (greeter.narrowMode) {
337 if (greeter.narrowMode) {
327 if (isDefaultPrompt) {
338 if (isDefaultPrompt) {
328 if (lockscreen.alphaNumeric) {
339 if (lockscreen.alphaNumeric) {
329− lockscreen.infoText = i18n.tr("Enter your passphrase")
340+ lockscreen.infoText = i18n.tr("Enter passphrase")
330− lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
341+ lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
342+ i18n.tr("Please re-enter")
331 } else {
343 } else {
332− lockscreen.infoText = i18n.tr("Enter your passcode")
344+ lockscreen.infoText = i18n.tr("Enter passcode")
333 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
345 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
334 }
346 }
335 } else {
347 } else {
336− lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
348+ lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
337 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
349 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
338 }
350 }
339
351
@@ -381,7 +393,8 @@ Item {
381 }
393 }
382 }
394 }
383 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
395 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
384− lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
396+ lockscreen.delayMinutes = failedLoginsDelayMinutes
397+ forcedDelayTimer.start()
385 }
398 }
386
399
387 lockscreen.clear(true);
400 lockscreen.clear(true);
@@ -427,7 +440,15 @@ Item {
427 }
440 }
428
441
429 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
442 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
443+ onShowProgressChanged: {
444+ if (showProgress === 0) {
430− onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
445+ if (LightDM.Greeter.authenticated) {
446+ greeter.login()
447+ } else if (greeter.narrowMode) {
448+ lockscreen.clear(false) // to reset focus if necessary
449+ }
450+ }
451+ }
431
452
432 Greeter {
453 Greeter {
433 id: greeter
454 id: greeter
tests/qmltests/Greeter/tst_Lockscreen.qml+24−14
@@ -26,7 +26,7 @@ import Unity.Test 0.1 as UT
26Rectangle {
26Rectangle {
27 id: root
27 id: root
28 width: units.gu(80)
28 width: units.gu(80)
29− height: units.gu(70)
29+ height: units.gu(76)
30 color: "orange"
30 color: "orange"
31
31
32 Lockscreen {
32 Lockscreen {
@@ -39,7 +39,7 @@ Rectangle {
39 alphaNumeric: pinPadCheckBox.checked
39 alphaNumeric: pinPadCheckBox.checked
40 minPinLength: minPinLengthTextField.text
40 minPinLength: minPinLengthTextField.text
41 maxPinLength: maxPinLengthTextField.text
41 maxPinLength: maxPinLengthTextField.text
42− username: "Lola"
42+ delayMinutes: delayMinutesTextField.text
43 background: "../../../qml/graphics/phone_background.jpg"
43 background: "../../../qml/graphics/phone_background.jpg"
44 }
44 }
45
45
@@ -129,6 +129,16 @@ Rectangle {
129 text: "Retries left"
129 text: "Retries left"
130 }
130 }
131 }
131 }
132+ Row {
133+ TextField {
134+ id: delayMinutesTextField
135+ width: units.gu(7)
136+ text: "0"
137+ }
138+ Label {
139+ text: "Delay Minutes"
140+ }
141+ }
132 Label {
142 Label {
133 id: pinLabel
143 id: pinLabel
134 anchors.verticalCenter: parent.verticalCenter
144 anchors.verticalCenter: parent.verticalCenter
@@ -193,6 +203,7 @@ Rectangle {
193
203
194 function cleanup() {
204 function cleanup() {
195 lockscreen.clear(false);
205 lockscreen.clear(false);
206+ delayMinutesTextField.text = "0"
196 }
207 }
197
208
198 function waitForLockscreenReady() {
209 function waitForLockscreenReady() {
@@ -237,8 +248,8 @@ Rectangle {
237
248
238 function test_labels_data() {
249 function test_labels_data() {
239 return [
250 return [
240− {tag: "numeric", alphanumeric: false, infoText: "Please enter your PIN", username: "foobar" },
251+ {tag: "numeric", alphanumeric: false, infoText: "Please enter your PIN" },
241− {tag: "alphanumeric", alphanumeric: true, infoText: "Please enter your password", username: "Lola" }
252+ {tag: "alphanumeric", alphanumeric: true, infoText: "Please enter your password" }
242 ]
253 ]
243 }
254 }
244
255
@@ -246,15 +257,9 @@ Rectangle {
246 pinPadCheckBox.checked = data.alphanumeric
257 pinPadCheckBox.checked = data.alphanumeric
247 lockscreen.infoText = data.infoText
258 lockscreen.infoText = data.infoText
248 waitForLockscreenReady();
259 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")
260+ compare(findChild(lockscreen, "infoTextLabel").text, data.infoText, "Placeholdertext is not what it should be")
254− }
255 }
261 }
256
262
257−
258 function test_unlock_data() {
263 function test_unlock_data() {
259 return [
264 return [
260 {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
265 {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
@@ -274,8 +279,7 @@ Rectangle {
274
279
275 var inputField = findChild(lockscreen, "pinentryField")
280 var inputField = findChild(lockscreen, "pinentryField")
276 if (data.alphanumeric) {
281 if (data.alphanumeric) {
277− mouseClick(inputField, units.gu(1), units.gu(1))
278− tryCompare(inputField, "focus", true);
282+ tryCompare(inputField, "activeFocus", true);
279 typeString(data.password)
283 typeString(data.password)
280 keyClick(Qt.Key_Enter)
284 keyClick(Qt.Key_Enter)
281 } else {
285 } else {
@@ -305,7 +309,6 @@ Rectangle {
305
309
306 var inputField = findChild(lockscreen, "pinentryField")
310 var inputField = findChild(lockscreen, "pinentryField")
307 if (data.alphanumeric) {
311 if (data.alphanumeric) {
308− mouseClick(inputField, units.gu(1), units.gu(1))
309 tryCompare(inputField, "activeFocus", true);
312 tryCompare(inputField, "activeFocus", true);
310 typeString("1")
313 typeString("1")
311 } else {
314 } else {
@@ -470,6 +473,13 @@ Rectangle {
470 var label = findChild(lockscreen, "infoTextLabel")
473 var label = findChild(lockscreen, "infoTextLabel")
471 compare(label.text, data.text);
474 compare(label.text, data.text);
472 }
475 }
476+
477+ function test_delayMinutes() {
478+ delayMinutesTextField.text = "4"
479+ waitForLockscreenReady()
480+ var label = findChild(lockscreen, "deviceLockedLabel")
481+ compare(label.text, "Device Locked")
482+ }
473 }
483 }
474
484
475 SignalSpy {
485 SignalSpy {
tests/qmltests/tst_ShellWithPin.qml+3−1
@@ -230,8 +230,10 @@ Item {
230 enterPin("1111")
230 enterPin("1111")
231 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
231 tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
232
232
233+ var lockscreen = findChild(shell, "lockscreen")
234+ tryCompare(lockscreen, "delayMinutes", 0)
233 enterPin("1111")
235 enterPin("1111")
234− tryCompare(placeHolder, "text", "Too many incorrect attempts")
236+ tryCompare(lockscreen, "delayMinutes", shell.failedLoginsDelayMinutes)
235 }
237 }
236
238
237 function test_factoryReset() {
239 function test_factoryReset() {