[High5] Add pin delay message on the login screen
Screenshot: https://screenshot.googleplex.com/3qTaoPtM59yRY5m Bug: b:357606198, b:348326316 Change-Id: I9e725d561ae3bba216846c98b1f0b9ea71a41491 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5822817 Reviewed-by: Denis Kuznetsov <antrim@chromium.org> Commit-Queue: Hardik Goyal <hardikgoyal@chromium.org> Cr-Commit-Position: refs/heads/main@{#1351445}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
829fb808f7
commit
5fa96ceb9a
ash
@ -979,6 +979,8 @@ component("ash") {
|
||||
"login/ui/pin_request_view.h",
|
||||
"login/ui/pin_request_widget.cc",
|
||||
"login/ui/pin_request_widget.h",
|
||||
"login/ui/pin_status_message_view.cc",
|
||||
"login/ui/pin_status_message_view.h",
|
||||
"login/ui/public_account_menu_view.cc",
|
||||
"login/ui/public_account_menu_view.h",
|
||||
"login/ui/public_account_monitoring_info_dialog.cc",
|
||||
|
@ -5224,6 +5224,9 @@ No devices connected.
|
||||
<message name="IDS_ASH_LOGIN_POD_TPM_LOCKED_ISSUE_DESCRIPTION" desc="Message shown as part of the TPM locked warning bubble when TPM is locked.">
|
||||
Your Chromebook needs to stay on and connected to power during this time. Make sure the charger or adapter cables are completely plugged in, both to your Chromebook and the power outlet. Do not turn off your Chromebook.
|
||||
</message>
|
||||
<message name="IDS_ASH_LOGIN_POD_PIN_LOCKED_WARNING" desc="Message shown when pin is soft locked due to multiple wrong attempts.">
|
||||
Too many PIN attempts. Wait <ph name="TIME_LEFT">$1<ex>15 minutes, 10 seconds</ex></ph> and try PIN again, or enter password to sign in immediately.
|
||||
</message>
|
||||
|
||||
<!-- Authentication strings -->
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
ec6827976a596e4c596cb3cf23154fbdbe423ec6
|
@ -2176,6 +2176,8 @@ std::unique_ptr<LoginBigUserView> LockContentsView::AllocateLoginBigUserView(
|
||||
base::BindRepeating(
|
||||
&LockContentsView::OnAuthFactorIsHidingPasswordChanged,
|
||||
base::Unretained(this), user.basic_user_info.account_id);
|
||||
auth_user_callbacks.on_pin_unlock = base::BindRepeating(
|
||||
&LockContentsView::OnPinUnlock, base::Unretained(this), is_primary);
|
||||
|
||||
LoginPublicAccountUserView::Callbacks public_account_callbacks;
|
||||
public_account_callbacks.on_tap = auth_user_callbacks.on_tap;
|
||||
@ -2410,6 +2412,15 @@ void LockContentsView::RecordAndResetPasswordAttempts(
|
||||
unlock_attempt_by_user_[account_id] = 0;
|
||||
}
|
||||
|
||||
void LockContentsView::OnPinUnlock(bool is_primary) {
|
||||
LoginBigUserView* to_update =
|
||||
is_primary ? primary_big_view_.get() : opt_secondary_big_view_.get();
|
||||
AccountId user = to_update->GetCurrentUser().basic_user_info.account_id;
|
||||
data_dispatcher_->SetPinEnabledForUser(user, true,
|
||||
/*avaiable_at*/ std::nullopt);
|
||||
HideAuthErrorMessage();
|
||||
}
|
||||
|
||||
BEGIN_METADATA(LockContentsView)
|
||||
END_METADATA
|
||||
|
||||
|
@ -410,6 +410,10 @@ class ASH_EXPORT LockContentsView
|
||||
// Updates the layout with the new users list.
|
||||
void ApplyUserChanges(const std::vector<LoginUserInfo>& users);
|
||||
|
||||
// Shows the pin auth and hides the pin delay message on the user pod when pin
|
||||
// becomes available after being soft-locked.
|
||||
void OnPinUnlock(bool is_primary);
|
||||
|
||||
const LockScreen::ScreenType screen_type_;
|
||||
|
||||
std::vector<UserState> users_;
|
||||
|
@ -3575,4 +3575,76 @@ TEST_F(LockContentsViewUnitTest, LoginToolTipViewAccessibleProperties) {
|
||||
EXPECT_EQ(data.role, ax::mojom::Role::kTooltip);
|
||||
}
|
||||
|
||||
class LockContentsViewPinTimeoutUnitTest : public LockContentsViewUnitTest {
|
||||
public:
|
||||
LockContentsViewPinTimeoutUnitTest() {
|
||||
scoped_feature_list_.InitAndEnableFeature(features::kAllowPinTimeoutSetup);
|
||||
}
|
||||
|
||||
void AdvanceClock(base::TimeDelta time_delta) {
|
||||
task_environment()->AdvanceClock(time_delta);
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
std::u16string GetExpectedPinStatusMessage(
|
||||
const std::u16string& time_string) {
|
||||
return l10n_util::GetStringFUTF16(IDS_ASH_LOGIN_POD_PIN_LOCKED_WARNING,
|
||||
time_string);
|
||||
}
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
TEST_F(LockContentsViewPinTimeoutUnitTest, PinDelayMessageCorrectness) {
|
||||
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
|
||||
LockContentsView* contents =
|
||||
LockScreen::TestApi(LockScreen::Get()).contents_view();
|
||||
|
||||
AddUsers(1);
|
||||
|
||||
LoginBigUserView* big_view =
|
||||
LockContentsViewTestApi(contents).primary_big_view();
|
||||
LoginAuthUserView::TestApi auth_test_api(big_view->auth_user());
|
||||
LoginPinView* pin_view = auth_test_api.pin_view();
|
||||
PinStatusMessageView* pin_status_message_view =
|
||||
auth_test_api.pin_status_message_view();
|
||||
|
||||
AccountId account_id = big_view->GetCurrentUser().basic_user_info.account_id;
|
||||
contents->OnPinEnabledForUserChanged(account_id, true,
|
||||
/*available_at*/ std::nullopt);
|
||||
|
||||
EXPECT_TRUE(pin_view->GetVisible());
|
||||
EXPECT_FALSE(pin_status_message_view->GetVisible());
|
||||
|
||||
// Lock pin for 2 hours.
|
||||
contents->OnPinEnabledForUserChanged(
|
||||
account_id, false,
|
||||
/*available_at*/ base::Time::Now() + base::Hours(2));
|
||||
|
||||
// Remaining 1:59:00.
|
||||
AdvanceClock(base::Minutes(1));
|
||||
EXPECT_FALSE(pin_view->GetVisible());
|
||||
EXPECT_TRUE(pin_status_message_view->GetVisible());
|
||||
EXPECT_EQ(GetExpectedPinStatusMessage(u"1 hour, 59 minutes"),
|
||||
auth_test_api.GetPinStatusMessageContent());
|
||||
|
||||
// Remaining 1:00:00.
|
||||
AdvanceClock(base::Minutes(59));
|
||||
EXPECT_TRUE(pin_status_message_view->GetVisible());
|
||||
EXPECT_EQ(GetExpectedPinStatusMessage(u"1 hour, 0 minutes"),
|
||||
auth_test_api.GetPinStatusMessageContent());
|
||||
|
||||
// Remaining 0:59:30.
|
||||
AdvanceClock(base::Seconds(30));
|
||||
EXPECT_TRUE(pin_status_message_view->GetVisible());
|
||||
EXPECT_EQ(GetExpectedPinStatusMessage(u"59 minutes, 30 seconds"),
|
||||
auth_test_api.GetPinStatusMessageContent());
|
||||
|
||||
// Pin becomes available again.
|
||||
AdvanceClock(base::Hours(1));
|
||||
EXPECT_TRUE(pin_view->GetVisible());
|
||||
EXPECT_FALSE(pin_status_message_view->GetVisible());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -467,6 +467,11 @@ AuthFactorModel* LoginAuthUserView::TestApi::smart_lock_auth_factor_model()
|
||||
return view_->smart_lock_auth_factor_model_;
|
||||
}
|
||||
|
||||
PinStatusMessageView* LoginAuthUserView::TestApi::pin_status_message_view()
|
||||
const {
|
||||
return view_->pin_status_message_view_;
|
||||
}
|
||||
|
||||
bool LoginAuthUserView::TestApi::HasAuthMethod(AuthMethods auth_method) const {
|
||||
return view_->HasAuthMethod(auth_method);
|
||||
}
|
||||
@ -490,6 +495,12 @@ LoginAuthUserView::TestApi::GetDisabledAuthMessageContent() const {
|
||||
.GetDisabledAuthMessageContent();
|
||||
}
|
||||
|
||||
const std::u16string& LoginAuthUserView::TestApi::GetPinStatusMessageContent()
|
||||
const {
|
||||
return PinStatusMessageView::TestApi(view_->pin_status_message_view_)
|
||||
.GetPinStatusMessageContent();
|
||||
}
|
||||
|
||||
LoginAuthUserView::Callbacks::Callbacks() = default;
|
||||
|
||||
LoginAuthUserView::Callbacks::Callbacks(const Callbacks& other) = default;
|
||||
@ -502,11 +513,13 @@ LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
|
||||
on_auth_(callbacks.on_auth),
|
||||
on_tap_(callbacks.on_tap),
|
||||
on_remove_warning_shown_(callbacks.on_remove_warning_shown),
|
||||
on_remove_(callbacks.on_remove) {
|
||||
on_remove_(callbacks.on_remove),
|
||||
on_pin_unlock_(callbacks.on_pin_unlock) {
|
||||
DCHECK(callbacks.on_auth);
|
||||
DCHECK(callbacks.on_tap);
|
||||
DCHECK(callbacks.on_remove);
|
||||
DCHECK(callbacks.on_auth_factor_is_hiding_password_changed);
|
||||
DCHECK(callbacks.on_pin_unlock);
|
||||
DCHECK_NE(user.basic_user_info.type, user_manager::UserType::kPublicAccount);
|
||||
if (Shell::Get()->login_screen_controller()->IsAuthenticating()) {
|
||||
// TODO(b/276246832): We should avoid re-layouting during Authentication.
|
||||
@ -601,6 +614,10 @@ LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
|
||||
auto locked_tpm_message_view = std::make_unique<LockedTpmMessageView>();
|
||||
locked_tpm_message_view_ = locked_tpm_message_view.get();
|
||||
|
||||
auto pin_status_message_view =
|
||||
std::make_unique<PinStatusMessageView>(on_pin_unlock_);
|
||||
pin_status_message_view_ = pin_status_message_view.get();
|
||||
|
||||
auto fingerprint_auth_factor_model =
|
||||
FingerprintAuthFactorModel::Factory::Create(user.fingerprint_state);
|
||||
fingerprint_auth_factor_model_ = fingerprint_auth_factor_model.get();
|
||||
@ -654,6 +671,9 @@ LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
|
||||
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_pin_status_message_view =
|
||||
login_views_utils::WrapViewForPreferredSize(
|
||||
std::move(pin_status_message_view));
|
||||
auto wrapped_auth_factors_view =
|
||||
login_views_utils::WrapViewForPreferredSize(std::move(auth_factors_view));
|
||||
auto wrapped_challenge_response_view =
|
||||
@ -686,6 +706,7 @@ LoginAuthUserView::LoginAuthUserView(const LoginUserInfo& user,
|
||||
AddChildView(std::move(wrapped_padding_below_password_view));
|
||||
AddChildView(std::move(wrapped_pin_view));
|
||||
AddChildView(std::move(wrapped_pin_password_toggle_view));
|
||||
AddChildView(std::move(wrapped_pin_status_message_view));
|
||||
AddChildView(std::move(wrapped_auth_factors_view));
|
||||
auto* challenge_ptr =
|
||||
AddChildView(std::move(wrapped_challenge_response_view));
|
||||
@ -757,6 +778,15 @@ void LoginAuthUserView::SetAuthMethods(
|
||||
auth_metadata.time_until_tpm_unlock.value());
|
||||
}
|
||||
|
||||
const bool is_pin_soft_locked =
|
||||
input_field_mode_ == InputFieldMode::kPasswordOnly &&
|
||||
auth_metadata.pin_available_at.has_value();
|
||||
pin_status_message_view_->SetVisible(is_pin_soft_locked);
|
||||
if (is_pin_soft_locked) {
|
||||
pin_status_message_view_->SetPinAvailbleAt(
|
||||
auth_metadata.pin_available_at.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.
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "ash/login/ui/login_password_view.h"
|
||||
#include "ash/login/ui/login_user_view.h"
|
||||
#include "ash/login/ui/non_accessible_view.h"
|
||||
#include "ash/login/ui/pin_status_message_view.h"
|
||||
#include "ash/public/cpp/login_types.h"
|
||||
#include "ash/public/cpp/session/user_info.h"
|
||||
#include "ash/style/pill_button.h"
|
||||
@ -136,11 +137,13 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView {
|
||||
LoginAuthFactorsView* auth_factors_view() const;
|
||||
AuthFactorModel* fingerprint_auth_factor_model() const;
|
||||
AuthFactorModel* smart_lock_auth_factor_model() const;
|
||||
PinStatusMessageView* pin_status_message_view() const;
|
||||
bool HasAuthMethod(AuthMethods auth_method) const;
|
||||
const std::u16string& GetDisabledAuthMessageContent() const;
|
||||
void SetFingerprintState(FingerprintState state) const;
|
||||
void SetSmartLockState(SmartLockState state) const;
|
||||
void ShowDialog();
|
||||
const std::u16string& GetPinStatusMessageContent() const;
|
||||
|
||||
private:
|
||||
const raw_ptr<LoginAuthUserView, DanglingUntriaged> view_;
|
||||
@ -170,6 +173,9 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView {
|
||||
// factor wants to hide the password and pin.
|
||||
base::RepeatingCallback<void(bool)>
|
||||
on_auth_factor_is_hiding_password_changed;
|
||||
// Called when the pin becomes available after being soft-locked due to
|
||||
// multiple wrong pin attempts.
|
||||
PinStatusMessageView::OnPinUnlock on_pin_unlock;
|
||||
};
|
||||
|
||||
LoginAuthUserView(const LoginUserInfo& user, const Callbacks& callbacks);
|
||||
@ -347,6 +353,7 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView {
|
||||
raw_ptr<SmartLockAuthFactorModel> smart_lock_auth_factor_model_ = nullptr;
|
||||
raw_ptr<ChallengeResponseView> challenge_response_view_ = nullptr;
|
||||
raw_ptr<LockedTpmMessageView> locked_tpm_message_view_ = nullptr;
|
||||
raw_ptr<PinStatusMessageView> pin_status_message_view_ = nullptr;
|
||||
|
||||
// Padding below the user view. Grows when there isn't an input field
|
||||
// or smart card login.
|
||||
@ -367,6 +374,7 @@ class ASH_EXPORT LoginAuthUserView : public NonAccessibleView {
|
||||
const LoginUserView::OnTap on_tap_;
|
||||
const LoginUserView::OnRemoveWarningShown on_remove_warning_shown_;
|
||||
const LoginUserView::OnRemove on_remove_;
|
||||
const PinStatusMessageView::OnPinUnlock on_pin_unlock_;
|
||||
|
||||
// UI state that was stored before setting new authentication methods.
|
||||
// Generated by `CaptureStateForAnimationPreLayout` and consumed by
|
||||
|
@ -143,6 +143,7 @@ class LoginAuthUserViewTestBase : public LoginTestBase {
|
||||
auth_callbacks.on_remove = base::DoNothing();
|
||||
auth_callbacks.on_auth_factor_is_hiding_password_changed =
|
||||
base::DoNothing();
|
||||
auth_callbacks.on_pin_unlock = base::DoNothing();
|
||||
view_ = new LoginAuthUserView(user_, auth_callbacks);
|
||||
|
||||
// We proxy |view_| inside of |container_| so we can control layout.
|
||||
|
@ -147,6 +147,7 @@ class LoginAuthUserViewTestBase : public LoginTestBase {
|
||||
auth_callbacks.on_remove = base::DoNothing();
|
||||
auth_callbacks.on_auth_factor_is_hiding_password_changed =
|
||||
base::DoNothing();
|
||||
auth_callbacks.on_pin_unlock = base::DoNothing();
|
||||
view_ = new LoginAuthUserView(user_, auth_callbacks);
|
||||
|
||||
// We proxy |view_| inside of |container_| so we can control layout.
|
||||
|
113
ash/login/ui/pin_status_message_view.cc
Normal file
113
ash/login/ui/pin_status_message_view.cc
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/login/ui/pin_status_message_view.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/ash_color_id.h"
|
||||
#include "base/i18n/time_formatting.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/gfx/font_list.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "ui/views/accessibility/view_accessibility.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kVerticalBorderDp = 20;
|
||||
constexpr int kHorizontalBorderDp = 0;
|
||||
constexpr int kWidthDp = 320;
|
||||
constexpr int kHeightDp = 100;
|
||||
constexpr int kDeltaDp = 0;
|
||||
// The time interval to refresh the pin delay message.
|
||||
constexpr base::TimeDelta kRefreshInterval = base::Milliseconds(200);
|
||||
|
||||
bool TimeDurationFormat(base::TimeDelta time, std::u16string* out) {
|
||||
if (!time.is_positive()) {
|
||||
return false;
|
||||
}
|
||||
// Shows "x hours, y minutes" when the time is more than an hour, otherwise
|
||||
// shows "x minutes, y seconds".
|
||||
if (time.InHours() >= 1) {
|
||||
return base::TimeDurationFormat(
|
||||
time, base::DurationFormatWidth::DURATION_WIDTH_WIDE, out);
|
||||
} else {
|
||||
return base::TimeDurationCompactFormatWithSeconds(
|
||||
time, base::DurationFormatWidth::DURATION_WIDTH_WIDE, out);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PinStatusMessageView::TestApi::TestApi(PinStatusMessageView* view)
|
||||
: view_(view) {}
|
||||
|
||||
PinStatusMessageView::TestApi::~TestApi() = default;
|
||||
|
||||
const std::u16string&
|
||||
PinStatusMessageView::TestApi::GetPinStatusMessageContent() const {
|
||||
return view_->message_->GetText();
|
||||
}
|
||||
|
||||
PinStatusMessageView::PinStatusMessageView(base::RepeatingClosure on_pin_unlock)
|
||||
: on_pin_unlock_(on_pin_unlock) {
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical,
|
||||
gfx::Insets::VH(kVerticalBorderDp, kHorizontalBorderDp)));
|
||||
SetPaintToLayer();
|
||||
layer()->SetFillsBoundsOpaquely(false);
|
||||
SetPreferredSize(gfx::Size(kWidthDp, kHeightDp));
|
||||
|
||||
message_ = new views::Label(std::u16string(), views::style::CONTEXT_LABEL,
|
||||
views::style::STYLE_PRIMARY);
|
||||
message_->SetFontList(gfx::FontList().Derive(kDeltaDp, gfx::Font::NORMAL,
|
||||
gfx::Font::Weight::NORMAL));
|
||||
message_->SetSubpixelRenderingEnabled(false);
|
||||
message_->SetAutoColorReadabilityEnabled(false);
|
||||
message_->SetMultiLine(true);
|
||||
message_->SetEnabledColorId(kColorAshTextColorPrimary);
|
||||
message_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
|
||||
message_->GetViewAccessibility().SetName(
|
||||
std::u16string(), ax::mojom::NameFrom::kAttributeExplicitlyEmpty);
|
||||
AddChildView(message_.get());
|
||||
}
|
||||
|
||||
PinStatusMessageView::~PinStatusMessageView() {
|
||||
message_ = nullptr;
|
||||
}
|
||||
|
||||
void PinStatusMessageView::SetPinAvailbleAt(base::Time available_at) {
|
||||
available_at_ = available_at;
|
||||
timer_.Start(FROM_HERE, kRefreshInterval, this,
|
||||
&PinStatusMessageView::UpdateUI, base::TimeTicks::Now());
|
||||
SetVisible(true);
|
||||
}
|
||||
|
||||
void PinStatusMessageView::UpdateUI() {
|
||||
base::TimeDelta time_left = available_at_ - base::Time::Now();
|
||||
if (!time_left.is_positive()) {
|
||||
on_pin_unlock_.Run();
|
||||
SetVisible(false);
|
||||
message_->SetText(std::u16string());
|
||||
timer_.Stop();
|
||||
return;
|
||||
}
|
||||
std::u16string time_left_message;
|
||||
if (TimeDurationFormat(time_left, &time_left_message)) {
|
||||
std::u16string message_warning = l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_LOGIN_POD_PIN_LOCKED_WARNING, time_left_message);
|
||||
message_->SetText(message_warning);
|
||||
}
|
||||
}
|
||||
|
||||
void PinStatusMessageView::RequestFocus() {
|
||||
message_->RequestFocus();
|
||||
}
|
||||
|
||||
} // namespace ash
|
59
ash/login/ui/pin_status_message_view.h
Normal file
59
ash/login/ui/pin_status_message_view.h
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2024 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_LOGIN_UI_PIN_STATUS_MESSAGE_VIEW_H_
|
||||
#define ASH_LOGIN_UI_PIN_STATUS_MESSAGE_VIEW_H_
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
// The message that can be shown to the user when the PIN is soft-locked.
|
||||
class PinStatusMessageView : public views::View {
|
||||
public:
|
||||
class ASH_EXPORT TestApi {
|
||||
public:
|
||||
explicit TestApi(PinStatusMessageView* view);
|
||||
~TestApi();
|
||||
|
||||
const std::u16string& GetPinStatusMessageContent() const;
|
||||
|
||||
private:
|
||||
const raw_ptr<PinStatusMessageView> view_;
|
||||
};
|
||||
|
||||
using OnPinUnlock = base::RepeatingClosure;
|
||||
|
||||
explicit PinStatusMessageView(base::RepeatingClosure on_pin_unlocked);
|
||||
|
||||
PinStatusMessageView(const PinStatusMessageView&) = delete;
|
||||
PinStatusMessageView& operator=(const PinStatusMessageView&) = delete;
|
||||
|
||||
~PinStatusMessageView() override;
|
||||
|
||||
// Set remaining time to be shown in the message.
|
||||
void SetPinAvailbleAt(base::Time available_at);
|
||||
|
||||
// views::View:
|
||||
void RequestFocus() override;
|
||||
|
||||
private:
|
||||
// Refresh the UI to show the latest remaining time.
|
||||
void UpdateUI();
|
||||
|
||||
raw_ptr<views::Label> message_;
|
||||
|
||||
OnPinUnlock on_pin_unlock_;
|
||||
base::Time available_at_;
|
||||
base::MetronomeTimer timer_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_LOGIN_UI_PIN_STATUS_MESSAGE_VIEW_H_
|
Reference in New Issue
Block a user