1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ash/login/ui/login_auth_user_view.h"
6
7 #include <map>
8 #include <memory>
9 #include <utility>
10
11 #include "ash/login/login_screen_controller.h"
12 #include "ash/login/resources/grit/login_resources.h"
13 #include "ash/login/ui/arrow_button_view.h"
14 #include "ash/login/ui/horizontal_image_sequence_animation_decoder.h"
15 #include "ash/login/ui/lock_screen.h"
16 #include "ash/login/ui/login_display_style.h"
17 #include "ash/login/ui/login_password_view.h"
18 #include "ash/login/ui/login_pin_input_view.h"
19 #include "ash/login/ui/login_pin_view.h"
20 #include "ash/login/ui/login_user_view.h"
21 #include "ash/login/ui/non_accessible_view.h"
22 #include "ash/login/ui/pin_keyboard_animation.h"
23 #include "ash/login/ui/pin_request_view.h"
24 #include "ash/login/ui/system_label_button.h"
25 #include "ash/login/ui/views_utils.h"
26 #include "ash/public/cpp/login_constants.h"
27 #include "ash/resources/vector_icons/vector_icons.h"
28 #include "ash/session/session_controller_impl.h"
29 #include "ash/shell.h"
30 #include "ash/strings/grit/ash_strings.h"
31 #include "ash/style/ash_color_provider.h"
32 #include "ash/system/model/clock_model.h"
33 #include "ash/system/model/system_tray_model.h"
34 #include "ash/system/night_light/time_of_day.h"
35 #include "ash/wallpaper/wallpaper_controller_impl.h"
36 #include "base/bind.h"
37 #include "base/i18n/time_formatting.h"
38 #include "base/memory/ptr_util.h"
39 #include "base/strings/utf_string_conversions.h"
40 #include "base/time/time.h"
41 #include "base/timer/timer.h"
42 #include "chromeos/constants/chromeos_features.h"
43 #include "components/user_manager/user.h"
44 #include "ui/accessibility/ax_enums.mojom.h"
45 #include "ui/accessibility/ax_node_data.h"
46 #include "ui/base/l10n/l10n_util.h"
47 #include "ui/base/resource/resource_bundle.h"
48 #include "ui/compositor/callback_layer_animation_observer.h"
49 #include "ui/compositor/layer_animation_sequence.h"
50 #include "ui/compositor/layer_animator.h"
51 #include "ui/compositor/scoped_layer_animation_settings.h"
52 #include "ui/gfx/color_analysis.h"
53 #include "ui/gfx/geometry/size.h"
54 #include "ui/gfx/interpolated_transform.h"
55 #include "ui/gfx/paint_vector_icon.h"
56 #include "ui/gfx/vector_icon_types.h"
57 #include "ui/views/background.h"
58 #include "ui/views/border.h"
59 #include "ui/views/controls/button/md_text_button.h"
60 #include "ui/views/controls/highlight_path_generator.h"
61 #include "ui/views/controls/label.h"
62 #include "ui/views/layout/box_layout.h"
63 #include "ui/views/layout/fill_layout.h"
64 #include "ui/views/layout/flex_layout.h"
65 #include "ui/views/layout/grid_layout.h"
66 #include "ui/views/style/typography.h"
67 #include "ui/views/view.h"
68
69 namespace ash {
70 namespace {
71
72 constexpr const char kLoginAuthUserViewClassName[] = "LoginAuthUserView";
73
74 // Distance between the user view (ie, the icon and name) and other elements
75 const int kDistanceBetweenUserViewAndPasswordDp = 24;
76 const int kDistanceBetweenUserViewAndPinInputDp = 32;
77 const int kDistanceBetweenUserViewAndOnlineSigninDp = 24;
78 const int kDistanceBetweenUserViewAndChallengeResponseDp = 32;
79
80 // Distance between the password textfield and the the pin keyboard.
81 const int kDistanceBetweenPasswordFieldAndPinKeyboardDp = 16;
82
83 // Height of button used for switching between pin and password authentication.
84 const int kPinPasswordToggleButtonHeight = 32;
85 const int kPinPasswordToggleButtonPaddingTop = 24;
86
87 // Distance from the end of pin keyboard to the bottom of the big user view.
88 const int kDistanceFromPinKeyboardToBigUserViewBottomDp = 50;
89
90 // Distance from the top of the user view to the user icon.
91 constexpr int kDistanceFromTopOfBigUserViewToUserIconDp = 24;
92
93 constexpr SkColor kChallengeResponseErrorColor = gfx::kGoogleRed300;
94
95 // Date time format containing only the day of the week, for example: "Tuesday".
96 constexpr char kDayOfWeekOnlyTimeFormat[] = "EEEE";
97
98 constexpr int kFingerprintIconSizeDp = 28;
99 constexpr int kResetToDefaultIconDelayMs = 1300;
100 constexpr base::TimeDelta kResetToDefaultMessageDelayMs =
101 base::TimeDelta::FromMilliseconds(3000);
102 constexpr int kFingerprintIconTopSpacingDp = 20;
103 constexpr int kSpacingBetweenFingerprintIconAndLabelDp = 15;
104 constexpr int kFingerprintViewWidthDp = 204;
105 constexpr int kDistanceBetweenPasswordFieldAndFingerprintViewDp = 90;
106 constexpr int kFingerprintFailedAnimationDurationMs = 700;
107 constexpr int kFingerprintFailedAnimationNumFrames = 45;
108
109 constexpr base::TimeDelta kChallengeResponseResetAfterFailureDelay =
110 base::TimeDelta::FromSeconds(5);
111 constexpr int kChallengeResponseArrowSizeDp = 48;
112 constexpr int kSpacingBetweenChallengeResponseArrowAndIconDp = 100;
113 constexpr int kSpacingBetweenChallengeResponseIconAndLabelDp = 15;
114 constexpr int kChallengeResponseIconSizeDp = 28;
115 constexpr int kDistanceBetweenPwdFieldAndChallengeResponseViewDp = 0;
116
117 constexpr int kDisabledAuthMessageVerticalBorderDp = 16;
118 constexpr int kDisabledAuthMessageHorizontalBorderDp = 16;
119 constexpr int kDisabledAuthMessageChildrenSpacingDp = 4;
120 constexpr int kDisabledAuthMessageTimeWidthDp = 204;
121 constexpr int kDisabledAuthMessageMultiprofileWidthDp = 304;
122 constexpr int kDisabledAuthMessageHeightDp = 98;
123 constexpr int kDisabledAuthMessageIconSizeDp = 24;
124 constexpr int kDisabledAuthMessageTitleFontSizeDeltaDp = 3;
125 constexpr int kDisabledAuthMessageContentsFontSizeDeltaDp = -1;
126 constexpr int kDisabledAuthMessageRoundedCornerRadiusDp = 8;
127
128 constexpr int kLockedTpmMessageVerticalBorderDp = 16;
129 constexpr int kLockedTpmMessageHorizontalBorderDp = 16;
130 constexpr int kLockedTpmMessageChildrenSpacingDp = 4;
131 constexpr int kLockedTpmMessageWidthDp = 360;
132 constexpr int kLockedTpmMessageHeightDp = 108;
133 constexpr int kLockedTpmMessageIconSizeDp = 24;
134 constexpr int kLockedTpmMessageDeltaDp = 0;
135 constexpr int kLockedTpmMessageRoundedCornerRadiusDp = 8;
136
137 constexpr int kNonEmptyWidthDp = 1;
SizeFromHeight(int height)138 gfx::Size SizeFromHeight(int height) {
139 return gfx::Size(kNonEmptyWidthDp, height);
140 }
141
142 // Returns an observer that will hide |view| when it fires. The observer will
143 // delete itself after firing (by returning true). Make sure to call
144 // |observer->SetActive()| after attaching it.
BuildObserverToHideView(views::View * view)145 ui::CallbackLayerAnimationObserver* BuildObserverToHideView(views::View* view) {
146 return new ui::CallbackLayerAnimationObserver(base::BindRepeating(
147 [](views::View* view,
148 const ui::CallbackLayerAnimationObserver& observer) {
149 // Don't hide the view if the animation is aborted, as |view| may no
150 // longer be valid.
151 if (observer.aborted_count())
152 return true;
153
154 view->SetVisible(false);
155 return true;
156 },
157 view));
158 }
159
BuildObserverToNotifyA11yLocationChanged(views::View * view)160 ui::CallbackLayerAnimationObserver* BuildObserverToNotifyA11yLocationChanged(
161 views::View* view) {
162 return new ui::CallbackLayerAnimationObserver(base::BindRepeating(
163 [](views::View* view,
164 const ui::CallbackLayerAnimationObserver& observer) {
165 // Don't notify a11y event if the animation is aborted, as |view| may no
166 // longer be valid.
167 if (observer.aborted_count())
168 return true;
169
170 view->NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged,
171 false /*send_native_event*/);
172 return true;
173 },
174 view));
175 }
176
BuildObserverToNotifyA11yLocationChanged(LoginPinView * view)177 ui::CallbackLayerAnimationObserver* BuildObserverToNotifyA11yLocationChanged(
178 LoginPinView* view) {
179 return new ui::CallbackLayerAnimationObserver(base::BindRepeating(
180 [](LoginPinView* view,
181 const ui::CallbackLayerAnimationObserver& observer) {
182 // Don't notify a11y event if the animation is aborted, as |view| may no
183 // longer be valid.
184 if (observer.aborted_count())
185 return true;
186
187 view->NotifyAccessibilityLocationChanged();
188 return true;
189 },
190 view));
191 }
192
193 // Clears the password for the given |LoginPasswordView| instance, hides it, and
194 // then deletes itself.
195 class ClearPasswordAndHideAnimationObserver
196 : public ui::ImplicitAnimationObserver {
197 public:
ClearPasswordAndHideAnimationObserver(LoginPasswordView * view)198 explicit ClearPasswordAndHideAnimationObserver(LoginPasswordView* view)
199 : password_view_(view) {}
200 ~ClearPasswordAndHideAnimationObserver() override = default;
201
202 // ui::ImplicitAnimationObserver:
OnImplicitAnimationsCompleted()203 void OnImplicitAnimationsCompleted() override {
204 password_view_->Reset();
205 password_view_->SetVisible(false);
206 delete this;
207 }
208
209 private:
210 LoginPasswordView* const password_view_;
211
212 DISALLOW_COPY_AND_ASSIGN(ClearPasswordAndHideAnimationObserver);
213 };
214
215 // The label shown below the fingerprint icon.
216 class FingerprintLabel : public views::Label {
217 public:
FingerprintLabel()218 FingerprintLabel() {
219 SetSubpixelRenderingEnabled(false);
220 SetAutoColorReadabilityEnabled(false);
221 SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
222 AshColorProvider::ContentLayerType::kTextColorSecondary));
223 SetMultiLine(true);
224
225 SetTextBasedOnState(FingerprintState::AVAILABLE_DEFAULT,
226 false /*can_use_pin*/);
227 }
228
SetTextBasedOnAuthAttempt(bool success)229 void SetTextBasedOnAuthAttempt(bool success) {
230 SetText(l10n_util::GetStringUTF16(
231 success ? IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_SUCCESS
232 : IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_FAILED));
233 SetAccessibleName(l10n_util::GetStringUTF16(
234 success ? IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_SUCCESS
235 : IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_FAILED));
236 }
237
SetTextBasedOnState(FingerprintState state,bool can_use_pin)238 void SetTextBasedOnState(FingerprintState state, bool can_use_pin) {
239 auto get_displayed_id = [&]() {
240 switch (state) {
241 case FingerprintState::UNAVAILABLE:
242 case FingerprintState::AVAILABLE_DEFAULT:
243 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE;
244 case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
245 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_TOUCH_SENSOR;
246 case FingerprintState::DISABLED_FROM_ATTEMPTS:
247 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS;
248 case FingerprintState::DISABLED_FROM_TIMEOUT:
249 if (can_use_pin)
250 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_PIN_OR_PASSWORD_REQUIRED;
251 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_PASSWORD_REQUIRED;
252 }
253 NOTREACHED();
254 };
255
256 auto get_accessible_id = [&]() {
257 if (state == FingerprintState::DISABLED_FROM_ATTEMPTS)
258 return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_DISABLED_FROM_ATTEMPTS;
259 return get_displayed_id();
260 };
261
262 SetText(l10n_util::GetStringUTF16(get_displayed_id()));
263 SetAccessibleName(l10n_util::GetStringUTF16(get_accessible_id()));
264 }
265
266 // views::View:
GetAccessibleNodeData(ui::AXNodeData * node_data)267 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
268 node_data->role = ax::mojom::Role::kStaticText;
269 node_data->SetName(accessible_name_);
270 }
271
272 private:
SetAccessibleName(const base::string16 & name)273 void SetAccessibleName(const base::string16& name) {
274 accessible_name_ = name;
275 NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
276 true /*send_native_event*/);
277 }
278
279 base::string16 accessible_name_;
280
281 DISALLOW_COPY_AND_ASSIGN(FingerprintLabel);
282 };
283
284 // The content needed to render the disabled auth message view.
285 struct LockScreenMessage {
286 base::string16 title;
287 base::string16 content;
288 const gfx::VectorIcon* icon;
289 };
290
291 // Returns the message used when the device was locked due to a time window
292 // limit.
GetWindowLimitMessage(const base::Time & unlock_time,bool use_24hour_clock)293 LockScreenMessage GetWindowLimitMessage(const base::Time& unlock_time,
294 bool use_24hour_clock) {
295 LockScreenMessage message;
296 message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_FOR_BED_MESSAGE);
297
298 base::Time local_midnight = base::Time::Now().LocalMidnight();
299
300 base::string16 time_to_display;
301 if (use_24hour_clock) {
302 time_to_display = base::TimeFormatTimeOfDayWithHourClockType(
303 unlock_time, base::k24HourClock, base::kDropAmPm);
304 } else {
305 time_to_display = base::TimeFormatTimeOfDayWithHourClockType(
306 unlock_time, base::k12HourClock, base::kKeepAmPm);
307 }
308
309 if (unlock_time < local_midnight + base::TimeDelta::FromDays(1)) {
310 // Unlock time is today.
311 message.content = l10n_util::GetStringFUTF16(
312 IDS_ASH_LOGIN_COME_BACK_MESSAGE, time_to_display);
313 } else if (unlock_time < local_midnight + base::TimeDelta::FromDays(2)) {
314 // Unlock time is tomorrow.
315 message.content = l10n_util::GetStringFUTF16(
316 IDS_ASH_LOGIN_COME_BACK_TOMORROW_MESSAGE, time_to_display);
317 } else {
318 message.content = l10n_util::GetStringFUTF16(
319 IDS_ASH_LOGIN_COME_BACK_DAY_OF_WEEK_MESSAGE,
320 base::TimeFormatWithPattern(unlock_time, kDayOfWeekOnlyTimeFormat),
321 time_to_display);
322 }
323 message.icon = &kLockScreenTimeLimitMoonIcon;
324 return message;
325 }
326
327 // Returns the message used when the device was locked due to a time usage
328 // limit.
GetUsageLimitMessage(const base::TimeDelta & used_time)329 LockScreenMessage GetUsageLimitMessage(const base::TimeDelta& used_time) {
330 LockScreenMessage message;
331
332 // 1 minute is used instead of 0, because the device is used for a few
333 // milliseconds before locking.
334 if (used_time < base::TimeDelta::FromMinutes(1)) {
335 // The device was locked all day.
336 message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TAKE_BREAK_MESSAGE);
337 message.content =
338 l10n_util::GetStringUTF16(IDS_ASH_LOGIN_LOCKED_ALL_DAY_MESSAGE);
339 } else {
340 // The usage limit is over.
341 message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_IS_UP_MESSAGE);
342
343 // TODO(933973): Stop displaying the hours part of the string when duration
344 // is less than 1 hour. Example: change "0 hours, 7 minutes" to "7 minutes".
345 base::string16 used_time_string;
346 if (!base::TimeDurationFormat(
347 used_time, base::DurationFormatWidth::DURATION_WIDTH_WIDE,
348 &used_time_string)) {
349 LOG(ERROR) << "Failed to generate time duration string.";
350 return message;
351 }
352
353 message.content = l10n_util::GetStringFUTF16(
354 IDS_ASH_LOGIN_SCREEN_TIME_USED_MESSAGE, used_time_string);
355 }
356 message.icon = &kLockScreenTimeLimitTimerIcon;
357 return message;
358 }
359
360 // Returns the message used when the device was locked due to a time limit
361 // override.
GetOverrideMessage()362 LockScreenMessage GetOverrideMessage() {
363 LockScreenMessage message;
364 message.title =
365 l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_FOR_A_BREAK_MESSAGE);
366 message.content =
367 l10n_util::GetStringUTF16(IDS_ASH_LOGIN_MANUAL_LOCK_MESSAGE);
368 message.icon = &kLockScreenTimeLimitLockIcon;
369 return message;
370 }
371
GetLockScreenMessage(AuthDisabledReason lock_reason,const base::Time & unlock_time,const base::TimeDelta & used_time,bool use_24hour_clock)372 LockScreenMessage GetLockScreenMessage(AuthDisabledReason lock_reason,
373 const base::Time& unlock_time,
374 const base::TimeDelta& used_time,
375 bool use_24hour_clock) {
376 switch (lock_reason) {
377 case AuthDisabledReason::kTimeWindowLimit:
378 return GetWindowLimitMessage(unlock_time, use_24hour_clock);
379 case AuthDisabledReason::kTimeUsageLimit:
380 return GetUsageLimitMessage(used_time);
381 case AuthDisabledReason::kTimeLimitOverride:
382 return GetOverrideMessage();
383 default:
384 NOTREACHED();
385 }
386 }
387
388 } // namespace
389
390 // Consists of fingerprint icon view and a label.
391 class LoginAuthUserView::FingerprintView : public views::View {
392 public:
FingerprintView()393 FingerprintView() {
394 SetPaintToLayer();
395 layer()->SetFillsBoundsOpaquely(false);
396 SetBorder(views::CreateEmptyBorder(kFingerprintIconTopSpacingDp, 0, 0, 0));
397
398 auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
399 views::BoxLayout::Orientation::kVertical, gfx::Insets(),
400 kSpacingBetweenFingerprintIconAndLabelDp));
401 layout->set_main_axis_alignment(
402 views::BoxLayout::MainAxisAlignment::kCenter);
403
404 icon_ = new AnimatedRoundedImageView(
405 gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp),
406 0 /*corner_radius*/);
407 icon_->SetImage(gfx::CreateVectorIcon(
408 kLockScreenFingerprintIcon, kFingerprintIconSizeDp,
409 AshColorProvider::Get()->GetContentLayerColor(
410 AshColorProvider::ContentLayerType::kIconColorPrimary)));
411 AddChildView(icon_);
412
413 label_ = new FingerprintLabel();
414 AddChildView(label_);
415
416 DisplayCurrentState();
417 }
418
419 ~FingerprintView() override = default;
420
SetState(FingerprintState state)421 void SetState(FingerprintState state) {
422 if (state_ == state)
423 return;
424
425 reset_state_.Stop();
426 state_ = state;
427
428 DisplayCurrentState();
429
430 if (ShouldFireChromeVoxAlert(state))
431 FireAlert();
432 }
433
SetCanUsePin(bool value)434 void SetCanUsePin(bool value) {
435 if (can_use_pin_ == value)
436 return;
437
438 can_use_pin_ = value;
439 label_->SetTextBasedOnState(state_, can_use_pin_);
440 }
441
NotifyFingerprintAuthResult(bool success)442 void NotifyFingerprintAuthResult(bool success) {
443 reset_state_.Stop();
444 label_->SetTextBasedOnAuthAttempt(success);
445
446 if (success) {
447 icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintSuccessIcon,
448 kFingerprintIconSizeDp,
449 gfx::kGoogleGreen300));
450 } else {
451 SetIcon(FingerprintState::DISABLED_FROM_ATTEMPTS);
452 // base::Unretained is safe because reset_state_ is owned by |this|.
453 reset_state_.Start(
454 FROM_HERE,
455 base::TimeDelta::FromMilliseconds(kResetToDefaultIconDelayMs),
456 base::BindOnce(&FingerprintView::DisplayCurrentState,
457 base::Unretained(this)));
458
459 FireAlert();
460 }
461 }
462
463 // views::View:
CalculatePreferredSize() const464 gfx::Size CalculatePreferredSize() const override {
465 gfx::Size size = views::View::CalculatePreferredSize();
466 size.set_width(kFingerprintViewWidthDp);
467 return size;
468 }
469
470 // views::View:
OnGestureEvent(ui::GestureEvent * event)471 void OnGestureEvent(ui::GestureEvent* event) override {
472 if (event->type() != ui::ET_GESTURE_TAP)
473 return;
474 if (state_ == FingerprintState::AVAILABLE_DEFAULT ||
475 state_ == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING) {
476 SetState(FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING);
477 reset_state_.Start(
478 FROM_HERE, kResetToDefaultMessageDelayMs,
479 base::BindOnce(&FingerprintView::SetState, base::Unretained(this),
480 FingerprintState::AVAILABLE_DEFAULT));
481 }
482 }
483
484 private:
DisplayCurrentState()485 void DisplayCurrentState() {
486 SetVisible(state_ != FingerprintState::UNAVAILABLE);
487 SetIcon(state_);
488 label_->SetTextBasedOnState(state_, can_use_pin_);
489 }
490
FireAlert()491 void FireAlert() {
492 label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
493 true /*send_native_event*/);
494 }
495
SetIcon(FingerprintState state)496 void SetIcon(FingerprintState state) {
497 const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
498 AshColorProvider::ContentLayerType::kIconColorPrimary);
499 const SkColor color =
500 state == FingerprintState::AVAILABLE_DEFAULT ||
501 state == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING
502 ? icon_color
503 : AshColorProvider::Get()->GetDisabledColor(icon_color);
504 switch (state) {
505 case FingerprintState::UNAVAILABLE:
506 case FingerprintState::AVAILABLE_DEFAULT:
507 case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
508 case FingerprintState::DISABLED_FROM_TIMEOUT:
509 icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintIcon,
510 kFingerprintIconSizeDp, color));
511 break;
512 case FingerprintState::DISABLED_FROM_ATTEMPTS:
513 icon_->SetAnimationDecoder(
514 std::make_unique<HorizontalImageSequenceAnimationDecoder>(
515 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
516 IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER),
517 base::TimeDelta::FromMilliseconds(
518 kFingerprintFailedAnimationDurationMs),
519 kFingerprintFailedAnimationNumFrames),
520 AnimatedRoundedImageView::Playback::kSingle);
521 break;
522 }
523 }
524
ShouldFireChromeVoxAlert(FingerprintState state)525 bool ShouldFireChromeVoxAlert(FingerprintState state) {
526 return state == FingerprintState::DISABLED_FROM_ATTEMPTS ||
527 state == FingerprintState::DISABLED_FROM_TIMEOUT;
528 }
529
530 FingerprintLabel* label_ = nullptr;
531 AnimatedRoundedImageView* icon_ = nullptr;
532 base::OneShotTimer reset_state_;
533 FingerprintState state_ = FingerprintState::AVAILABLE_DEFAULT;
534
535 // Affects DISABLED_FROM_TIMEOUT message.
536 bool can_use_pin_ = false;
537
538 DISALLOW_COPY_AND_ASSIGN(FingerprintView);
539 };
540
541 // Consists of challenge-response icon view and a label.
542 class LoginAuthUserView::ChallengeResponseView : public views::View {
543 public:
544 enum class State { kInitial, kAuthenticating, kFailure };
545
ChallengeResponseView(base::RepeatingClosure on_start_tap)546 explicit ChallengeResponseView(base::RepeatingClosure on_start_tap)
547 : on_start_tap_(std::move(on_start_tap)) {
548 SetPaintToLayer();
549 layer()->SetFillsBoundsOpaquely(false);
550
551 auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
552 views::BoxLayout::Orientation::kVertical));
553 layout->set_cross_axis_alignment(
554 views::BoxLayout::CrossAxisAlignment::kCenter);
555 auto arrow_button_view = std::make_unique<ArrowButtonView>(
556 base::BindRepeating(&ChallengeResponseView::ArrowButtonPressed,
557 base::Unretained(this)),
558 kChallengeResponseArrowSizeDp);
559 arrow_button_view->SetInstallFocusRingOnFocus(true);
560 views::InstallCircleHighlightPathGenerator(arrow_button_view.get());
561 arrow_button_ = AddChildView(std::move(arrow_button_view));
562 arrow_button_->SetAccessibleName(l10n_util::GetStringUTF16(
563 IDS_ASH_LOGIN_START_SMART_CARD_AUTH_BUTTON_ACCESSIBLE_NAME));
564
565 arrow_to_icon_spacer_ = AddChildView(std::make_unique<NonAccessibleView>());
566 arrow_to_icon_spacer_->SetPreferredSize(
567 gfx::Size(0, kSpacingBetweenChallengeResponseArrowAndIconDp));
568
569 icon_ = AddChildView(std::make_unique<views::ImageView>());
570 icon_->SetImage(GetImageForIcon());
571
572 auto* icon_to_label_spacer =
573 AddChildView(std::make_unique<NonAccessibleView>());
574 icon_to_label_spacer->SetPreferredSize(
575 gfx::Size(0, kSpacingBetweenChallengeResponseIconAndLabelDp));
576
577 label_ = AddChildView(std::make_unique<views::Label>(
578 GetTextForLabel(), views::style::CONTEXT_LABEL,
579 views::style::STYLE_PRIMARY));
580 label_->SetAutoColorReadabilityEnabled(false);
581 label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
582 AshColorProvider::ContentLayerType::kTextColorSecondary));
583 label_->SetSubpixelRenderingEnabled(false);
584 label_->SetFontList(views::Label::GetDefaultFontList().Derive(
585 /*size_delta=*/1, gfx::Font::FontStyle::ITALIC,
586 gfx::Font::Weight::NORMAL));
587 }
588
589 ~ChallengeResponseView() override = default;
590
SetState(State state)591 void SetState(State state) {
592 if (state_ == state)
593 return;
594 state_ = state;
595
596 reset_state_timer_.Stop();
597 if (state == State::kFailure) {
598 reset_state_timer_.Start(
599 FROM_HERE, kChallengeResponseResetAfterFailureDelay,
600 base::BindOnce(&ChallengeResponseView::SetState,
601 base::Unretained(this), State::kInitial));
602 }
603
604 arrow_button_->EnableLoadingAnimation(state == State::kAuthenticating);
605 icon_->SetImage(GetImageForIcon());
606 label_->SetText(GetTextForLabel());
607
608 if (state == State::kFailure) {
609 label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
610 /*send_native_event=*/true);
611 }
612
613 Layout();
614 }
615
RequestFocus()616 void RequestFocus() override { arrow_button_->RequestFocus(); }
617
GetButtonForTesting()618 views::Button* GetButtonForTesting() { return arrow_button_; }
GetLabelForTesting()619 views::Label* GetLabelForTesting() { return label_; }
620
621 private:
GetImageForIcon() const622 gfx::ImageSkia GetImageForIcon() const {
623 switch (state_) {
624 case State::kInitial:
625 case State::kAuthenticating:
626 return gfx::CreateVectorIcon(
627 kLockScreenSmartCardIcon, kChallengeResponseIconSizeDp,
628 AshColorProvider::Get()->GetContentLayerColor(
629 AshColorProvider::ContentLayerType::kIconColorPrimary));
630 case State::kFailure:
631 return gfx::CreateVectorIcon(kLockScreenSmartCardFailureIcon,
632 kChallengeResponseIconSizeDp,
633 kChallengeResponseErrorColor);
634 }
635 }
636
GetTextForLabel() const637 base::string16 GetTextForLabel() const {
638 switch (state_) {
639 case State::kInitial:
640 case State::kAuthenticating:
641 return l10n_util::GetStringUTF16(
642 IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE);
643 case State::kFailure:
644 return l10n_util::GetStringUTF16(
645 IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE);
646 }
647 }
648
ArrowButtonPressed()649 void ArrowButtonPressed() {
650 // Ignore further clicks while handling the previous one.
651 if (state_ != State::kAuthenticating)
652 on_start_tap_.Run();
653 }
654
655 base::RepeatingClosure on_start_tap_;
656 State state_ = State::kInitial;
657 ArrowButtonView* arrow_button_ = nullptr;
658 NonAccessibleView* arrow_to_icon_spacer_ = nullptr;
659 views::ImageView* icon_ = nullptr;
660 views::Label* label_ = nullptr;
661 base::OneShotTimer reset_state_timer_;
662
663 DISALLOW_COPY_AND_ASSIGN(ChallengeResponseView);
664 };
665
666 // The message shown to user when the auth method is |AUTH_DISABLED|.
667 class LoginAuthUserView::DisabledAuthMessageView : public views::View {
668 public:
669 class ASH_EXPORT TestApi {
670 public:
TestApi(DisabledAuthMessageView * view)671 explicit TestApi(DisabledAuthMessageView* view) : view_(view) {}
672 ~TestApi() = default;
673
GetDisabledAuthMessageContent() const674 const base::string16& GetDisabledAuthMessageContent() const {
675 return view_->message_contents_->GetText();
676 }
677
678 private:
679 DisabledAuthMessageView* const view_;
680 };
681
682 // If the reason of disabled auth is multiprofile policy, then we can already
683 // set the text and message. Otherwise, in case of disabled auth because of
684 // time limit exceeded on child account, we wait for SetAuthDisabledMessage to
685 // be called.
DisabledAuthMessageView(bool shown_because_of_multiprofile_policy,MultiProfileUserBehavior multiprofile_policy)686 DisabledAuthMessageView(bool shown_because_of_multiprofile_policy,
687 MultiProfileUserBehavior multiprofile_policy)
688 : shown_because_of_multiprofile_policy_(
689 shown_because_of_multiprofile_policy) {
690 SetLayoutManager(std::make_unique<views::BoxLayout>(
691 views::BoxLayout::Orientation::kVertical,
692 gfx::Insets(kDisabledAuthMessageVerticalBorderDp,
693 kDisabledAuthMessageHorizontalBorderDp),
694 kDisabledAuthMessageChildrenSpacingDp));
695 SetPaintToLayer();
696 layer()->SetFillsBoundsOpaquely(false);
697 SetPreferredSize(gfx::Size(shown_because_of_multiprofile_policy
698 ? kDisabledAuthMessageMultiprofileWidthDp
699 : kDisabledAuthMessageTimeWidthDp,
700 kDisabledAuthMessageHeightDp));
701 SetFocusBehavior(FocusBehavior::ALWAYS);
702 if (!shown_because_of_multiprofile_policy) {
703 message_icon_ = new views::ImageView();
704 message_icon_->SetPreferredSize(gfx::Size(
705 kDisabledAuthMessageIconSizeDp, kDisabledAuthMessageIconSizeDp));
706 message_icon_->SetImage(gfx::CreateVectorIcon(
707 kLockScreenTimeLimitMoonIcon, kDisabledAuthMessageIconSizeDp,
708 AshColorProvider::Get()->GetContentLayerColor(
709 AshColorProvider::ContentLayerType::kIconColorPrimary)));
710 AddChildView(message_icon_);
711 }
712
713 auto decorate_label = [](views::Label* label) {
714 label->SetSubpixelRenderingEnabled(false);
715 label->SetAutoColorReadabilityEnabled(false);
716 label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
717 AshColorProvider::ContentLayerType::kTextColorPrimary));
718 label->SetFocusBehavior(FocusBehavior::ALWAYS);
719 };
720 message_title_ =
721 new views::Label(base::string16(), views::style::CONTEXT_LABEL,
722 views::style::STYLE_PRIMARY);
723 message_title_->SetFontList(
724 gfx::FontList().Derive(kDisabledAuthMessageTitleFontSizeDeltaDp,
725 gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
726 decorate_label(message_title_);
727 AddChildView(message_title_);
728
729 message_contents_ =
730 new views::Label(base::string16(), views::style::CONTEXT_LABEL,
731 views::style::STYLE_PRIMARY);
732 message_contents_->SetFontList(
733 gfx::FontList().Derive(kDisabledAuthMessageContentsFontSizeDeltaDp,
734 gfx::Font::NORMAL, gfx::Font::Weight::NORMAL));
735 decorate_label(message_contents_);
736 message_contents_->SetMultiLine(true);
737 AddChildView(message_contents_);
738
739 if (shown_because_of_multiprofile_policy) {
740 message_title_->SetText(l10n_util::GetStringUTF16(
741 IDS_ASH_LOGIN_MULTI_PROFILES_RESTRICTED_POLICY_TITLE));
742 switch (multiprofile_policy) {
743 case MultiProfileUserBehavior::PRIMARY_ONLY:
744 message_contents_->SetText(l10n_util::GetStringUTF16(
745 IDS_ASH_LOGIN_MULTI_PROFILES_PRIMARY_ONLY_POLICY_MSG));
746 break;
747 case MultiProfileUserBehavior::NOT_ALLOWED:
748 message_contents_->SetText(l10n_util::GetStringUTF16(
749 IDS_ASH_LOGIN_MULTI_PROFILES_NOT_ALLOWED_POLICY_MSG));
750 break;
751 case MultiProfileUserBehavior::OWNER_PRIMARY_ONLY:
752 message_contents_->SetText(l10n_util::GetStringUTF16(
753 IDS_ASH_LOGIN_MULTI_PROFILES_OWNER_PRIMARY_ONLY_MSG));
754 break;
755 default:
756 NOTREACHED();
757 }
758 }
759 }
760
761 ~DisabledAuthMessageView() override = default;
762
763 // Set the parameters needed to render the message.
SetAuthDisabledMessage(const AuthDisabledData & auth_disabled_data,bool use_24hour_clock)764 void SetAuthDisabledMessage(const AuthDisabledData& auth_disabled_data,
765 bool use_24hour_clock) {
766 // Do not do anything if message is already shown.
767 if (shown_because_of_multiprofile_policy_)
768 return;
769 LockScreenMessage message = GetLockScreenMessage(
770 auth_disabled_data.reason, auth_disabled_data.auth_reenabled_time,
771 auth_disabled_data.device_used_time, use_24hour_clock);
772 message_icon_->SetImage(gfx::CreateVectorIcon(
773 *message.icon, kDisabledAuthMessageIconSizeDp,
774 AshColorProvider::Get()->GetContentLayerColor(
775 AshColorProvider::ContentLayerType::kIconColorPrimary)));
776 message_title_->SetText(message.title);
777 message_contents_->SetText(message.content);
778 Layout();
779 }
780
781 // views::View:
OnPaint(gfx::Canvas * canvas)782 void OnPaint(gfx::Canvas* canvas) override {
783 views::View::OnPaint(canvas);
784
785 cc::PaintFlags flags;
786 flags.setStyle(cc::PaintFlags::kFill_Style);
787 flags.setColor(
788 PinRequestView::GetChildUserDialogColor(false /*using blur*/));
789 canvas->DrawRoundRect(GetContentsBounds(),
790 kDisabledAuthMessageRoundedCornerRadiusDp, flags);
791 }
RequestFocus()792 void RequestFocus() override { message_title_->RequestFocus(); }
793
794 private:
795 views::Label* message_title_;
796 views::Label* message_contents_;
797 views::ImageView* message_icon_;
798 // Used in case a child account has triggered the disabled auth message
799 // because of time limit exceeded while it also has disabled auth by
800 // multiprofile policy.
801 bool shown_because_of_multiprofile_policy_ = false;
802
803 DISALLOW_COPY_AND_ASSIGN(DisabledAuthMessageView);
804 };
805
806 // The message shown to user when TPM is locked.
807 class LoginAuthUserView::LockedTpmMessageView : public views::View {
808 public:
LockedTpmMessageView()809 LockedTpmMessageView() {
810 SetLayoutManager(std::make_unique<views::BoxLayout>(
811 views::BoxLayout::Orientation::kVertical,
812 gfx::Insets(kLockedTpmMessageVerticalBorderDp,
813 kLockedTpmMessageHorizontalBorderDp),
814 kLockedTpmMessageChildrenSpacingDp));
815 SetPaintToLayer();
816 layer()->SetFillsBoundsOpaquely(false);
817 SetPreferredSize(
818 gfx::Size(kLockedTpmMessageWidthDp, kLockedTpmMessageHeightDp));
819 SetFocusBehavior(FocusBehavior::ALWAYS);
820
821 auto message_icon = std::make_unique<views::ImageView>();
822 message_icon->SetPreferredSize(
823 gfx::Size(kLockedTpmMessageIconSizeDp, kLockedTpmMessageIconSizeDp));
824 message_icon->SetImage(gfx::CreateVectorIcon(
825 kLockScreenAlertIcon,
826 AshColorProvider::Get()->GetContentLayerColor(
827 AshColorProvider::ContentLayerType::kIconColorPrimary)));
828 message_icon_ = AddChildView(std::move(message_icon));
829
830 message_warning_ = CreateLabel();
831 message_description_ = CreateLabel();
832
833 // Set content.
834 base::string16 message_description = l10n_util::GetStringUTF16(
835 IDS_ASH_LOGIN_POD_TPM_LOCKED_ISSUE_DESCRIPTION);
836 message_description_->SetText(message_description);
837 }
838
839 LockedTpmMessageView(const LockedTpmMessageView&) = delete;
840 LockedTpmMessageView& operator=(const LockedTpmMessageView&) = delete;
841 ~LockedTpmMessageView() override = default;
842
843 // Set the parameters needed to render the message.
SetRemainingTime(base::TimeDelta time_left)844 void SetRemainingTime(base::TimeDelta time_left) {
845 base::string16 time_left_message;
846 if (base::TimeDurationFormatWithSeconds(
847 time_left, base::DurationFormatWidth::DURATION_WIDTH_WIDE,
848 &time_left_message)) {
849 base::string16 message_warning = l10n_util::GetStringFUTF16(
850 IDS_ASH_LOGIN_POD_TPM_LOCKED_ISSUE_WARNING, time_left_message);
851 message_warning_->SetText(message_warning);
852
853 if (time_left.InMinutes() != prev_time_left_.InMinutes()) {
854 message_warning_->NotifyAccessibilityEvent(
855 ax::mojom::Event::kTextChanged, true);
856 }
857 prev_time_left_ = time_left;
858 }
859 }
860
861 // views::View:
OnPaint(gfx::Canvas * canvas)862 void OnPaint(gfx::Canvas* canvas) override {
863 views::View::OnPaint(canvas);
864
865 cc::PaintFlags flags;
866 flags.setStyle(cc::PaintFlags::kFill_Style);
867 flags.setColor(
868 PinRequestView::GetChildUserDialogColor(false /*using blur*/));
869 canvas->DrawRoundRect(GetContentsBounds(),
870 kLockedTpmMessageRoundedCornerRadiusDp, flags);
871 }
RequestFocus()872 void RequestFocus() override { message_warning_->RequestFocus(); }
873
874 private:
CreateLabel()875 views::Label* CreateLabel() {
876 auto label = std::make_unique<views::Label>(base::string16(),
877 views::style::CONTEXT_LABEL,
878 views::style::STYLE_PRIMARY);
879 label->SetFontList(gfx::FontList().Derive(kLockedTpmMessageDeltaDp,
880 gfx::Font::NORMAL,
881 gfx::Font::Weight::NORMAL));
882 label->SetSubpixelRenderingEnabled(false);
883 label->SetAutoColorReadabilityEnabled(false);
884 label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
885 AshColorProvider::ContentLayerType::kTextColorPrimary));
886 label->SetFocusBehavior(FocusBehavior::ALWAYS);
887 label->SetMultiLine(true);
888 return AddChildView(std::move(label));
889 }
890
891 base::TimeDelta prev_time_left_;
892 views::Label* message_warning_;
893 views::Label* message_description_;
894 views::ImageView* message_icon_;
895 };
896
897 LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata() = default;
898 LoginAuthUserView::AuthMethodsMetadata::~AuthMethodsMetadata() = default;
899 LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata(
900 const AuthMethodsMetadata&) = default;
901
902 struct LoginAuthUserView::UiState {
UiStateash::LoginAuthUserView::UiState903 explicit UiState(const LoginAuthUserView* view) {
904 has_password = view->ShouldShowPasswordField();
905 has_pin_input = view->ShouldShowPinInputField();
906 has_pinpad = view->ShouldShowPinPad();
907 has_toggle = view->ShouldShowToggle();
908 has_fingerprint = view->HasAuthMethod(LoginAuthUserView::AUTH_FINGERPRINT);
909 has_challenge_response =
910 view->HasAuthMethod(LoginAuthUserView::AUTH_CHALLENGE_RESPONSE);
911 auth_disabled = view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED);
912 tpm_is_locked =
913 view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED_TPM_LOCKED);
914 force_online_sign_in =
915 view->HasAuthMethod(LoginAuthUserView::AUTH_ONLINE_SIGN_IN);
916
917 non_pin_y_start_in_screen = view->GetBoundsInScreen().y();
918 pin_start_in_screen = view->pin_view_->GetBoundsInScreen().origin();
919 }
920
921 bool has_password = false;
922 bool has_pin_input = false;
923 bool has_pinpad = false;
924 bool has_toggle = false;
925 bool has_fingerprint = false;
926 bool has_challenge_response = false;
927 bool auth_disabled = false;
928 bool tpm_is_locked = false;
929 bool force_online_sign_in = false;
930 // Used for this view's animation in `ApplyAnimationPostLayout`.
931 int non_pin_y_start_in_screen = 0;
932 gfx::Point pin_start_in_screen;
933 };
934
TestApi(LoginAuthUserView * view)935 LoginAuthUserView::TestApi::TestApi(LoginAuthUserView* view) : view_(view) {}
936
937 LoginAuthUserView::TestApi::~TestApi() = default;
938
user_view() const939 LoginUserView* LoginAuthUserView::TestApi::user_view() const {
940 return view_->user_view_;
941 }
942
password_view() const943 LoginPasswordView* LoginAuthUserView::TestApi::password_view() const {
944 return view_->password_view_;
945 }
946
pin_view() const947 LoginPinView* LoginAuthUserView::TestApi::pin_view() const {
948 return view_->pin_view_;
949 }
950
pin_input_view() const951 LoginPinInputView* LoginAuthUserView::TestApi::pin_input_view() const {
952 return view_->pin_input_view_;
953 }
954
pin_password_toggle() const955 views::Button* LoginAuthUserView::TestApi::pin_password_toggle() const {
956 return view_->pin_password_toggle_;
957 }
958
online_sign_in_message() const959 views::Button* LoginAuthUserView::TestApi::online_sign_in_message() const {
960 return view_->online_sign_in_message_;
961 }
962
disabled_auth_message() const963 views::View* LoginAuthUserView::TestApi::disabled_auth_message() const {
964 return view_->disabled_auth_message_;
965 }
966
challenge_response_button()967 views::Button* LoginAuthUserView::TestApi::challenge_response_button() {
968 return view_->challenge_response_view_->GetButtonForTesting();
969 }
970
challenge_response_label()971 views::Label* LoginAuthUserView::TestApi::challenge_response_label() {
972 return view_->challenge_response_view_->GetLabelForTesting();
973 }
974
HasAuthMethod(AuthMethods auth_method) const975 bool LoginAuthUserView::TestApi::HasAuthMethod(AuthMethods auth_method) const {
976 return view_->HasAuthMethod(auth_method);
977 }
978
979 const base::string16&
GetDisabledAuthMessageContent() const980 LoginAuthUserView::TestApi::GetDisabledAuthMessageContent() const {
981 return LoginAuthUserView::DisabledAuthMessageView::TestApi(
982 view_->disabled_auth_message_)
983 .GetDisabledAuthMessageContent();
984 }
985
986 LoginAuthUserView::Callbacks::Callbacks() = default;
987
988 LoginAuthUserView::Callbacks::Callbacks(const Callbacks& other) = default;
989
990 LoginAuthUserView::Callbacks::~Callbacks() = default;
991
LoginAuthUserView(const LoginUserInfo & user,const Callbacks & callbacks)992 LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
993 const Callbacks& callbacks)
994 : NonAccessibleView(kLoginAuthUserViewClassName),
995 on_auth_(callbacks.on_auth),
996 on_tap_(callbacks.on_tap) {
997 DCHECK(callbacks.on_auth);
998 DCHECK(callbacks.on_tap);
999 DCHECK(callbacks.on_remove_warning_shown);
1000 DCHECK(callbacks.on_remove);
1001 DCHECK(callbacks.on_easy_unlock_icon_hovered);
1002 DCHECK_NE(user.basic_user_info.type, user_manager::USER_TYPE_PUBLIC_ACCOUNT);
1003
1004 // Build child views.
1005 auto user_view = std::make_unique<LoginUserView>(
1006 LoginDisplayStyle::kLarge, true /*show_dropdown*/,
1007 base::BindRepeating(&LoginAuthUserView::OnUserViewTap,
1008 base::Unretained(this)),
1009 callbacks.on_remove_warning_shown, callbacks.on_remove);
1010 user_view_ = user_view.get();
1011
1012 const LoginPalette palette = CreateDefaultLoginPalette();
1013
1014 auto password_view = std::make_unique<LoginPasswordView>(palette);
1015 password_view_ = password_view.get();
1016 password_view->SetPaintToLayer(); // Needed for opacity animation.
1017 password_view->layer()->SetFillsBoundsOpaquely(false);
1018 password_view_->SetDisplayPasswordButtonVisible(
1019 user.show_display_password_button);
1020 password_view->Init(
1021 base::BindRepeating(&LoginAuthUserView::OnAuthSubmit,
1022 base::Unretained(this)),
1023 base::BindRepeating(&LoginAuthUserView::OnPasswordTextChanged,
1024 base::Unretained(this)),
1025 callbacks.on_easy_unlock_icon_hovered,
1026 callbacks.on_easy_unlock_icon_tapped);
1027
1028 auto pin_input_view = std::make_unique<LoginPinInputView>(palette);
1029 pin_input_view_ = pin_input_view.get();
1030 pin_input_view->Init(base::BindRepeating(&LoginAuthUserView::OnAuthSubmit,
1031 base::Unretained(this)),
1032 base::BindRepeating(&LoginAuthUserView::OnPinTextChanged,
1033 base::Unretained(this)));
1034
1035 auto toggle_container = std::make_unique<NonAccessibleView>();
1036 toggle_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
1037 views::BoxLayout::Orientation::kVertical,
1038 gfx::Insets(kPinPasswordToggleButtonPaddingTop, 0, 0, 0)));
1039 pin_password_toggle_ =
1040 toggle_container->AddChildView(std::make_unique<SystemLabelButton>(
1041 base::BindRepeating(&LoginAuthUserView::OnSwitchButtonClicked,
1042 base::Unretained(this)),
1043 GetPinPasswordToggleText(), SystemLabelButton::DisplayType::DEFAULT,
1044 /*multiline*/ false));
1045 pin_password_toggle_->SetMaxSize(
1046 gfx::Size(/*ignored*/ 0, kPinPasswordToggleButtonHeight));
1047
1048 auto pin_view = std::make_unique<LoginPinView>(
1049 LoginPinView::Style::kAlphanumeric, palette,
1050 base::BindRepeating(&LoginAuthUserView::OnPinPadInsertDigit,
1051 base::Unretained(this)),
1052 base::BindRepeating(&LoginAuthUserView::OnPinPadBackspace,
1053 base::Unretained(this)));
1054 pin_view_ = pin_view.get();
1055 DCHECK(pin_view_->layer());
1056
1057 auto padding_below_password_view = std::make_unique<NonAccessibleView>();
1058 padding_below_password_view->SetPreferredSize(gfx::Size(
1059 kNonEmptyWidthDp, kDistanceBetweenPasswordFieldAndPinKeyboardDp));
1060 padding_below_password_view_ = padding_below_password_view.get();
1061
1062 auto padding_below_user_view = std::make_unique<NonAccessibleView>();
1063 padding_below_user_view->SetPreferredSize(
1064 gfx::Size(kNonEmptyWidthDp, kDistanceBetweenUserViewAndPasswordDp));
1065 padding_below_user_view_ = padding_below_user_view.get();
1066
1067 base::string16 button_message =
1068 l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE);
1069 if (user.is_signed_in) {
1070 button_message =
1071 l10n_util::GetStringUTF16(IDS_ASH_LOCK_SCREEN_VERIFY_ACCOUNT_MESSAGE);
1072 }
1073 bool is_login_secondary =
1074 Shell::Get()->session_controller()->GetSessionState() ==
1075 session_manager::SessionState::LOGIN_SECONDARY;
1076 auto online_sign_in_button = std::make_unique<SystemLabelButton>(
1077 base::BindRepeating(&LoginAuthUserView::OnOnlineSignInMessageTap,
1078 base::Unretained(this)),
1079 button_message, SystemLabelButton::DisplayType::ALERT_WITH_ICON,
1080 /*multiline*/ false);
1081 // Disable online sign-in on secondary login screen as there is no OOBE there.
1082 online_sign_in_button->SetEnabled(!is_login_secondary);
1083 online_sign_in_message_ = online_sign_in_button.get();
1084
1085 bool shown_because_of_multiprofile_policy =
1086 !user.is_multiprofile_allowed && is_login_secondary;
1087 auto disabled_auth_message = std::make_unique<DisabledAuthMessageView>(
1088 shown_because_of_multiprofile_policy, user.multiprofile_policy);
1089 disabled_auth_message_ = disabled_auth_message.get();
1090
1091 auto locked_tpm_message_view = std::make_unique<LockedTpmMessageView>();
1092 locked_tpm_message_view_ = locked_tpm_message_view.get();
1093
1094 auto fingerprint_view = std::make_unique<FingerprintView>();
1095 fingerprint_view_ = fingerprint_view.get();
1096
1097 auto challenge_response_view =
1098 std::make_unique<ChallengeResponseView>(base::BindRepeating(
1099 &LoginAuthUserView::AttemptAuthenticateWithChallengeResponse,
1100 weak_factory_.GetWeakPtr()));
1101 challenge_response_view_ = challenge_response_view.get();
1102
1103 SetPaintToLayer(ui::LayerType::LAYER_NOT_DRAWN);
1104
1105 // Build layout.
1106 auto wrapped_password_view =
1107 login_views_utils::WrapViewForPreferredSize(std::move(password_view));
1108 auto wrapped_online_sign_in_message_view =
1109 login_views_utils::WrapViewForPreferredSize(
1110 std::move(online_sign_in_button));
1111 auto wrapped_disabled_auth_message_view =
1112 login_views_utils::WrapViewForPreferredSize(
1113 std::move(disabled_auth_message));
1114 auto wrapped_locked_tpm_message_view =
1115 login_views_utils::WrapViewForPreferredSize(
1116 std::move(locked_tpm_message_view));
1117 auto wrapped_user_view =
1118 login_views_utils::WrapViewForPreferredSize(std::move(user_view));
1119 auto wrapped_pin_view =
1120 login_views_utils::WrapViewForPreferredSize(std::move(pin_view));
1121 auto wrapped_pin_input_view =
1122 login_views_utils::WrapViewForPreferredSize(std::move(pin_input_view));
1123 auto wrapped_pin_password_toggle_view =
1124 login_views_utils::WrapViewForPreferredSize(std::move(toggle_container));
1125 auto wrapped_fingerprint_view =
1126 login_views_utils::WrapViewForPreferredSize(std::move(fingerprint_view));
1127 auto wrapped_challenge_response_view =
1128 login_views_utils::WrapViewForPreferredSize(
1129 std::move(challenge_response_view));
1130 auto wrapped_padding_below_password_view =
1131 login_views_utils::WrapViewForPreferredSize(
1132 std::move(padding_below_password_view));
1133 auto wrapped_padding_below_user_view =
1134 login_views_utils::WrapViewForPreferredSize(
1135 std::move(padding_below_user_view));
1136
1137 // Add views in tabbing order; they are rendered in a different order below.
1138 views::View* wrapped_password_view_ptr =
1139 AddChildView(std::move(wrapped_password_view));
1140 views::View* wrapped_online_sign_in_message_view_ptr =
1141 AddChildView(std::move(wrapped_online_sign_in_message_view));
1142 views::View* wrapped_disabled_auth_message_view_ptr =
1143 AddChildView(std::move(wrapped_disabled_auth_message_view));
1144 views::View* wrapped_locked_tpm_message_view_ptr =
1145 AddChildView(std::move(wrapped_locked_tpm_message_view));
1146 views::View* wrapped_pin_input_view_ptr =
1147 AddChildView(std::move(wrapped_pin_input_view));
1148 views::View* wrapped_pin_view_ptr = AddChildView(std::move(wrapped_pin_view));
1149 views::View* wrapped_pin_password_toggle_view_ptr =
1150 AddChildView(std::move(wrapped_pin_password_toggle_view));
1151 views::View* wrapped_fingerprint_view_ptr =
1152 AddChildView(std::move(wrapped_fingerprint_view));
1153 views::View* wrapped_challenge_response_view_ptr =
1154 AddChildView(std::move(wrapped_challenge_response_view));
1155 views::View* wrapped_user_view_ptr =
1156 AddChildView(std::move(wrapped_user_view));
1157 views::View* wrapped_padding_below_password_view_ptr =
1158 AddChildView(std::move(wrapped_padding_below_password_view));
1159 views::View* wrapped_padding_below_user_view_ptr =
1160 AddChildView(std::move(wrapped_padding_below_user_view));
1161
1162 // Use views::GridLayout instead of views::BoxLayout because views::BoxLayout
1163 // lays out children according to the view->children order.
1164 views::GridLayout* grid_layout =
1165 SetLayoutManager(std::make_unique<views::GridLayout>());
1166 views::ColumnSet* column_set = grid_layout->AddColumnSet(0);
1167 column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING,
1168 0 /*resize_percent*/,
1169 views::GridLayout::ColumnSize::kUsePreferred,
1170 0 /*fixed_width*/, 0 /*min_width*/);
1171 auto add_view = [&](views::View* view) {
1172 grid_layout->StartRow(0 /*vertical_resize*/, 0 /*column_set_id*/);
1173 grid_layout->AddExistingView(view);
1174 };
1175 auto add_padding = [&](int amount) {
1176 grid_layout->AddPaddingRow(0 /*vertical_resize*/, amount /*size*/);
1177 };
1178
1179 // Add views in rendering order.
1180 add_padding(kDistanceFromTopOfBigUserViewToUserIconDp);
1181 add_view(wrapped_user_view_ptr);
1182 add_view(wrapped_padding_below_user_view_ptr);
1183 add_view(wrapped_locked_tpm_message_view_ptr);
1184 add_view(wrapped_password_view_ptr);
1185 add_view(wrapped_online_sign_in_message_view_ptr);
1186 add_view(wrapped_disabled_auth_message_view_ptr);
1187 add_view(wrapped_pin_input_view_ptr);
1188 add_view(wrapped_padding_below_password_view_ptr);
1189 add_view(wrapped_pin_view_ptr);
1190 add_view(wrapped_pin_password_toggle_view_ptr);
1191 add_view(wrapped_fingerprint_view_ptr);
1192 add_view(wrapped_challenge_response_view_ptr);
1193 add_padding(kDistanceFromPinKeyboardToBigUserViewBottomDp);
1194
1195 // Update authentication UI.
1196 CaptureStateForAnimationPreLayout();
1197 SetAuthMethods(auth_methods_);
1198 ApplyAnimationPostLayout(/*animate*/ false);
1199 user_view_->UpdateForUser(user, /*animate*/ false);
1200 }
1201
1202 LoginAuthUserView::~LoginAuthUserView() = default;
1203
SetAuthMethods(uint32_t auth_methods,const AuthMethodsMetadata & auth_metadata)1204 void LoginAuthUserView::SetAuthMethods(
1205 uint32_t auth_methods,
1206 const AuthMethodsMetadata& auth_metadata) {
1207 // It is an error to call this method without storing the previous state.
1208 DCHECK(previous_state_);
1209
1210 // Apply changes and determine the new state of input fields.
1211 auth_methods_ = static_cast<AuthMethods>(auth_methods);
1212 auth_metadata_ = auth_metadata;
1213 UpdateInputFieldMode();
1214 const UiState current_state{this};
1215
1216 online_sign_in_message_->SetVisible(current_state.force_online_sign_in);
1217 disabled_auth_message_->SetVisible(current_state.auth_disabled);
1218 locked_tpm_message_view_->SetVisible(current_state.tpm_is_locked);
1219 if (current_state.tpm_is_locked &&
1220 auth_metadata.time_until_tpm_unlock.has_value())
1221 locked_tpm_message_view_->SetRemainingTime(
1222 auth_metadata.time_until_tpm_unlock.value());
1223
1224 // Adjust the PIN keyboard visibility before the password textfield's one, so
1225 // that when both are about to be hidden the focus doesn't jump to the "1"
1226 // keyboard button, causing unexpected accessibility effects.
1227 pin_view_->SetVisible(current_state.has_pinpad);
1228
1229 password_view_->SetEnabled(current_state.has_password);
1230 password_view_->SetEnabledOnEmptyPassword(HasAuthMethod(AUTH_TAP));
1231 password_view_->SetFocusEnabledForChildViews(current_state.has_password);
1232 password_view_->SetVisible(current_state.has_password);
1233 password_view_->layer()->SetOpacity(current_state.has_password ? 1 : 0);
1234
1235 pin_input_view_->UpdateLength(auth_metadata_.autosubmit_pin_length);
1236 pin_input_view_->SetAuthenticateWithEmptyPinOnReturnKey(
1237 HasAuthMethod(AUTH_TAP));
1238 pin_input_view_->SetVisible(current_state.has_pin_input);
1239
1240 pin_password_toggle_->SetVisible(current_state.has_toggle);
1241 pin_password_toggle_->SetText(GetPinPasswordToggleText());
1242
1243 fingerprint_view_->SetVisible(current_state.has_fingerprint);
1244 fingerprint_view_->SetCanUsePin(HasAuthMethod(AUTH_PIN));
1245 challenge_response_view_->SetVisible(current_state.has_challenge_response);
1246
1247 padding_below_user_view_->SetPreferredSize(GetPaddingBelowUserView());
1248 padding_below_password_view_->SetPreferredSize(GetPaddingBelowPasswordView());
1249
1250 password_view_->SetPlaceholderText(GetPasswordViewPlaceholder());
1251 const std::string& user_display_email =
1252 current_user().basic_user_info.display_email;
1253 password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
1254 IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
1255 base::UTF8ToUTF16(user_display_email)));
1256
1257 // Only the active auth user view has authentication methods. If that is the
1258 // case, then render the user view as if it was always focused, since clicking
1259 // on it will not do anything (such as swapping users).
1260 user_view_->SetForceOpaque(auth_methods_ != AUTH_NONE);
1261 user_view_->SetTapEnabled(auth_methods_ == AUTH_NONE);
1262
1263 UpdateFocus();
1264 PreferredSizeChanged();
1265 }
1266
SetEasyUnlockIcon(EasyUnlockIconId id,const base::string16 & accessibility_label)1267 void LoginAuthUserView::SetEasyUnlockIcon(
1268 EasyUnlockIconId id,
1269 const base::string16& accessibility_label) {
1270 password_view_->SetEasyUnlockIcon(id, accessibility_label);
1271
1272 const std::string& user_display_email =
1273 current_user().basic_user_info.display_email;
1274 if (id == EasyUnlockIconId::UNLOCKED) {
1275 password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
1276 IDS_ASH_LOGIN_POD_AUTH_TAP_PASSWORD_FIELD_ACCESSIBLE_NAME,
1277 base::UTF8ToUTF16(user_display_email)));
1278 } else {
1279 password_view_->SetAccessibleName(l10n_util::GetStringFUTF16(
1280 IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
1281 base::UTF8ToUTF16(user_display_email)));
1282 }
1283 }
1284
CaptureStateForAnimationPreLayout()1285 void LoginAuthUserView::CaptureStateForAnimationPreLayout() {
1286 auto stop_animation = [](views::View* view) {
1287 if (view->layer()->GetAnimator()->is_animating())
1288 view->layer()->GetAnimator()->StopAnimating();
1289 };
1290
1291 // Stop any running animation scheduled in ApplyAnimationPostLayout.
1292 stop_animation(this);
1293 stop_animation(password_view_);
1294 stop_animation(pin_view_);
1295 stop_animation(fingerprint_view_);
1296 stop_animation(challenge_response_view_);
1297 stop_animation(pin_password_toggle_);
1298
1299 DCHECK(!previous_state_);
1300 previous_state_ = std::make_unique<UiState>(this);
1301 }
1302
ApplyAnimationPostLayout(bool animate)1303 void LoginAuthUserView::ApplyAnimationPostLayout(bool animate) {
1304 DCHECK(previous_state_);
1305 // Release the previous state if no animation should be performed.
1306 if (!animate) {
1307 previous_state_.reset();
1308 return;
1309 }
1310
1311 const UiState current_state{this};
1312
1313 ////////
1314 // Animate the user info (ie, icon, name) up or down the screen.
1315 {
1316 int non_pin_y_end_in_screen = GetBoundsInScreen().y();
1317
1318 // Transform the layer so the user view renders where it used to be. This
1319 // requires a y offset.
1320 // Note: Doing this animation via ui::ScopedLayerAnimationSettings works,
1321 // but it seems that the timing gets slightly out of sync with the PIN
1322 // animation.
1323 auto move_to_center = std::make_unique<ui::InterpolatedTranslation>(
1324 gfx::PointF(0, previous_state_->non_pin_y_start_in_screen -
1325 non_pin_y_end_in_screen),
1326 gfx::PointF());
1327 auto transition =
1328 ui::LayerAnimationElement::CreateInterpolatedTransformElement(
1329 std::move(move_to_center),
1330 base::TimeDelta::FromMilliseconds(
1331 login_constants::kChangeUserAnimationDurationMs));
1332 transition->set_tween_type(gfx::Tween::Type::FAST_OUT_SLOW_IN);
1333 auto* sequence = new ui::LayerAnimationSequence(std::move(transition));
1334 auto* observer = BuildObserverToNotifyA11yLocationChanged(this);
1335 sequence->AddObserver(observer);
1336 observer->SetActive();
1337 layer()->GetAnimator()->StartAnimation(sequence);
1338 }
1339
1340 ////////
1341 // Fade the password view if it is being hidden or shown.
1342
1343 if (current_state.has_password != previous_state_->has_password) {
1344 float opacity_start = 0, opacity_end = 1;
1345 if (!current_state.has_password)
1346 std::swap(opacity_start, opacity_end);
1347
1348 password_view_->layer()->SetOpacity(opacity_start);
1349
1350 {
1351 ui::ScopedLayerAnimationSettings settings(
1352 password_view_->layer()->GetAnimator());
1353 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
1354 login_constants::kChangeUserAnimationDurationMs));
1355 settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
1356 if (previous_state_->has_password && !current_state.has_password) {
1357 settings.AddObserver(
1358 new ClearPasswordAndHideAnimationObserver(password_view_));
1359 }
1360
1361 password_view_->layer()->SetOpacity(opacity_end);
1362 }
1363 }
1364
1365 ////////
1366 // Fade the pin/pwd toggle if its being hidden or shown.
1367 if (previous_state_->has_toggle != current_state.has_toggle) {
1368 float opacity_start = 0, opacity_end = 1;
1369 if (!current_state.has_toggle)
1370 std::swap(opacity_start, opacity_end);
1371
1372 pin_password_toggle_->layer()->SetOpacity(opacity_start);
1373
1374 {
1375 ui::ScopedLayerAnimationSettings settings(
1376 pin_password_toggle_->layer()->GetAnimator());
1377 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
1378 login_constants::kChangeUserAnimationDurationMs));
1379 settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
1380 pin_password_toggle_->layer()->SetOpacity(opacity_end);
1381 }
1382 }
1383
1384 ////////
1385 // Grow/shrink the PIN keyboard if it is being hidden or shown.
1386
1387 if (previous_state_->has_pinpad != current_state.has_pinpad) {
1388 if (!current_state.has_pinpad) {
1389 gfx::Point pin_end_in_screen = pin_view_->GetBoundsInScreen().origin();
1390 gfx::Rect pin_bounds = pin_view_->bounds();
1391 pin_bounds.set_x(previous_state_->pin_start_in_screen.x() -
1392 pin_end_in_screen.x());
1393 pin_bounds.set_y(previous_state_->pin_start_in_screen.y() -
1394 pin_end_in_screen.y());
1395
1396 // Since PIN is disabled, the previous Layout() hid the PIN keyboard.
1397 // We need to redisplay it where it used to be.
1398 // pin_view_->SetVisible(true);
1399 pin_view_->SetBoundsRect(pin_bounds);
1400 }
1401
1402 auto transition = std::make_unique<PinKeyboardAnimation>(
1403 current_state.has_pinpad /*grow*/, pin_view_->height(),
1404 // TODO(https://crbug.com/955119): Implement proper animation.
1405 base::TimeDelta::FromMilliseconds(
1406 login_constants::kChangeUserAnimationDurationMs / 2.0f),
1407 gfx::Tween::FAST_OUT_SLOW_IN);
1408 auto* sequence = new ui::LayerAnimationSequence(std::move(transition));
1409
1410 // Hide the PIN keyboard after animation if needed.
1411 if (!current_state.has_pinpad) {
1412 auto* observer = BuildObserverToHideView(pin_view_);
1413 sequence->AddObserver(observer);
1414 observer->SetActive();
1415 }
1416 auto* observer = BuildObserverToNotifyA11yLocationChanged(pin_view_);
1417 sequence->AddObserver(observer);
1418 observer->SetActive();
1419 pin_view_->layer()->GetAnimator()->ScheduleAnimation(sequence);
1420 }
1421
1422 ////////
1423 // Fade the fingerprint view if it is being hidden or shown.
1424
1425 if (previous_state_->has_fingerprint != current_state.has_fingerprint) {
1426 float opacity_start = 0, opacity_end = 1;
1427 if (!current_state.has_fingerprint)
1428 std::swap(opacity_start, opacity_end);
1429
1430 fingerprint_view_->layer()->SetOpacity(opacity_start);
1431
1432 {
1433 ui::ScopedLayerAnimationSettings settings(
1434 fingerprint_view_->layer()->GetAnimator());
1435 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
1436 login_constants::kChangeUserAnimationDurationMs));
1437 settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
1438 fingerprint_view_->layer()->SetOpacity(opacity_end);
1439 }
1440 }
1441
1442 ////////
1443 // Fade the challenge response (Smart Card) if it is being hidden or shown.
1444 if (previous_state_->has_challenge_response !=
1445 current_state.has_challenge_response) {
1446 float opacity_start = 0, opacity_end = 1;
1447 if (!current_state.has_challenge_response)
1448 std::swap(opacity_start, opacity_end);
1449
1450 challenge_response_view_->layer()->SetOpacity(opacity_start);
1451
1452 {
1453 ui::ScopedLayerAnimationSettings settings(
1454 challenge_response_view_->layer()->GetAnimator());
1455 settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
1456 login_constants::kChangeUserAnimationDurationMs));
1457 settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
1458 challenge_response_view_->layer()->SetOpacity(opacity_end);
1459 }
1460 }
1461
1462 previous_state_.reset();
1463 }
1464
UpdateForUser(const LoginUserInfo & user)1465 void LoginAuthUserView::UpdateForUser(const LoginUserInfo& user) {
1466 const bool user_changed = current_user().basic_user_info.account_id !=
1467 user.basic_user_info.account_id;
1468 user_view_->UpdateForUser(user, true /*animate*/);
1469 if (user_changed) {
1470 password_view_->Reset();
1471 password_view_->SetDisplayPasswordButtonVisible(
1472 user.show_display_password_button);
1473 }
1474 online_sign_in_message_->SetText(
1475 l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SIGN_IN_REQUIRED_MESSAGE));
1476 }
1477
SetFingerprintState(FingerprintState state)1478 void LoginAuthUserView::SetFingerprintState(FingerprintState state) {
1479 fingerprint_view_->SetState(state);
1480 }
1481
NotifyFingerprintAuthResult(bool success)1482 void LoginAuthUserView::NotifyFingerprintAuthResult(bool success) {
1483 fingerprint_view_->NotifyFingerprintAuthResult(success);
1484 }
1485
SetAuthDisabledMessage(const AuthDisabledData & auth_disabled_data)1486 void LoginAuthUserView::SetAuthDisabledMessage(
1487 const AuthDisabledData& auth_disabled_data) {
1488 disabled_auth_message_->SetAuthDisabledMessage(
1489 auth_disabled_data, current_user().use_24hour_clock);
1490 Layout();
1491 }
1492
current_user() const1493 const LoginUserInfo& LoginAuthUserView::current_user() const {
1494 return user_view_->current_user();
1495 }
1496
GetActiveInputView()1497 views::View* LoginAuthUserView::GetActiveInputView() {
1498 if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1499 return pin_input_view_;
1500
1501 return password_view_;
1502 }
1503
CalculatePreferredSize() const1504 gfx::Size LoginAuthUserView::CalculatePreferredSize() const {
1505 gfx::Size size = views::View::CalculatePreferredSize();
1506 // Make sure we are at least as big as the user view. If we do not do this the
1507 // view will be below minimum size when no auth methods are displayed.
1508 size.SetToMax(user_view_->GetPreferredSize());
1509 return size;
1510 }
1511
RequestFocus()1512 void LoginAuthUserView::RequestFocus() {
1513 if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1514 pin_input_view_->RequestFocus();
1515 else
1516 password_view_->RequestFocus();
1517 }
1518
OnAuthSubmit(const base::string16 & password)1519 void LoginAuthUserView::OnAuthSubmit(const base::string16& password) {
1520 // Pressing enter when the password field is empty and tap-to-unlock is
1521 // enabled should attempt unlock.
1522 if (HasAuthMethod(AUTH_TAP) && password.empty()) {
1523 Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock(
1524 current_user().basic_user_info.account_id);
1525 return;
1526 }
1527
1528 password_view_->SetReadOnly(true);
1529 pin_input_view_->SetReadOnly(true);
1530
1531 Shell::Get()->login_screen_controller()->AuthenticateUserWithPasswordOrPin(
1532 current_user().basic_user_info.account_id, base::UTF16ToUTF8(password),
1533 HasAuthMethod(AUTH_PIN),
1534 base::BindOnce(&LoginAuthUserView::OnAuthComplete,
1535 weak_factory_.GetWeakPtr()));
1536 }
1537
OnAuthComplete(base::Optional<bool> auth_success)1538 void LoginAuthUserView::OnAuthComplete(base::Optional<bool> auth_success) {
1539 // Clear the password only if auth fails. Make sure to keep the password view
1540 // disabled even if auth succeededs, as if the user submits a password while
1541 // animating the next lock screen will not work as expected. See
1542 // https://crbug.com/808486.
1543 if (!auth_success.has_value() || !auth_success.value()) {
1544 password_view_->Reset();
1545 password_view_->SetReadOnly(false);
1546 pin_input_view_->Reset();
1547 pin_input_view_->SetReadOnly(false);
1548 }
1549
1550 on_auth_.Run(auth_success.value(), /*display_error_messages=*/true);
1551 }
1552
OnChallengeResponseAuthComplete(base::Optional<bool> auth_success)1553 void LoginAuthUserView::OnChallengeResponseAuthComplete(
1554 base::Optional<bool> auth_success) {
1555 if (!auth_success.has_value() || !auth_success.value()) {
1556 password_view_->Reset();
1557 password_view_->SetReadOnly(false);
1558 // If the user canceled the PIN request during ChallengeResponse,
1559 // ChallengeResponse will fail with an unknown error. Since this is
1560 // expected, we do not show this error.
1561 if (Shell::Get()
1562 ->login_screen_controller()
1563 ->GetSecurityTokenPinRequestCanceled()) {
1564 challenge_response_view_->SetState(
1565 ChallengeResponseView::State::kInitial);
1566 } else {
1567 challenge_response_view_->SetState(
1568 ChallengeResponseView::State::kFailure);
1569 }
1570 }
1571
1572 on_auth_.Run(auth_success.value_or(false), /*display_error_messages=*/false);
1573 }
1574
OnUserViewTap()1575 void LoginAuthUserView::OnUserViewTap() {
1576 if (HasAuthMethod(AUTH_TAP)) {
1577 Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock(
1578 current_user().basic_user_info.account_id);
1579 } else if (HasAuthMethod(AUTH_ONLINE_SIGN_IN)) {
1580 // Tapping anywhere in the user view is the same with tapping the message.
1581 OnOnlineSignInMessageTap();
1582 } else {
1583 on_tap_.Run();
1584 }
1585 }
1586
OnOnlineSignInMessageTap()1587 void LoginAuthUserView::OnOnlineSignInMessageTap() {
1588 Shell::Get()->login_screen_controller()->ShowGaiaSignin(
1589 current_user().basic_user_info.account_id);
1590 }
1591
OnPinPadBackspace()1592 void LoginAuthUserView::OnPinPadBackspace() {
1593 DCHECK(pin_input_view_);
1594 DCHECK(password_view_);
1595 if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1596 pin_input_view_->Backspace();
1597 else
1598 password_view_->Backspace();
1599 }
1600
OnPinPadInsertDigit(int digit)1601 void LoginAuthUserView::OnPinPadInsertDigit(int digit) {
1602 DCHECK(pin_input_view_);
1603 DCHECK(password_view_);
1604 if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1605 pin_input_view_->InsertDigit(digit);
1606 else
1607 password_view_->InsertNumber(digit);
1608 }
1609
OnPasswordTextChanged(bool is_empty)1610 void LoginAuthUserView::OnPasswordTextChanged(bool is_empty) {
1611 DCHECK(pin_view_);
1612 if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE)
1613 pin_view_->OnPasswordTextChanged(is_empty);
1614 }
1615
OnPinTextChanged(bool is_empty)1616 void LoginAuthUserView::OnPinTextChanged(bool is_empty) {
1617 DCHECK(pin_view_);
1618 if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1619 pin_view_->OnPasswordTextChanged(is_empty);
1620 }
1621
HasAuthMethod(AuthMethods auth_method) const1622 bool LoginAuthUserView::HasAuthMethod(AuthMethods auth_method) const {
1623 return (auth_methods_ & auth_method) != 0;
1624 }
1625
AttemptAuthenticateWithChallengeResponse()1626 void LoginAuthUserView::AttemptAuthenticateWithChallengeResponse() {
1627 challenge_response_view_->SetState(
1628 ChallengeResponseView::State::kAuthenticating);
1629 Shell::Get()
1630 ->login_screen_controller()
1631 ->AuthenticateUserWithChallengeResponse(
1632 current_user().basic_user_info.account_id,
1633 base::BindOnce(&LoginAuthUserView::OnChallengeResponseAuthComplete,
1634 weak_factory_.GetWeakPtr()));
1635 }
1636
UpdateFocus()1637 void LoginAuthUserView::UpdateFocus() {
1638 DCHECK(previous_state_);
1639 const UiState current_state{this};
1640
1641 if (current_state.tpm_is_locked) {
1642 locked_tpm_message_view_->RequestFocus();
1643 return;
1644 }
1645 // All further states are exclusive.
1646 if (current_state.auth_disabled)
1647 disabled_auth_message_->RequestFocus();
1648 if (current_state.has_challenge_response)
1649 challenge_response_view_->RequestFocus();
1650 if (current_state.has_password && !previous_state_->has_password)
1651 password_view_->RequestFocus();
1652 if (current_state.has_pin_input)
1653 pin_input_view_->RequestFocus();
1654 // Tapping the user view will trigger the online sign-in flow when
1655 // |force_online_sign_in| is true.
1656 if (current_state.force_online_sign_in)
1657 user_view_->RequestFocus();
1658 }
1659
OnSwitchButtonClicked()1660 void LoginAuthUserView::OnSwitchButtonClicked() {
1661 // Ignore events from the switch button if no longer present.
1662 if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE &&
1663 input_field_mode_ != InputFieldMode::PWD_WITH_TOGGLE) {
1664 return;
1665 }
1666
1667 // Clear both input fields.
1668 password_view_->Reset();
1669 pin_input_view_->Reset();
1670 // Cache the current state of the UI.
1671 CaptureStateForAnimationPreLayout();
1672 // Same auth methods, but the input field mode has changed.
1673 input_field_mode_ = (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE)
1674 ? InputFieldMode::PWD_WITH_TOGGLE
1675 : InputFieldMode::PIN_WITH_TOGGLE;
1676 SetAuthMethods(auth_methods_, auth_metadata_);
1677 // Layout and animate.
1678 Layout();
1679 ApplyAnimationPostLayout(/*animate*/ true);
1680 }
1681
UpdateInputFieldMode()1682 void LoginAuthUserView::UpdateInputFieldMode() {
1683 // There isn't an input field when any of the following is true:
1684 // - Challenge response is active (Smart Card)
1685 // - Online sign in message shown
1686 // - Disabled message shown
1687 // - No password auth available
1688 if (HasAuthMethod(AUTH_CHALLENGE_RESPONSE) ||
1689 HasAuthMethod(AUTH_ONLINE_SIGN_IN) ||
1690 HasAuthMethod(AUTH_DISABLED) ||
1691 !HasAuthMethod(AUTH_PASSWORD)) {
1692 input_field_mode_ = InputFieldMode::NONE;
1693 return;
1694 }
1695
1696 if (!HasAuthMethod(AUTH_PIN)) {
1697 input_field_mode_ = InputFieldMode::PASSWORD_ONLY;
1698 return;
1699 }
1700
1701 // Default to combined password/pin if autosubmit is disabled.
1702 const int pin_length = auth_metadata_.autosubmit_pin_length;
1703 if (!LoginPinInputView::IsAutosubmitSupported(pin_length)) {
1704 input_field_mode_ = InputFieldMode::PIN_AND_PASSWORD;
1705 return;
1706 }
1707
1708 // Defaults to PIN + switch button if not showing the switch button already.
1709 if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE &&
1710 input_field_mode_ != InputFieldMode::PWD_WITH_TOGGLE) {
1711 input_field_mode_ = InputFieldMode::PIN_WITH_TOGGLE;
1712 return;
1713 }
1714 }
1715
ShouldShowPinPad() const1716 bool LoginAuthUserView::ShouldShowPinPad() const {
1717 if (auth_metadata_.virtual_keyboard_visible)
1718 return false;
1719 switch (input_field_mode_) {
1720 case InputFieldMode::NONE:
1721 return false;
1722 case InputFieldMode::PASSWORD_ONLY:
1723 case InputFieldMode::PWD_WITH_TOGGLE:
1724 return auth_metadata_.show_pinpad_for_pw;
1725 case InputFieldMode::PIN_AND_PASSWORD:
1726 case InputFieldMode::PIN_WITH_TOGGLE:
1727 return true;
1728 }
1729 }
1730
ShouldShowPasswordField() const1731 bool LoginAuthUserView::ShouldShowPasswordField() const {
1732 return input_field_mode_ == InputFieldMode::PASSWORD_ONLY ||
1733 input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD ||
1734 input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE;
1735 }
1736
ShouldShowPinInputField() const1737 bool LoginAuthUserView::ShouldShowPinInputField() const {
1738 return input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE;
1739 }
1740
ShouldShowToggle() const1741 bool LoginAuthUserView::ShouldShowToggle() const {
1742 return input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE ||
1743 input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE;
1744 }
1745
GetPaddingBelowUserView() const1746 gfx::Size LoginAuthUserView::GetPaddingBelowUserView() const {
1747 const UiState state{this};
1748
1749 if (state.has_password)
1750 return SizeFromHeight(kDistanceBetweenUserViewAndPasswordDp);
1751 if (state.has_pin_input)
1752 return SizeFromHeight(kDistanceBetweenUserViewAndPinInputDp);
1753 if (state.force_online_sign_in)
1754 return SizeFromHeight(kDistanceBetweenUserViewAndOnlineSigninDp);
1755 if (state.has_challenge_response)
1756 return SizeFromHeight(kDistanceBetweenUserViewAndChallengeResponseDp);
1757
1758 return SizeFromHeight(0);
1759 }
1760
GetPaddingBelowPasswordView() const1761 gfx::Size LoginAuthUserView::GetPaddingBelowPasswordView() const {
1762 const UiState state{this};
1763
1764 if (state.has_pinpad)
1765 return SizeFromHeight(kDistanceBetweenPasswordFieldAndPinKeyboardDp);
1766 if (state.has_fingerprint)
1767 return SizeFromHeight(kDistanceBetweenPasswordFieldAndFingerprintViewDp);
1768 if (state.has_challenge_response)
1769 return SizeFromHeight(kDistanceBetweenPwdFieldAndChallengeResponseViewDp);
1770
1771 return SizeFromHeight(0);
1772 }
1773
GetPinPasswordToggleText()1774 base::string16 LoginAuthUserView::GetPinPasswordToggleText() {
1775 if (input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE)
1776 return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SWITCH_TO_PIN);
1777 else
1778 return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SWITCH_TO_PASSWORD);
1779 }
1780
GetPasswordViewPlaceholder() const1781 base::string16 LoginAuthUserView::GetPasswordViewPlaceholder() const {
1782 // Note: |AUTH_TAP| must have higher priority than |AUTH_PIN| when
1783 // determining the placeholder.
1784 if (HasAuthMethod(AUTH_TAP))
1785 return l10n_util::GetStringUTF16(
1786 IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER);
1787 if (input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD)
1788 return l10n_util::GetStringUTF16(
1789 IDS_ASH_LOGIN_POD_PASSWORD_PIN_PLACEHOLDER);
1790
1791 return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PLACEHOLDER);
1792 }
1793
1794 } // namespace ash
1795