[go: nahoru, domu]

blob: ffbd5d763e49000ea97dbdf57f10d0b5fde04dd9 [file] [log] [blame]
jdufaultd979ce22017-06-12 21:00:101// 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_password_view.h"
6
Sarah Hue16b6ef2018-08-06 17:50:227#include "ash/login/ui/horizontal_image_sequence_animation_decoder.h"
Jacob Dufaulta0225592017-10-17 21:53:388#include "ash/login/ui/hover_notifier.h"
Quan Nguyen59b4cd572018-07-10 20:49:239#include "ash/login/ui/lock_screen.h"
Sarah Hua93125b2017-10-30 19:18:1810#include "ash/login/ui/login_button.h"
Jacob Dufault916b6e432017-09-15 00:15:5511#include "ash/login/ui/non_accessible_view.h"
James Cookab528d02018-01-09 00:06:0812#include "ash/public/cpp/login_constants.h"
jdufaultd979ce22017-06-12 21:00:1013#include "ash/resources/vector_icons/vector_icons.h"
Darren Shen7db16032018-03-06 21:02:3314#include "ash/shell.h"
Jacob Dufault916b6e432017-09-15 00:15:5515#include "ash/strings/grit/ash_strings.h"
Jacob Dufault8b152b72017-06-16 17:52:2516#include "base/strings/string_number_conversions.h"
jdufaultd979ce22017-06-12 21:00:1017#include "base/strings/utf_string_conversions.h"
Jacob Dufault916b6e432017-09-15 00:15:5518#include "ui/accessibility/ax_node_data.h"
19#include "ui/base/l10n/l10n_util.h"
Jacob Dufaulta0225592017-10-17 21:53:3820#include "ui/base/resource/resource_bundle.h"
Jacob Dufault8b152b72017-06-16 17:52:2521#include "ui/events/event_constants.h"
22#include "ui/events/keycodes/dom/dom_code.h"
jdufaultd979ce22017-06-12 21:00:1023#include "ui/gfx/paint_vector_icon.h"
Jacob Dufaulta0225592017-10-17 21:53:3824#include "ui/resources/grit/ui_resources.h"
Sarah Hu745c82a2017-07-14 23:21:5825#include "ui/views/background.h"
jdufaultd979ce22017-06-12 21:00:1026#include "ui/views/border.h"
Wenzhao Zang8c9d6902017-10-01 07:48:4327#include "ui/views/controls/button/image_button.h"
Wenzhao Zang0b3fce272017-10-13 04:48:2028#include "ui/views/controls/image_view.h"
jdufaultd979ce22017-06-12 21:00:1029#include "ui/views/controls/separator.h"
30#include "ui/views/controls/textfield/textfield.h"
31#include "ui/views/layout/box_layout.h"
32#include "ui/views/layout/fill_layout.h"
Jacob Dufaulta0225592017-10-17 21:53:3833#include "ui/views/widget/widget.h"
jdufaultd979ce22017-06-12 21:00:1034
Jacob Dufault916b6e432017-09-15 00:15:5535// TODO(jdufault): On two user view the password prompt is visible to
36// accessibility using special navigation keys even though it is invisible. We
37// probably need to customize the text box quite a bit, we may want to do
38// something similar to SearchBoxView.
39
jdufaultd979ce22017-06-12 21:00:1040namespace ash {
41namespace {
42
Jacob Dufaulta0225592017-10-17 21:53:3843// How long the user must hover over the easy unlock icon before showing the
44// tooltip.
45const int kDelayBeforeShowingTooltipMs = 500;
jdufaultd979ce22017-06-12 21:00:1046
Wenzhao Zang0b3fce272017-10-13 04:48:2047// Margin above/below the password view.
48constexpr const int kMarginAboveBelowPasswordIconsDp = 8;
49
Jacob Dufaulta0225592017-10-17 21:53:3850// Total width of the password view.
51constexpr int kPasswordTotalWidthDp = 204;
52
Wenzhao Zang0b3fce272017-10-13 04:48:2053// Size (width/height) of the submit button.
54constexpr int kSubmitButtonSizeDp = 20;
55
56// Size (width/height) of the caps lock hint icon.
57constexpr int kCapsLockIconSizeDp = 20;
jdufaultd979ce22017-06-12 21:00:1058
Jacob Dufaulta0225592017-10-17 21:53:3859// Width and height of the easy unlock icon.
Jacob Dufault525a1fd12017-10-20 17:00:3560constexpr const int kEasyUnlockIconSizeDp = 20;
Jacob Dufaulta0225592017-10-17 21:53:3861
62// Horizontal distance/margin between the easy unlock icon and the start of
63// the password view.
64constexpr const int kHorizontalDistanceBetweenEasyUnlockAndPasswordDp = 12;
65
66// Non-empty height, useful for debugging/visualization.
67constexpr const int kNonEmptyHeight = 1;
68
Jacob Dufault916b6e432017-09-15 00:15:5569constexpr const char kLoginPasswordViewName[] = "LoginPasswordView";
70
71class NonAccessibleSeparator : public views::Separator {
72 public:
73 NonAccessibleSeparator() = default;
74 ~NonAccessibleSeparator() override = default;
75
76 // views::Separator:
77 void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
78 views::Separator::GetAccessibleNodeData(node_data);
Dominic Mazzonidcef1b732018-01-26 17:57:0479 node_data->AddState(ax::mojom::State::kInvisible);
Jacob Dufault916b6e432017-09-15 00:15:5580 }
81
82 private:
83 DISALLOW_COPY_AND_ASSIGN(NonAccessibleSeparator);
84};
jdufaultd979ce22017-06-12 21:00:1085
Jacob Dufault9e2a6402017-12-22 05:40:0286// A textfield that selects all text on focus.
87class LoginTextfield : public views::Textfield {
88 public:
89 LoginTextfield() {}
90 ~LoginTextfield() override {}
91
92 // views::Textfield:
93 void OnFocus() override {
94 views::Textfield::OnFocus();
95 SelectAll(false /*reverse*/);
96 }
97
98 private:
99 DISALLOW_COPY_AND_ASSIGN(LoginTextfield);
100};
101
Jacob Dufaulta0225592017-10-17 21:53:38102// Set of resources for an easy unlock icon.
103struct IconBundle {
104 // Creates an IconBundle for a static image.
105 IconBundle(int normal, int hover, int pressed)
106 : normal(normal), hover(hover), pressed(pressed) {}
107 // Creates an IconBundle instance for an animation.
108 IconBundle(int resource, base::TimeDelta duration, int num_frames)
109 : normal(resource),
110 hover(resource),
111 pressed(resource),
112 duration(duration),
113 num_frames(num_frames) {}
114
115 // Icons for different button states.
116 const int normal;
117 const int hover;
118 const int pressed;
119
120 // Animation metadata. If these are set then |normal| == |hover| == |pressed|.
121 const base::TimeDelta duration;
122 const int num_frames = 0;
123};
124
125// Construct an IconBundle instance for a given mojom::EasyUnlockIconId value.
126IconBundle GetEasyUnlockResources(mojom::EasyUnlockIconId id) {
127 switch (id) {
128 case mojom::EasyUnlockIconId::NONE:
129 break;
130 case mojom::EasyUnlockIconId::HARDLOCKED:
131 return IconBundle(IDR_EASY_UNLOCK_HARDLOCKED,
132 IDR_EASY_UNLOCK_HARDLOCKED_HOVER,
133 IDR_EASY_UNLOCK_HARDLOCKED_PRESSED);
134 case mojom::EasyUnlockIconId::LOCKED:
135 return IconBundle(IDR_EASY_UNLOCK_LOCKED, IDR_EASY_UNLOCK_LOCKED_HOVER,
136 IDR_EASY_UNLOCK_LOCKED_PRESSED);
137 case mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED:
138 return IconBundle(IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED,
139 IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_HOVER,
140 IDR_EASY_UNLOCK_LOCKED_TO_BE_ACTIVATED_PRESSED);
141 case mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT:
142 return IconBundle(IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT,
143 IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_HOVER,
144 IDR_EASY_UNLOCK_LOCKED_WITH_PROXIMITY_HINT_PRESSED);
145 case mojom::EasyUnlockIconId::UNLOCKED:
146 return IconBundle(IDR_EASY_UNLOCK_UNLOCKED,
147 IDR_EASY_UNLOCK_UNLOCKED_HOVER,
148 IDR_EASY_UNLOCK_UNLOCKED_PRESSED);
149 case mojom::EasyUnlockIconId::SPINNER:
150 return IconBundle(IDR_EASY_UNLOCK_SPINNER,
151 base::TimeDelta::FromSeconds(2), 45 /*num_frames*/);
Lei Zhanga2c5a9e22017-10-25 04:13:54152 }
Jacob Dufaulta0225592017-10-17 21:53:38153
154 NOTREACHED();
155 return IconBundle(IDR_EASY_UNLOCK_LOCKED, IDR_EASY_UNLOCK_LOCKED_HOVER,
156 IDR_EASY_UNLOCK_LOCKED_PRESSED);
157}
158
jdufaultd979ce22017-06-12 21:00:10159} // namespace
160
Jacob Dufaulta0225592017-10-17 21:53:38161class LoginPasswordView::EasyUnlockIcon : public views::Button,
162 public views::ButtonListener {
163 public:
164 EasyUnlockIcon(const gfx::Size& size, int corner_radius)
165 : views::Button(this) {
166 SetPreferredSize(size);
Brett Wilson501d6642017-12-14 17:58:18167 SetLayoutManager(std::make_unique<views::FillLayout>());
Jacob Dufaulta0225592017-10-17 21:53:38168 icon_ = new AnimatedRoundedImageView(size, corner_radius);
Jacob Dufaulta0225592017-10-17 21:53:38169 AddChildView(icon_);
170 }
171 ~EasyUnlockIcon() override = default;
172
173 void Init(const OnEasyUnlockIconHovered& on_hovered,
174 const OnEasyUnlockIconTapped& on_tapped) {
175 DCHECK(on_hovered);
176 DCHECK(on_tapped);
177
178 on_hovered_ = on_hovered;
179 on_tapped_ = on_tapped;
180
181 hover_notifier_ = std::make_unique<HoverNotifier>(
182 this,
183 base::Bind(&LoginPasswordView::EasyUnlockIcon::OnHoverStateChanged,
184 base::Unretained(this)));
185 }
186
187 void SetEasyUnlockIcon(mojom::EasyUnlockIconId icon_id,
188 const base::string16& accessibility_label) {
189 bool changed_states = icon_id != icon_id_;
190 icon_id_ = icon_id;
191 UpdateImage(changed_states);
192 SetAccessibleName(accessibility_label);
193 }
194
195 void set_immediately_hover_for_test() { immediately_hover_for_test_ = true; }
196
197 // views::Button:
198 void StateChanged(ButtonState old_state) override {
199 // Stop showing tooltip, as we most likely exited hover state.
200 invoke_hover_.Stop();
201
202 switch (state()) {
203 case ButtonState::STATE_NORMAL:
204 UpdateImage(false /*changed_states*/);
205 break;
206 case ButtonState::STATE_HOVERED:
207 UpdateImage(false /*changed_states*/);
208 if (immediately_hover_for_test_) {
209 on_hovered_.Run();
210 } else {
211 invoke_hover_.Start(
212 FROM_HERE,
213 base::TimeDelta::FromMilliseconds(kDelayBeforeShowingTooltipMs),
214 on_hovered_);
215 }
216 break;
217 case ButtonState::STATE_PRESSED:
218 UpdateImage(false /*changed_states*/);
219 break;
220 case ButtonState::STATE_DISABLED:
221 break;
222 case ButtonState::STATE_COUNT:
223 break;
224 }
225 }
226
227 // views::ButtonListener:
228 void ButtonPressed(Button* sender, const ui::Event& event) override {
229 on_tapped_.Run();
230 }
231
232 private:
233 void OnHoverStateChanged(bool has_hover) {
234 SetState(has_hover ? ButtonState::STATE_HOVERED
235 : ButtonState::STATE_NORMAL);
236 }
237
238 void UpdateImage(bool changed_states) {
239 // Ignore any calls happening while the view is not attached;
240 // IsMouseHovered() will CHECK(false) in that scenario. GetWidget() may be
241 // null during construction and GetWidget()->GetRootView() may be null
242 // during destruction. Both scenarios only happen in tests.
243 if (!GetWidget() || !GetWidget()->GetRootView())
244 return;
245
246 if (icon_id_ == mojom::EasyUnlockIconId::NONE)
247 return;
248
249 IconBundle resources = GetEasyUnlockResources(icon_id_);
250
251 int active_resource = resources.normal;
252 if (IsMouseHovered())
253 active_resource = resources.hover;
254 if (state() == ButtonState::STATE_PRESSED)
255 active_resource = resources.pressed;
256
257 // Image to show. It may or may not be an animation, depending on
258 // |resources.duration|.
259 gfx::ImageSkia* image =
Lei Zhanga2c5a9e22017-10-25 04:13:54260 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
261 active_resource);
Jacob Dufaulta0225592017-10-17 21:53:38262
263 if (!resources.duration.is_zero()) {
264 // Only change the animation if the state itself has changed, otherwise
265 // the active animation frame is reset and there is a lot of unecessary
266 // decoding/image resizing work. This optimization only is valid only if
267 // all three resource assets are the same.
268 DCHECK_EQ(resources.normal, resources.hover);
269 DCHECK_EQ(resources.normal, resources.pressed);
270 if (changed_states) {
Sarah Hue16b6ef2018-08-06 17:50:22271 icon_->SetAnimationDecoder(
272 std::make_unique<HorizontalImageSequenceAnimationDecoder>(
273 *image, resources.duration, resources.num_frames),
274 AnimatedRoundedImageView::Playback::kRepeat);
Jacob Dufaulta0225592017-10-17 21:53:38275 }
276 } else {
277 icon_->SetImage(*image);
278 }
279 }
280
281 // Icon we are currently displaying.
282 mojom::EasyUnlockIconId icon_id_ = mojom::EasyUnlockIconId::NONE;
283
284 // View which renders the icon.
285 AnimatedRoundedImageView* icon_;
286
287 // Callbacks run when icon is hovered or tapped.
288 OnEasyUnlockIconHovered on_hovered_;
289 OnEasyUnlockIconTapped on_tapped_;
290
291 std::unique_ptr<HoverNotifier> hover_notifier_;
292
293 // Timer used to control when we invoke |on_hover_|.
294 base::OneShotTimer invoke_hover_;
295
296 // If true, the tooltip/hover timer will be skipped and |on_hover_| will be
297 // run immediately.
298 bool immediately_hover_for_test_ = false;
299
300 DISALLOW_COPY_AND_ASSIGN(EasyUnlockIcon);
301};
302
jdufaultd979ce22017-06-12 21:00:10303LoginPasswordView::TestApi::TestApi(LoginPasswordView* view) : view_(view) {}
304
305LoginPasswordView::TestApi::~TestApi() = default;
306
Jacob Dufaultec9eead2018-11-16 22:24:47307void LoginPasswordView::TestApi::SubmitPassword(const std::string& password) {
308 view_->textfield_->SetText(base::ASCIIToUTF16(password));
309 view_->UpdateUiState();
310 view_->SubmitPassword();
311}
312
Jacob Dufaultb7a2d842017-12-01 23:21:15313views::Textfield* LoginPasswordView::TestApi::textfield() const {
jdufaultd979ce22017-06-12 21:00:10314 return view_->textfield_;
315}
316
317views::View* LoginPasswordView::TestApi::submit_button() const {
318 return view_->submit_button_;
319}
320
Jacob Dufaulta0225592017-10-17 21:53:38321views::View* LoginPasswordView::TestApi::easy_unlock_icon() const {
322 return view_->easy_unlock_icon_;
323}
324
325void LoginPasswordView::TestApi::set_immediately_hover_easy_unlock_icon() {
326 view_->easy_unlock_icon_->set_immediately_hover_for_test();
327}
328
Darren Shen7db16032018-03-06 21:02:33329LoginPasswordView::LoginPasswordView() {
330 Shell::Get()->ime_controller()->AddObserver(this);
331
Jacob Dufault11904912018-10-01 19:41:35332 auto* root_layout = SetLayoutManager(
333 std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
jdufaultd979ce22017-06-12 21:00:10334 root_layout->set_main_axis_alignment(
335 views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
jdufaultd979ce22017-06-12 21:00:10336
Wenzhao Zang0b3fce272017-10-13 04:48:20337 password_row_ = new NonAccessibleView();
338
Brett Wilson501d6642017-12-14 17:58:18339 auto layout = std::make_unique<views::BoxLayout>(
340 views::BoxLayout::kHorizontal,
341 gfx::Insets(kMarginAboveBelowPasswordIconsDp, 0));
jdufaultd979ce22017-06-12 21:00:10342 layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
Wenzhao Zang0b3fce272017-10-13 04:48:20343 layout->set_cross_axis_alignment(
344 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
Brett Wilson501d6642017-12-14 17:58:18345 auto* layout_ptr = password_row_->SetLayoutManager(std::move(layout));
Wenzhao Zang0b3fce272017-10-13 04:48:20346 AddChildView(password_row_);
jdufaultd979ce22017-06-12 21:00:10347
Jacob Dufaulta0225592017-10-17 21:53:38348 // Add easy unlock icon.
349 easy_unlock_icon_ = new EasyUnlockIcon(
350 gfx::Size(kEasyUnlockIconSizeDp, kEasyUnlockIconSizeDp),
351 0 /*corner_radius*/);
352 password_row_->AddChildView(easy_unlock_icon_);
353
354 easy_unlock_right_margin_ = new NonAccessibleView();
355 easy_unlock_right_margin_->SetPreferredSize(gfx::Size(
356 kHorizontalDistanceBetweenEasyUnlockAndPasswordDp, kNonEmptyHeight));
357 password_row_->AddChildView(easy_unlock_right_margin_);
358
359 // Easy unlock starts invisible. There will be an event later to show it if
360 // needed.
361 easy_unlock_icon_->SetVisible(false);
362 easy_unlock_right_margin_->SetVisible(false);
363
jdufaultd979ce22017-06-12 21:00:10364 // Password textfield. We control the textfield size by sizing the parent
365 // view, as the textfield will expand to fill it.
Jacob Dufault9e2a6402017-12-22 05:40:02366 textfield_ = new LoginTextfield();
Wenzhao Zang8c9d6902017-10-01 07:48:43367 textfield_->set_controller(this);
jdufaultd979ce22017-06-12 21:00:10368 textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
Jacob Dufaultec147082018-10-11 20:59:46369 textfield_->SetTextColor(login_constants::kAuthMethodsTextColor);
Wenzhao Zangf141de42017-11-10 06:20:15370 textfield_->SetFontList(views::Textfield::GetDefaultFontList().Derive(
371 5, gfx::Font::FontStyle::NORMAL, gfx::Font::Weight::NORMAL));
372 textfield_->set_placeholder_font_list(views::Textfield::GetDefaultFontList());
Jacob Dufaultec147082018-10-11 20:59:46373 textfield_->set_placeholder_text_color(
374 login_constants::kAuthMethodsTextColor);
Wenzhao Zangf141de42017-11-10 06:20:15375 textfield_->SetGlyphSpacing(6);
jdufaultd979ce22017-06-12 21:00:10376 textfield_->SetBorder(nullptr);
Sarah Hu745c82a2017-07-14 23:21:58377 textfield_->SetBackgroundColor(SK_ColorTRANSPARENT);
jdufaultd979ce22017-06-12 21:00:10378
Wenzhao Zang0b3fce272017-10-13 04:48:20379 password_row_->AddChildView(textfield_);
Brett Wilson501d6642017-12-14 17:58:18380 layout_ptr->SetFlexForView(textfield_, 1);
Wenzhao Zang0b3fce272017-10-13 04:48:20381
382 // Caps lock hint icon.
383 capslock_icon_ = new views::ImageView();
384 capslock_icon_->SetImage(gfx::CreateVectorIcon(
385 kLockScreenCapsLockIcon, kCapsLockIconSizeDp,
386 SkColorSetA(login_constants::kButtonEnabledColor,
387 login_constants::kButtonDisabledAlpha)));
388 password_row_->AddChildView(capslock_icon_);
389 // Caps lock hint starts invisible. This constructor will call
390 // OnCapsLockChanged with the actual caps lock state.
391 capslock_icon_->SetVisible(false);
jdufaultd979ce22017-06-12 21:00:10392
393 // Submit button.
Sarah Hua93125b2017-10-30 19:18:18394 submit_button_ = new LoginButton(this);
Wenzhao Zang8c9d6902017-10-01 07:48:43395 submit_button_->SetImage(
396 views::Button::STATE_NORMAL,
397 gfx::CreateVectorIcon(kLockScreenArrowIcon, kSubmitButtonSizeDp,
398 login_constants::kButtonEnabledColor));
399 submit_button_->SetImage(
400 views::Button::STATE_DISABLED,
401 gfx::CreateVectorIcon(
402 kLockScreenArrowIcon, kSubmitButtonSizeDp,
403 SkColorSetA(login_constants::kButtonEnabledColor,
404 login_constants::kButtonDisabledAlpha)));
Jacob Dufault916b6e432017-09-15 00:15:55405 submit_button_->SetAccessibleName(l10n_util::GetStringUTF16(
406 IDS_ASH_LOGIN_POD_SUBMIT_BUTTON_ACCESSIBLE_NAME));
Wenzhao Zang0b3fce272017-10-13 04:48:20407 password_row_->AddChildView(submit_button_);
jdufaultd979ce22017-06-12 21:00:10408
409 // Separator on bottom.
Wenzhao Zang8c9d6902017-10-01 07:48:43410 separator_ = new NonAccessibleSeparator();
411 AddChildView(separator_);
jdufaultd979ce22017-06-12 21:00:10412
413 // Make sure the textfield always starts with focus.
414 textfield_->RequestFocus();
Wenzhao Zang0b3fce272017-10-13 04:48:20415
Darren Shen7db16032018-03-06 21:02:33416 // Initialize with the initial state of caps lock.
417 OnCapsLockChanged(Shell::Get()->ime_controller()->IsCapsLockEnabled());
Wenzhao Zangc61be252017-12-28 08:34:53418
419 // Make sure the UI start with the correct states.
420 UpdateUiState();
jdufaultd979ce22017-06-12 21:00:10421}
422
Darren Shen7db16032018-03-06 21:02:33423LoginPasswordView::~LoginPasswordView() {
424 Shell::Get()->ime_controller()->RemoveObserver(this);
425}
jdufaultd979ce22017-06-12 21:00:10426
Wenzhao Zang8c9d6902017-10-01 07:48:43427void LoginPasswordView::Init(
428 const OnPasswordSubmit& on_submit,
Jacob Dufaulta0225592017-10-17 21:53:38429 const OnPasswordTextChanged& on_password_text_changed,
430 const OnEasyUnlockIconHovered& on_easy_unlock_icon_hovered,
431 const OnEasyUnlockIconTapped& on_easy_unlock_icon_tapped) {
Wenzhao Zang8c9d6902017-10-01 07:48:43432 DCHECK(on_submit);
433 DCHECK(on_password_text_changed);
434 on_submit_ = on_submit;
435 on_password_text_changed_ = on_password_text_changed;
Jacob Dufaulta0225592017-10-17 21:53:38436 easy_unlock_icon_->Init(on_easy_unlock_icon_hovered,
437 on_easy_unlock_icon_tapped);
438}
439
Jacob Dufault21cd5662018-08-07 18:04:33440void LoginPasswordView::SetEnabledOnEmptyPassword(bool enabled) {
441 enabled_on_empty_password_ = enabled;
442 UpdateUiState();
443}
444
Jacob Dufaulta0225592017-10-17 21:53:38445void LoginPasswordView::SetEasyUnlockIcon(
446 mojom::EasyUnlockIconId id,
447 const base::string16& accessibility_label) {
448 // Update icon.
449 easy_unlock_icon_->SetEasyUnlockIcon(id, accessibility_label);
450
451 // Update icon visiblity.
452 bool has_icon = id != mojom::EasyUnlockIconId::NONE;
453 easy_unlock_icon_->SetVisible(has_icon);
454 easy_unlock_right_margin_->SetVisible(has_icon);
455 password_row_->Layout();
Wenzhao Zang8c9d6902017-10-01 07:48:43456}
457
Sarah Huf3a99dd02017-10-03 22:04:11458void LoginPasswordView::UpdateForUser(const mojom::LoginUserInfoPtr& user) {
Jacob Dufault916b6e432017-09-15 00:15:55459 textfield_->SetAccessibleName(l10n_util::GetStringFUTF16(
460 IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME,
Sarah Huf3a99dd02017-10-03 22:04:11461 base::UTF8ToUTF16(user->basic_user_info->display_email)));
Jacob Dufault916b6e432017-09-15 00:15:55462}
463
Jacob Dufaultb7795a82017-09-08 20:42:36464void LoginPasswordView::SetFocusEnabledForChildViews(bool enable) {
465 auto behavior = enable ? FocusBehavior::ALWAYS : FocusBehavior::NEVER;
466 textfield_->SetFocusBehavior(behavior);
Jacob Dufaultb7795a82017-09-08 20:42:36467}
468
469void LoginPasswordView::Clear() {
470 textfield_->SetText(base::string16());
Wenzhao Zang8c9d6902017-10-01 07:48:43471 // |ContentsChanged| won't be called by |Textfield| if the text is changed
472 // by |Textfield::SetText()|.
473 ContentsChanged(textfield_, textfield_->text());
Jacob Dufaultb7795a82017-09-08 20:42:36474}
475
Jacob Dufault9ccbec22017-12-04 21:53:10476void LoginPasswordView::InsertNumber(int value) {
477 textfield_->InsertOrReplaceText(base::IntToString16(value));
Jacob Dufault8b152b72017-06-16 17:52:25478}
479
480void LoginPasswordView::Backspace() {
481 // Instead of just adjusting textfield_ text directly, fire a backspace key
482 // event as this handles the various edge cases (ie, selected text).
483
484 // views::Textfield::OnKeyPressed is private, so we call it via views::View.
485 auto* view = static_cast<views::View*>(textfield_);
486 view->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_BACK,
487 ui::DomCode::BACKSPACE, ui::EF_NONE));
488 view->OnKeyPressed(ui::KeyEvent(ui::ET_KEY_RELEASED, ui::VKEY_BACK,
489 ui::DomCode::BACKSPACE, ui::EF_NONE));
490}
491
Wenzhao Zang8c9d6902017-10-01 07:48:43492void LoginPasswordView::SetPlaceholderText(
493 const base::string16& placeholder_text) {
494 textfield_->set_placeholder_text(placeholder_text);
James Hawkinsa2708ead2018-05-24 20:19:11495 SchedulePaint();
Wenzhao Zang8c9d6902017-10-01 07:48:43496}
497
Jacob Dufaultb7a2d842017-12-01 23:21:15498void LoginPasswordView::SetReadOnly(bool read_only) {
499 textfield_->SetReadOnly(read_only);
500 textfield_->SetCursorEnabled(!read_only);
501 UpdateUiState();
502}
503
jdufaultd979ce22017-06-12 21:00:10504const char* LoginPasswordView::GetClassName() const {
505 return kLoginPasswordViewName;
506}
507
508gfx::Size LoginPasswordView::CalculatePreferredSize() const {
509 gfx::Size size = views::View::CalculatePreferredSize();
510 size.set_width(kPasswordTotalWidthDp);
511 return size;
512}
513
Jacob Dufault81565fa2017-08-17 21:45:54514void LoginPasswordView::RequestFocus() {
515 textfield_->RequestFocus();
516}
517
jdufaultd979ce22017-06-12 21:00:10518bool LoginPasswordView::OnKeyPressed(const ui::KeyEvent& event) {
Jacob Dufault21cd5662018-08-07 18:04:33519 if (event.key_code() == ui::KeyboardCode::VKEY_RETURN &&
520 submit_button_->enabled()) {
jdufaultd979ce22017-06-12 21:00:10521 SubmitPassword();
522 return true;
523 }
524
525 return false;
526}
527
528void LoginPasswordView::ButtonPressed(views::Button* sender,
529 const ui::Event& event) {
530 DCHECK_EQ(sender, submit_button_);
531 SubmitPassword();
532}
533
Wenzhao Zang8c9d6902017-10-01 07:48:43534void LoginPasswordView::ContentsChanged(views::Textfield* sender,
535 const base::string16& new_contents) {
536 DCHECK_EQ(sender, textfield_);
Jacob Dufaultb7a2d842017-12-01 23:21:15537 UpdateUiState();
538 on_password_text_changed_.Run(new_contents.empty() /*is_empty*/);
539}
540
Quan Nguyen59b4cd572018-07-10 20:49:23541// Implements swapping active user with arrow keys
542bool LoginPasswordView::HandleKeyEvent(views::Textfield* sender,
543 const ui::KeyEvent& key_event) {
544 // Treat the password field as normal if it has text
545 if (!textfield_->text().empty())
546 return false;
547
548 if (key_event.type() != ui::ET_KEY_PRESSED)
549 return false;
550
551 if (key_event.is_repeat())
552 return false;
553
554 switch (key_event.key_code()) {
555 case ui::VKEY_LEFT:
556 LockScreen::Get()->FocusPreviousUser();
557 break;
558 case ui::VKEY_RIGHT:
559 LockScreen::Get()->FocusNextUser();
560 break;
561 default:
562 return false;
563 }
564
565 return true;
566}
567
Jacob Dufaultb7a2d842017-12-01 23:21:15568void LoginPasswordView::UpdateUiState() {
Jacob Dufault21cd5662018-08-07 18:04:33569 bool is_enabled = !textfield_->read_only() &&
570 (enabled_on_empty_password_ || !textfield_->text().empty());
Wenzhao Zang8c9d6902017-10-01 07:48:43571 submit_button_->SetEnabled(is_enabled);
Wenzhao Zang0b3fce272017-10-13 04:48:20572 SkColor color = is_enabled
573 ? login_constants::kButtonEnabledColor
574 : SkColorSetA(login_constants::kButtonEnabledColor,
575 login_constants::kButtonDisabledAlpha);
576 separator_->SetColor(color);
577 capslock_icon_->SetImage(gfx::CreateVectorIcon(kLockScreenCapsLockIcon,
578 kCapsLockIconSizeDp, color));
579}
580
581void LoginPasswordView::OnCapsLockChanged(bool enabled) {
582 capslock_icon_->SetVisible(enabled);
583 password_row_->Layout();
Wenzhao Zang8c9d6902017-10-01 07:48:43584}
585
jdufaultd979ce22017-06-12 21:00:10586void LoginPasswordView::SubmitPassword() {
Jacob Dufault21cd5662018-08-07 18:04:33587 DCHECK(submit_button_->enabled());
Jacob Dufaultb7a2d842017-12-01 23:21:15588 if (textfield_->read_only())
589 return;
jdufaultd979ce22017-06-12 21:00:10590 on_submit_.Run(textfield_->text());
jdufaultd979ce22017-06-12 21:00:10591}
592
Jacob Dufault81565fa2017-08-17 21:45:54593} // namespace ash