| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/login/ui/login_auth_user_view.h" |
| |
| #include <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "ash/login/login_screen_controller.h" |
| #include "ash/login/resources/grit/login_resources.h" |
| #include "ash/login/ui/arrow_button_view.h" |
| #include "ash/login/ui/horizontal_image_sequence_animation_decoder.h" |
| #include "ash/login/ui/lock_screen.h" |
| #include "ash/login/ui/login_constants.h" |
| #include "ash/login/ui/login_display_style.h" |
| #include "ash/login/ui/login_password_view.h" |
| #include "ash/login/ui/login_pin_input_view.h" |
| #include "ash/login/ui/login_pin_view.h" |
| #include "ash/login/ui/login_user_view.h" |
| #include "ash/login/ui/non_accessible_view.h" |
| #include "ash/login/ui/pin_keyboard_animation.h" |
| #include "ash/login/ui/pin_request_view.h" |
| #include "ash/login/ui/system_label_button.h" |
| #include "ash/login/ui/views_utils.h" |
| #include "ash/public/cpp/smartlock_state.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/style/ash_color_provider.h" |
| #include "ash/system/model/clock_model.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/night_light/time_of_day.h" |
| #include "base/bind.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "components/user_manager/user.h" |
| #include "ui/accessibility/ax_enums.mojom.h" |
| #include "ui/accessibility/ax_node_data.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/l10n/time_format.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/compositor/callback_layer_animation_observer.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/compositor/layer_animation_sequence.h" |
| #include "ui/compositor/layer_animator.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/color_analysis.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gfx/interpolated_transform.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/gfx/vector_icon_types.h" |
| #include "ui/views/background.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/button/md_text_button.h" |
| #include "ui/views/controls/highlight_path_generator.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/layout/fill_layout.h" |
| #include "ui/views/layout/flex_layout.h" |
| #include "ui/views/layout/grid_layout.h" |
| #include "ui/views/style/typography.h" |
| #include "ui/views/view.h" |
| |
| namespace ash { |
| namespace { |
| |
| constexpr const char kLoginAuthUserViewClassName[] = "LoginAuthUserView"; |
| |
| // Distance between the user view (ie, the icon and name) and other elements |
| const int kDistanceBetweenUserViewAndPasswordDp = 24; |
| const int kDistanceBetweenUserViewAndPinInputDp = 32; |
| const int kDistanceBetweenUserViewAndOnlineSigninDp = 24; |
| const int kDistanceBetweenUserViewAndChallengeResponseDp = 32; |
| |
| // Distance between the password textfield and the the pin keyboard. |
| const int kDistanceBetweenPasswordFieldAndPinKeyboardDp = 16; |
| |
| // Height of button used for switching between pin and password authentication. |
| const int kPinPasswordToggleButtonHeight = 32; |
| const int kPinPasswordToggleButtonPaddingTop = 24; |
| |
| // Distance from the end of pin keyboard to the bottom of the big user view. |
| const int kDistanceFromPinKeyboardToBigUserViewBottomDp = 50; |
| |
| // Distance from the top of the user view to the user icon. |
| constexpr int kDistanceFromTopOfBigUserViewToUserIconDp = 24; |
| |
| // Date time format containing only the day of the week, for example: "Tuesday". |
| constexpr char kDayOfWeekOnlyTimeFormat[] = "EEEE"; |
| |
| constexpr int kFingerprintIconSizeDp = 28; |
| constexpr int kResetToDefaultIconDelayMs = 1300; |
| constexpr base::TimeDelta kResetToDefaultMessageDelayMs = |
| base::TimeDelta::FromMilliseconds(3000); |
| constexpr int kFingerprintIconTopSpacingDp = 20; |
| constexpr int kSpacingBetweenFingerprintIconAndLabelDp = 15; |
| constexpr int kFingerprintViewWidthDp = 204; |
| constexpr int kDistanceBetweenPasswordFieldAndFingerprintViewDp = 90; |
| constexpr int kFingerprintFailedAnimationDurationMs = 700; |
| constexpr int kFingerprintFailedAnimationNumFrames = 45; |
| |
| constexpr base::TimeDelta kChallengeResponseResetAfterFailureDelay = |
| base::TimeDelta::FromSeconds(5); |
| constexpr int kChallengeResponseArrowSizeDp = 48; |
| constexpr int kSpacingBetweenChallengeResponseArrowAndIconDp = 100; |
| constexpr int kSpacingBetweenChallengeResponseIconAndLabelDp = 15; |
| constexpr int kChallengeResponseIconSizeDp = 28; |
| constexpr int kDistanceBetweenPwdFieldAndChallengeResponseViewDp = 0; |
| |
| constexpr int kDisabledAuthMessageVerticalBorderDp = 16; |
| constexpr int kDisabledAuthMessageHorizontalBorderDp = 16; |
| constexpr int kDisabledAuthMessageChildrenSpacingDp = 4; |
| constexpr int kDisabledAuthMessageTimeWidthDp = 204; |
| constexpr int kDisabledAuthMessageMultiprofileWidthDp = 304; |
| constexpr int kDisabledAuthMessageHeightDp = 98; |
| constexpr int kDisabledAuthMessageIconSizeDp = 24; |
| constexpr int kDisabledAuthMessageTitleFontSizeDeltaDp = 3; |
| constexpr int kDisabledAuthMessageContentsFontSizeDeltaDp = -1; |
| constexpr int kDisabledAuthMessageRoundedCornerRadiusDp = 8; |
| |
| constexpr int kLockedTpmMessageVerticalBorderDp = 16; |
| constexpr int kLockedTpmMessageHorizontalBorderDp = 16; |
| constexpr int kLockedTpmMessageChildrenSpacingDp = 4; |
| constexpr int kLockedTpmMessageWidthDp = 360; |
| constexpr int kLockedTpmMessageHeightDp = 108; |
| constexpr int kLockedTpmMessageIconSizeDp = 24; |
| constexpr int kLockedTpmMessageDeltaDp = 0; |
| constexpr int kLockedTpmMessageRoundedCornerRadiusDp = 8; |
| |
| constexpr int kNonEmptyWidthDp = 1; |
| gfx::Size SizeFromHeight(int height) { |
| return gfx::Size(kNonEmptyWidthDp, height); |
| } |
| |
| // Returns an observer that will hide |view| when it fires. The observer will |
| // delete itself after firing (by returning true). Make sure to call |
| // |observer->SetActive()| after attaching it. |
| ui::CallbackLayerAnimationObserver* BuildObserverToHideView(views::View* view) { |
| return new ui::CallbackLayerAnimationObserver(base::BindRepeating( |
| [](views::View* view, |
| const ui::CallbackLayerAnimationObserver& observer) { |
| // Don't hide the view if the animation is aborted, as |view| may no |
| // longer be valid. |
| if (observer.aborted_count()) |
| return true; |
| |
| view->SetVisible(false); |
| return true; |
| }, |
| view)); |
| } |
| |
| ui::CallbackLayerAnimationObserver* BuildObserverToNotifyA11yLocationChanged( |
| views::View* view) { |
| return new ui::CallbackLayerAnimationObserver(base::BindRepeating( |
| [](views::View* view, |
| const ui::CallbackLayerAnimationObserver& observer) { |
| // Don't notify a11y event if the animation is aborted, as |view| may no |
| // longer be valid. |
| if (observer.aborted_count()) |
| return true; |
| |
| view->NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, |
| false /*send_native_event*/); |
| return true; |
| }, |
| view)); |
| } |
| |
| ui::CallbackLayerAnimationObserver* BuildObserverToNotifyA11yLocationChanged( |
| LoginPinView* view) { |
| return new ui::CallbackLayerAnimationObserver(base::BindRepeating( |
| [](LoginPinView* view, |
| const ui::CallbackLayerAnimationObserver& observer) { |
| // Don't notify a11y event if the animation is aborted, as |view| may no |
| // longer be valid. |
| if (observer.aborted_count()) |
| return true; |
| |
| view->NotifyAccessibilityLocationChanged(); |
| return true; |
| }, |
| view)); |
| } |
| |
| // Clears the password for the given |LoginPasswordView| instance, hides it, and |
| // then deletes itself. |
| class ClearPasswordAndHideAnimationObserver |
| : public ui::ImplicitAnimationObserver { |
| public: |
| explicit ClearPasswordAndHideAnimationObserver(LoginPasswordView* view) |
| : password_view_(view) {} |
| |
| ClearPasswordAndHideAnimationObserver( |
| const ClearPasswordAndHideAnimationObserver&) = delete; |
| ClearPasswordAndHideAnimationObserver& operator=( |
| const ClearPasswordAndHideAnimationObserver&) = delete; |
| |
| ~ClearPasswordAndHideAnimationObserver() override = default; |
| |
| // ui::ImplicitAnimationObserver: |
| void OnImplicitAnimationsCompleted() override { |
| password_view_->Reset(); |
| password_view_->SetVisible(false); |
| delete this; |
| } |
| |
| private: |
| LoginPasswordView* const password_view_; |
| }; |
| |
| // The label shown below the fingerprint icon. |
| class FingerprintLabel : public views::Label { |
| public: |
| FingerprintLabel() { |
| SetSubpixelRenderingEnabled(false); |
| SetAutoColorReadabilityEnabled(false); |
| SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorSecondary)); |
| SetMultiLine(true); |
| |
| SetTextBasedOnState(FingerprintState::AVAILABLE_DEFAULT, |
| false /*can_use_pin*/); |
| } |
| |
| void SetTextBasedOnAuthAttempt(bool success) { |
| SetText(l10n_util::GetStringUTF16( |
| success ? IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_SUCCESS |
| : IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_FAILED)); |
| SetAccessibleName(l10n_util::GetStringUTF16( |
| success ? IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_SUCCESS |
| : IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_FAILED)); |
| } |
| |
| void SetTextBasedOnState(FingerprintState state, bool can_use_pin) { |
| auto get_displayed_id = [&]() { |
| switch (state) { |
| case FingerprintState::UNAVAILABLE: |
| case FingerprintState::AVAILABLE_DEFAULT: |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE; |
| case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING: |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_TOUCH_SENSOR; |
| case FingerprintState::DISABLED_FROM_ATTEMPTS: |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS; |
| case FingerprintState::DISABLED_FROM_TIMEOUT: |
| if (can_use_pin) |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_PIN_OR_PASSWORD_REQUIRED; |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_PASSWORD_REQUIRED; |
| } |
| NOTREACHED(); |
| }; |
| |
| auto get_accessible_id = [&]() { |
| if (state == FingerprintState::DISABLED_FROM_ATTEMPTS) |
| return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_DISABLED_FROM_ATTEMPTS; |
| return get_displayed_id(); |
| }; |
| |
| SetText(l10n_util::GetStringUTF16(get_displayed_id())); |
| SetAccessibleName(l10n_util::GetStringUTF16(get_accessible_id())); |
| } |
| |
| // views::Label: |
| void GetAccessibleNodeData(ui::AXNodeData* node_data) override { |
| node_data->role = ax::mojom::Role::kStaticText; |
| node_data->SetName(accessible_name_); |
| } |
| |
| // views::Label: |
| void OnThemeChanged() override { |
| views::Label::OnThemeChanged(); |
| SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorSecondary)); |
| } |
| |
| private: |
| void SetAccessibleName(const std::u16string& name) { |
| accessible_name_ = name; |
| NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, |
| true /*send_native_event*/); |
| } |
| |
| std::u16string accessible_name_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FingerprintLabel); |
| }; |
| |
| // The content needed to render the disabled auth message view. |
| struct LockScreenMessage { |
| std::u16string title; |
| std::u16string content; |
| const gfx::VectorIcon* icon; |
| }; |
| |
| // Returns the message used when the device was locked due to a time window |
| // limit. |
| LockScreenMessage GetWindowLimitMessage(const base::Time& unlock_time, |
| bool use_24hour_clock) { |
| LockScreenMessage message; |
| message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_FOR_BED_MESSAGE); |
| |
| base::Time local_midnight = base::Time::Now().LocalMidnight(); |
| |
| std::u16string time_to_display; |
| if (use_24hour_clock) { |
| time_to_display = base::TimeFormatTimeOfDayWithHourClockType( |
| unlock_time, base::k24HourClock, base::kDropAmPm); |
| } else { |
| time_to_display = base::TimeFormatTimeOfDayWithHourClockType( |
| unlock_time, base::k12HourClock, base::kKeepAmPm); |
| } |
| |
| if (unlock_time < local_midnight + base::TimeDelta::FromDays(1)) { |
| // Unlock time is today. |
| message.content = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_COME_BACK_MESSAGE, time_to_display); |
| } else if (unlock_time < local_midnight + base::TimeDelta::FromDays(2)) { |
| // Unlock time is tomorrow. |
| message.content = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_COME_BACK_TOMORROW_MESSAGE, time_to_display); |
| } else { |
| message.content = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_COME_BACK_DAY_OF_WEEK_MESSAGE, |
| base::TimeFormatWithPattern(unlock_time, kDayOfWeekOnlyTimeFormat), |
| time_to_display); |
| } |
| message.icon = &kLockScreenTimeLimitMoonIcon; |
| return message; |
| } |
| |
| // Returns the message used when the device was locked due to a time usage |
| // limit. |
| LockScreenMessage GetUsageLimitMessage(const base::TimeDelta& used_time) { |
| LockScreenMessage message; |
| |
| // 1 minute is used instead of 0, because the device is used for a few |
| // milliseconds before locking. |
| if (used_time < base::TimeDelta::FromMinutes(1)) { |
| // The device was locked all day. |
| message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TAKE_BREAK_MESSAGE); |
| message.content = |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_LOCKED_ALL_DAY_MESSAGE); |
| } else { |
| // The usage limit is over. |
| message.title = l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_IS_UP_MESSAGE); |
| |
| const std::u16string used_time_string = ui::TimeFormat::Detailed( |
| ui::TimeFormat::Format::FORMAT_DURATION, |
| ui::TimeFormat::Length::LENGTH_LONG, /*cutoff=*/3, used_time); |
| message.content = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_SCREEN_TIME_USED_MESSAGE, used_time_string); |
| } |
| message.icon = &kLockScreenTimeLimitTimerIcon; |
| return message; |
| } |
| |
| // Returns the message used when the device was locked due to a time limit |
| // override. |
| LockScreenMessage GetOverrideMessage() { |
| LockScreenMessage message; |
| message.title = |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_TIME_FOR_A_BREAK_MESSAGE); |
| message.content = |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_MANUAL_LOCK_MESSAGE); |
| message.icon = &kLockScreenTimeLimitLockIcon; |
| return message; |
| } |
| |
| LockScreenMessage GetLockScreenMessage(AuthDisabledReason lock_reason, |
| const base::Time& unlock_time, |
| const base::TimeDelta& used_time, |
| bool use_24hour_clock) { |
| switch (lock_reason) { |
| case AuthDisabledReason::kTimeWindowLimit: |
| return GetWindowLimitMessage(unlock_time, use_24hour_clock); |
| case AuthDisabledReason::kTimeUsageLimit: |
| return GetUsageLimitMessage(used_time); |
| case AuthDisabledReason::kTimeLimitOverride: |
| return GetOverrideMessage(); |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| } // namespace |
| |
| // Consists of fingerprint icon view and a label. |
| class LoginAuthUserView::FingerprintView : public views::View { |
| public: |
| FingerprintView() { |
| SetPaintToLayer(); |
| layer()->SetFillsBoundsOpaquely(false); |
| SetBorder(views::CreateEmptyBorder(kFingerprintIconTopSpacingDp, 0, 0, 0)); |
| |
| auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical, gfx::Insets(), |
| kSpacingBetweenFingerprintIconAndLabelDp)); |
| layout->set_main_axis_alignment( |
| views::BoxLayout::MainAxisAlignment::kCenter); |
| |
| icon_ = new AnimatedRoundedImageView( |
| gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp), |
| 0 /*corner_radius*/); |
| |
| AddChildView(icon_); |
| |
| label_ = new FingerprintLabel(); |
| AddChildView(label_); |
| |
| DisplayCurrentState(); |
| } |
| |
| FingerprintView(const FingerprintView&) = delete; |
| FingerprintView& operator=(const FingerprintView&) = delete; |
| |
| ~FingerprintView() override = default; |
| |
| void SetState(FingerprintState state) { |
| if (state_ == state) |
| return; |
| |
| reset_state_.Stop(); |
| state_ = state; |
| |
| DisplayCurrentState(); |
| |
| if (ShouldFireChromeVoxAlert(state)) |
| FireAlert(); |
| } |
| |
| void SetCanUsePin(bool value) { |
| if (can_use_pin_ == value) |
| return; |
| |
| can_use_pin_ = value; |
| label_->SetTextBasedOnState(state_, can_use_pin_); |
| } |
| |
| void NotifyFingerprintAuthResult(bool success) { |
| reset_state_.Stop(); |
| label_->SetTextBasedOnAuthAttempt(success); |
| |
| if (success) { |
| // We do not need to treat the light/dark mode for this use-case since |
| // this hint is shown for a short time interval. |
| icon_->SetImage(gfx::CreateVectorIcon( |
| kLockScreenFingerprintSuccessIcon, kFingerprintIconSizeDp, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPositive))); |
| } else { |
| SetIcon(FingerprintState::DISABLED_FROM_ATTEMPTS); |
| // base::Unretained is safe because reset_state_ is owned by |this|. |
| reset_state_.Start( |
| FROM_HERE, |
| base::TimeDelta::FromMilliseconds(kResetToDefaultIconDelayMs), |
| base::BindOnce(&FingerprintView::DisplayCurrentState, |
| base::Unretained(this))); |
| |
| FireAlert(); |
| } |
| } |
| |
| // views::View: |
| gfx::Size CalculatePreferredSize() const override { |
| gfx::Size size = views::View::CalculatePreferredSize(); |
| size.set_width(kFingerprintViewWidthDp); |
| return size; |
| } |
| |
| // views::View: |
| void OnGestureEvent(ui::GestureEvent* event) override { |
| if (event->type() != ui::ET_GESTURE_TAP) |
| return; |
| if (state_ == FingerprintState::AVAILABLE_DEFAULT || |
| state_ == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING) { |
| SetState(FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING); |
| reset_state_.Start( |
| FROM_HERE, kResetToDefaultMessageDelayMs, |
| base::BindOnce(&FingerprintView::SetState, base::Unretained(this), |
| FingerprintState::AVAILABLE_DEFAULT)); |
| } |
| } |
| |
| // views::View: |
| void OnThemeChanged() override { |
| views::View::OnThemeChanged(); |
| SetIcon(state_); |
| } |
| |
| private: |
| void DisplayCurrentState() { |
| SetVisible(state_ != FingerprintState::UNAVAILABLE); |
| SetIcon(state_); |
| label_->SetTextBasedOnState(state_, can_use_pin_); |
| } |
| |
| void FireAlert() { |
| label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, |
| true /*send_native_event*/); |
| } |
| |
| void SetIcon(FingerprintState state) { |
| const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary); |
| const SkColor color = |
| state == FingerprintState::AVAILABLE_DEFAULT || |
| state == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING |
| ? icon_color |
| : AshColorProvider::Get()->GetDisabledColor(icon_color); |
| switch (state) { |
| case FingerprintState::UNAVAILABLE: |
| case FingerprintState::AVAILABLE_DEFAULT: |
| case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING: |
| case FingerprintState::DISABLED_FROM_TIMEOUT: |
| icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintIcon, |
| kFingerprintIconSizeDp, color)); |
| break; |
| case FingerprintState::DISABLED_FROM_ATTEMPTS: |
| icon_->SetAnimationDecoder( |
| std::make_unique<HorizontalImageSequenceAnimationDecoder>( |
| *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER), |
| base::TimeDelta::FromMilliseconds( |
| kFingerprintFailedAnimationDurationMs), |
| kFingerprintFailedAnimationNumFrames), |
| AnimatedRoundedImageView::Playback::kSingle); |
| break; |
| } |
| } |
| |
| bool ShouldFireChromeVoxAlert(FingerprintState state) { |
| return state == FingerprintState::DISABLED_FROM_ATTEMPTS || |
| state == FingerprintState::DISABLED_FROM_TIMEOUT; |
| } |
| |
| FingerprintLabel* label_ = nullptr; |
| AnimatedRoundedImageView* icon_ = nullptr; |
| base::OneShotTimer reset_state_; |
| FingerprintState state_ = FingerprintState::AVAILABLE_DEFAULT; |
| |
| // Affects DISABLED_FROM_TIMEOUT message. |
| bool can_use_pin_ = false; |
| }; |
| |
| // Consists of challenge-response icon view and a label. |
| class LoginAuthUserView::ChallengeResponseView : public views::View { |
| public: |
| enum class State { kInitial, kAuthenticating, kFailure }; |
| |
| explicit ChallengeResponseView(base::RepeatingClosure on_start_tap) |
| : on_start_tap_(std::move(on_start_tap)) { |
| SetPaintToLayer(); |
| layer()->SetFillsBoundsOpaquely(false); |
| |
| auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical)); |
| layout->set_cross_axis_alignment( |
| views::BoxLayout::CrossAxisAlignment::kCenter); |
| auto arrow_button_view = std::make_unique<ArrowButtonView>( |
| base::BindRepeating(&ChallengeResponseView::ArrowButtonPressed, |
| base::Unretained(this)), |
| kChallengeResponseArrowSizeDp); |
| arrow_button_view->SetInstallFocusRingOnFocus(true); |
| views::InstallCircleHighlightPathGenerator(arrow_button_view.get()); |
| arrow_button_ = AddChildView(std::move(arrow_button_view)); |
| arrow_button_->SetAccessibleName(l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_START_SMART_CARD_AUTH_BUTTON_ACCESSIBLE_NAME)); |
| |
| arrow_to_icon_spacer_ = AddChildView(std::make_unique<NonAccessibleView>()); |
| arrow_to_icon_spacer_->SetPreferredSize( |
| gfx::Size(0, kSpacingBetweenChallengeResponseArrowAndIconDp)); |
| |
| icon_ = AddChildView(std::make_unique<views::ImageView>()); |
| icon_->SetImage(GetImageForIcon()); |
| |
| auto* icon_to_label_spacer = |
| AddChildView(std::make_unique<NonAccessibleView>()); |
| icon_to_label_spacer->SetPreferredSize( |
| gfx::Size(0, kSpacingBetweenChallengeResponseIconAndLabelDp)); |
| |
| label_ = AddChildView(std::make_unique<views::Label>( |
| GetTextForLabel(), views::style::CONTEXT_LABEL, |
| views::style::STYLE_PRIMARY)); |
| label_->SetAutoColorReadabilityEnabled(false); |
| label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorSecondary)); |
| label_->SetSubpixelRenderingEnabled(false); |
| label_->SetFontList(views::Label::GetDefaultFontList().Derive( |
| /*size_delta=*/1, gfx::Font::FontStyle::ITALIC, |
| gfx::Font::Weight::NORMAL)); |
| } |
| |
| ChallengeResponseView(const ChallengeResponseView&) = delete; |
| ChallengeResponseView& operator=(const ChallengeResponseView&) = delete; |
| |
| ~ChallengeResponseView() override = default; |
| |
| void SetState(State state) { |
| if (state_ == state) |
| return; |
| state_ = state; |
| |
| reset_state_timer_.Stop(); |
| if (state == State::kFailure) { |
| reset_state_timer_.Start( |
| FROM_HERE, kChallengeResponseResetAfterFailureDelay, |
| base::BindOnce(&ChallengeResponseView::SetState, |
| base::Unretained(this), State::kInitial)); |
| } |
| |
| arrow_button_->EnableLoadingAnimation(state == State::kAuthenticating); |
| icon_->SetImage(GetImageForIcon()); |
| label_->SetText(GetTextForLabel()); |
| |
| if (state == State::kFailure) { |
| label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert, |
| /*send_native_event=*/true); |
| } |
| |
| Layout(); |
| } |
| |
| // views::View: |
| void RequestFocus() override { arrow_button_->RequestFocus(); } |
| |
| // views::View: |
| void OnThemeChanged() override { |
| views::View::OnThemeChanged(); |
| icon_->SetImage(GetImageForIcon()); |
| label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorSecondary)); |
| } |
| |
| views::Button* GetButtonForTesting() { return arrow_button_; } |
| views::Label* GetLabelForTesting() { return label_; } |
| |
| private: |
| gfx::ImageSkia GetImageForIcon() const { |
| switch (state_) { |
| case State::kInitial: |
| case State::kAuthenticating: |
| return gfx::CreateVectorIcon( |
| kLockScreenSmartCardIcon, kChallengeResponseIconSizeDp, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary)); |
| case State::kFailure: |
| return gfx::CreateVectorIcon( |
| kLockScreenSmartCardFailureIcon, kChallengeResponseIconSizeDp, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorAlert)); |
| } |
| } |
| |
| std::u16string GetTextForLabel() const { |
| switch (state_) { |
| case State::kInitial: |
| case State::kAuthenticating: |
| return l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_MESSAGE); |
| case State::kFailure: |
| return l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_SMART_CARD_SIGN_IN_FAILURE_MESSAGE); |
| } |
| } |
| |
| void ArrowButtonPressed() { |
| // Ignore further clicks while handling the previous one. |
| if (state_ != State::kAuthenticating) |
| on_start_tap_.Run(); |
| } |
| |
| base::RepeatingClosure on_start_tap_; |
| State state_ = State::kInitial; |
| ArrowButtonView* arrow_button_ = nullptr; |
| NonAccessibleView* arrow_to_icon_spacer_ = nullptr; |
| views::ImageView* icon_ = nullptr; |
| views::Label* label_ = nullptr; |
| base::OneShotTimer reset_state_timer_; |
| }; |
| |
| // The message shown to the user when auth is disabled. This could happen when: |
| // -auth method is |AUTH_DISABLED| |
| // -auth method is |AUTH_ONLINE_SIGN_IN| and the user is on the secondary login |
| // screen |
| // -the selected account has a restricted multiprofile policy set and the user |
| // is on the secondary login screen |
| class LoginAuthUserView::DisabledAuthMessageView : public views::View { |
| public: |
| class ASH_EXPORT TestApi { |
| public: |
| explicit TestApi(DisabledAuthMessageView* view) : view_(view) {} |
| ~TestApi() = default; |
| |
| const std::u16string& GetDisabledAuthMessageContent() const { |
| return view_->message_contents_->GetText(); |
| } |
| |
| private: |
| DisabledAuthMessageView* const view_; |
| }; |
| |
| DisabledAuthMessageView() { |
| SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical, |
| gfx::Insets(kDisabledAuthMessageVerticalBorderDp, |
| kDisabledAuthMessageHorizontalBorderDp), |
| kDisabledAuthMessageChildrenSpacingDp)); |
| SetPaintToLayer(); |
| layer()->SetFillsBoundsOpaquely(false); |
| SetFocusBehavior(FocusBehavior::ALWAYS); |
| |
| // The icon size has to be defined later if the image will be visible. |
| message_icon_ = AddChildView(std::make_unique<views::ImageView>()); |
| message_icon_->SetImageSize(gfx::Size(kDisabledAuthMessageIconSizeDp, |
| kDisabledAuthMessageIconSizeDp)); |
| |
| auto decorate_label = [](views::Label* label) { |
| label->SetSubpixelRenderingEnabled(false); |
| label->SetAutoColorReadabilityEnabled(false); |
| label->SetFocusBehavior(FocusBehavior::ALWAYS); |
| }; |
| message_title_ = AddChildView(std::make_unique<views::Label>( |
| std::u16string(), views::style::CONTEXT_LABEL, |
| views::style::STYLE_PRIMARY)); |
| message_title_->SetFontList( |
| gfx::FontList().Derive(kDisabledAuthMessageTitleFontSizeDeltaDp, |
| gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM)); |
| decorate_label(message_title_); |
| |
| message_contents_ = AddChildView(std::make_unique<views::Label>( |
| std::u16string(), views::style::CONTEXT_LABEL, |
| views::style::STYLE_PRIMARY)); |
| message_contents_->SetFontList( |
| gfx::FontList().Derive(kDisabledAuthMessageContentsFontSizeDeltaDp, |
| gfx::Font::NORMAL, gfx::Font::Weight::NORMAL)); |
| decorate_label(message_contents_); |
| message_contents_->SetMultiLine(true); |
| } |
| |
| ~DisabledAuthMessageView() override = default; |
| |
| // Set the parameters needed to render the message. |
| void SetAuthDisabledMessage(const AuthDisabledData& auth_disabled_data, |
| bool use_24hour_clock) { |
| LockScreenMessage message = GetLockScreenMessage( |
| auth_disabled_data.reason, auth_disabled_data.auth_reenabled_time, |
| auth_disabled_data.device_used_time, use_24hour_clock); |
| SetPreferredSize(gfx::Size(kDisabledAuthMessageTimeWidthDp, |
| kDisabledAuthMessageHeightDp)); |
| message_icon_->SetVisible(true); |
| message_vector_icon_ = message.icon; |
| message_title_->SetText(message.title); |
| message_contents_->SetText(message.content); |
| UpdateColors(); |
| } |
| |
| void SetAuthDisabledMessage(const std::u16string& title, |
| const std::u16string& content) { |
| SetPreferredSize(gfx::Size(kDisabledAuthMessageMultiprofileWidthDp, |
| kDisabledAuthMessageHeightDp)); |
| message_icon_->SetVisible(false); |
| message_title_->SetText(title); |
| message_contents_->SetText(content); |
| UpdateColors(); |
| } |
| |
| // views::View: |
| void OnPaint(gfx::Canvas* canvas) override { |
| views::View::OnPaint(canvas); |
| |
| cc::PaintFlags flags; |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| flags.setColor( |
| PinRequestView::GetChildUserDialogColor(false /*using blur*/)); |
| canvas->DrawRoundRect(GetContentsBounds(), |
| kDisabledAuthMessageRoundedCornerRadiusDp, flags); |
| } |
| |
| // views::View: |
| void RequestFocus() override { message_title_->RequestFocus(); } |
| |
| // views::View: |
| void OnThemeChanged() override { |
| views::View::OnThemeChanged(); |
| UpdateColors(); |
| } |
| |
| private: |
| void UpdateColors() { |
| message_title_->SetEnabledColor( |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| message_contents_->SetEnabledColor( |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| if (message_vector_icon_) { |
| message_icon_->SetImage(gfx::CreateVectorIcon( |
| *message_vector_icon_, kDisabledAuthMessageIconSizeDp, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary))); |
| } |
| } |
| |
| views::Label* message_title_; |
| views::Label* message_contents_; |
| views::ImageView* message_icon_; |
| const gfx::VectorIcon* message_vector_icon_ = nullptr; |
| |
| DISALLOW_COPY_AND_ASSIGN(DisabledAuthMessageView); |
| }; |
| |
| // The message shown to user when TPM is locked. |
| class LoginAuthUserView::LockedTpmMessageView : public views::View { |
| public: |
| LockedTpmMessageView() { |
| SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical, |
| gfx::Insets(kLockedTpmMessageVerticalBorderDp, |
| kLockedTpmMessageHorizontalBorderDp), |
| kLockedTpmMessageChildrenSpacingDp)); |
| SetPaintToLayer(); |
| layer()->SetFillsBoundsOpaquely(false); |
| SetPreferredSize( |
| gfx::Size(kLockedTpmMessageWidthDp, kLockedTpmMessageHeightDp)); |
| SetFocusBehavior(FocusBehavior::ALWAYS); |
| |
| message_icon_ = AddChildView(std::make_unique<views::ImageView>()); |
| message_icon_->SetPreferredSize( |
| gfx::Size(kLockedTpmMessageIconSizeDp, kLockedTpmMessageIconSizeDp)); |
| |
| message_warning_ = CreateLabel(); |
| message_description_ = CreateLabel(); |
| |
| // Set content. |
| std::u16string message_description = l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_POD_TPM_LOCKED_ISSUE_DESCRIPTION); |
| message_description_->SetText(message_description); |
| } |
| |
| LockedTpmMessageView(const LockedTpmMessageView&) = delete; |
| LockedTpmMessageView& operator=(const LockedTpmMessageView&) = delete; |
| ~LockedTpmMessageView() override = default; |
| |
| // Set the parameters needed to render the message. |
| void SetRemainingTime(base::TimeDelta time_left) { |
| std::u16string time_left_message; |
| if (base::TimeDurationFormatWithSeconds( |
| time_left, base::DurationFormatWidth::DURATION_WIDTH_WIDE, |
| &time_left_message)) { |
| std::u16string message_warning = l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_POD_TPM_LOCKED_ISSUE_WARNING, time_left_message); |
| message_warning_->SetText(message_warning); |
| |
| if (time_left.InMinutes() != prev_time_left_.InMinutes()) { |
| message_warning_->NotifyAccessibilityEvent( |
| ax::mojom::Event::kTextChanged, true); |
| } |
| prev_time_left_ = time_left; |
| } |
| } |
| |
| // views::View: |
| void OnPaint(gfx::Canvas* canvas) override { |
| views::View::OnPaint(canvas); |
| |
| cc::PaintFlags flags; |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| flags.setColor( |
| PinRequestView::GetChildUserDialogColor(false /*using blur*/)); |
| canvas->DrawRoundRect(GetContentsBounds(), |
| kLockedTpmMessageRoundedCornerRadiusDp, flags); |
| } |
| |
| // views::View: |
| void RequestFocus() override { message_warning_->RequestFocus(); } |
| |
| // views::View: |
| void OnThemeChanged() override { |
| views::View::OnThemeChanged(); |
| message_icon_->SetImage(gfx::CreateVectorIcon( |
| kLockScreenAlertIcon, |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kIconColorPrimary))); |
| message_warning_->SetEnabledColor( |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| message_description_->SetEnabledColor( |
| AshColorProvider::Get()->GetContentLayerColor( |
| AshColorProvider::ContentLayerType::kTextColorPrimary)); |
| } |
| |
| private: |
| views::Label* CreateLabel() { |
| auto label = std::make_unique<views::Label>(std::u16string(), |
| views::style::CONTEXT_LABEL, |
| views::style::STYLE_PRIMARY); |
| label->SetFontList(gfx::FontList().Derive(kLockedTpmMessageDeltaDp, |
| gfx::Font::NORMAL, |
| gfx::Font::Weight::NORMAL)); |
| label->SetSubpixelRenderingEnabled(false); |
| label->SetAutoColorReadabilityEnabled(false); |
| label->SetFocusBehavior(FocusBehavior::ALWAYS); |
| label->SetMultiLine(true); |
| return AddChildView(std::move(label)); |
| } |
| |
| base::TimeDelta prev_time_left_; |
| views::Label* message_warning_; |
| views::Label* message_description_; |
| views::ImageView* message_icon_; |
| }; |
| |
| LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata() = default; |
| LoginAuthUserView::AuthMethodsMetadata::~AuthMethodsMetadata() = default; |
| LoginAuthUserView::AuthMethodsMetadata::AuthMethodsMetadata( |
| const AuthMethodsMetadata&) = default; |
| |
| struct LoginAuthUserView::UiState { |
| explicit UiState(const LoginAuthUserView* view) { |
| has_password = view->ShouldShowPasswordField(); |
| has_pin_input = view->ShouldShowPinInputField(); |
| has_pinpad = view->ShouldShowPinPad(); |
| has_toggle = view->ShouldShowToggle(); |
| has_fingerprint = view->HasAuthMethod(LoginAuthUserView::AUTH_FINGERPRINT); |
| has_challenge_response = |
| view->HasAuthMethod(LoginAuthUserView::AUTH_CHALLENGE_RESPONSE); |
| auth_disabled = view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED); |
| tpm_is_locked = |
| view->HasAuthMethod(LoginAuthUserView::AUTH_DISABLED_TPM_LOCKED); |
| force_online_sign_in = |
| view->HasAuthMethod(LoginAuthUserView::AUTH_ONLINE_SIGN_IN); |
| |
| non_pin_y_start_in_screen = view->GetBoundsInScreen().y(); |
| pin_start_in_screen = view->pin_view_->GetBoundsInScreen().origin(); |
| } |
| |
| bool has_password = false; |
| bool has_pin_input = false; |
| bool has_pinpad = false; |
| bool has_toggle = false; |
| bool has_fingerprint = false; |
| bool has_challenge_response = false; |
| bool auth_disabled = false; |
| bool tpm_is_locked = false; |
| bool force_online_sign_in = false; |
| // Used for this view's animation in `ApplyAnimationPostLayout`. |
| int non_pin_y_start_in_screen = 0; |
| gfx::Point pin_start_in_screen; |
| }; |
| |
| LoginAuthUserView::TestApi::TestApi(LoginAuthUserView* view) : view_(view) {} |
| |
| LoginAuthUserView::TestApi::~TestApi() = default; |
| |
| LoginUserView* LoginAuthUserView::TestApi::user_view() const { |
| return view_->user_view_; |
| } |
| |
| LoginPasswordView* LoginAuthUserView::TestApi::password_view() const { |
| return view_->password_view_; |
| } |
| |
| LoginPinView* LoginAuthUserView::TestApi::pin_view() const { |
| return view_->pin_view_; |
| } |
| |
| LoginPinInputView* LoginAuthUserView::TestApi::pin_input_view() const { |
| return view_->pin_input_view_; |
| } |
| |
| views::Button* LoginAuthUserView::TestApi::pin_password_toggle() const { |
| return view_->pin_password_toggle_; |
| } |
| |
| views::Button* LoginAuthUserView::TestApi::online_sign_in_message() const { |
| return view_->online_sign_in_button_; |
| } |
| |
| views::View* LoginAuthUserView::TestApi::disabled_auth_message() const { |
| return view_->disabled_auth_message_; |
| } |
| |
| views::Button* LoginAuthUserView::TestApi::challenge_response_button() { |
| return view_->challenge_response_view_->GetButtonForTesting(); |
| } |
| |
| views::Label* LoginAuthUserView::TestApi::challenge_response_label() { |
| return view_->challenge_response_view_->GetLabelForTesting(); |
| } |
| |
| bool LoginAuthUserView::TestApi::HasAuthMethod(AuthMethods auth_method) const { |
| return view_->HasAuthMethod(auth_method); |
| } |
| |
| const std::u16string& |
| LoginAuthUserView::TestApi::GetDisabledAuthMessageContent() const { |
| return LoginAuthUserView::DisabledAuthMessageView::TestApi( |
| view_->disabled_auth_message_) |
| .GetDisabledAuthMessageContent(); |
| } |
| |
| LoginAuthUserView::Callbacks::Callbacks() = default; |
| |
| LoginAuthUserView::Callbacks::Callbacks(const Callbacks& other) = default; |
| |
| LoginAuthUserView::Callbacks::~Callbacks() = default; |
| |
| LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user, |
| const Callbacks& callbacks) |
| : NonAccessibleView(kLoginAuthUserViewClassName), |
| on_auth_(callbacks.on_auth), |
| on_tap_(callbacks.on_tap) { |
| DCHECK(callbacks.on_auth); |
| DCHECK(callbacks.on_tap); |
| DCHECK(callbacks.on_remove_warning_shown); |
| DCHECK(callbacks.on_remove); |
| DCHECK(callbacks.on_easy_unlock_icon_hovered); |
| DCHECK_NE(user.basic_user_info.type, user_manager::USER_TYPE_PUBLIC_ACCOUNT); |
| |
| // Build child views. |
| auto user_view = std::make_unique<LoginUserView>( |
| LoginDisplayStyle::kLarge, true /*show_dropdown*/, |
| base::BindRepeating(&LoginAuthUserView::OnUserViewTap, |
| base::Unretained(this)), |
| callbacks.on_remove_warning_shown, callbacks.on_remove); |
| user_view_ = user_view.get(); |
| |
| const LoginPalette palette = CreateDefaultLoginPalette(); |
| |
| auto password_view = std::make_unique<LoginPasswordView>(palette); |
| password_view_ = password_view.get(); |
| password_view->SetPaintToLayer(); // Needed for opacity animation. |
| password_view->layer()->SetFillsBoundsOpaquely(false); |
| password_view_->SetDisplayPasswordButtonVisible( |
| user.show_display_password_button); |
| password_view->Init( |
| base::BindRepeating(&LoginAuthUserView::OnAuthSubmit, |
| base::Unretained(this)), |
| base::BindRepeating(&LoginAuthUserView::OnPasswordTextChanged, |
| base::Unretained(this)), |
| callbacks.on_easy_unlock_icon_hovered, |
| callbacks.on_easy_unlock_icon_tapped); |
| |
| auto pin_input_view = std::make_unique<LoginPinInputView>(palette); |
| pin_input_view_ = pin_input_view.get(); |
| pin_input_view->Init(base::BindRepeating(&LoginAuthUserView::OnAuthSubmit, |
| base::Unretained(this)), |
| base::BindRepeating(&LoginAuthUserView::OnPinTextChanged, |
| base::Unretained(this))); |
| |
| auto toggle_container = std::make_unique<NonAccessibleView>(); |
| toggle_container->SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical, |
| gfx::Insets(kPinPasswordToggleButtonPaddingTop, 0, 0, 0))); |
| pin_password_toggle_ = |
| toggle_container->AddChildView(std::make_unique<SystemLabelButton>( |
| base::BindRepeating(&LoginAuthUserView::OnSwitchButtonClicked, |
| base::Unretained(this)), |
| GetPinPasswordToggleText())); |
| pin_password_toggle_->SetMaxSize( |
| gfx::Size(/*ignored*/ 0, kPinPasswordToggleButtonHeight)); |
| |
| auto pin_view = std::make_unique<LoginPinView>( |
| LoginPinView::Style::kAlphanumeric, palette, |
| base::BindRepeating(&LoginAuthUserView::OnPinPadInsertDigit, |
| base::Unretained(this)), |
| base::BindRepeating(&LoginAuthUserView::OnPinPadBackspace, |
| base::Unretained(this))); |
| pin_view_ = pin_view.get(); |
| DCHECK(pin_view_->layer()); |
| |
| auto padding_below_password_view = std::make_unique<NonAccessibleView>(); |
| padding_below_password_view->SetPreferredSize(gfx::Size( |
| kNonEmptyWidthDp, kDistanceBetweenPasswordFieldAndPinKeyboardDp)); |
| padding_below_password_view_ = padding_below_password_view.get(); |
| |
| auto padding_below_user_view = std::make_unique<NonAccessibleView>(); |
| padding_below_user_view->SetPreferredSize( |
| gfx::Size(kNonEmptyWidthDp, kDistanceBetweenUserViewAndPasswordDp)); |
| padding_below_user_view_ = padding_below_user_view.get(); |
| |
| std::u16string button_message = |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_ONLINE_SIGN_IN_MESSAGE); |
| if (user.is_signed_in) { |
| button_message = |
| l10n_util::GetStringUTF16(IDS_ASH_LOCK_SCREEN_VERIFY_ACCOUNT_MESSAGE); |
| } |
| |
| auto online_sign_in_button = std::make_unique<SystemLabelButton>( |
| base::BindRepeating(&LoginAuthUserView::OnOnlineSignInMessageTap, |
| base::Unretained(this)), |
| button_message); |
| online_sign_in_button_ = online_sign_in_button.get(); |
| |
| auto disabled_auth_message = std::make_unique<DisabledAuthMessageView>(); |
| disabled_auth_message_ = disabled_auth_message.get(); |
| |
| auto locked_tpm_message_view = std::make_unique<LockedTpmMessageView>(); |
| locked_tpm_message_view_ = locked_tpm_message_view.get(); |
| |
| auto fingerprint_view = std::make_unique<FingerprintView>(); |
| fingerprint_view_ = fingerprint_view.get(); |
| |
| auto challenge_response_view = |
| std::make_unique<ChallengeResponseView>(base::BindRepeating( |
| &LoginAuthUserView::AttemptAuthenticateWithChallengeResponse, |
| weak_factory_.GetWeakPtr())); |
| challenge_response_view_ = challenge_response_view.get(); |
| |
| SetPaintToLayer(ui::LayerType::LAYER_NOT_DRAWN); |
| |
| // Build layout. |
| auto wrapped_password_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(password_view)); |
| auto wrapped_online_sign_in_message_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(online_sign_in_button)); |
| auto wrapped_disabled_auth_message_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(disabled_auth_message)); |
| auto wrapped_locked_tpm_message_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(locked_tpm_message_view)); |
| auto wrapped_user_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(user_view)); |
| auto wrapped_pin_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(pin_view)); |
| auto wrapped_pin_input_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(pin_input_view)); |
| auto wrapped_pin_password_toggle_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(toggle_container)); |
| auto wrapped_fingerprint_view = |
| login_views_utils::WrapViewForPreferredSize(std::move(fingerprint_view)); |
| auto wrapped_challenge_response_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(challenge_response_view)); |
| auto wrapped_padding_below_password_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(padding_below_password_view)); |
| auto wrapped_padding_below_user_view = |
| login_views_utils::WrapViewForPreferredSize( |
| std::move(padding_below_user_view)); |
| |
| // Add views in tabbing order; they are rendered in a different order below. |
| views::View* wrapped_password_view_ptr = |
| AddChildView(std::move(wrapped_password_view)); |
| views::View* wrapped_online_sign_in_message_view_ptr = |
| AddChildView(std::move(wrapped_online_sign_in_message_view)); |
| views::View* wrapped_disabled_auth_message_view_ptr = |
| AddChildView(std::move(wrapped_disabled_auth_message_view)); |
| views::View* wrapped_locked_tpm_message_view_ptr = |
| AddChildView(std::move(wrapped_locked_tpm_message_view)); |
| views::View* wrapped_pin_input_view_ptr = |
| AddChildView(std::move(wrapped_pin_input_view)); |
| views::View* wrapped_pin_view_ptr = AddChildView(std::move(wrapped_pin_view)); |
| views::View* wrapped_pin_password_toggle_view_ptr = |
| AddChildView(std::move(wrapped_pin_password_toggle_view)); |
| views::View* wrapped_fingerprint_view_ptr = |
| AddChildView(std::move(wrapped_fingerprint_view)); |
| views::View* wrapped_challenge_response_view_ptr = |
| AddChildView(std::move(wrapped_challenge_response_view)); |
| views::View* wrapped_user_view_ptr = |
| AddChildView(std::move(wrapped_user_view)); |
| views::View* wrapped_padding_below_password_view_ptr = |
| AddChildView(std::move(wrapped_padding_below_password_view)); |
| views::View* wrapped_padding_below_user_view_ptr = |
| AddChildView(std::move(wrapped_padding_below_user_view)); |
| |
| // Use views::GridLayout instead of views::BoxLayout because views::BoxLayout |
| // lays out children according to the view->children order. |
| views::GridLayout* grid_layout = |
| SetLayoutManager(std::make_unique<views::GridLayout>()); |
| views::ColumnSet* column_set = grid_layout->AddColumnSet(0); |
| column_set->AddColumn(views::GridLayout::CENTER, views::GridLayout::LEADING, |
| 0 /*resize_percent*/, |
| views::GridLayout::ColumnSize::kUsePreferred, |
| 0 /*fixed_width*/, 0 /*min_width*/); |
| auto add_view = [&](views::View* view) { |
| grid_layout->StartRow(0 /*vertical_resize*/, 0 /*column_set_id*/); |
| grid_layout->AddExistingView(view); |
| }; |
| auto add_padding = [&](int amount) { |
| grid_layout->AddPaddingRow(0 /*vertical_resize*/, amount /*size*/); |
| }; |
| |
| // Add views in rendering order. |
| add_padding(kDistanceFromTopOfBigUserViewToUserIconDp); |
| add_view(wrapped_user_view_ptr); |
| add_view(wrapped_padding_below_user_view_ptr); |
| add_view(wrapped_locked_tpm_message_view_ptr); |
| add_view(wrapped_password_view_ptr); |
| add_view(wrapped_online_sign_in_message_view_ptr); |
| add_view(wrapped_disabled_auth_message_view_ptr); |
| add_view(wrapped_pin_input_view_ptr); |
| add_view(wrapped_padding_below_password_view_ptr); |
| add_view(wrapped_pin_view_ptr); |
| add_view(wrapped_pin_password_toggle_view_ptr); |
| add_view(wrapped_fingerprint_view_ptr); |
| add_view(wrapped_challenge_response_view_ptr); |
| add_padding(kDistanceFromPinKeyboardToBigUserViewBottomDp); |
| |
| // The user needs to be set before SetAuthMethods is called. |
| user_view_->UpdateForUser(user, /*animate*/ false); |
| |
| // Update authentication UI. |
| CaptureStateForAnimationPreLayout(); |
| SetAuthMethods(auth_methods_); |
| ApplyAnimationPostLayout(/*animate*/ false); |
| } |
| |
| LoginAuthUserView::~LoginAuthUserView() = default; |
| |
| void LoginAuthUserView::SetAuthMethods( |
| uint32_t auth_methods, |
| const AuthMethodsMetadata& auth_metadata) { |
| // It is an error to call this method without storing the previous state. |
| DCHECK(previous_state_); |
| |
| // Apply changes and determine the new state of input fields. |
| auth_methods_ = static_cast<AuthMethods>(auth_methods); |
| auth_metadata_ = auth_metadata; |
| UpdateInputFieldMode(); |
| const UiState current_state{this}; |
| |
| auto user = current_user(); |
| const std::u16string user_display_email = |
| base::UTF8ToUTF16(user.basic_user_info.display_email); |
| bool is_secondary_login = |
| Shell::Get()->session_controller()->GetSessionState() == |
| session_manager::SessionState::LOGIN_SECONDARY; |
| |
| if (is_secondary_login && !user.is_multiprofile_allowed) { |
| online_sign_in_button_->SetVisible(false); |
| disabled_auth_message_->SetVisible(true); |
| disabled_auth_message_->SetAuthDisabledMessage( |
| l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_MULTI_PROFILES_RESTRICTED_POLICY_TITLE), |
| GetMultiprofileDisableAuthMessage()); |
| } |
| // We do not want the online sign in button to be visible on the secondary |
| // login screen since we can not call OOBE there. In such a case, we indicate |
| // the user to return on the login screen to go through online sign in. |
| else if (is_secondary_login && current_state.force_online_sign_in) { |
| online_sign_in_button_->SetVisible(false); |
| disabled_auth_message_->SetVisible(true); |
| disabled_auth_message_->SetAuthDisabledMessage( |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SIGN_IN_REQUIRED_DIALOG_TITLE), |
| l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_SIGN_IN_REQUIRED_SECONDARY_LOGIN_MESSAGE, |
| user_display_email, user_display_email)); |
| } else { |
| online_sign_in_button_->SetVisible(current_state.force_online_sign_in); |
| disabled_auth_message_->SetVisible(current_state.auth_disabled); |
| } |
| |
| locked_tpm_message_view_->SetVisible(current_state.tpm_is_locked); |
| if (current_state.tpm_is_locked && |
| auth_metadata.time_until_tpm_unlock.has_value()) |
| locked_tpm_message_view_->SetRemainingTime( |
| auth_metadata.time_until_tpm_unlock.value()); |
| |
| // Adjust the PIN keyboard visibility before the password textfield's one, so |
| // that when both are about to be hidden the focus doesn't jump to the "1" |
| // keyboard button, causing unexpected accessibility effects. |
| pin_view_->SetVisible(current_state.has_pinpad); |
| |
| password_view_->SetEnabled(current_state.has_password); |
| password_view_->SetEnabledOnEmptyPassword(HasAuthMethod(AUTH_TAP)); |
| password_view_->SetFocusEnabledForTextfield(current_state.has_password); |
| password_view_->SetVisible(current_state.has_password); |
| password_view_->layer()->SetOpacity(current_state.has_password); |
| |
| pin_input_view_->UpdateLength(auth_metadata_.autosubmit_pin_length); |
| pin_input_view_->SetAuthenticateWithEmptyPinOnReturnKey( |
| HasAuthMethod(AUTH_TAP)); |
| pin_input_view_->SetVisible(current_state.has_pin_input); |
| |
| pin_password_toggle_->SetVisible(current_state.has_toggle); |
| pin_password_toggle_->SetText(GetPinPasswordToggleText()); |
| |
| fingerprint_view_->SetVisible(current_state.has_fingerprint); |
| fingerprint_view_->SetCanUsePin(HasAuthMethod(AUTH_PIN)); |
| challenge_response_view_->SetVisible(current_state.has_challenge_response); |
| |
| padding_below_user_view_->SetPreferredSize(GetPaddingBelowUserView()); |
| padding_below_password_view_->SetPreferredSize(GetPaddingBelowPasswordView()); |
| |
| password_view_->SetPlaceholderText(GetPasswordViewPlaceholder()); |
| password_view_->SetAccessibleName(l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME, user_display_email)); |
| |
| // Only the active auth user view has authentication methods. If that is the |
| // case, then render the user view as if it was always focused, since clicking |
| // on it will not do anything (such as swapping users). |
| user_view_->SetForceOpaque(auth_methods_ != AUTH_NONE); |
| user_view_->SetTapEnabled(auth_methods_ == AUTH_NONE); |
| |
| UpdateFocus(); |
| PreferredSizeChanged(); |
| } |
| |
| void LoginAuthUserView::SetEasyUnlockIcon( |
| EasyUnlockIconState icon_state, |
| const std::u16string& accessibility_label) { |
| password_view_->SetEasyUnlockIcon(icon_state, accessibility_label); |
| |
| const std::string& user_display_email = |
| current_user().basic_user_info.display_email; |
| if (icon_state == EasyUnlockIconState::UNLOCKED) { |
| password_view_->SetAccessibleName(l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_POD_AUTH_TAP_PASSWORD_FIELD_ACCESSIBLE_NAME, |
| base::UTF8ToUTF16(user_display_email))); |
| } else { |
| password_view_->SetAccessibleName(l10n_util::GetStringFUTF16( |
| IDS_ASH_LOGIN_POD_PASSWORD_FIELD_ACCESSIBLE_NAME, |
| base::UTF8ToUTF16(user_display_email))); |
| } |
| } |
| |
| void LoginAuthUserView::CaptureStateForAnimationPreLayout() { |
| auto stop_animation = [](views::View* view) { |
| if (view->layer()->GetAnimator()->is_animating()) |
| view->layer()->GetAnimator()->StopAnimating(); |
| }; |
| |
| // Stop any running animation scheduled in ApplyAnimationPostLayout. |
| stop_animation(this); |
| stop_animation(password_view_); |
| stop_animation(pin_view_); |
| stop_animation(fingerprint_view_); |
| stop_animation(challenge_response_view_); |
| stop_animation(pin_password_toggle_); |
| |
| DCHECK(!previous_state_); |
| previous_state_ = std::make_unique<UiState>(this); |
| } |
| |
| void LoginAuthUserView::ApplyAnimationPostLayout(bool animate) { |
| DCHECK(previous_state_); |
| // Release the previous state if no animation should be performed. |
| if (!animate) { |
| previous_state_.reset(); |
| return; |
| } |
| |
| const UiState current_state{this}; |
| |
| //////// |
| // Animate the user info (ie, icon, name) up or down the screen. |
| { |
| int non_pin_y_end_in_screen = GetBoundsInScreen().y(); |
| |
| // Transform the layer so the user view renders where it used to be. This |
| // requires a y offset. |
| // Note: Doing this animation via ui::ScopedLayerAnimationSettings works, |
| // but it seems that the timing gets slightly out of sync with the PIN |
| // animation. |
| auto move_to_center = std::make_unique<ui::InterpolatedTranslation>( |
| gfx::PointF(0, previous_state_->non_pin_y_start_in_screen - |
| non_pin_y_end_in_screen), |
| gfx::PointF()); |
| auto transition = |
| ui::LayerAnimationElement::CreateInterpolatedTransformElement( |
| std::move(move_to_center), |
| base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs)); |
| transition->set_tween_type(gfx::Tween::Type::FAST_OUT_SLOW_IN); |
| auto* sequence = new ui::LayerAnimationSequence(std::move(transition)); |
| auto* observer = BuildObserverToNotifyA11yLocationChanged(this); |
| sequence->AddObserver(observer); |
| observer->SetActive(); |
| layer()->GetAnimator()->StartAnimation(sequence); |
| } |
| |
| //////// |
| // Fade the password view if it is being hidden or shown. |
| |
| if (current_state.has_password != previous_state_->has_password) { |
| float opacity_start = 0, opacity_end = 1; |
| if (!current_state.has_password) |
| std::swap(opacity_start, opacity_end); |
| |
| password_view_->layer()->SetOpacity(opacity_start); |
| |
| { |
| ui::ScopedLayerAnimationSettings settings( |
| password_view_->layer()->GetAnimator()); |
| settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs)); |
| settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN); |
| if (previous_state_->has_password && !current_state.has_password) { |
| settings.AddObserver( |
| new ClearPasswordAndHideAnimationObserver(password_view_)); |
| } |
| |
| password_view_->layer()->SetOpacity(opacity_end); |
| } |
| } |
| |
| //////// |
| // Fade the pin/pwd toggle if its being hidden or shown. |
| if (previous_state_->has_toggle != current_state.has_toggle) { |
| float opacity_start = 0, opacity_end = 1; |
| if (!current_state.has_toggle) |
| std::swap(opacity_start, opacity_end); |
| |
| pin_password_toggle_->layer()->SetOpacity(opacity_start); |
| |
| { |
| ui::ScopedLayerAnimationSettings settings( |
| pin_password_toggle_->layer()->GetAnimator()); |
| settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs)); |
| settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN); |
| pin_password_toggle_->layer()->SetOpacity(opacity_end); |
| } |
| } |
| |
| //////// |
| // Grow/shrink the PIN keyboard if it is being hidden or shown. |
| |
| if (previous_state_->has_pinpad != current_state.has_pinpad) { |
| if (!current_state.has_pinpad) { |
| gfx::Point pin_end_in_screen = pin_view_->GetBoundsInScreen().origin(); |
| gfx::Rect pin_bounds = pin_view_->bounds(); |
| pin_bounds.set_x(previous_state_->pin_start_in_screen.x() - |
| pin_end_in_screen.x()); |
| pin_bounds.set_y(previous_state_->pin_start_in_screen.y() - |
| pin_end_in_screen.y()); |
| |
| // Since PIN is disabled, the previous Layout() hid the PIN keyboard. |
| // We need to redisplay it where it used to be. |
| // pin_view_->SetVisible(true); |
| pin_view_->SetBoundsRect(pin_bounds); |
| } |
| |
| auto transition = std::make_unique<PinKeyboardAnimation>( |
| current_state.has_pinpad /*grow*/, pin_view_->height(), |
| // TODO(https://crbug.com/955119): Implement proper animation. |
| base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs / 2.0f), |
| gfx::Tween::FAST_OUT_SLOW_IN); |
| auto* sequence = new ui::LayerAnimationSequence(std::move(transition)); |
| |
| // Hide the PIN keyboard after animation if needed. |
| if (!current_state.has_pinpad) { |
| auto* observer = BuildObserverToHideView(pin_view_); |
| sequence->AddObserver(observer); |
| observer->SetActive(); |
| } |
| auto* observer = BuildObserverToNotifyA11yLocationChanged(pin_view_); |
| sequence->AddObserver(observer); |
| observer->SetActive(); |
| pin_view_->layer()->GetAnimator()->ScheduleAnimation(sequence); |
| } |
| |
| //////// |
| // Fade the fingerprint view if it is being hidden or shown. |
| |
| if (previous_state_->has_fingerprint != current_state.has_fingerprint) { |
| float opacity_start = 0, opacity_end = 1; |
| if (!current_state.has_fingerprint) |
| std::swap(opacity_start, opacity_end); |
| |
| fingerprint_view_->layer()->SetOpacity(opacity_start); |
| |
| { |
| ui::ScopedLayerAnimationSettings settings( |
| fingerprint_view_->layer()->GetAnimator()); |
| settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs)); |
| settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN); |
| fingerprint_view_->layer()->SetOpacity(opacity_end); |
| } |
| } |
| |
| //////// |
| // Fade the challenge response (Smart Card) if it is being hidden or shown. |
| if (previous_state_->has_challenge_response != |
| current_state.has_challenge_response) { |
| float opacity_start = 0, opacity_end = 1; |
| if (!current_state.has_challenge_response) |
| std::swap(opacity_start, opacity_end); |
| |
| challenge_response_view_->layer()->SetOpacity(opacity_start); |
| |
| { |
| ui::ScopedLayerAnimationSettings settings( |
| challenge_response_view_->layer()->GetAnimator()); |
| settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
| login::kChangeUserAnimationDurationMs)); |
| settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN); |
| challenge_response_view_->layer()->SetOpacity(opacity_end); |
| } |
| } |
| |
| previous_state_.reset(); |
| } |
| |
| void LoginAuthUserView::UpdateForUser(const LoginUserInfo& user) { |
| const bool user_changed = current_user().basic_user_info.account_id != |
| user.basic_user_info.account_id; |
| user_view_->UpdateForUser(user, true /*animate*/); |
| if (user_changed) { |
| password_view_->Reset(); |
| password_view_->SetDisplayPasswordButtonVisible( |
| user.show_display_password_button); |
| } |
| online_sign_in_button_->SetText( |
| l10n_util::GetStringUTF16(IDS_ASH_LOGIN_ONLINE_SIGN_IN_MESSAGE)); |
| } |
| |
| void LoginAuthUserView::SetFingerprintState(FingerprintState state) { |
| fingerprint_view_->SetState(state); |
| } |
| |
| void LoginAuthUserView::NotifyFingerprintAuthResult(bool success) { |
| fingerprint_view_->NotifyFingerprintAuthResult(success); |
| } |
| |
| void LoginAuthUserView::SetAuthDisabledMessage( |
| const AuthDisabledData& auth_disabled_data) { |
| disabled_auth_message_->SetAuthDisabledMessage( |
| auth_disabled_data, current_user().use_24hour_clock); |
| } |
| |
| const LoginUserInfo& LoginAuthUserView::current_user() const { |
| return user_view_->current_user(); |
| } |
| |
| views::View* LoginAuthUserView::GetActiveInputView() { |
| if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| return pin_input_view_; |
| |
| return password_view_; |
| } |
| |
| gfx::Size LoginAuthUserView::CalculatePreferredSize() const { |
| gfx::Size size = views::View::CalculatePreferredSize(); |
| // Make sure we are at least as big as the user view. If we do not do this the |
| // view will be below minimum size when no auth methods are displayed. |
| size.SetToMax(user_view_->GetPreferredSize()); |
| return size; |
| } |
| |
| void LoginAuthUserView::RequestFocus() { |
| if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| pin_input_view_->RequestFocus(); |
| else if (password_view_->GetEnabled()) |
| RequestFocusOnPasswordView(); |
| } |
| |
| void LoginAuthUserView::OnGestureEvent(ui::GestureEvent* event) { |
| // The textfield area may be too small for the user to successfully tap |
| // inside it at their first attempt. We also want to bring up the virtual |
| // keyboard if the user taps in the auth user view area. |
| RequestFocus(); |
| } |
| |
| void LoginAuthUserView::OnThemeChanged() { |
| NonAccessibleView::OnThemeChanged(); |
| const LoginPalette palette = CreateDefaultLoginPalette(); |
| password_view_->UpdatePalette(palette); |
| pin_input_view_->UpdatePalette(palette); |
| pin_view_->UpdatePalette(palette); |
| } |
| |
| void LoginAuthUserView::OnAuthSubmit(const std::u16string& password) { |
| // Pressing enter when the password field is empty and tap-to-unlock is |
| // enabled should attempt unlock. |
| if (HasAuthMethod(AUTH_TAP) && password.empty()) { |
| Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock( |
| current_user().basic_user_info.account_id); |
| return; |
| } |
| |
| password_view_->SetReadOnly(true); |
| pin_input_view_->SetReadOnly(true); |
| |
| Shell::Get()->login_screen_controller()->AuthenticateUserWithPasswordOrPin( |
| current_user().basic_user_info.account_id, base::UTF16ToUTF8(password), |
| ShouldAuthenticateWithPin(), |
| base::BindOnce(&LoginAuthUserView::OnAuthComplete, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void LoginAuthUserView::OnAuthComplete(absl::optional<bool> auth_success) { |
| // Clear the password only if auth fails. Make sure to keep the password view |
| // disabled even if auth succeededs, as if the user submits a password while |
| // animating the next lock screen will not work as expected. See |
| // https://crbug.com/808486. |
| if (!auth_success.has_value() || !auth_success.value()) { |
| password_view_->Reset(); |
| password_view_->SetReadOnly(false); |
| pin_input_view_->Reset(); |
| pin_input_view_->SetReadOnly(false); |
| } |
| |
| on_auth_.Run(auth_success.value(), /*display_error_messages=*/true); |
| } |
| |
| void LoginAuthUserView::OnChallengeResponseAuthComplete( |
| absl::optional<bool> auth_success) { |
| if (!auth_success.has_value() || !auth_success.value()) { |
| password_view_->Reset(); |
| password_view_->SetReadOnly(false); |
| // If the user canceled the PIN request during ChallengeResponse, |
| // ChallengeResponse will fail with an unknown error. Since this is |
| // expected, we do not show this error. |
| if (Shell::Get() |
| ->login_screen_controller() |
| ->GetSecurityTokenPinRequestCanceled()) { |
| challenge_response_view_->SetState( |
| ChallengeResponseView::State::kInitial); |
| } else { |
| challenge_response_view_->SetState( |
| ChallengeResponseView::State::kFailure); |
| } |
| } |
| |
| on_auth_.Run(auth_success.value_or(false), /*display_error_messages=*/false); |
| } |
| |
| void LoginAuthUserView::OnUserViewTap() { |
| if (HasAuthMethod(AUTH_TAP)) { |
| Shell::Get()->login_screen_controller()->AuthenticateUserWithEasyUnlock( |
| current_user().basic_user_info.account_id); |
| } else if (HasAuthMethod(AUTH_ONLINE_SIGN_IN)) { |
| // Tapping anywhere in the user view is the same with tapping the message. |
| OnOnlineSignInMessageTap(); |
| } else { |
| on_tap_.Run(); |
| } |
| } |
| |
| void LoginAuthUserView::OnOnlineSignInMessageTap() { |
| // Do not show on secondary login screen as there is no OOBE there. |
| if (Shell::Get()->session_controller()->GetSessionState() == |
| session_manager::SessionState::LOGIN_SECONDARY) { |
| return; |
| } |
| Shell::Get()->login_screen_controller()->ShowGaiaSignin( |
| current_user().basic_user_info.account_id); |
| } |
| |
| void LoginAuthUserView::OnPinPadBackspace() { |
| DCHECK(pin_input_view_); |
| DCHECK(password_view_); |
| if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| pin_input_view_->Backspace(); |
| else |
| password_view_->Backspace(); |
| } |
| |
| void LoginAuthUserView::OnPinPadInsertDigit(int digit) { |
| DCHECK(pin_input_view_); |
| DCHECK(password_view_); |
| if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| pin_input_view_->InsertDigit(digit); |
| else |
| password_view_->InsertNumber(digit); |
| } |
| |
| void LoginAuthUserView::OnPasswordTextChanged(bool is_empty) { |
| DCHECK(pin_view_); |
| if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE) |
| pin_view_->OnPasswordTextChanged(is_empty); |
| } |
| |
| void LoginAuthUserView::OnPinTextChanged(bool is_empty) { |
| DCHECK(pin_view_); |
| if (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| pin_view_->OnPasswordTextChanged(is_empty); |
| } |
| |
| bool LoginAuthUserView::HasAuthMethod(AuthMethods auth_method) const { |
| return (auth_methods_ & auth_method) != 0; |
| } |
| |
| bool LoginAuthUserView::ShouldAuthenticateWithPin() const { |
| return input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD || |
| input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE; |
| } |
| |
| void LoginAuthUserView::AttemptAuthenticateWithChallengeResponse() { |
| challenge_response_view_->SetState( |
| ChallengeResponseView::State::kAuthenticating); |
| Shell::Get() |
| ->login_screen_controller() |
| ->AuthenticateUserWithChallengeResponse( |
| current_user().basic_user_info.account_id, |
| base::BindOnce(&LoginAuthUserView::OnChallengeResponseAuthComplete, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void LoginAuthUserView::RequestFocusOnPasswordView() { |
| password_view_->RequestFocus(); |
| |
| const UiState current_state{this}; |
| // Bring up the virtual keyboard if enabled as soon as we get the focus. |
| // This way, the user does not have to type twice (on the user pod and |
| // additionally on the textfield or user view). |
| if (GetInputMethod() && !current_state.has_pinpad) |
| GetInputMethod()->ShowVirtualKeyboardIfEnabled(); |
| } |
| |
| void LoginAuthUserView::UpdateFocus() { |
| DCHECK(previous_state_); |
| const UiState current_state{this}; |
| |
| if (current_state.tpm_is_locked) { |
| locked_tpm_message_view_->RequestFocus(); |
| return; |
| } |
| // All further states are exclusive. |
| if (current_state.auth_disabled) |
| disabled_auth_message_->RequestFocus(); |
| if (current_state.has_challenge_response) |
| challenge_response_view_->RequestFocus(); |
| if (current_state.has_password && !previous_state_->has_password) |
| RequestFocusOnPasswordView(); |
| if (current_state.has_pin_input) |
| pin_input_view_->RequestFocus(); |
| // Tapping the user view will trigger the online sign-in flow when |
| // |force_online_sign_in| is true. |
| if (current_state.force_online_sign_in) |
| user_view_->RequestFocus(); |
| } |
| |
| void LoginAuthUserView::OnSwitchButtonClicked() { |
| // Ignore events from the switch button if no longer present. |
| if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE && |
| input_field_mode_ != InputFieldMode::PWD_WITH_TOGGLE) { |
| return; |
| } |
| |
| // Clear both input fields. |
| password_view_->Reset(); |
| pin_input_view_->Reset(); |
| // Cache the current state of the UI. |
| CaptureStateForAnimationPreLayout(); |
| // Same auth methods, but the input field mode has changed. |
| input_field_mode_ = (input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE) |
| ? InputFieldMode::PWD_WITH_TOGGLE |
| : InputFieldMode::PIN_WITH_TOGGLE; |
| SetAuthMethods(auth_methods_, auth_metadata_); |
| // Layout and animate. |
| Layout(); |
| ApplyAnimationPostLayout(/*animate*/ true); |
| } |
| |
| void LoginAuthUserView::UpdateInputFieldMode() { |
| // There isn't an input field when any of the following is true: |
| // - Challenge response is active (Smart Card) |
| // - Online sign in message shown |
| // - Disabled message shown |
| // - No password auth available |
| if (HasAuthMethod(AUTH_CHALLENGE_RESPONSE) || |
| HasAuthMethod(AUTH_ONLINE_SIGN_IN) || |
| HasAuthMethod(AUTH_DISABLED) || |
| !HasAuthMethod(AUTH_PASSWORD)) { |
| input_field_mode_ = InputFieldMode::NONE; |
| return; |
| } |
| |
| if (!HasAuthMethod(AUTH_PIN)) { |
| input_field_mode_ = InputFieldMode::PASSWORD_ONLY; |
| return; |
| } |
| |
| // Default to combined password/pin if autosubmit is disabled. |
| const int pin_length = auth_metadata_.autosubmit_pin_length; |
| if (!LoginPinInputView::IsAutosubmitSupported(pin_length)) { |
| input_field_mode_ = InputFieldMode::PIN_AND_PASSWORD; |
| return; |
| } |
| |
| // Defaults to PIN + switch button if not showing the switch button already. |
| if (input_field_mode_ != InputFieldMode::PIN_WITH_TOGGLE && |
| input_field_mode_ != InputFieldMode::PWD_WITH_TOGGLE) { |
| input_field_mode_ = InputFieldMode::PIN_WITH_TOGGLE; |
| return; |
| } |
| } |
| |
| bool LoginAuthUserView::ShouldShowPinPad() const { |
| if (auth_metadata_.virtual_keyboard_visible) |
| return false; |
| switch (input_field_mode_) { |
| case InputFieldMode::NONE: |
| return false; |
| case InputFieldMode::PASSWORD_ONLY: |
| case InputFieldMode::PWD_WITH_TOGGLE: |
| return auth_metadata_.show_pinpad_for_pw; |
| case InputFieldMode::PIN_AND_PASSWORD: |
| case InputFieldMode::PIN_WITH_TOGGLE: |
| return true; |
| } |
| } |
| |
| bool LoginAuthUserView::ShouldShowPasswordField() const { |
| return input_field_mode_ == InputFieldMode::PASSWORD_ONLY || |
| input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD || |
| input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE; |
| } |
| |
| bool LoginAuthUserView::ShouldShowPinInputField() const { |
| return input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE; |
| } |
| |
| bool LoginAuthUserView::ShouldShowToggle() const { |
| return input_field_mode_ == InputFieldMode::PIN_WITH_TOGGLE || |
| input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE; |
| } |
| |
| gfx::Size LoginAuthUserView::GetPaddingBelowUserView() const { |
| const UiState state{this}; |
| |
| if (state.has_password) |
| return SizeFromHeight(kDistanceBetweenUserViewAndPasswordDp); |
| if (state.has_pin_input) |
| return SizeFromHeight(kDistanceBetweenUserViewAndPinInputDp); |
| if (state.force_online_sign_in) |
| return SizeFromHeight(kDistanceBetweenUserViewAndOnlineSigninDp); |
| if (state.has_challenge_response) |
| return SizeFromHeight(kDistanceBetweenUserViewAndChallengeResponseDp); |
| |
| return SizeFromHeight(0); |
| } |
| |
| gfx::Size LoginAuthUserView::GetPaddingBelowPasswordView() const { |
| const UiState state{this}; |
| |
| if (state.has_pinpad) |
| return SizeFromHeight(kDistanceBetweenPasswordFieldAndPinKeyboardDp); |
| if (state.has_fingerprint) |
| return SizeFromHeight(kDistanceBetweenPasswordFieldAndFingerprintViewDp); |
| if (state.has_challenge_response) |
| return SizeFromHeight(kDistanceBetweenPwdFieldAndChallengeResponseViewDp); |
| |
| return SizeFromHeight(0); |
| } |
| |
| std::u16string LoginAuthUserView::GetPinPasswordToggleText() const { |
| if (input_field_mode_ == InputFieldMode::PWD_WITH_TOGGLE) |
| return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SWITCH_TO_PIN); |
| else |
| return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_SWITCH_TO_PASSWORD); |
| } |
| |
| std::u16string LoginAuthUserView::GetPasswordViewPlaceholder() const { |
| // Note: |AUTH_TAP| must have higher priority than |AUTH_PIN| when |
| // determining the placeholder. |
| if (HasAuthMethod(AUTH_TAP)) |
| return l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER); |
| if (input_field_mode_ == InputFieldMode::PIN_AND_PASSWORD) |
| return l10n_util::GetStringUTF16( |
| IDS_ASH_LOGIN_POD_PASSWORD_PIN_PLACEHOLDER); |
| |
| return l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PLACEHOLDER); |
| } |
| |
| std::u16string LoginAuthUserView::GetMultiprofileDisableAuthMessage() const { |
| int message_id; |
| switch (current_user().multiprofile_policy) { |
| case MultiProfileUserBehavior::PRIMARY_ONLY: |
| message_id = IDS_ASH_LOGIN_MULTI_PROFILES_PRIMARY_ONLY_POLICY_MSG; |
| break; |
| case MultiProfileUserBehavior::NOT_ALLOWED: |
| message_id = IDS_ASH_LOGIN_MULTI_PROFILES_NOT_ALLOWED_POLICY_MSG; |
| break; |
| case MultiProfileUserBehavior::OWNER_PRIMARY_ONLY: |
| message_id = IDS_ASH_LOGIN_MULTI_PROFILES_OWNER_PRIMARY_ONLY_MSG; |
| break; |
| case MultiProfileUserBehavior::UNRESTRICTED: |
| NOTREACHED(); |
| message_id = 0; |
| break; |
| } |
| return l10n_util::GetStringUTF16(message_id); |
| } |
| |
| } // namespace ash |