Skip to content

Commit

Permalink
cros: Send accessibility alerts after fingerprint events.
Browse files Browse the repository at this point in the history
Also improve accessibility labels when hovering over the fingerprint label,
i.e., the label visually displays "Too many attempts" but ChromeVox will read
"Too many fingerprint attempts".

Bug: 881579
Change-Id: I37cb944e1cfe5703a92e6a63463e7ecea1d8a53c
Reviewed-on: https://chromium-review.googlesource.com/c/1289442
Commit-Queue: Jacob Dufault <jdufault@chromium.org>
Reviewed-by: Wenzhao (Colin) Zang <wzang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601698}
  • Loading branch information
jacobdufault-google authored and Commit Bot committed Oct 22, 2018
1 parent 3bdb686 commit 16d8aba
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 61 deletions.
23 changes: 16 additions & 7 deletions ash/ash_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -1457,21 +1457,30 @@ This file contains the strings for ash.
<message name="IDS_ASH_LOGIN_KEYBOARD_SELECTION_SELECT" desc="Label for keyboard selection dropdown">
Set your keyboard
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_MESSAGE" desc="Text shown in the user pod to remind user that fingerprint unlock is supported">
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE" desc="Text shown in the user pod to remind user that fingerprint unlock is supported">
Unlock with fingerprint
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AGE_AUTH_SUCCESS" desc="Text shown in the user pod that fingerprint unlock succeeded">
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_SUCCESS" desc="Text shown in the user pod that fingerprint unlock succeeded">
Signing in
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT_MESSAGE" desc="Text shown when fingerprint has been disabled because it has been too long since the user last used the device">
To sign back in, enter your password
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_FAILED_MESSAGE" desc="Text shown in the user pod to tell user that couldn't unlock because finger is not recognized">
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_FAILED" desc="Text shown in the user pod to tell user that couldn't unlock because finger is not recognized">
Not recognized
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_MESSAGE" desc="Text shown in the user pod to tell user that fingerprint unlock has reached maximum attempt">
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT" desc="Text shown when fingerprint has been disabled because it has been too long since the user last used the device">
To unlock, enter your password
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS" desc="Text shown in the user pod to tell user that fingerprint unlock has reached maximum attempt">
Too many attempts
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_SUCCESS" desc="Accessibility text read by chromevox when the user successfully authenticated with fingerprint on the lock screen">
Unlocking with fingerprint
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_FAILED" desc="Accessibility text read by chromevox that the user's fingerprint authentication attempt was not successful">
Fingerprint not recognized
</message>
<message name="IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_DISABLED_FROM_ATTEMPTS" desc="Accessibility text read by chromevox when the user has made too many unsuccessful fingerprint unlock attempts; fingerprint is now disabled until the user unlocks with a different authentication method">
Too many fingerprint attempts
</message>
<message name="IDS_ASH_LOGIN_TAKE_BREAK_MESSAGE" desc="Message shown to user when unlocking the device is not allowed because their parent locked it for the day.">
Take a break!
</message>
Expand Down
2 changes: 1 addition & 1 deletion ash/login/ui/lock_contents_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ void LockContentsView::OnFingerprintUnlockStateChanged(
if (user_state->fingerprint_state ==
mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT) {
base::string16 error_text = l10n_util::GetStringUTF16(
IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT_MESSAGE);
IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT);
auto* label = new views::Label(error_text);
label->SetAutoColorReadabilityEnabled(false);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
Expand Down
161 changes: 108 additions & 53 deletions ash/login/ui/login_auth_user_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "components/user_manager/user.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/compositor/callback_layer_animation_observer.h"
#include "ui/compositor/layer_animation_sequence.h"
Expand Down Expand Up @@ -152,6 +153,70 @@ void DecorateOnlineSignInMessage(views::LabelButton* label_button) {
label_button->SetBorder(views::CreateEmptyBorder(gfx::Insets(9, 0)));
}

// The label shown below the fingerprint icon.
class FingerprintLabel : public views::Label {
public:
explicit FingerprintLabel(mojom::FingerprintUnlockState state) {
SetSubpixelRenderingEnabled(false);
SetAutoColorReadabilityEnabled(false);
SetEnabledColor(login_constants::kAuthMethodsTextColor);

SetState(state);
}

void SetState(mojom::FingerprintUnlockState state) {
auto get_label_id = [&]() {
switch (state) {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE;
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_SUCCESS;
case mojom::FingerprintUnlockState::AUTH_FAILED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AUTH_FAILED;
case mojom::FingerprintUnlockState::AUTH_DISABLED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_ATTEMPTS;
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT;
}
NOTREACHED();
};

state_ = state;
SetText(l10n_util::GetStringUTF16(get_label_id()));
NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
true /*send_native_event*/);
}

// views::View:
void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
auto get_string_id = [&]() {
switch (state_) {
case mojom::FingerprintUnlockState::UNAVAILABLE:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT;
case mojom::FingerprintUnlockState::AVAILABLE:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AVAILABLE;
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_SUCCESS;
case mojom::FingerprintUnlockState::AUTH_FAILED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_FAILED;
case mojom::FingerprintUnlockState::AUTH_DISABLED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_ACCESSIBLE_AUTH_DISABLED_FROM_ATTEMPTS;
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_FROM_TIMEOUT;
}
};

node_data->role = ax::mojom::Role::kStaticText;
node_data->SetName(l10n_util::GetStringUTF16(get_string_id()));
}

