Index: chrome/browser/resources/settings/people_page/quick_unlock_setup_pin.js |
diff --git a/chrome/browser/resources/settings/people_page/quick_unlock_setup_pin.js b/chrome/browser/resources/settings/people_page/quick_unlock_setup_pin.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..881190f41d027ad96465aecb0e51208fee7cd28d |
--- /dev/null |
+++ b/chrome/browser/resources/settings/people_page/quick_unlock_setup_pin.js |
@@ -0,0 +1,335 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+/** |
+ * @fileoverview |
+ * 'settings-quick-unlock-setup-pin' is the settings page for choosing a PIN. |
+ * |
+ * Example: |
+ * |
+ * <settings-quick-unlock-setup-pin |
+ * set-modes="[[quickUnlockSetModes]]" |
+ * current-route="{{currentRoute}}"> |
+ * </settings-quick-unlock-setup-pin> |
+ */ |
+ |
+(function() { |
+'use strict'; |
+ |
+/** |
+ * The step in the setup PIN flow. |
+ * @const |
+ */ |
+var QuickUnlockSetupPinStep = { |
tommycli
2016/07/13 19:31:11
I think the confirm/choose stuff could be simplifi
jdufault
2016/07/13 20:38:13
Done.
|
+ CHOOSE_PIN: 'choose-pin', |
+ CONFIRM_PIN: 'confirm-pin' |
+}; |
+ |
+/** |
+ * The type of problem message to display. Each entry contains the i18n message |
+ * id and the associated css class to display the problem with. At most one |
+ * problem can be shown at a time. |
+ * @const |
+ */ |
+var QuickUnlockSetupPinProblemType = { |
tommycli
2016/07/13 19:31:12
If there's not going to be more than 3 errors, I t
jdufault
2016/07/13 20:38:13
I've done some renaming, but this makes the showPr
tommycli
2016/07/13 20:57:10
Okay, sounds good. Thanks for trying it.
|
+ TOO_SHORT: { |
+ messageId: 'quickUnlockConfigurePinChoosePinTooShort', |
+ class: 'error' |
+ }, |
+ WEAK: { |
+ messageId: 'quickUnlockConfigurePinChoosePinWeakPinWarning', |
+ class: 'warning' |
+ }, |
+ MISMATCHED: { |
+ messageId: 'quickUnlockConfigurePinMismatchedPins', |
+ class: 'error' |
+ } |
+}; |
+ |
+/** |
+ * A list of the top-10 most commmonly used PINs. This list is taken from |
+ * www.datagenetics.com/blog/september32012/. |
+ * @const |
+ */ |
+var WEAK_PINS = [ |
+ '1234', '1111', '0000', '1212', '7777', '1004', '2000', '4444', '2222', |
+ '6969' |
+]; |
+ |
+/** |
+ * When the user should be shown an error/warning, wait for this long until |
+ * showing them the problem, to give them a chance to stop typing. |
+ * @const |
+ */ |
+var SHOW_PROBLEM_DELAY_IN_MS = 500; |
tommycli
2016/07/13 19:31:12
Is the delay mechanism a requirement? Because othe
jdufault
2016/07/13 20:38:13
Without the delay, as you type the PIN you'll imme
|
+ |
+Polymer({ |
+ is: 'settings-quick-unlock-setup-pin', |
+ |
+ behaviors: [ |
tommycli
2016/07/13 19:31:12
formatting seems odd here
jdufault
2016/07/13 20:38:13
I've added a newline. I'm not sure otherwise what
|
+ QuickUnlockPasswordDetectBehavior, I18nBehavior |
+ ], |
+ |
+ properties: { |
+ /** |
+ * The current PIN keyboard value. |
+ * @private |
+ */ |
+ pinKeyboardValue_: String, |
+ |
+ /** |
+ * Stores the initial PIN value so it can be confirmed. |
+ * @private |
+ */ |
+ initialPin_: String, |
+ |
+ /** |
+ * String content of the title element. |
+ * @private |
+ */ |
+ titleMessage_: String, |
tommycli
2016/07/13 19:31:11
Instead of a message variable, how about two dom-i
jdufault
2016/07/13 20:38:13
Done, I like this better.
|
+ |
+ /** |
+ * String content of the continue/save button. |
+ * @private |
+ */ |
+ continueMessage_: String, |
tommycli
2016/07/13 19:31:11
Ditto here.
jdufault
2016/07/13 20:38:13
Done.
|
+ |
+ /** |
+ * Should the step-specific problem message be displayed? |
+ * @private |
+ */ |
+ showProblem_: Boolean, |
tommycli
2016/07/13 19:31:12
I think you can axe this boolean and just make int
jdufault
2016/07/13 20:38:13
Done.
|
+ |
+ /** |
+ * The actual problem message to display. |
+ * @private |
+ */ |
+ problemMessage_: String, |
+ |
+ /** |
+ * The type of problem class to show (warning or error). |
+ */ |
+ problemClass_: String, |
+ |
+ /** |
+ * Should the step-specific submit button be displayed? |
+ * @private |
+ */ |
+ enableSubmit_: Boolean, |
+ |
+ /** |
+ * The current step/subpage we are on. |
+ * @private |
+ */ |
+ step_: { |
+ type: String, |
+ value: QuickUnlockSetupPinStep.CHOOSE_PIN, |
+ }, |
+ }, |
+ |
+ observers: [ |
+ 'onRouteChanged_(currentRoute)', |
+ 'onSetModesChanged_(setModes)' |
+ ], |
+ |
+ /** @override */ |
+ attached: function() { |
+ this.resetState_(); |
+ this.askForPasswordIfUnset(); |
+ }, |
+ |
+ /** @private */ |
+ onRouteChanged_: function(currentRoute) { |
+ if (this.isScreenActive(QuickUnlockScreen.SETUP_PIN)) { |
+ this.askForPasswordIfUnset(); |
+ } else { |
+ // If the user hits the back button, they can leave the element |
+ // half-completed; therefore, reset state if the element is not active. |
+ this.resetState_(); |
+ } |
+ }, |
+ |
+ /** @private */ |
+ onSetModesChanged_: function() { |
+ if (this.isScreenActive(QuickUnlockScreen.SETUP_PIN)) |
+ this.askForPasswordIfUnset(); |
+ }, |
+ |
+ /** |
+ * Resets the element to the initial state. |
+ * @private |
+ */ |
+ resetState_: function() { |
+ this.initialPin_ = ''; |
+ this.pinKeyboardValue_ = ''; |
+ this.showProblem_ = false; |
+ this.enableSubmit_ = false; |
+ this.setStep_(QuickUnlockSetupPinStep.CHOOSE_PIN); |
+ this.onPinChange_(); |
+ }, |
+ |
+ /** |
+ * Returns true if the given PIN is likely easy to guess. |
+ * @private |
+ */ |
+ isPinWeak_: function(pin) { |
+ // Warn if it's a top-10 pin. |
+ if (WEAK_PINS.includes(pin)) |
+ return true; |
+ |
+ // Warn if the PIN is consecutive digits. |
+ var delta = 0; |
+ for (var i = 1; i < pin.length; ++i) { |
+ var prev = Number(pin[i - 1]); |
+ var num = Number(pin[i]); |
+ if (Number.isNaN(prev) || Number.isNaN(num)) |
+ return false; |
+ delta = Math.max(delta, Math.abs(num - prev)); |
+ } |
+ |
+ return delta <= 1; |
+ }, |
+ |
+ /** |
+ * Returns true if the given PIN matches PIN requirements, such as minimum |
+ * length. |
+ * @private |
+ */ |
+ isPinLongEnough_: function(pin) { |
+ return pin.length >= 4; |
+ }, |
+ |
+ /** |
+ * Returns true if the PIN is ready to be changed to a new value. |
+ * @private |
+ */ |
+ canSubmit_: function() { |
+ return this.isPinLongEnough_(this.pinKeyboardValue_) && |
+ this.initialPin_ == this.pinKeyboardValue_; |
+ }, |
+ |
+ /** |
+ * Notify the user about a problem. The problem will be shown after |
+ * SHOW_PROBLEM_DELAY_IN_MS. The problem show can be cancelled by calling |
+ * |this.cancelPendingProblem_()|. |
+ * @private |
+ */ |
+ postProblem_: function(problemType) { |
+ function activateProblem() { |
+ this.showProblem_ = true; |
+ this.problemMessage_ = this.i18n(problemType.messageId); |
+ this.problemClass_ = problemType.class; |
+ setTimeout(this.updateStyles.bind(this)); |
tommycli
2016/07/13 19:31:11
Hmm... does it not work without this call? I've ne
jdufault
2016/07/13 20:38:13
The icon color is not updated without the call. I'
tommycli
2016/07/13 20:57:09
Cool. Getting rid of the setTimeout makes it much
|
+ } |
+ |
+ this.cancelPendingProblem_(); |
+ this.pendingProblem_ = setTimeout(activateProblem.bind(this), |
+ SHOW_PROBLEM_DELAY_IN_MS); |
+ }, |
+ |
+ /** @private */ |
+ cancelPendingProblem_: function() { |
+ if (this.pendingProblem_) { |
+ clearTimeout(this.pendingProblem_); |
+ delete this.pendingProblem_; |
+ } |
+ }, |
+ |
+ /** @private */ |
+ onPinChange_: function() { |
+ // If the user typed, we want to reset the show problem timeout. |
+ this.cancelPendingProblem_(); |
+ |
+ if (this.isChooseStep_()) { |
+ var isPinLongEnough = this.isPinLongEnough_(this.pinKeyboardValue_); |
+ var isWeak = isPinLongEnough && this.isPinWeak_(this.pinKeyboardValue_); |
+ |
+ if (!isPinLongEnough && this.pinKeyboardValue_) |
+ this.postProblem_(QuickUnlockSetupPinProblemType.TOO_SHORT); |
+ else if (isWeak) |
+ this.postProblem_(QuickUnlockSetupPinProblemType.WEAK); |
+ else |
+ this.showProblem_ = false; |
+ |
+ this.enableSubmit_ = isPinLongEnough; |
+ |
+ } else if (this.isConfirmStep_()) { |
+ var canSubmit = this.canSubmit_(); |
+ |
+ if (!canSubmit && this.pinKeyboardValue_) |
+ this.postProblem_(QuickUnlockSetupPinProblemType.MISMATCHED); |
+ else |
+ this.showProblem_ = false; |
+ |
+ this.enableSubmit_ = canSubmit; |
+ } |
+ }, |
+ |
+ /** @private */ |
+ onPinSubmit_: function() { |
+ if (this.isChooseStep_()) { |
+ if (this.isPinLongEnough_(this.pinKeyboardValue_)) { |
+ this.initialPin_ = this.pinKeyboardValue_; |
+ this.pinKeyboardValue_ = ''; |
+ this.setStep_(QuickUnlockSetupPinStep.CONFIRM_PIN); |
+ this.onPinChange_(); |
+ } |
+ } else if (this.isConfirmStep_()) { |
+ // onPinSubmit_ gets called if the user hits enter on the PIN keyboard. |
+ // The PIN is not guaranteed to be valid in that case. |
+ if (!this.canSubmit_()) |
+ return; |
+ |
+ function onSetModesCompleted(didSet) { |
+ if (!didSet) { |
+ console.error('Failed to update pin'); |
+ return; |
+ } |
+ |
+ this.resetState_(); |
+ this.currentRoute = { |
+ page: 'basic', |
+ section: 'people', |
+ subpage: [QuickUnlockScreen.CHOOSE_METHOD] |
+ }; |
+ } |
+ |
+ this.setModes.call( |
+ null, |
+ [chrome.quickUnlockPrivate.QuickUnlockMode.PIN], |
+ [this.pinKeyboardValue_], |
+ onSetModesCompleted.bind(this)); |
+ } |
+ }, |
+ |
+ /** @private */ |
+ setStep_: function(step) { |
+ this.step_ = step; |
+ switch (this.step_) { |
+ case QuickUnlockSetupPinStep.CHOOSE_PIN: |
+ this.titleMessage_ = this.i18n('quickUnlockConfigurePinChoosePinTitle'); |
+ this.continueMessage_ = |
+ this.i18n('quickUnlockConfigurePinContinueButton'); |
+ break; |
+ case QuickUnlockSetupPinStep.CONFIRM_PIN: |
+ this.titleMessage_ = |
+ this.i18n('quickUnlockConfigurePinConfirmPinTitle'); |
+ this.continueMessage_ = this.i18n('save'); |
+ break; |
+ } |
+ }, |
+ |
+ /** @private */ |
+ isChooseStep_: function() { |
+ return this.step_ == QuickUnlockSetupPinStep.CHOOSE_PIN; |
+ }, |
+ |
+ /** @private */ |
+ isConfirmStep_: function() { |
+ return this.step_ == QuickUnlockSetupPinStep.CONFIRM_PIN; |
+ }, |
+}); |
+ |
+})(); |