on xenial help sign in
Merge in passphrase-ui-tweaks
commit4b5bd6c119910d2e3892538c5a9e324185d308b8 authorMichael Terry <michael.terry@canonical.com> dateTue 23 Sep 2014, 18:38 (12 years ago) parenta4986619b6e865
8 files changed+287−124
qml/Components/DelayedLockscreen.qml+92 qml/Components/Lockscreen.qml+57−50 qml/Components/PassphraseLockscreen.qml+73−44 qml/Components/PinLockscreen.qml+7−5 qml/Greeter/Greeter.qml+1−1 qml/Shell.qml+30−9 tests/qmltests/Greeter/tst_Lockscreen.qml+24−14 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+57−50
@@ -34,13 +34,14 @@ Showable {
3434 property string infoText: ""
3535
3636 // Retries text (e.g. 3 retries left)
37+ // (This is not currently used, but will be necessary for SIM unlock screen)
3738 property string retryText: ""
3839
3940 // The text to be displayed in case the login failed
4041 property string errorText: ""
4142
42− // In case the Lockscreen can show a greeter message, this is the username
43− property string username: ""
43+ // If > 0, a forced delay is happening
44+ property int delayMinutes: 0
4445
4546 // Set those to a value greater 0 to restrict the pin length.
4647 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
@@ -60,17 +61,6 @@ Showable {
6061 signal emergencyCall()
6162 signal infoPopupConfirmed()
6263
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−
7464 function reset() {
7565 // This causes the loader below to destry and recreate the source
7666 pinPadLoader.resetting = true;
@@ -89,11 +79,6 @@ Showable {
8979 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
9080 }
9181
92− Timer {
93− id: forcedDelayTimer
94− onTriggered: pinPadLoader.showWrongText = false
95− }
96−
9782 Rectangle {
9883 // In case background fails to load
9984 id: backgroundBackup
@@ -119,25 +104,26 @@ Showable {
119104 Loader {
120105 id: pinPadLoader
121106 objectName: "pinPadLoader"
122− anchors {
123− left: parent.left
124− right: parent.right
125− verticalCenter: parent.verticalCenter
126− verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : 0
127− }
107+ anchors.fill: parent
128108 property bool resetting: false
129109 property bool waiting: false
130110 property bool showWrongText: false
131111
132− source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
112+ source: {
113+ if (resetting || !root.required) {
114+ return ""
115+ } else if (root.delayMinutes > 0) {
116+ return "DelayedLockscreen.qml"
117+ } else if (root.alphaNumeric) {
118+ return "PassphraseLockscreen.qml"
119+ } else {
120+ return "PinLockscreen.qml"
121+ }
122+ }
133123 onSourceChanged: {
134124 waiting = false
135125 showWrongText = false
136− }
137− onLoaded: {
138− if (forcedDelayTimer.running) {
139− pinPadLoader.item.clear(true)
140− }
126+ clear(false)
141127 }
142128
143129 Connections {
@@ -171,23 +157,27 @@ Showable {
171157 Binding {
172158 target: pinPadLoader.item
173159 property: "retryText"
174− value: forcedDelayTimer.running ? i18n.tr("Please wait") : root.retryText
160+ value: root.retryText
175161 }
176162 Binding {
177163 target: pinPadLoader.item
178164 property: "errorText"
179− value: forcedDelayTimer.running ? i18n.tr("Too many incorrect attempts") :
180− (pinPadLoader.showWrongText ? root.errorText : "")
165+ value: pinPadLoader.showWrongText ? root.errorText : ""
181166 }
182167 Binding {
183168 target: pinPadLoader.item
184− property: "username"
185− value: root.username
169+ property: "entryEnabled"
170+ value: !pinPadLoader.waiting
186171 }
187172 Binding {
188173 target: pinPadLoader.item
189− property: "entryEnabled"
190− value: !pinPadLoader.waiting && !forcedDelayTimer.running
174+ property: "alphaNumeric"
175+ value: root.alphaNumeric
176+ }
177+ Binding {
178+ target: pinPadLoader.item
179+ property: "delayMinutes"
180+ value: root.delayMinutes
191181 }
192182 Binding {
193183 target: pinPadLoader.item
@@ -196,27 +186,44 @@ Showable {
196186 }
197187 }
198188
199− Label {
200− id: emergencyCallLabel
201− objectName: "emergencyCallLabel"
189+ Item {
190+ id: emergencyCallRow
202191
203192 visible: showEmergencyCallButton
204193
205194 anchors {
206195 bottom: parent.bottom
207− bottomMargin: units.gu(4)
208− horizontalCenter: parent.horizontalCenter
196+ bottomMargin: units.gu(7) + (Qt.inputMethod.visible ? Qt.inputMethod.keyboardRectangle.height : 0)
197+ left: parent.left
198+ right: parent.right
209199 }
210200
211− text: i18n.tr("Emergency Call")
212− color: "#f3f3e7"
213− opacity: 0.6
214− }
201+ Label {
202+ id: emergencyCallLabel
203+ objectName: "emergencyCallLabel"
204+ anchors.horizontalCenter: parent.horizontalCenter
215205
216− MouseArea {
217− anchors.fill: emergencyCallLabel
218− onClicked: root.emergencyCall()
219− enabled: emergencyCallLabel.visible
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+
220+ MouseArea {
221+ anchors.top: emergencyCallLabel.top
222+ anchors.bottom: emergencyCallLabel.bottom
223+ anchors.left: emergencyCallLabel.left
224+ anchors.right: emergencyCallIcon.right
225+ onClicked: root.emergencyCall()
226+ }
220227 }
221228
222229 Component {
qml/Components/PassphraseLockscreen.qml+73−44
@@ -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,57 +33,84 @@ 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+ Column {
44+ id: shakeContainer
45+ anchors.horizontalCenter: parent.horizontalCenter
46+ width: parent.width
47+ spacing: units.gu(2)
5248
5349 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"
50+ id: infoField
51+ objectName: "infoTextLabel"
52+ fontSize: "x-large"
53+ color: "#f3f3e7"
54+ anchors.horizontalCenter: parent.horizontalCenter
55+ text: root.infoText
6356 }
6457
65− TextField {
66− id: pinentryField
67− objectName: "pinentryField"
68− anchors {
69− horizontalCenter: parent.horizontalCenter
70− bottom: parent.bottom
71− margins: units.gu(1)
72− }
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);
58+ Item {
59+ id: entryContainer
60+ anchors { left: parent.left; right: parent.right; margins: units.gu(2) }
61+ height: units.gu(4)
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")
76+ echoMode: TextInput.Password
77+ inputMethodHints: Qt.ImhHiddenText | Qt.ImhSensitiveData |
78+ Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
79+ color: "#f3f3e7"
80+ cursorDelegate: Item {} // disable cursor
81+ onCursorPositionChanged: {
82+ // And because we don't show the cursor, always position the
83+ // cursor at the end of the string (so backspace works like
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+ }
8489 }
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
85114 }
86115 }
87116 }
@@ -89,6 +118,6 @@ Item {
89118 WrongPasswordAnimation {
90119 id: wrongPasswordAnimation
91120 objectName: "wrongPasswordAnimation"
92− target: pinentryField
121+ target: shakeContainer
93122 }
94123}
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
@@ -50,7 +52,7 @@ Column {
5052 id: shakeContainer
5153 anchors.horizontalCenter: parent.horizontalCenter
5254 width: parent.width
53− spacing: units.gu(2)
55+ spacing: units.gu(1)
5456
5557 Label {
5658 id: infoField
@@ -195,20 +197,20 @@ Column {
195197 }
196198 PinPadButton {
197199 iconName: "close"
198− height: numbersGrid.buttonHeight
200+ height: units.gu(5) // visual spec has this row a little closer in
199201 width: numbersGrid.buttonWidth
200202
201203 onClicked: root.cancel()
202204 visible: root.showCancelButton
203205 }
204206 Item {
205− height: numbersGrid.buttonHeight
207+ height: units.gu(5)
206208 width: numbersGrid.buttonWidth
207209 }
208210 PinPadButton {
209211 iconName: "tick"
210212 objectName: "confirmButton"
211− height: numbersGrid.buttonHeight
213+ height: units.gu(5)
212214 width: numbersGrid.buttonWidth
213215 enabled: root.enabled && pinentryField.text.length >= root.minPinLength
214216 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
@@ -309,6 +307,19 @@ Item {
309307
310308 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
311309
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+
312323 Component.onCompleted: {
313324 if (greeter.narrowMode) {
314325 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -326,14 +337,15 @@ Item {
326337 if (greeter.narrowMode) {
327338 if (isDefaultPrompt) {
328339 if (lockscreen.alphaNumeric) {
329− lockscreen.infoText = i18n.tr("Enter your passphrase")
330− lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase")
340+ lockscreen.infoText = i18n.tr("Enter passphrase")
341+ lockscreen.errorText = i18n.tr("Sorry, incorrect passphrase") + "\n" +
342+ i18n.tr("Please re-enter")
331343 } else {
332− lockscreen.infoText = i18n.tr("Enter your passcode")
344+ lockscreen.infoText = i18n.tr("Enter passcode")
333345 lockscreen.errorText = i18n.tr("Sorry, incorrect passcode")
334346 }
335347 } else {
336− lockscreen.infoText = i18n.tr("Enter your %1").arg(text.toLowerCase())
348+ lockscreen.infoText = i18n.tr("Enter %1").arg(text.toLowerCase())
337349 lockscreen.errorText = i18n.tr("Sorry, incorrect %1").arg(text.toLowerCase())
338350 }
339351
@@ -381,7 +393,8 @@ Item {
381393 }
382394 }
383395 if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
384− lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
396+ lockscreen.delayMinutes = failedLoginsDelayMinutes
397+ forcedDelayTimer.start()
385398 }
386399
387400 lockscreen.clear(true);
@@ -427,7 +440,15 @@ Item {
427440 }
428441
429442 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
430− onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
443+ onShowProgressChanged: {
444+ if (showProgress === 0) {
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+ }
431452
432453 Greeter {
433454 id: greeter
tests/qmltests/Greeter/tst_Lockscreen.qml+24−14
@@ -26,7 +26,7 @@ import Unity.Test 0.1 as UT
2626Rectangle {
2727 id: root
2828 width: units.gu(80)
29− height: units.gu(70)
29+ height: units.gu(76)
3030 color: "orange"
3131
3232 Lockscreen {
@@ -39,7 +39,7 @@ Rectangle {
3939 alphaNumeric: pinPadCheckBox.checked
4040 minPinLength: minPinLengthTextField.text
4141 maxPinLength: maxPinLengthTextField.text
42− username: "Lola"
42+ delayMinutes: delayMinutesTextField.text
4343 background: "../../../qml/graphics/phone_background.jpg"
4444 }
4545
@@ -129,6 +129,16 @@ Rectangle {
129129 text: "Retries left"
130130 }
131131 }
132+ Row {
133+ TextField {
134+ id: delayMinutesTextField
135+ width: units.gu(7)
136+ text: "0"
137+ }
138+ Label {
139+ text: "Delay Minutes"
140+ }
141+ }
132142 Label {
133143 id: pinLabel
134144 anchors.verticalCenter: parent.verticalCenter
@@ -193,6 +203,7 @@ Rectangle {
193203
194204 function cleanup() {
195205 lockscreen.clear(false);
206+ delayMinutesTextField.text = "0"
196207 }
197208
198209 function waitForLockscreenReady() {
@@ -237,8 +248,8 @@ Rectangle {
237248
238249 function test_labels_data() {
239250 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" }
251+ {tag: "numeric", alphanumeric: false, infoText: "Please enter your PIN" },
252+ {tag: "alphanumeric", alphanumeric: true, infoText: "Please enter your password" }
242253 ]
243254 }
244255
@@ -246,15 +257,9 @@ Rectangle {
246257 pinPadCheckBox.checked = data.alphanumeric
247258 lockscreen.infoText = data.infoText
248259 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− }
260+ compare(findChild(lockscreen, "infoTextLabel").text, data.infoText, "Placeholdertext is not what it should be")
255261 }
256262
257−
258263 function test_unlock_data() {
259264 return [
260265 {tag: "numeric", alphanumeric: false, username: "has-pin", password: "1234", minPinLength: 4, maxPinLength: 4},
@@ -274,8 +279,7 @@ Rectangle {
274279
275280 var inputField = findChild(lockscreen, "pinentryField")
276281 if (data.alphanumeric) {
277− mouseClick(inputField, units.gu(1), units.gu(1))
278− tryCompare(inputField, "focus", true);
282+ tryCompare(inputField, "activeFocus", true);
279283 typeString(data.password)
280284 keyClick(Qt.Key_Enter)
281285 } else {
@@ -305,7 +309,6 @@ Rectangle {
305309
306310 var inputField = findChild(lockscreen, "pinentryField")
307311 if (data.alphanumeric) {
308− mouseClick(inputField, units.gu(1), units.gu(1))
309312 tryCompare(inputField, "activeFocus", true);
310313 typeString("1")
311314 } else {
@@ -470,6 +473,13 @@ Rectangle {
470473 var label = findChild(lockscreen, "infoTextLabel")
471474 compare(label.text, data.text);
472475 }
476+
477+ function test_delayMinutes() {
478+ delayMinutesTextField.text = "4"
479+ waitForLockscreenReady()
480+ var label = findChild(lockscreen, "deviceLockedLabel")
481+ compare(label.text, "Device Locked")
482+ }
473483 }
474484
475485 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() {