@@ -19,7 +19,9 @@ import AccountsService 0.1
1919import GSettings 1.0
2020import Unity.Application 0.1
2121import Ubuntu.Components 0.1
22+import Ubuntu.Components.Popups 1.0
2223import Ubuntu.Gestures 0.1
24+import Ubuntu.SystemImage 0.1
2325import Unity.Launcher 0.1
2426import Utils 0.1
2527import LightDM 0.1 as LightDM
@@ -53,6 +55,10 @@ Item {
5355 property bool sideStageEnabled: shell.width >= units.gu(100)
5456 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
5557
58+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
59+ property int failedLoginsDelayAttempts: 7 // number of failed logins
60+ property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
61+
5662 function activateApplication(appId) {
5763 if (ApplicationManager.findApplication(appId)) {
5864 ApplicationManager.requestFocusApplication(appId);
@@ -62,6 +68,13 @@ Item {
6268 }
6369 }
6470
71+ function setFakeActiveForApp(app) {
72+ if (shell.locked) {
73+ greeter.fakeActiveForApp = app
74+ lockscreen.hide()
75+ }
76+ }
77+
6578 Binding {
6679 target: LauncherModel
6780 property: "applicationManager"
@@ -124,7 +137,11 @@ Item {
124137 Connections {
125138 target: ApplicationManager
126139 onFocusRequested: {
127− if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
140+ if (appId === "dialer-app") {
141+ // Always let the dialer-app through. Either user asked
142+ // for an emergency call or accepted an incoming call.
143+ setFakeActiveForApp(appId)
144+ } else if (greeter.fakeActiveForApp !== "" && greeter.fakeActiveForApp !== appId) {
128145 lockscreen.show();
129146 }
130147 greeter.hide();
@@ -141,6 +158,11 @@ Item {
141158 if (greeter.shown && appId != "unity8-dash") {
142159 greeter.hide();
143160 }
161+ if (appId === "dialer-app") {
162+ // Always let the dialer-app through. Either user asked
163+ // for an emergency call or accepted an incoming call.
164+ setFakeActiveForApp(appId)
165+ }
144166 }
145167 }
146168
@@ -233,11 +255,7 @@ Item {
233255
234256 onEntered: LightDM.Greeter.respond(passphrase);
235257 onCancel: greeter.show()
236− onEmergencyCall: {
237− greeter.fakeActiveForApp = "dialer-app"
238− shell.activateApplication("dialer-app")
239− lockscreen.hide()
240− }
258+ onEmergencyCall: shell.activateApplication("dialer-app") // will automatically enter fake-active mode
241259
242260 onShownChanged: if (shown) greeter.fakeActiveForApp = ""
243261
@@ -255,13 +273,21 @@ Item {
255273
256274 onShowPrompt: {
257275 if (greeter.narrowMode) {
258− lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
276+ var promptText = text.toLowerCase()
277+ if (isDefaultPrompt) {
278+ promptText = lockscreen.alphaNumeric ?
279+ i18n.tr("passphrase") : i18n.tr("passcode")
280+ }
281+ lockscreen.placeholderText = i18n.tr("Enter your %1").arg(promptText)
282+ lockscreen.wrongPlaceholderText = i18n.tr("Incorrect %1").arg(promptText) +
283+ "\n" +
284+ i18n.tr("Please re-enter")
259285 lockscreen.show();
260286 }
261287 }
262288
263289 onPromptlessChanged: {
264− if (LightDM.Greeter.promptless) {
290+ if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
265291 lockscreen.hide()
266292 } else {
267293 lockscreen.reset();
@@ -270,13 +296,40 @@ Item {
270296 }
271297
272298 onAuthenticationComplete: {
299+ if (LightDM.Greeter.authenticated) {
300+ AccountsService.failedLogins = 0
301+ }
302+ // Else only penalize user for a failed login if they actually were
303+ // prompted for a password. We do this below after the promptless
304+ // early exit.
305+
273306 if (LightDM.Greeter.promptless) {
274307 return;
275308 }
309+
276310 if (LightDM.Greeter.authenticated) {
277311 lockscreen.hide();
278312 greeter.login();
279313 } else {
314+ AccountsService.failedLogins++
315+ if (maxFailedLogins >= 2) { // require at least a warning
316+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
317+ var title = lockscreen.alphaNumeric ?
318+ i18n.tr("Sorry, incorrect passphrase.") :
319+ i18n.tr("Sorry, incorrect passcode.")
320+ var text = i18n.tr("This will be your last attempt.") + " " +
321+ (lockscreen.alphaNumeric ?
322+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
323+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
324+ lockscreen.showInfoPopup(title, text)
325+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
326+ SystemImage.factoryReset() // Ouch!
327+ }
328+ }
329+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
330+ lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
331+ }
332+
280333 lockscreen.clear(true);
281334 if (greeter.narrowMode) {
282335 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
@@ -311,8 +364,16 @@ Item {
311364 }
312365
313366 property bool fullyShown: showProgress === 1.0
367+ onFullyShownChanged: {
368+ // Wait until the greeter is completely covering lockscreen before resetting it.
369+ if (fullyShown && !LightDM.Greeter.authenticated) {
370+ lockscreen.reset();
371+ lockscreen.show();
372+ }
373+ }
374+
314375 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
315− onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
376+ onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
316377
317378 Greeter {
318379 id: greeter
@@ -350,10 +411,6 @@ Item {
350411 if (greeter.narrowMode) {
351412 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
352413 }
353− if (!LightDM.Greeter.promptless) {
354− lockscreen.reset();
355− lockscreen.show();
356− }
357414 greeter.fakeActiveForApp = "";
358415 greeter.forceActiveFocus();
359416 }
@@ -403,7 +460,7 @@ Item {
403460 }
404461
405462 if (LightDM.Greeter.active) {
406− if (!LightDM.Greeter.promptless) {
463+ if (!LightDM.Greeter.authenticated) {
407464 lockscreen.show()
408465 }
409466 greeter.hide()