1 // Copyright 2020 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/in_session_auth/auth_dialog_contents_view.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "ash/login/resources/grit/login_resources.h"
11 #include "ash/login/ui/horizontal_image_sequence_animation_decoder.h"
12 #include "ash/login/ui/login_password_view.h"
13 #include "ash/login/ui/login_pin_input_view.h"
14 #include "ash/login/ui/login_pin_view.h"
15 #include "ash/login/ui/non_accessible_view.h"
16 #include "ash/login/ui/views_utils.h"
17 #include "ash/public/cpp/in_session_auth_dialog_controller.h"
18 #include "ash/resources/vector_icons/vector_icons.h"
19 #include "ash/strings/grit/ash_strings.h"
20 #include "base/callback_helpers.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/timer/timer.h"
23 #include "ui/accessibility/ax_enums.mojom.h"
24 #include "ui/accessibility/ax_node_data.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/resource/resource_bundle.h"
27 #include "ui/gfx/paint_vector_icon.h"
28 #include "ui/views/background.h"
29 #include "ui/views/bubble/bubble_border.h"
30 #include "ui/views/controls/button/md_text_button.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/layout/box_layout.h"
33 #include "ui/views/layout/fill_layout.h"
34
35 namespace ash {
36 namespace {
37
38 constexpr int kContainerPreferredWidth = 340;
39
40 constexpr int kBorderTopDp = 36;
41 constexpr int kBorderLeftDp = 24;
42 constexpr int kBorderBottomDp = 20;
43 constexpr int kBorderRightDp = 24;
44 constexpr int kCornerRadius = 12;
45
46 constexpr int kTitleFontSizeDeltaDp = 4;
47 constexpr int kOriginNameLineHeight = 18;
48
49 constexpr int kSpacingAfterAvatar = 18;
50 constexpr int kSpacingAfterTitle = 8;
51 constexpr int kSpacingAfterOriginName = 32;
52 constexpr int kSpacingAfterInputField = 16;
53
54 constexpr int kAvatarSizeDp = 36;
55 constexpr int kFingerprintIconSizeDp = 28;
56 constexpr int kSpacingBetweenPinPadAndFingerprintIcon = 24;
57 constexpr int kSpacingBetweenFingerprintIconAndLabelDp = 15;
58 constexpr int kFingerprintViewWidthDp = 204;
59 constexpr int kFingerprintFailedAnimationNumFrames = 45;
60 constexpr base::TimeDelta kResetToDefaultIconDelay =
61 base::TimeDelta::FromMilliseconds(1300);
62 constexpr base::TimeDelta kResetToDefaultMessageDelay =
63 base::TimeDelta::FromMilliseconds(3000);
64 constexpr base::TimeDelta kFingerprintFailedAnimationDuration =
65 base::TimeDelta::FromMilliseconds(700);
66
67 // 38% opacity.
68 constexpr SkColor kDisabledFingerprintIconColor =
69 SkColorSetA(gfx::kGoogleGrey900, 97);
70 constexpr SkColor kBackgroundColor = SK_ColorWHITE;
71 constexpr SkColor kTextColorSecondary = gfx::kGoogleGrey700;
72 constexpr SkColor kTextColorPrimary = gfx::kGoogleGrey900;
73 constexpr SkColor kErrorColor = gfx::kGoogleRed600;
74
75 constexpr int kSpacingBeforeButtons = 32;
76
77 constexpr int kMaxPinAttempts = 5;
78
79 } // namespace
80
81 // Consists of fingerprint icon view and a label.
82 class AuthDialogContentsView::FingerprintView : public views::View {
83 public:
84 // Use a subclass that inherit views::Label so that GetAccessibleNodeData
85 // override is respected.
86 class FingerprintLabel : public views::Label {
87 public:
88 // views::View
GetAccessibleNodeData(ui::AXNodeData * node_data)89 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
90 node_data->role = ax::mojom::Role::kStaticText;
91 node_data->SetName(accessible_name_);
92 }
93
SetAccessibleName(const base::string16 & name)94 void SetAccessibleName(const base::string16& name) {
95 accessible_name_ = name;
96 NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
97 true /*send_native_event*/);
98 }
99
100 private:
101 base::string16 accessible_name_;
102 };
103
FingerprintView()104 FingerprintView() {
105 auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
106 views::BoxLayout::Orientation::kVertical, gfx::Insets(),
107 kSpacingBetweenFingerprintIconAndLabelDp));
108 layout->set_main_axis_alignment(
109 views::BoxLayout::MainAxisAlignment::kCenter);
110
111 icon_ = AddChildView(std::make_unique<AnimatedRoundedImageView>(
112 gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp),
113 0 /*corner_radius*/));
114
115 label_ = AddChildView(std::make_unique<FingerprintLabel>());
116 label_->SetSubpixelRenderingEnabled(false);
117 label_->SetAutoColorReadabilityEnabled(false);
118 label_->SetEnabledColor(kTextColorPrimary);
119 label_->SetMultiLine(true);
120 label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
121
122 DisplayCurrentState();
123 }
124 FingerprintView(const FingerprintView&) = delete;
125 FingerprintView& operator=(const FingerprintView&) = delete;
126 ~FingerprintView() override = default;
127
SetState(FingerprintState state)128 void SetState(FingerprintState state) {
129 if (state_ == state)
130 return;
131
132 state_ = state;
133 DisplayCurrentState();
134 }
135
SetCanUsePin(bool can_use_pin)136 void SetCanUsePin(bool can_use_pin) {
137 if (can_use_pin_ == can_use_pin)
138 return;
139
140 can_use_pin_ = can_use_pin;
141 DisplayCurrentState();
142 }
143
144 // Notify the user of the fingerprint auth result. Should be called after
145 // SetState. If fingerprint auth failed and retry is allowed, reset to
146 // default state after animation.
NotifyFingerprintAuthResult(bool success)147 void NotifyFingerprintAuthResult(bool success) {
148 reset_state_.Stop();
149 if (state_ == FingerprintState::DISABLED_FROM_ATTEMPTS) {
150 label_->SetText(l10n_util::GetStringUTF16(
151 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_DISABLED_FROM_ATTEMPTS));
152 label_->SetAccessibleName(l10n_util::GetStringUTF16(
153 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS));
154 } else if (success) {
155 label_->SetText(l10n_util::GetStringUTF16(
156 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_SUCCESS));
157 label_->SetAccessibleName(l10n_util::GetStringUTF16(
158 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_SUCCESS));
159 } else {
160 label_->SetText(l10n_util::GetStringUTF16(
161 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_FAILED));
162 label_->SetAccessibleName(l10n_util::GetStringUTF16(
163 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_FAILED));
164 }
165
166 if (!success) {
167 // This is just to display the "fingerprint auth failure" animation. It
168 // does not necessarily mean |state_| is DISABLED_FROM_ATTEMPTS.
169 SetIcon(FingerprintState::DISABLED_FROM_ATTEMPTS);
170 // base::Unretained is safe because reset_state_ is owned by |this|.
171 reset_state_.Start(FROM_HERE, kResetToDefaultIconDelay,
172 base::BindOnce(&FingerprintView::DisplayCurrentState,
173 base::Unretained(this)));
174 label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
175 true /*send_native_event*/);
176 }
177 }
178
179 // views::View:
CalculatePreferredSize() const180 gfx::Size CalculatePreferredSize() const override {
181 gfx::Size size = views::View::CalculatePreferredSize();
182 size.set_width(kFingerprintViewWidthDp);
183 return size;
184 }
185
186 // views::View:
OnGestureEvent(ui::GestureEvent * event)187 void OnGestureEvent(ui::GestureEvent* event) override {
188 if (event->type() != ui::ET_GESTURE_TAP)
189 return;
190 if (state_ == FingerprintState::AVAILABLE_DEFAULT ||
191 state_ == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING) {
192 SetState(FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING);
193 reset_state_.Start(
194 FROM_HERE, kResetToDefaultMessageDelay,
195 base::BindOnce(&FingerprintView::SetState, base::Unretained(this),
196 FingerprintState::AVAILABLE_DEFAULT));
197 }
198 }
199
200 private:
DisplayCurrentState()201 void DisplayCurrentState() {
202 SetVisible(state_ != FingerprintState::UNAVAILABLE);
203 SetIcon(state_);
204 if (state_ != FingerprintState::UNAVAILABLE) {
205 base::string16 fingerprint_text =
206 l10n_util::GetStringUTF16(GetTextIdFromState());
207 label_->SetText(fingerprint_text);
208 label_->SetAccessibleName(
209 state_ == FingerprintState::DISABLED_FROM_ATTEMPTS
210 ? l10n_util::GetStringUTF16(
211 IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS)
212 : fingerprint_text);
213 }
214 }
215
SetIcon(FingerprintState state)216 void SetIcon(FingerprintState state) {
217 const SkColor color =
218 (state == FingerprintState::AVAILABLE_DEFAULT ||
219 state == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING
220 ? kTextColorPrimary
221 : kDisabledFingerprintIconColor);
222 switch (state) {
223 case FingerprintState::UNAVAILABLE:
224 case FingerprintState::AVAILABLE_DEFAULT:
225 case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
226 case FingerprintState::DISABLED_FROM_TIMEOUT:
227 icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintIcon,
228 kFingerprintIconSizeDp, color));
229 break;
230 case FingerprintState::DISABLED_FROM_ATTEMPTS:
231 icon_->SetAnimationDecoder(
232 std::make_unique<HorizontalImageSequenceAnimationDecoder>(
233 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
234 IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER),
235 kFingerprintFailedAnimationDuration,
236 kFingerprintFailedAnimationNumFrames),
237 AnimatedRoundedImageView::Playback::kSingle);
238 break;
239 }
240 }
241
GetTextIdFromState() const242 int GetTextIdFromState() const {
243 switch (state_) {
244 case FingerprintState::AVAILABLE_DEFAULT:
245 return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_AVAILABLE;
246 case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
247 return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_TOUCH_SENSOR;
248 case FingerprintState::DISABLED_FROM_ATTEMPTS:
249 return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_DISABLED_FROM_ATTEMPTS;
250 case FingerprintState::DISABLED_FROM_TIMEOUT:
251 if (can_use_pin_)
252 return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_PIN_OR_PASSWORD_REQUIRED;
253 return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_PASSWORD_REQUIRED;
254 case FingerprintState::UNAVAILABLE:
255 NOTREACHED();
256 return 0;
257 }
258 }
259
260 FingerprintLabel* label_ = nullptr;
261 AnimatedRoundedImageView* icon_ = nullptr;
262 FingerprintState state_ = FingerprintState::AVAILABLE_DEFAULT;
263 bool can_use_pin_ = false;
264 base::OneShotTimer reset_state_;
265 };
266
267 class AuthDialogContentsView::TitleLabel : public views::Label {
268 public:
TitleLabel()269 TitleLabel() {
270 SetSubpixelRenderingEnabled(false);
271 SetAutoColorReadabilityEnabled(false);
272 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
273
274 const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
275
276 SetFontList(base_font_list.Derive(kTitleFontSizeDeltaDp,
277 gfx::Font::FontStyle::NORMAL,
278 gfx::Font::Weight::MEDIUM));
279 SetMaximumWidth(kContainerPreferredWidth);
280 SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
281
282 SetPreferredSize(gfx::Size(kContainerPreferredWidth,
283 GetHeightForWidth(kContainerPreferredWidth)));
284 SetHorizontalAlignment(gfx::ALIGN_CENTER);
285 }
286
IsShowingError() const287 bool IsShowingError() const { return is_showing_error_; }
288
ShowTitle()289 void ShowTitle() {
290 base::string16 title =
291 l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_TITLE);
292 SetText(title);
293 SetEnabledColor(kTextColorPrimary);
294 is_showing_error_ = false;
295 SetAccessibleName(title);
296 }
297
ShowError(const base::string16 & error_text)298 void ShowError(const base::string16& error_text) {
299 SetText(error_text);
300 SetEnabledColor(kErrorColor);
301 is_showing_error_ = true;
302 SetAccessibleName(error_text);
303 NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
304 true /*send_native_event*/);
305 }
306
307 // views::View
GetAccessibleNodeData(ui::AXNodeData * node_data)308 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
309 node_data->role = ax::mojom::Role::kStaticText;
310 node_data->SetName(accessible_name_);
311 }
312
313 private:
SetAccessibleName(const base::string16 & name)314 void SetAccessibleName(const base::string16& name) {
315 accessible_name_ = name;
316 NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
317 true /*send_native_event*/);
318 }
319
320 bool is_showing_error_ = false;
321 base::string16 accessible_name_;
322 };
323
AuthDialogContentsView(uint32_t auth_methods,const std::string & origin_name,const AuthMethodsMetadata & auth_metadata,const UserAvatar & avatar)324 AuthDialogContentsView::AuthDialogContentsView(
325 uint32_t auth_methods,
326 const std::string& origin_name,
327 const AuthMethodsMetadata& auth_metadata,
328 const UserAvatar& avatar)
329 : auth_methods_(auth_methods),
330 origin_name_(origin_name),
331 auth_metadata_(auth_metadata) {
332 DCHECK(auth_methods_ & kAuthPassword);
333
334 SetLayoutManager(std::make_unique<views::FillLayout>());
335 auto border = std::make_unique<views::BubbleBorder>(
336 views::BubbleBorder::FLOAT, views::BubbleBorder::BIG_SHADOW,
337 kBackgroundColor);
338 border->SetCornerRadius(kCornerRadius);
339 SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
340 SetBorder(std::move(border));
341
342 container_ = AddChildView(std::make_unique<NonAccessibleView>());
343 container_->SetBorder(views::CreateEmptyBorder(
344 kBorderTopDp, kBorderLeftDp, kBorderBottomDp, kBorderRightDp));
345
346 main_layout_ =
347 container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
348 views::BoxLayout::Orientation::kVertical));
349 main_layout_->set_main_axis_alignment(
350 views::BoxLayout::MainAxisAlignment::kStart);
351 main_layout_->set_cross_axis_alignment(
352 views::BoxLayout::CrossAxisAlignment::kCenter);
353
354 AddAvatarView(avatar);
355 AddVerticalSpacing(kSpacingAfterAvatar);
356 AddTitleView();
357 AddVerticalSpacing(kSpacingAfterTitle);
358 AddOriginNameView();
359 AddVerticalSpacing(kSpacingAfterOriginName);
360 if (auth_methods_ & kAuthPin) {
361 if (LoginPinInputView::IsAutosubmitSupported(
362 auth_metadata_.autosubmit_pin_length)) {
363 pin_autosubmit_on_ = true;
364 AddPinDigitInputView();
365 } else {
366 pin_autosubmit_on_ = false;
367 AddPinTextInputView();
368 }
369 AddVerticalSpacing(kSpacingAfterInputField);
370 // PIN pad is always visible regardless of PIN autosubmit status.
371 AddPinPadView();
372 }
373
374 if (auth_methods_ & kAuthFingerprint) {
375 if (pin_pad_view_)
376 AddVerticalSpacing(kSpacingBetweenPinPadAndFingerprintIcon);
377
378 fingerprint_view_ =
379 container_->AddChildView(std::make_unique<FingerprintView>());
380 fingerprint_view_->SetCanUsePin(auth_methods_ & kAuthPin);
381 }
382
383 AddVerticalSpacing(kSpacingBeforeButtons);
384 AddActionButtonsView();
385 }
386
387 AuthDialogContentsView::~AuthDialogContentsView() = default;
388
AddedToWidget()389 void AuthDialogContentsView::AddedToWidget() {
390 if (auth_methods_ & kAuthFingerprint) {
391 // Inject a callback from the contents view so that we can show retry
392 // prompt.
393 InSessionAuthDialogController::Get()->AuthenticateUserWithFingerprint(
394 base::BindOnce(&AuthDialogContentsView::OnFingerprintAuthComplete,
395 weak_factory_.GetWeakPtr()));
396 }
397 }
398
AddAvatarView(const UserAvatar & avatar)399 void AuthDialogContentsView::AddAvatarView(const UserAvatar& avatar) {
400 avatar_view_ =
401 container_->AddChildView(std::make_unique<AnimatedRoundedImageView>(
402 gfx::Size(kAvatarSizeDp, kAvatarSizeDp),
403 kAvatarSizeDp / 2 /*corner_radius*/));
404 avatar_view_->SetImage(avatar.image);
405 }
406
AddTitleView()407 void AuthDialogContentsView::AddTitleView() {
408 title_ = container_->AddChildView(std::make_unique<TitleLabel>());
409 title_->ShowTitle();
410 }
411
AddOriginNameView()412 void AuthDialogContentsView::AddOriginNameView() {
413 origin_name_view_ =
414 container_->AddChildView(std::make_unique<views::Label>());
415 origin_name_view_->SetEnabledColor(kTextColorSecondary);
416 origin_name_view_->SetSubpixelRenderingEnabled(false);
417 origin_name_view_->SetAutoColorReadabilityEnabled(false);
418 origin_name_view_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
419
420 origin_name_view_->SetText(
421 l10n_util::GetStringFUTF16(IDS_ASH_IN_SESSION_AUTH_ORIGIN_NAME_PROMPT,
422 base::UTF8ToUTF16(origin_name_)));
423 origin_name_view_->SetMaximumWidth(kContainerPreferredWidth);
424 origin_name_view_->SetMultiLine(true);
425 origin_name_view_->SetLineHeight(kOriginNameLineHeight);
426
427 origin_name_view_->SetPreferredSize(gfx::Size(
428 kContainerPreferredWidth,
429 origin_name_view_->GetHeightForWidth(kContainerPreferredWidth)));
430 origin_name_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
431 }
432
AddPinTextInputView()433 void AuthDialogContentsView::AddPinTextInputView() {
434 pin_text_input_view_ =
435 container_->AddChildView(std::make_unique<LoginPasswordView>(palette_));
436
437 pin_text_input_view_->SetPaintToLayer();
438 pin_text_input_view_->layer()->SetFillsBoundsOpaquely(false);
439 pin_text_input_view_->SetDisplayPasswordButtonVisible(true);
440 pin_text_input_view_->SetEnabled(true);
441 pin_text_input_view_->SetEnabledOnEmptyPassword(false);
442 pin_text_input_view_->SetFocusEnabledForChildViews(true);
443 pin_text_input_view_->SetVisible(true);
444
445 pin_text_input_view_->SetPlaceholderText(
446 l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_PLACEHOLDER));
447 }
448
AddPinDigitInputView()449 void AuthDialogContentsView::AddPinDigitInputView() {
450 pin_digit_input_view_ =
451 container_->AddChildView(std::make_unique<LoginPinInputView>(palette_));
452 pin_digit_input_view_->UpdateLength(auth_metadata_.autosubmit_pin_length);
453 pin_digit_input_view_->SetVisible(true);
454 }
455
AddPinPadView()456 void AuthDialogContentsView::AddPinPadView() {
457 DCHECK(auth_methods_ & kAuthPin);
458 if (pin_autosubmit_on_) {
459 pin_pad_view_ = container_->AddChildView(std::make_unique<LoginPinView>(
460 LoginPinView::Style::kAlphanumeric, palette_,
461 base::BindRepeating(&AuthDialogContentsView::OnInsertDigitFromPinPad,
462 base::Unretained(this)),
463 base::BindRepeating(&AuthDialogContentsView::OnBackspaceFromPinPad,
464 base::Unretained(this))));
465 pin_digit_input_view_->Init(
466 base::BindRepeating(&AuthDialogContentsView::OnAuthSubmit,
467 base::Unretained(this)),
468 base::BindRepeating(&AuthDialogContentsView::OnPinTextChanged,
469 base::Unretained(this)));
470 } else {
471 pin_pad_view_ = container_->AddChildView(std::make_unique<LoginPinView>(
472 LoginPinView::Style::kAlphanumeric, palette_,
473 base::BindRepeating(&AuthDialogContentsView::OnInsertDigitFromPinPad,
474 base::Unretained(this)),
475 base::BindRepeating(&AuthDialogContentsView::OnBackspaceFromPinPad,
476 base::Unretained(this)),
477 base::BindRepeating(&LoginPasswordView::SubmitPassword,
478 base::Unretained(pin_text_input_view_))));
479 pin_text_input_view_->Init(
480 base::BindRepeating(&AuthDialogContentsView::OnAuthSubmit,
481 base::Unretained(this)),
482 base::BindRepeating(&AuthDialogContentsView::OnPinTextChanged,
483 base::Unretained(this)),
484 base::DoNothing(), views::Button::PressedCallback());
485 }
486 pin_pad_view_->SetVisible(true);
487 }
488
OnInsertDigitFromPinPad(int digit)489 void AuthDialogContentsView::OnInsertDigitFromPinPad(int digit) {
490 // Ignore anything if reached max attempts.
491 if (pin_attempts_ >= kMaxPinAttempts)
492 return;
493
494 if (title_->IsShowingError())
495 title_->ShowTitle();
496
497 if (pin_autosubmit_on_) {
498 pin_digit_input_view_->InsertDigit(digit);
499 } else {
500 pin_text_input_view_->InsertNumber(digit);
501 }
502 }
503
OnBackspaceFromPinPad()504 void AuthDialogContentsView::OnBackspaceFromPinPad() {
505 // Ignore anything if reached max attempts.
506 if (pin_attempts_ >= kMaxPinAttempts)
507 return;
508
509 if (title_->IsShowingError())
510 title_->ShowTitle();
511
512 if (pin_autosubmit_on_) {
513 pin_digit_input_view_->Backspace();
514 } else {
515 pin_text_input_view_->Backspace();
516 }
517 }
518
OnPinTextChanged(bool is_empty)519 void AuthDialogContentsView::OnPinTextChanged(bool is_empty) {
520 // If the user is interacting with the input field, restore the title (clear
521 // error message).
522 //
523 // If |is_empty| is true, this call may come from resetting
524 // |pin_text_input_view_| or |pin_digit_input_view_|, when the error message
525 // hasn't been shown and read yet. In this case we don't restore the title.
526 if (title_->IsShowingError() && !is_empty)
527 title_->ShowTitle();
528
529 pin_pad_view_->OnPasswordTextChanged(is_empty);
530 }
531
AddVerticalSpacing(int height)532 void AuthDialogContentsView::AddVerticalSpacing(int height) {
533 auto* spacing =
534 container_->AddChildView(std::make_unique<NonAccessibleView>());
535 spacing->SetPreferredSize(gfx::Size(kContainerPreferredWidth, height));
536 }
537
AddActionButtonsView()538 void AuthDialogContentsView::AddActionButtonsView() {
539 action_view_container_ =
540 container_->AddChildView(std::make_unique<NonAccessibleView>());
541 auto* buttons_layout = action_view_container_->SetLayoutManager(
542 std::make_unique<views::BoxLayout>(
543 views::BoxLayout::Orientation::kHorizontal));
544 buttons_layout->set_main_axis_alignment(
545 views::BoxLayout::MainAxisAlignment::kStart);
546
547 help_button_ =
548 action_view_container_->AddChildView(std::make_unique<views::LabelButton>(
549 base::BindRepeating(&AuthDialogContentsView::OnNeedHelpButtonPressed,
550 base::Unretained(this)),
551 l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_HELP),
552 views::style::CONTEXT_BUTTON));
553 help_button_->SetEnabledTextColors(kTextColorPrimary);
554
555 auto* spacing = action_view_container_->AddChildView(
556 std::make_unique<NonAccessibleView>());
557 buttons_layout->SetFlexForView(spacing, 1);
558
559 cancel_button_ = action_view_container_->AddChildView(
560 std::make_unique<views::MdTextButton>(
561 base::BindRepeating(&AuthDialogContentsView::OnCancelButtonPressed,
562 base::Unretained(this)),
563 l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_CANCEL)));
564
565 action_view_container_->SetPreferredSize(
566 gfx::Size(kContainerPreferredWidth, cancel_button_->height()));
567 }
568
OnCancelButtonPressed(const ui::Event & event)569 void AuthDialogContentsView::OnCancelButtonPressed(const ui::Event& event) {
570 InSessionAuthDialogController::Get()->Cancel();
571 }
572
OnNeedHelpButtonPressed(const ui::Event & event)573 void AuthDialogContentsView::OnNeedHelpButtonPressed(const ui::Event& event) {
574 InSessionAuthDialogController::Get()->OpenInSessionAuthHelpPage();
575 }
576
OnAuthSubmit(const base::string16 & pin)577 void AuthDialogContentsView::OnAuthSubmit(const base::string16& pin) {
578 if (pin_autosubmit_on_) {
579 pin_digit_input_view_->SetReadOnly(true);
580 } else {
581 pin_text_input_view_->SetReadOnly(true);
582 }
583 InSessionAuthDialogController::Get()->AuthenticateUserWithPin(
584 base::UTF16ToUTF8(pin),
585 base::BindOnce(&AuthDialogContentsView::OnPinAuthComplete,
586 weak_factory_.GetWeakPtr()));
587 }
588
589 // TODO(b/156258540): Clear PIN if auth failed and retry is allowed.
OnPinAuthComplete(base::Optional<bool> success)590 void AuthDialogContentsView::OnPinAuthComplete(base::Optional<bool> success) {
591 // On success, do nothing, and the dialog will dismiss.
592 if (success.has_value() && success.value())
593 return;
594
595 pin_attempts_++;
596 base::string16 error_text =
597 pin_attempts_ >= kMaxPinAttempts
598 ? l10n_util::GetStringUTF16(
599 IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS)
600 : l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_INCORRECT);
601 title_->ShowError(error_text);
602
603 if (pin_attempts_ < kMaxPinAttempts) {
604 if (pin_autosubmit_on_) {
605 pin_digit_input_view_->Reset();
606 pin_digit_input_view_->SetReadOnly(false);
607 } else {
608 pin_text_input_view_->Reset();
609 pin_text_input_view_->SetReadOnly(false);
610 }
611 }
612 }
613
OnFingerprintAuthComplete(bool success,FingerprintState fingerprint_state)614 void AuthDialogContentsView::OnFingerprintAuthComplete(
615 bool success,
616 FingerprintState fingerprint_state) {
617 fingerprint_view_->SetState(fingerprint_state);
618 // Prepare for the next fingerprint scan.
619 if (!success && fingerprint_state == FingerprintState::AVAILABLE_DEFAULT) {
620 InSessionAuthDialogController::Get()->AuthenticateUserWithFingerprint(
621 base::BindOnce(&AuthDialogContentsView::OnFingerprintAuthComplete,
622 weak_factory_.GetWeakPtr()));
623 }
624 fingerprint_view_->NotifyFingerprintAuthResult(success);
625 }
626
GetAccessibleNodeData(ui::AXNodeData * node_data)627 void AuthDialogContentsView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
628 views::View::GetAccessibleNodeData(node_data);
629 node_data->role = ax::mojom::Role::kDialog;
630 node_data->SetName(
631 l10n_util::GetStringFUTF16(IDS_ASH_IN_SESSION_AUTH_ACCESSIBLE_TITLE,
632 base::UTF8ToUTF16(origin_name_)));
633 }
634
RequestFocus()635 void AuthDialogContentsView::RequestFocus() {
636 if (auth_methods_ == kAuthFingerprint) {
637 // There's no PIN input field, so let the focus be on the cancel button
638 // (instead of the help button) because it is more often used.
639 cancel_button_->RequestFocus();
640 return;
641 }
642
643 // For other cases, the base method correctly sets focus to the input field.
644 views::View::RequestFocus();
645 }
646
647 } // namespace ash
648