private:
mojom::FingerprintUnlockState state_;

DISALLOW_COPY_AND_ASSIGN(FingerprintLabel);
};

} // namespace

// Consists of fingerprint icon view and a label.
Expand All @@ -175,62 +240,12 @@ class LoginAuthUserView::FingerprintView : public views::View {
kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorWHITE));
AddChildView(icon_);

label_ = new views::Label(
l10n_util::GetStringUTF16(IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_MESSAGE));
label_->SetSubpixelRenderingEnabled(false);
label_->SetAutoColorReadabilityEnabled(false);
label_->SetEnabledColor(login_constants::kAuthMethodsTextColor);
label_ = new FingerprintLabel(mojom::FingerprintUnlockState::AVAILABLE);
AddChildView(label_);
}

~FingerprintView() override = default;

void SetIcon(mojom::FingerprintUnlockState state) {
switch (state) {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
icon_->SetImage(gfx::CreateVectorIcon(
kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorWHITE));
break;
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintSuccessIcon,
kFingerprintIconSizeDp,
gfx::kGoogleGreenDark500));
break;
case mojom::FingerprintUnlockState::AUTH_FAILED:
case mojom::FingerprintUnlockState::AUTH_DISABLED:
icon_->SetAnimationDecoder(
std::make_unique<HorizontalImageSequenceAnimationDecoder>(
*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER),
base::TimeDelta::FromMilliseconds(
kFingerprintFailedAnimationDurationMs),
kFingerprintFailedAnimationNumFrames),
AnimatedRoundedImageView::Playback::kSingle);
break;
}
}

void SetText(mojom::FingerprintUnlockState state) {
auto get_label_id = [&]() -> int {
switch (state) {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_MESSAGE;
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_AGE_AUTH_SUCCESS;
case mojom::FingerprintUnlockState::AUTH_FAILED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_FAILED_MESSAGE;
case mojom::FingerprintUnlockState::AUTH_DISABLED:
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_DISABLED_MESSAGE;
}
};

label_->SetText(l10n_util::GetStringUTF16(get_label_id()));
}

void SetState(mojom::FingerprintUnlockState state) {
if (state_ == state)
return;
Expand All @@ -240,11 +255,17 @@ class LoginAuthUserView::FingerprintView : public views::View {
state !=
mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT);
SetIcon(state);
SetText(state);
label_->SetState(state);

if (ShouldFireChromeVoxAlert(state)) {
label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
true /*send_native_event*/);
}

// Fingerprint icon reset to default sometime after AUTH_FAILED.
reset_state_.Stop();
if (state == mojom::FingerprintUnlockState::AUTH_FAILED) {
// base::Unretained is safe because reset_state_ is owned by |this|.
reset_state_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kResetToDefaultIconDelayMs),
Expand All @@ -254,14 +275,48 @@ class LoginAuthUserView::FingerprintView : public views::View {
}
}

// views::View:
gfx::Size CalculatePreferredSize() const override {
gfx::Size size = views::View::CalculatePreferredSize();
size.set_width(kFingerprintViewWidthDp);
return size;
}

private:
views::Label* label_ = nullptr;
void SetIcon(mojom::FingerprintUnlockState state) {
switch (state) {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
icon_->SetImage(gfx::CreateVectorIcon(
kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorWHITE));
break;
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintSuccessIcon,
kFingerprintIconSizeDp,
gfx::kGoogleGreenDark500));
break;
case mojom::FingerprintUnlockState::AUTH_FAILED:
case mojom::FingerprintUnlockState::AUTH_DISABLED:
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(mojom::FingerprintUnlockState state) {
return state == mojom::FingerprintUnlockState::AUTH_FAILED ||
state == mojom::FingerprintUnlockState::AUTH_DISABLED ||
state == mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT;
}

FingerprintLabel* label_ = nullptr;
AnimatedRoundedImageView* icon_ = nullptr;
base::OneShotTimer reset_state_;
mojom::FingerprintUnlockState state_ =
Expand Down

0 comments on commit 16d8aba

Please sign in to comment.