Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1246)

Unified Diff: ui/login/account_picker/user_pod_row.js

Issue 2733953008: Add fingerprint UI in user pod (Closed)
Patch Set: consolidate strings Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/login/account_picker/user_pod_row.js
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index c52ccfa36948317df4a453d3197ba3dc7e0dd191..718681b71c58391ce1d0421924ab719e931116e8 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -125,6 +125,30 @@ cr.define('login', function() {
5: 'forceOfflinePassword'
};
+ /**
+ * Supported fingerprint unlock states.
+ * @enum {number}
+ * @const
+ */
+ var FINGERPRINT_STATES = {
+ DEFAULT: 0,
+ FINGERPRINT_SIGNIN: 1,
+ FINGERPRINT_FAILED: 2,
+ };
+
+ /**
+ * The fingerprint states to classes mapping.
+ * {@code state} properties indicate current fingerprint unlock state.
+ * {@code class} properties are CSS classes used to set the icons' background
+ * and password placeholder color.
+ * @const {Array<{type: !number, class: !string}>}
+ */
+ var FINGERPRINT_STATES_MAPPING = [
+ {state: 0, class: 'default'},
jdufault 2017/03/08 23:21:28 fingerprintDefault? Or remove fingerprint prefix f
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
+ {state: 1, class: 'fingerprintSignin'},
+ {state: 2, class: 'fingerprintFailed'}
+ ];
+
// Focus and tab order are organized as follows:
//
// (1) all user pods have tab index 1 so they are traversed first;
@@ -711,6 +735,13 @@ cr.define('login', function() {
*/
userClickAuthAllowed_: false,
+ /**
+ * Whether the user has recently authencated with fingerprint.
jdufault 2017/03/08 23:21:27 nit: authencated -> authenticated
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
+ * @type {boolean}
+ * @private
+ */
+ fingerprintAuthenticated_: false,
+
/** @override */
decorate: function() {
this.tabIndex = UserPodTabOrder.POD_INPUT;
@@ -750,6 +781,16 @@ cr.define('login', function() {
this.actionBoxRemoveUserWarningButtonElement.addEventListener('keydown',
this.handleRemoveUserConfirmationKeyDown_.bind(this));
+ if (this.fingerprintIconElement) {
+ this.fingerprintIconElement.addEventListener(
+ 'mouseover', this.handleFingerprintIconMouseOver_.bind(this));
+ this.fingerprintIconElement.addEventListener(
+ 'mouseout', this.handleFingerprintIconMouseOut_.bind(this));
+ this.fingerprintIconElement.addEventListener(
+ 'mousedown', stopEventPropagation);
+ this.fingerprintIconElement.tabIndex = UserPodTabOrder.POD_CUSTOM_ICON;
jdufault 2017/03/08 23:21:27 Does tabbing work properly when easy unlock and PI
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 easy unlock seems to set the tabIndex only when th
+ }
+
var customIcon = this.customIconElement;
customIcon.parentNode.replaceChild(new UserPodCustomIcon(), customIcon);
},
@@ -1096,6 +1137,14 @@ cr.define('login', function() {
},
/**
+ * Gets the fingerprint icon area.
+ * @type {!HTMLDivElement}
+ */
+ get fingerprintIconElement() {
+ return this.querySelector('.fingerprint-icon-container');
+ },
+
+ /**
* Updates the user pod element.
*/
update: function() {
@@ -1216,6 +1265,10 @@ cr.define('login', function() {
this.userTypeIconAreaElement.hidden = false;
},
+ isFingerprintIconShown: function() {
+ return this.fingerprintIconElement && !this.fingerprintIconElement.hidden;
+ },
+
/**
* The user that this pod represents.
* @type {!Object}
@@ -1412,6 +1465,10 @@ cr.define('login', function() {
this.classList.toggle('signing-in', true);
chrome.send('attemptUnlock', [this.user.username]);
} else if (this.isAuthTypePassword) {
+ if (this.fingerprintAuthenticated_) {
+ this.fingerprintAuthenticated_ = false;
+ return true;
+ }
var pinValue = this.pinKeyboard ? this.pinKeyboard.value : '';
var password = this.passwordElement.value || pinValue;
if (!password)
@@ -1822,6 +1879,45 @@ cr.define('login', function() {
},
/**
+ * Handles mouseover event on fingerprint icon.
+ * @param {Event} e MouseOver event.
+ */
+ handleFingerprintIconMouseOver_: function(e) {
+ var bubbleContent = document.createElement('div');
+ bubbleContent.textContent =
+ loadTimeData.getString('fingerprintIconMessage');
+ this.passwordElement.placeholder =
+ loadTimeData.getString('fingerprintHint');
+
+ /** @const */ var BUBBLE_OFFSET = 25;
+ /** @const */ var BUBBLE_PADDING = -8;
+ var attachment = this.isPinShown() ? cr.ui.Bubble.Attachment.RIGHT :
+ cr.ui.Bubble.Attachment.BOTTOM;
+ var bubbleAnchor = this;
+ if (this.isPinShown())
+ bubbleAnchor = (this.getElementsByClassName('auth-container'))[0];
+ $('bubble').showContentForElement(
+ bubbleAnchor, attachment, bubbleContent, BUBBLE_OFFSET,
+ BUBBLE_PADDING, true);
+ },
+
+ /**
+ * Handles mouseout event on fingerprint icon.
+ * @param {Event} e MouseOut event.
+ */
+ handleFingerprintIconMouseOut_: function(e) {
+ if(this.isPinShown()) {
+ $('bubble').hideForElement(
+ (this.getElementsByClassName('auth-container'))[0]);
+ } else {
+ $('bubble').hideForElement(this);
+ }
+ this.passwordElement.placeholder = loadTimeData.getString(
+ this.isPinShown() ? 'pinKeyboardPlaceholderPinPassword' :
+ 'passwordHint');
+ },
+
+ /**
* Handles a keydown event on remove user confirmation button.
* @param {Event} e KeyDown event.
*/
@@ -1899,8 +1995,14 @@ cr.define('login', function() {
* button color and state and hides the error popup bubble.
*/
updateInput_: function() {
- if (this.submitButton)
+ if (this.submitButton) {
this.submitButton.disabled = this.passwordElement.value.length <= 0;
jdufault 2017/03/08 23:21:27 Since you're here, can you change these <= to just
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
+ if (this.isFingerprintIconShown()) {
+ this.submitButton.hidden = this.passwordElement.value.length <= 0;
+ } else {
+ this.submitButton.hidden = false;
+ }
+ }
this.showError = false;
$('bubble').hide();
},
@@ -3001,6 +3103,9 @@ cr.define('login', function() {
// immediatelly.
pod.customIconElement.setTooltip(
icon.tooltip || {text: '', autoshow: false});
+
+ // Hide fingerprint icon when custom icon is shown.
+ this.setUserPodFingerprintIcon(false, username);
jdufault 2017/03/08 23:21:27 Looks like you're missing the third 'state' parame
jdufault 2017/03/08 23:21:28 Please annotate the false parameter, ie, false /*v
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 I've consolidate 'visible' with 'state'.
},
/**
@@ -3037,6 +3142,104 @@ cr.define('login', function() {
// TODO(tengs): Allow option for a fading transition.
pod.customIconElement.hide();
+
+ // Show fingerprint icon if applicable.
+ this.setUserPodFingerprintIcon(
+ true, username, FINGERPRINT_STATES.DEFAULT);
jdufault 2017/03/08 23:21:27 Please annotate true parameter, ie, true /*visible
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
+ },
+
+ /**
+ * Set a fingerprint icon in the user pod of |username|.
+ * @param {boolean} visible Set visiblity of the fingerprint icon
+ * @param {string} username Username of the selected user
+ * @param {number} state Fingerprint unlock state
+ */
+ setUserPodFingerprintIcon: function(visible, username, state) {
jdufault 2017/03/08 23:21:27 Instead of having |visible| and |state| be separat
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Thanks. I added a hidden class to map with hidden
+ var pod = this.getPodWithUsername_(username);
+ if (pod == null) {
+ console.error(
+ 'Unable to set user pod fingerprint icon: user pod not found.');
+ return;
+ }
+ pod.fingerprintAuthenticated_ = false;
+ if (!pod.fingerprintIconElement)
+ return;
+ if (!pod.user.allowFingerprint || !visible ||
+ !pod.customIconElement.hidden) {
+ pod.fingerprintIconElement.hidden = true;
+ pod.submitButton.hidden = false;
+ return;
+ }
+
+ FINGERPRINT_STATES_MAPPING.forEach(function(icon) {
+ pod.fingerprintIconElement.classList.toggle(
+ icon.class, state == icon.state);
+ });
+ pod.fingerprintIconElement.setAttribute(
+ 'aria-label', loadTimeData.getString('fingerprintIconMessage'));
+ pod.fingerprintIconElement.hidden = false;
+ pod.submitButton.hidden = pod.passwordElement.value.length <= 0;
+ this.updatePasswordField_(pod, state);
+ if (state == FINGERPRINT_STATES.DEFAULT)
+ return;
+
+ pod.fingerprintAuthenticated_ = true;
+ this.setActivatedPod(pod);
+ if (state == FINGERPRINT_STATES.FINGERPRINT_FAILED) {
+ /** @const */ var RESET_ICON_TIMEOUT_MS = 500;
+ setTimeout(
+ this.resetIconAndPasswordField_.bind(this, pod),
+ RESET_ICON_TIMEOUT_MS);
+ }
+ },
+
+ /**
+ * Reset the fingerprint icon and password field.
+ * @param {UserPod} pod Pod to reset.
+ */
+ resetIconAndPasswordField_:function(pod) {
+ if (!pod.fingerprintIconElement)
+ return;
+ this.setUserPodFingerprintIcon(
+ true, pod.user.username, FINGERPRINT_STATES.DEFAULT);
+ },
+
+ /**
+ * Remove the fingerprint icon in the user pod.
+ * @param {string} username Username of the selected user
+ */
+ removeUserPodFingerprintIcon: function(username) {
+ var pod = this.getPodWithUsername_(username);
+ if (pod == null) {
+ console.error(
+ 'Unable to remove user pod fingerprint icon: user pod not found.');
+ return;
+ }
+ this.resetIconAndPasswordField_(pod);
+ if (pod.fingerprintIconElement)
jdufault 2017/03/08 23:21:27 nit: {}
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:11 Done.
+ pod.fingerprintIconElement.parentNode.removeChild(
+ pod.fingerprintIconElement);
+ pod.submitButton.hidden = false;
+ },
+
+ /**
+ * Updates the password field in the user pod.
+ * @param {UserPod} pod Pod to update.
+ * @param {number} state Fingerprint unlock state
+ */
+ updatePasswordField_: function(pod, state) {
+ FINGERPRINT_STATES_MAPPING.forEach(function(item) {
+ pod.passwordElement.classList.toggle(item.class, state == item.state);
+ });
+ var placeholderStr = loadTimeData.getString(
+ pod.isPinShown() ? 'pinKeyboardPlaceholderPinPassword' :
+ 'passwordHint');
+ if (state == FINGERPRINT_STATES.FINGERPRINT_SIGNIN) {
jdufault 2017/03/08 23:21:27 There are only two ifs here so it is probably not
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Thanks for the suggestion. I will create the mappi
jdufault 2017/03/09 21:11:25 Yep, as-is is good - with only two elements it's n
+ placeholderStr = loadTimeData.getString('fingerprintSigningin');
+ } else if (state == FINGERPRINT_STATES.FINGERPRINT_FAILED) {
+ placeholderStr = loadTimeData.getString('fingerprintSigninFailed');
+ }
+ pod.passwordElement.placeholder = placeholderStr;
},
/**
@@ -3308,6 +3511,7 @@ cr.define('login', function() {
pod.isActionBoxMenuHovered = false;
pod.classList.remove('focused');
pod.setPinVisibility(false);
+ this.setUserPodFingerprintIcon(false, pod.user.username);
jdufault 2017/03/08 23:21:27 Annotate false parameter
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
// On Desktop, the faded style is not set correctly, so we should
// manually fade out non-focused pods if there is a focused pod.
if (pod.user.isDesktopUser && podToFocus)
@@ -3347,6 +3551,8 @@ cr.define('login', function() {
this.firstShown_ = false;
this.lastFocusedPod_ = podToFocus;
this.scrollFocusedPodIntoView();
+ this.setUserPodFingerprintIcon(
+ true, podToFocus.user.username, FINGERPRINT_STATES.DEFAULT);
jdufault 2017/03/08 23:21:27 Annotate true parameter
xiaoyinh(OOO Sep 11-29) 2017/03/09 20:59:12 Done.
} else {
chrome.send('noPodFocused');
}

Powered by Google App Engine
This is Rietveld 408576698