1 // Copyright 2019 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/pin_request_view.h"
6 
7 #include "ash/login/ui/arrow_button_view.h"
8 #include "ash/login/ui/login_pin_view.h"
9 #include "ash/login/ui/pin_request_widget.h"
10 #include "ash/public/cpp/shelf_config.h"
11 #include "ash/resources/vector_icons/vector_icons.h"
12 #include "ash/shell.h"
13 #include "ash/strings/grit/ash_strings.h"
14 #include "ash/style/ash_color_provider.h"
15 #include "ash/wallpaper/wallpaper_controller_impl.h"
16 #include "ui/accessibility/ax_enums.mojom.h"
17 #include "ui/accessibility/ax_node_data.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/gfx/canvas.h"
20 #include "ui/gfx/color_analysis.h"
21 #include "ui/gfx/color_utils.h"
22 #include "ui/gfx/paint_vector_icon.h"
23 #include "ui/views/background.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/controls/focus_ring.h"
26 #include "ui/views/controls/label.h"
27 #include "ui/views/layout/box_layout.h"
28 #include "ui/views/layout/fill_layout.h"
29 #include "ui/views/vector_icons.h"
30 
31 namespace ash {
32 
33 namespace {
34 
35 constexpr int kPinRequestViewWidthDp = 340;
36 constexpr int kPinKeyboardHeightDp = 224;
37 constexpr int kPinRequestViewRoundedCornerRadiusDp = 8;
38 constexpr int kPinRequestViewVerticalInsetDp = 8;
39 // Inset for all elements except the back button.
40 constexpr int kPinRequestViewMainHorizontalInsetDp = 36;
41 // Minimum inset (= back button inset).
42 constexpr int kPinRequestViewHorizontalInsetDp = 8;
43 
44 constexpr int kCrossSizeDp = 20;
45 constexpr int kBackButtonSizeDp = 36;
46 constexpr int kLockIconSizeDp = 24;
47 constexpr int kBackButtonLockIconVerticalOverlapDp = 8;
48 constexpr int kHeaderHeightDp =
49     kBackButtonSizeDp + kLockIconSizeDp - kBackButtonLockIconVerticalOverlapDp;
50 
51 constexpr int kIconToTitleDistanceDp = 24;
52 constexpr int kTitleToDescriptionDistanceDp = 8;
53 constexpr int kDescriptionToAccessCodeDistanceDp = 32;
54 constexpr int kAccessCodeToPinKeyboardDistanceDp = 16;
55 constexpr int kPinKeyboardToFooterDistanceDp = 16;
56 constexpr int kSubmitButtonBottomMarginDp = 28;
57 
58 constexpr int kTitleFontSizeDeltaDp = 4;
59 constexpr int kTitleLineWidthDp = 268;
60 constexpr int kTitleLineHeightDp = 24;
61 constexpr int kTitleMaxLines = 4;
62 constexpr int kDescriptionFontSizeDeltaDp = 0;
63 constexpr int kDescriptionLineWidthDp = 268;
64 constexpr int kDescriptionTextLineHeightDp = 18;
65 constexpr int kDescriptionMaxLines = 4;
66 
67 constexpr int kArrowButtonSizeDp = 48;
68 
69 constexpr int kPinRequestViewMinimumHeightDp =
70     kPinRequestViewMainHorizontalInsetDp + kLockIconSizeDp +
71     kIconToTitleDistanceDp + kTitleToDescriptionDistanceDp +
72     kDescriptionToAccessCodeDistanceDp +
73     AccessCodeInput::kAccessCodeInputFieldHeightDp +
74     kAccessCodeToPinKeyboardDistanceDp + kPinKeyboardToFooterDistanceDp +
75     kArrowButtonSizeDp + kPinRequestViewMainHorizontalInsetDp;  // = 266
76 
77 constexpr int kAlpha70Percent = 178;
78 constexpr int kAlpha74Percent = 189;
79 
80 constexpr SkColor kErrorColor = gfx::kGoogleRed300;
81 
IsTabletMode()82 bool IsTabletMode() {
83   return Shell::Get()->tablet_mode_controller()->InTabletMode();
84 }
85 
86 }  // namespace
87 
88 PinRequest::PinRequest() = default;
89 PinRequest::PinRequest(PinRequest&&) = default;
90 PinRequest& PinRequest::operator=(PinRequest&&) = default;
91 PinRequest::~PinRequest() = default;
92 
93 // Label button that displays focus ring.
94 class PinRequestView::FocusableLabelButton : public views::LabelButton {
95  public:
FocusableLabelButton(PressedCallback callback,const base::string16 & text)96   FocusableLabelButton(PressedCallback callback, const base::string16& text)
97       : views::LabelButton(std::move(callback), text) {
98     SetInstallFocusRingOnFocus(true);
99     focus_ring()->SetColor(ShelfConfig::Get()->shelf_focus_border_color());
100     SetFocusBehavior(FocusBehavior::ALWAYS);
101   }
102 
103   FocusableLabelButton(const FocusableLabelButton&) = delete;
104   FocusableLabelButton& operator=(const FocusableLabelButton&) = delete;
105   ~FocusableLabelButton() override = default;
106 };
107 
TestApi(PinRequestView * view)108 PinRequestView::TestApi::TestApi(PinRequestView* view) : view_(view) {
109   DCHECK(view_);
110 }
111 
112 PinRequestView::TestApi::~TestApi() = default;
113 
back_button()114 LoginButton* PinRequestView::TestApi::back_button() {
115   return view_->back_button_;
116 }
117 
title_label()118 views::Label* PinRequestView::TestApi::title_label() {
119   return view_->title_label_;
120 }
121 
description_label()122 views::Label* PinRequestView::TestApi::description_label() {
123   return view_->description_label_;
124 }
125 
access_code_view()126 views::View* PinRequestView::TestApi::access_code_view() {
127   return view_->access_code_view_;
128 }
129 
help_button()130 views::LabelButton* PinRequestView::TestApi::help_button() {
131   return view_->help_button_;
132 }
133 
submit_button()134 ArrowButtonView* PinRequestView::TestApi::submit_button() {
135   return view_->submit_button_;
136 }
137 
pin_keyboard_view()138 LoginPinView* PinRequestView::TestApi::pin_keyboard_view() {
139   return view_->pin_keyboard_view_;
140 }
141 
GetInputTextField(int index)142 views::Textfield* PinRequestView::TestApi::GetInputTextField(int index) {
143   return FixedLengthCodeInput::TestApi(
144              static_cast<FixedLengthCodeInput*>(view_->access_code_view_))
145       .GetInputTextField(index);
146 }
147 
state() const148 PinRequestViewState PinRequestView::TestApi::state() const {
149   return view_->state_;
150 }
151 
152 // static
GetChildUserDialogColor(bool using_blur)153 SkColor PinRequestView::GetChildUserDialogColor(bool using_blur) {
154   SkColor color = AshColorProvider::Get()->GetBaseLayerColor(
155       AshColorProvider::BaseLayerType::kOpaque);
156 
157   SkColor extracted_color =
158       Shell::Get()->wallpaper_controller()->GetProminentColor(
159           color_utils::ColorProfile(color_utils::LumaRange::DARK,
160                                     color_utils::SaturationRange::MUTED));
161 
162   if (extracted_color != kInvalidWallpaperColor &&
163       extracted_color != SK_ColorTRANSPARENT) {
164     color = color_utils::GetResultingPaintColor(
165         SkColorSetA(SK_ColorBLACK, kAlpha70Percent), extracted_color);
166   }
167 
168   return using_blur ? SkColorSetA(color, kAlpha74Percent) : color;
169 }
170 
171 // TODO(crbug.com/1061008): Make dialog look good on small screens with high
172 // zoom factor.
PinRequestView(PinRequest request,Delegate * delegate)173 PinRequestView::PinRequestView(PinRequest request, Delegate* delegate)
174     : delegate_(delegate),
175       on_pin_request_done_(std::move(request.on_pin_request_done)),
176       pin_keyboard_always_enabled_(request.pin_keyboard_always_enabled),
177       default_title_(request.title),
178       default_description_(request.description),
179       default_accessible_title_(request.accessible_title.empty()
180                                     ? request.title
181                                     : request.accessible_title) {
182   // MODAL_TYPE_SYSTEM is used to get a semi-transparent background behind the
183   // pin request view, when it is used directly on a widget. The overlay
184   // consumes all the inputs from the user, so that they can only interact with
185   // the pin request view while it is visible.
186   SetModalType(ui::MODAL_TYPE_SYSTEM);
187 
188   // Main view contains all other views aligned vertically and centered.
189   auto layout = std::make_unique<views::BoxLayout>(
190       views::BoxLayout::Orientation::kVertical,
191       gfx::Insets(kPinRequestViewVerticalInsetDp,
192                   kPinRequestViewHorizontalInsetDp),
193       0);
194   layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
195   layout->set_cross_axis_alignment(
196       views::BoxLayout::CrossAxisAlignment::kCenter);
197   SetLayoutManager(std::move(layout));
198 
199   SetPaintToLayer();
200   layer()->SetFillsBoundsOpaquely(false);
201   layer()->SetRoundedCornerRadius(
202       gfx::RoundedCornersF(kPinRequestViewRoundedCornerRadiusDp));
203   layer()->SetBackgroundBlur(ShelfConfig::Get()->shelf_blur_radius());
204 
205   const int child_view_width =
206       kPinRequestViewWidthDp - 2 * kPinRequestViewMainHorizontalInsetDp;
207 
208   // Header view which contains the back button that is aligned top right and
209   // the lock icon which is in the bottom center.
210   auto header_layout = std::make_unique<views::FillLayout>();
211   auto* header = new NonAccessibleView();
212   header->SetLayoutManager(std::move(header_layout));
213   AddChildView(header);
214   auto* header_spacer = new NonAccessibleView();
215   header_spacer->SetPreferredSize(gfx::Size(0, kHeaderHeightDp));
216   header->AddChildView(header_spacer);
217 
218   // Main view icon.
219   auto* icon_view = new NonAccessibleView();
220   icon_view->SetPreferredSize(gfx::Size(0, kHeaderHeightDp));
221   auto icon_layout = std::make_unique<views::BoxLayout>(
222       views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0);
223   icon_layout->set_main_axis_alignment(
224       views::BoxLayout::MainAxisAlignment::kEnd);
225   icon_layout->set_cross_axis_alignment(
226       views::BoxLayout::CrossAxisAlignment::kCenter);
227   icon_view->SetLayoutManager(std::move(icon_layout));
228   header->AddChildView(icon_view);
229 
230   views::ImageView* icon = new views::ImageView();
231   icon->SetPreferredSize(gfx::Size(kLockIconSizeDp, kLockIconSizeDp));
232   icon->SetImage(gfx::CreateVectorIcon(
233       kPinRequestLockIcon,
234       AshColorProvider::Get()->GetContentLayerColor(
235           AshColorProvider::ContentLayerType::kIconColorPrimary)));
236   icon_view->AddChildView(icon);
237 
238   // Back button. Note that it should be the last view added to |header| in
239   // order to be clickable.
240   auto* back_button_view = new NonAccessibleView();
241   back_button_view->SetPreferredSize(
242       gfx::Size(child_view_width + 2 * (kPinRequestViewMainHorizontalInsetDp -
243                                         kPinRequestViewHorizontalInsetDp),
244                 kHeaderHeightDp));
245   auto back_button_layout = std::make_unique<views::BoxLayout>(
246       views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0);
247   back_button_layout->set_main_axis_alignment(
248       views::BoxLayout::MainAxisAlignment::kEnd);
249   back_button_layout->set_cross_axis_alignment(
250       views::BoxLayout::CrossAxisAlignment::kStart);
251   back_button_view->SetLayoutManager(std::move(back_button_layout));
252   header->AddChildView(back_button_view);
253 
254   back_button_ = new LoginButton(
255       base::BindRepeating(&PinRequestView::OnBack, base::Unretained(this)));
256   back_button_->SetPreferredSize(
257       gfx::Size(kBackButtonSizeDp, kBackButtonSizeDp));
258   back_button_->SetBackground(
259       views::CreateSolidBackground(SK_ColorTRANSPARENT));
260   back_button_->SetImage(
261       views::Button::STATE_NORMAL,
262       gfx::CreateVectorIcon(
263           views::kIcCloseIcon, kCrossSizeDp,
264           AshColorProvider::Get()->GetContentLayerColor(
265               AshColorProvider::ContentLayerType::kIconColorPrimary)));
266   back_button_->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
267   back_button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
268   back_button_->SetAccessibleName(
269       l10n_util::GetStringUTF16(IDS_ASH_LOGIN_BACK_BUTTON_ACCESSIBLE_NAME));
270   back_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
271   back_button_view->AddChildView(back_button_);
272 
273   auto add_spacer = [&](int height) {
274     auto* spacer = new NonAccessibleView();
275     spacer->SetPreferredSize(gfx::Size(0, height));
276     AddChildView(spacer);
277   };
278 
279   add_spacer(kIconToTitleDistanceDp);
280 
281   auto decorate_label = [](views::Label* label) {
282     label->SetSubpixelRenderingEnabled(false);
283     label->SetAutoColorReadabilityEnabled(false);
284     label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
285         AshColorProvider::ContentLayerType::kTextColorPrimary));
286     label->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
287   };
288 
289   // Main view title.
290   title_label_ = new views::Label(default_title_, views::style::CONTEXT_LABEL,
291                                   views::style::STYLE_PRIMARY);
292   title_label_->SetMultiLine(true);
293   title_label_->SetMaxLines(kTitleMaxLines);
294   title_label_->SizeToFit(kTitleLineWidthDp);
295   title_label_->SetLineHeight(kTitleLineHeightDp);
296   title_label_->SetFontList(gfx::FontList().Derive(
297       kTitleFontSizeDeltaDp, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
298   decorate_label(title_label_);
299   AddChildView(title_label_);
300 
301   add_spacer(kTitleToDescriptionDistanceDp);
302 
303   // Main view description.
304   description_label_ =
305       new views::Label(default_description_, views::style::CONTEXT_LABEL,
306                        views::style::STYLE_PRIMARY);
307   description_label_->SetMultiLine(true);
308   description_label_->SetMaxLines(kDescriptionMaxLines);
309   description_label_->SizeToFit(kDescriptionLineWidthDp);
310   description_label_->SetLineHeight(kDescriptionTextLineHeightDp);
311   description_label_->SetFontList(
312       gfx::FontList().Derive(kDescriptionFontSizeDeltaDp, gfx::Font::NORMAL,
313                              gfx::Font::Weight::NORMAL));
314   decorate_label(description_label_);
315   AddChildView(description_label_);
316 
317   add_spacer(kDescriptionToAccessCodeDistanceDp);
318 
319   LoginPalette palette = CreateDefaultLoginPalette();
320 
321   // Access code input view.
322   if (request.pin_length.has_value()) {
323     CHECK_GT(request.pin_length.value(), 0);
324     access_code_view_ = AddChildView(std::make_unique<FixedLengthCodeInput>(
325         request.pin_length.value(),
326         base::BindRepeating(&PinRequestView::OnInputChange,
327                             base::Unretained(this)),
328         base::BindRepeating(&PinRequestView::SubmitCode,
329                             base::Unretained(this)),
330         base::BindRepeating(&PinRequestView::OnBack, base::Unretained(this)),
331         request.obscure_pin, palette.pin_input_text_color));
332     access_code_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
333   } else {
334     auto flex_code_input = std::make_unique<FlexCodeInput>(
335         base::BindRepeating(&PinRequestView::OnInputChange,
336                             base::Unretained(this), false),
337         base::BindRepeating(&PinRequestView::SubmitCode,
338                             base::Unretained(this)),
339         base::BindRepeating(&PinRequestView::OnBack, base::Unretained(this)),
340         request.obscure_pin, palette.pin_input_text_color);
341     flex_code_input->SetAccessibleName(default_accessible_title_);
342     access_code_view_ = AddChildView(std::move(flex_code_input));
343   }
344 
345   add_spacer(kAccessCodeToPinKeyboardDistanceDp);
346 
347   // Pin keyboard. Note that the keyboard's own submit button is disabled via
348   // passing a null |on_submit| callback.
349   pin_keyboard_view_ = new LoginPinView(
350       LoginPinView::Style::kAlphanumeric, CreateDefaultLoginPalette(),
351       base::BindRepeating(&AccessCodeInput::InsertDigit,
352                           base::Unretained(access_code_view_)),
353       base::BindRepeating(&AccessCodeInput::Backspace,
354                           base::Unretained(access_code_view_)),
355       /*on_submit=*/LoginPinView::OnPinSubmit());
356   // Backspace key is always enabled and |access_code_| field handles it.
357   pin_keyboard_view_->OnPasswordTextChanged(false);
358   AddChildView(pin_keyboard_view_);
359 
360   add_spacer(kPinKeyboardToFooterDistanceDp);
361 
362   // Footer view contains help text button aligned to its start, submit
363   // button aligned to its end and spacer view in between.
364   auto* footer = new NonAccessibleView();
365   footer->SetPreferredSize(gfx::Size(child_view_width, kArrowButtonSizeDp));
366   auto* bottom_layout =
367       footer->SetLayoutManager(std::make_unique<views::BoxLayout>(
368           views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
369   AddChildView(footer);
370 
371   help_button_ = new FocusableLabelButton(
372       base::BindRepeating(
373           [](PinRequestView* view) {
374             view->delegate_->OnHelp(view->GetWidget()->GetNativeWindow());
375           },
376           this),
377       l10n_util::GetStringUTF16(IDS_ASH_LOGIN_PIN_REQUEST_HELP));
378   help_button_->SetPaintToLayer();
379   help_button_->layer()->SetFillsBoundsOpaquely(false);
380   help_button_->SetTextSubpixelRenderingEnabled(false);
381   help_button_->SetEnabledTextColors(
382       AshColorProvider::Get()->GetContentLayerColor(
383           AshColorProvider::ContentLayerType::kTextColorPrimary));
384   help_button_->SetVisible(request.help_button_enabled);
385   footer->AddChildView(help_button_);
386 
387   auto* horizontal_spacer = new NonAccessibleView();
388   footer->AddChildView(horizontal_spacer);
389   bottom_layout->SetFlexForView(horizontal_spacer, 1);
390 
391   submit_button_ = new ArrowButtonView(
392       base::BindRepeating(&PinRequestView::SubmitCode, base::Unretained(this)),
393       kArrowButtonSizeDp);
394   submit_button_->SetPreferredSize(
395       gfx::Size(kArrowButtonSizeDp, kArrowButtonSizeDp));
396   submit_button_->SetEnabled(false);
397   submit_button_->SetAccessibleName(
398       l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SUBMIT_BUTTON_ACCESSIBLE_NAME));
399   submit_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
400   footer->AddChildView(submit_button_);
401   add_spacer(kSubmitButtonBottomMarginDp);
402 
403   pin_keyboard_view_->SetVisible(PinKeyboardVisible());
404 
405   tablet_mode_observer_.Add(Shell::Get()->tablet_mode_controller());
406 
407   SetPreferredSize(GetPinRequestViewSize());
408 }
409 
410 PinRequestView::~PinRequestView() = default;
411 
OnPaint(gfx::Canvas * canvas)412 void PinRequestView::OnPaint(gfx::Canvas* canvas) {
413   views::View::OnPaint(canvas);
414 
415   cc::PaintFlags flags;
416   flags.setStyle(cc::PaintFlags::kFill_Style);
417   flags.setColor(GetChildUserDialogColor(true));
418   canvas->DrawRoundRect(GetContentsBounds(),
419                         kPinRequestViewRoundedCornerRadiusDp, flags);
420 }
421 
RequestFocus()422 void PinRequestView::RequestFocus() {
423   access_code_view_->RequestFocus();
424 }
425 
CalculatePreferredSize() const426 gfx::Size PinRequestView::CalculatePreferredSize() const {
427   return GetPinRequestViewSize();
428 }
429 
GetInitiallyFocusedView()430 views::View* PinRequestView::GetInitiallyFocusedView() {
431   return access_code_view_;
432 }
433 
GetAccessibleWindowTitle() const434 base::string16 PinRequestView::GetAccessibleWindowTitle() const {
435   return default_accessible_title_;
436 }
437 
OnTabletModeStarted()438 void PinRequestView::OnTabletModeStarted() {
439   if (!pin_keyboard_always_enabled_) {
440     VLOG(1) << "Showing PIN keyboard in PinRequestView";
441     pin_keyboard_view_->SetVisible(true);
442     // This will trigger ChildPreferredSizeChanged in parent view and Layout()
443     // in view. As the result whole hierarchy will go through re-layout.
444     UpdatePreferredSize();
445   }
446 }
447 
OnTabletModeEnded()448 void PinRequestView::OnTabletModeEnded() {
449   if (!pin_keyboard_always_enabled_) {
450     VLOG(1) << "Hiding PIN keyboard in PinRequestView";
451     DCHECK(pin_keyboard_view_);
452     pin_keyboard_view_->SetVisible(false);
453     // This will trigger ChildPreferredSizeChanged in parent view and Layout()
454     // in view. As the result whole hierarchy will go through re-layout.
455     UpdatePreferredSize();
456   }
457 }
458 
OnTabletControllerDestroyed()459 void PinRequestView::OnTabletControllerDestroyed() {
460   tablet_mode_observer_.RemoveAll();
461 }
462 
SubmitCode()463 void PinRequestView::SubmitCode() {
464   base::Optional<std::string> code = access_code_view_->GetCode();
465   DCHECK(code.has_value());
466 
467   SubmissionResult result = delegate_->OnPinSubmitted(*code);
468   switch (result) {
469     case SubmissionResult::kPinAccepted: {
470       std::move(on_pin_request_done_).Run(true /* success */);
471       return;
472     }
473     case SubmissionResult::kPinError: {
474       // Caller is expected to call UpdateState() to allow for customization of
475       // error messages.
476       return;
477     }
478     case SubmissionResult::kSubmitPending: {
479       // Waiting on validation result - do nothing for now.
480       return;
481     }
482   }
483 }
484 
OnBack()485 void PinRequestView::OnBack() {
486   delegate_->OnBack();
487   if (PinRequestWidget::Get()) {
488     PinRequestWidget::Get()->Close(false /* success */);
489   }
490 }
491 
UpdateState(PinRequestViewState state,const base::string16 & title,const base::string16 & description)492 void PinRequestView::UpdateState(PinRequestViewState state,
493                                  const base::string16& title,
494                                  const base::string16& description) {
495   state_ = state;
496   title_label_->SetText(title);
497   description_label_->SetText(description);
498   UpdatePreferredSize();
499   switch (state_) {
500     case PinRequestViewState::kNormal: {
501       const SkColor kTextColor = AshColorProvider::Get()->GetContentLayerColor(
502           AshColorProvider::ContentLayerType::kTextColorPrimary);
503       access_code_view_->SetInputColor(kTextColor);
504       title_label_->SetEnabledColor(kTextColor);
505       return;
506     }
507     case PinRequestViewState::kError: {
508       access_code_view_->SetInputColor(kErrorColor);
509       title_label_->SetEnabledColor(kErrorColor);
510       // Read out the error.
511       title_label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
512       return;
513     }
514   }
515 }
516 
ClearInput()517 void PinRequestView::ClearInput() {
518   access_code_view_->ClearInput();
519 }
520 
SetInputEnabled(bool input_enabled)521 void PinRequestView::SetInputEnabled(bool input_enabled) {
522   access_code_view_->SetInputEnabled(input_enabled);
523 }
524 
UpdatePreferredSize()525 void PinRequestView::UpdatePreferredSize() {
526   SetPreferredSize(CalculatePreferredSize());
527   if (GetWidget())
528     GetWidget()->CenterWindow(GetPreferredSize());
529 }
530 
FocusSubmitButton()531 void PinRequestView::FocusSubmitButton() {
532   submit_button_->RequestFocus();
533 }
534 
OnInputChange(bool last_field_active,bool complete)535 void PinRequestView::OnInputChange(bool last_field_active, bool complete) {
536   if (state_ == PinRequestViewState::kError) {
537     UpdateState(PinRequestViewState::kNormal, default_title_,
538                 default_description_);
539   }
540 
541   submit_button_->SetEnabled(complete);
542 
543   if (complete && last_field_active) {
544     if (auto_submit_enabled_) {
545       auto_submit_enabled_ = false;
546       SubmitCode();
547       return;
548     }
549 
550     // Moving focus is delayed by using PostTask to allow for proper
551     // a11y announcements.
552     base::ThreadTaskRunnerHandle::Get()->PostTask(
553         FROM_HERE, base::BindOnce(&PinRequestView::FocusSubmitButton,
554                                   weak_ptr_factory_.GetWeakPtr()));
555   }
556 }
557 
GetAccessibleNodeData(ui::AXNodeData * node_data)558 void PinRequestView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
559   views::View::GetAccessibleNodeData(node_data);
560   node_data->role = ax::mojom::Role::kDialog;
561   node_data->SetName(default_accessible_title_);
562 }
563 
564 // If |pin_keyboard_always_enabled_| is not set, pin keyboard is only shown in
565 // tablet mode.
PinKeyboardVisible() const566 bool PinRequestView::PinKeyboardVisible() const {
567   return pin_keyboard_always_enabled_ || IsTabletMode();
568 }
569 
GetPinRequestViewSize() const570 gfx::Size PinRequestView::GetPinRequestViewSize() const {
571   int height = kPinRequestViewMinimumHeightDp +
572                std::min(int{title_label_->GetRequiredLines()}, kTitleMaxLines) *
573                    kTitleLineHeightDp +
574                std::min(int{description_label_->GetRequiredLines()},
575                         kDescriptionMaxLines) *
576                    kDescriptionTextLineHeightDp;
577   if (PinKeyboardVisible())
578     height += kPinKeyboardHeightDp;
579   return gfx::Size(kPinRequestViewWidthDp, height);
580 }
581 
582 }  // namespace ash
583