0

Update Accessibility Feature Tile behavior

This CL adds a toggled state for the accessibility feature
tile. Whenever an accessibility feature is enabled the tile
is now toggled and a sub label is displayed to signify the first
enabled feature along with a count to show how many features
are enabled.

This CL also adds a `name_resource_id` field for the
AccessibilityControllerImpl::Feature class so that we can
fetch a feature's name instead of hard coding it.

Screenshot: https://screenshot.googleplex.com/B7FvV87PXwtYWFu

Bug: b:280853527
Change-Id: I1a71e173ddedbe24c32568d0979cecafaa58cc2f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4704936
Reviewed-by: Jiaming Cheng <jiamingc@chromium.org>
Reviewed-by: Katie Dektar <katie@chromium.org>
Commit-Queue: Ahmed Mehfooz <amehfooz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1183251}
This commit is contained in:
Ahmed Mehfooz
2023-08-14 19:28:26 +00:00
committed by Chromium LUCI CQ
parent 60af84dfdc
commit d2722b695e
11 changed files with 360 additions and 36 deletions

@ -92,6 +92,8 @@ struct FeatureData {
// This field is not a raw_ptr<> because it was filtered by the rewriter
// for: #global-scope
RAW_PTR_EXCLUSION const gfx::VectorIcon* icon;
const int name_resource_id;
bool toggleable_in_quicksettings = true;
FeatureType conflicting_feature = FeatureType::kNoConflictingFeature;
};
@ -106,43 +108,56 @@ struct FeatureDialogData {
// A static array describing each feature.
const FeatureData kFeatures[] = {
{FeatureType::kAutoclick, prefs::kAccessibilityAutoclickEnabled,
&kSystemMenuAccessibilityAutoClickIcon},
&kSystemMenuAccessibilityAutoClickIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK},
{FeatureType::kCaretHighlight, prefs::kAccessibilityCaretHighlightEnabled,
nullptr},
nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_CARET_HIGHLIGHT},
{FeatureType::kCursorHighlight, prefs::kAccessibilityCursorHighlightEnabled,
nullptr},
nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_MOUSE_CURSOR},
{FeatureType::kCursorColor, prefs::kAccessibilityCursorColorEnabled,
nullptr},
nullptr, 0, /*toggleable_in_quicksettings*/ false},
{FeatureType::kDictation, prefs::kAccessibilityDictationEnabled,
&kDictationMenuIcon},
&kDictationMenuIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION},
{FeatureType::kColorCorrection, prefs::kAccessibilityColorCorrectionEnabled,
&kColorCorrectionIcon},
&kColorCorrectionIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_COLOR_CORRECTION},
{FeatureType::kFocusHighlight, prefs::kAccessibilityFocusHighlightEnabled,
nullptr, /* conflicting_feature= */ FeatureType::kSpokenFeedback},
nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_KEYBOARD_FOCUS,
/*toggleable_in_quicksettings*/ true,
/* conflicting_feature= */ FeatureType::kSpokenFeedback},
{FeatureType::kFloatingMenu, prefs::kAccessibilityFloatingMenuEnabled,
nullptr},
nullptr, IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU,
/*toggleable_in_quicksettings=*/false},
{FeatureType::kFullscreenMagnifier,
prefs::kAccessibilityScreenMagnifierEnabled,
&kSystemMenuAccessibilityFullscreenMagnifierIcon},
&kSystemMenuAccessibilityFullscreenMagnifierIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER},
{FeatureType::kDockedMagnifier, prefs::kDockedMagnifierEnabled,
&kSystemMenuAccessibilityDockedMagnifierIcon},
&kSystemMenuAccessibilityDockedMagnifierIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DOCKED_MAGNIFIER},
{FeatureType::kHighContrast, prefs::kAccessibilityHighContrastEnabled,
&kSystemMenuAccessibilityContrastIcon},
&kSystemMenuAccessibilityContrastIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE},
{FeatureType::kLargeCursor, prefs::kAccessibilityLargeCursorEnabled,
nullptr},
nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR},
{FeatureType::kLiveCaption, ::prefs::kLiveCaptionEnabled,
&vector_icons::kLiveCaptionOnIcon},
{FeatureType::kMonoAudio, prefs::kAccessibilityMonoAudioEnabled, nullptr},
&vector_icons::kLiveCaptionOnIcon, IDS_ASH_STATUS_TRAY_LIVE_CAPTION},
{FeatureType::kMonoAudio, prefs::kAccessibilityMonoAudioEnabled, nullptr,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MONO_AUDIO},
{FeatureType::kSpokenFeedback, prefs::kAccessibilitySpokenFeedbackEnabled,
&kSystemMenuAccessibilityChromevoxIcon},
&kSystemMenuAccessibilityChromevoxIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK},
{FeatureType::kSelectToSpeak, prefs::kAccessibilitySelectToSpeakEnabled,
&kSystemMenuAccessibilitySelectToSpeakIcon},
&kSystemMenuAccessibilitySelectToSpeakIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK},
{FeatureType::kStickyKeys, prefs::kAccessibilityStickyKeysEnabled, nullptr,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_STICKY_KEYS,
/*toggleable_in_quicksettings=*/true,
/*conflicting_feature=*/FeatureType::kSpokenFeedback},
{FeatureType::kSwitchAccess, prefs::kAccessibilitySwitchAccessEnabled,
&kSwitchAccessIcon},
&kSwitchAccessIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SWITCH_ACCESS},
{FeatureType::kVirtualKeyboard, prefs::kAccessibilityVirtualKeyboardEnabled,
&kSystemMenuKeyboardLegacyIcon}};
&kSystemMenuKeyboardLegacyIcon,
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD}};
// An array describing the confirmation dialogs for the features which have
// them.
@ -782,8 +797,21 @@ AccessibilityControllerImpl::Feature::Feature(
FeatureType type,
const std::string& pref_name,
const gfx::VectorIcon* icon,
const int name_resource_id,
const bool toggleable_in_quicksettings,
AccessibilityControllerImpl* controller)
: type_(type), pref_name_(pref_name), icon_(icon), owner_(controller) {}
: type_(type),
pref_name_(pref_name),
icon_(icon),
name_resource_id_(name_resource_id),
toggleable_in_quicksettings_(toggleable_in_quicksettings),
owner_(controller) {
// If a feature is toggleable in quicksettings it must have a
// `name_resource_id` so it's name can be looked up.
if (toggleable_in_quicksettings_) {
CHECK(name_resource_id);
}
}
AccessibilityControllerImpl::Feature::~Feature() = default;
@ -866,9 +894,16 @@ AccessibilityControllerImpl::FeatureWithDialog::FeatureWithDialog(
FeatureType type,
const std::string& pref_name,
const gfx::VectorIcon* icon,
const int name_resource_id,
const bool toggleable_in_quicksettings,
const Dialog& dialog,
AccessibilityControllerImpl* controller)
: AccessibilityControllerImpl::Feature(type, pref_name, icon, controller),
: AccessibilityControllerImpl::Feature(type,
pref_name,
icon,
name_resource_id,
toggleable_in_quicksettings,
controller),
dialog_(dialog) {}
AccessibilityControllerImpl::FeatureWithDialog::~FeatureWithDialog() = default;
@ -954,11 +989,14 @@ void AccessibilityControllerImpl::CreateAccessibilityFeatures() {
auto it = dialogs.find(feature_data.type);
if (it == dialogs.end()) {
features_[feature_index] = std::make_unique<Feature>(
feature_data.type, feature_data.pref, feature_data.icon, this);
feature_data.type, feature_data.pref, feature_data.icon,
feature_data.name_resource_id,
feature_data.toggleable_in_quicksettings, this);
} else {
features_[feature_index] = std::make_unique<FeatureWithDialog>(
feature_data.type, feature_data.pref, feature_data.icon, it->second,
this);
feature_data.type, feature_data.pref, feature_data.icon,
feature_data.name_resource_id,
feature_data.toggleable_in_quicksettings, it->second, this);
}
if (feature_data.conflicting_feature !=
FeatureType::kNoConflictingFeature) {
@ -1282,6 +1320,18 @@ AccessibilityControllerImpl::Feature& AccessibilityControllerImpl::GetFeature(
return *features_[feature_index].get();
}
std::vector<AccessibilityControllerImpl::Feature*>
AccessibilityControllerImpl::GetEnabledFeaturesInQuickSettings() const {
std::vector<Feature*> enabled_features;
for (auto& feature : features_) {
if (feature->enabled() && feature->toggleable_in_quicksettings()) {
enabled_features.push_back(feature.get());
}
}
return enabled_features;
}
base::WeakPtr<AccessibilityControllerImpl>
AccessibilityControllerImpl::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();

@ -97,6 +97,8 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
Feature(A11yFeatureType type,
const std::string& pref_name,
const gfx::VectorIcon* icon,
const int name_resource_id,
const bool toggleable_in_quicksettings,
AccessibilityControllerImpl* controller);
Feature(const Feature&) = delete;
Feature& operator=(Feature const&) = delete;
@ -113,6 +115,10 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
bool IsEnterpriseIconVisible() const;
const std::string& pref_name() const { return pref_name_; }
const gfx::VectorIcon& icon() const;
int name_resource_id() const { return name_resource_id_; }
bool toggleable_in_quicksettings() const {
return toggleable_in_quicksettings_;
}
A11yFeatureType conflicting_feature() const { return conflicting_feature_; }
void UpdateFromPref();
@ -133,6 +139,15 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
bool enabled_ = false;
const std::string pref_name_;
raw_ptr<const gfx::VectorIcon, ExperimentalAsh> icon_;
// The resource id used to fetch the string with this feature's name. Used
// in quicksettings.
const int name_resource_id_;
// Specifies if this feature can be toggled from the accessibility options
// available in the quicksettings menu.
const bool toggleable_in_quicksettings_;
const raw_ptr<AccessibilityControllerImpl, ExperimentalAsh> owner_;
};
@ -155,6 +170,8 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
FeatureWithDialog(A11yFeatureType type,
const std::string& pref_name,
const gfx::VectorIcon* icon,
const int name_resource_id,
const bool toggleable_in_quicksettings,
const Dialog& dialog,
AccessibilityControllerImpl* controller);
~FeatureWithDialog() override;
@ -203,6 +220,10 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
Feature& GetFeature(A11yFeatureType feature) const;
// Returns all `Feature`s that are toggleable in quicksettings and currently
// enabled.
std::vector<Feature*> GetEnabledFeaturesInQuickSettings() const;
base::WeakPtr<AccessibilityControllerImpl> GetWeakPtr();
// Getters for the corresponding features.

@ -7,6 +7,7 @@
#include <string>
#include <utility>
#include "ash/accessibility/a11y_feature_type.h"
#include "ash/accessibility/accessibility_observer.h"
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
@ -31,6 +32,7 @@
#include "media/base/media_switches.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/aura/aura_window_properties.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
using message_center::MessageCenter;
@ -1360,6 +1362,24 @@ TEST_F(AccessibilityControllerTest,
(*notifications.begin())->system_notification_warning_level());
}
// Test to ensure all features that are toggleable in the quicksettings menu
// have a valid `name_resource_id`.
TEST_F(AccessibilityControllerTest, AllAccessibilityFeaturesHaveValidNames) {
auto* accessibility_controller = Shell::Get()->accessibility_controller();
for (int type = 0; type != static_cast<int>(A11yFeatureType::kFeatureCount);
type++) {
auto& feature = accessibility_controller->GetFeature(
static_cast<A11yFeatureType>(type));
if (!feature.toggleable_in_quicksettings()) {
continue;
}
std::u16string feature_name =
l10n_util::GetStringUTF16(feature.name_resource_id());
EXPECT_GT(feature_name.length(), 0u);
}
}
// Verifies the behavior of EnableOrToggleDictation without the keyboard
// improvements feature (current behavior).
TEST_F(AccessibilityControllerTest, EnableOrToggleDictation) {
@ -1550,11 +1570,11 @@ TEST_P(AccessibilityControllerSigninTest, EnableOnLoginScreenAndLogin) {
EXPECT_FALSE(accessibility->autoclick().enabled());
EXPECT_FALSE(accessibility->mono_audio().enabled());
EXPECT_FALSE(docked_magnifier->GetEnabled());
using prefs::kAccessibilityLargeCursorEnabled;
using prefs::kAccessibilitySpokenFeedbackEnabled;
using prefs::kAccessibilityHighContrastEnabled;
using prefs::kAccessibilityAutoclickEnabled;
using prefs::kAccessibilityHighContrastEnabled;
using prefs::kAccessibilityLargeCursorEnabled;
using prefs::kAccessibilityMonoAudioEnabled;
using prefs::kAccessibilitySpokenFeedbackEnabled;
using prefs::kDockedMagnifierEnabled;
PrefService* signin_prefs = session->GetSigninScreenPrefService();
EXPECT_FALSE(signin_prefs->GetBoolean(kAccessibilityLargeCursorEnabled));

@ -1074,9 +1074,16 @@ Style notes:
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY" desc="The label used in the tray menu to show the accessibility option menu. [CHAR_LIMIT=14]">
Accessibility
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ENABLED_FEATURES_SEPARATOR" desc="The characters used to separate the first feature's
name from the counter in the label displayed when accessibility features are enabled in the quicksettings menu. Example: Select to Speak, +2">
, +
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TOOLTIP" desc="The tooltip text in the tray menu to show the accessibility option menu.">
Show accessibility settings
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TOGGLED_TOOLTIP" desc="The tooltip text in the tray menu to show the accessibility option menu.">
Show accessibility settings, "<ph name="ENABLED_FEATURES">$1<ex>Dictation</ex></ph>" enabled.
</message>
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TITLE" desc="The label used in the title of the accessibility option menu. [CHAR_LIMIT=18]">
Accessibility
</message>

@ -0,0 +1 @@
4583db2a273ca22e2b2afac7c84de83553210b64

@ -0,0 +1 @@
4583db2a273ca22e2b2afac7c84de83553210b64

@ -2,8 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include "ash/system/accessibility/accessibility_feature_pod_controller.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/accessibility/accessibility_delegate.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/ash_view_ids.h"
@ -15,16 +18,87 @@
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/text_constants.h"
#include "ui/gfx/text_elider.h"
#include "ui/gfx/text_utils.h"
#include "ui/views/controls/label.h"
namespace ash {
namespace {
std::u16string GenerateSublabelText(
std::vector<AccessibilityControllerImpl::Feature*> enabled_features,
int max_width,
gfx::FontList font_list) {
CHECK(!enabled_features.empty());
std::u16string feature_name =
l10n_util::GetStringUTF16(enabled_features.front()->name_resource_id());
if (enabled_features.size() == 1) {
return feature_name;
}
std::u16string separator = l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ENABLED_FEATURES_SEPARATOR);
std::u16string count_label =
base::NumberToString16(enabled_features.size() - 1);
int available_width_for_feature_name =
max_width - gfx::GetStringWidth(std::u16string(gfx::kEllipsisUTF16) +
separator + count_label,
font_list);
// Elide the first enabled feature's name if necessary and then append the
// number of other enabled features. This is to ensure the number is
// always visible.
std::u16string label = gfx::ElideText(feature_name, gfx::FontList(),
available_width_for_feature_name,
gfx::ElideBehavior::ELIDE_TAIL);
return base::JoinString({label, count_label}, separator);
}
std::u16string GenerateTooltipText(
std::vector<AccessibilityControllerImpl::Feature*> enabled_features) {
if (enabled_features.empty()) {
return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TOOLTIP);
}
std::u16string first_feature_name =
l10n_util::GetStringUTF16(enabled_features.front()->name_resource_id());
std::u16string separator = l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_ENABLED_FEATURES_SEPARATOR);
std::u16string enabled_features_string =
enabled_features.size() == 1
? first_feature_name
: base::JoinString(
{first_feature_name,
base::NumberToString16(enabled_features.size() - 1)},
separator);
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TOGGLED_TOOLTIP,
enabled_features_string);
}
} // namespace
AccessibilityFeaturePodController::AccessibilityFeaturePodController(
UnifiedSystemTrayController* tray_controller)
: tray_controller_(tray_controller) {}
: tray_controller_(tray_controller) {
Shell::Get()->accessibility_controller()->AddObserver(this);
}
AccessibilityFeaturePodController::~AccessibilityFeaturePodController() =
default;
AccessibilityFeaturePodController::~AccessibilityFeaturePodController() {
Shell::Get()->accessibility_controller()->RemoveObserver(this);
}
void AccessibilityFeaturePodController::OnAccessibilityStatusChanged() {
UpdateTileStateIfExists();
}
FeaturePodButton* AccessibilityFeaturePodController::CreateButton() {
auto* button = new FeaturePodButton(this, /*is_togglable=*/false);
@ -55,15 +129,12 @@ std::unique_ptr<FeatureTile> AccessibilityFeaturePodController::CreateTile(
DCHECK(features::IsQsRevampEnabled());
auto feature_tile = std::make_unique<FeatureTile>(
base::BindRepeating(&FeaturePodControllerBase::OnIconPressed,
weak_ptr_factory_.GetWeakPtr()),
/*is_togglable=*/false);
weak_ptr_factory_.GetWeakPtr()));
feature_tile->SetID(VIEW_ID_ACCESSIBILITY_FEATURE_TILE);
feature_tile->SetVectorIcon(kUnifiedMenuAccessibilityIcon);
feature_tile->SetLabel(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY));
feature_tile->SetSubLabelVisibility(false);
feature_tile->SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TOOLTIP));
feature_tile->CreateDecorativeDrillInArrow();
AccessibilityDelegate* delegate = Shell::Get()->accessibility_delegate();
@ -76,6 +147,8 @@ std::unique_ptr<FeatureTile> AccessibilityFeaturePodController::CreateTile(
TrackVisibilityUMA();
}
tile_ = feature_tile.get();
UpdateTileStateIfExists();
return feature_tile;
}
@ -88,4 +161,28 @@ void AccessibilityFeaturePodController::OnIconPressed() {
tray_controller_->ShowAccessibilityDetailedView();
}
void AccessibilityFeaturePodController::UpdateTileStateIfExists() {
if (!tile_) {
return;
}
auto enabled_features = Shell::Get()
->accessibility_controller()
->GetEnabledFeaturesInQuickSettings();
bool toggled = !enabled_features.empty();
tile_->SetToggled(toggled);
tile_->SetTooltipText(GenerateTooltipText(enabled_features));
tile_->SetSubLabelVisibility(toggled);
if (toggled) {
tile_->SetSubLabel(GenerateSublabelText(enabled_features,
tile_->GetSubLabelMaxWidth(),
tile_->sub_label()->font_list()));
tile_->sub_label()->SetElideBehavior(enabled_features.size() == 1
? gfx::ElideBehavior::ELIDE_TAIL
: gfx::ElideBehavior::NO_ELIDE);
}
}
} // namespace ash

@ -7,9 +7,11 @@
#include <memory>
#include "ash/accessibility/accessibility_observer.h"
#include "ash/ash_export.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "base/allocator/partition_allocator/pointers/raw_ptr.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
@ -20,9 +22,10 @@ class UnifiedSystemTrayController;
// Controller of accessibility feature pod button.
class ASH_EXPORT AccessibilityFeaturePodController
: public FeaturePodControllerBase {
: public FeaturePodControllerBase,
public AccessibilityObserver {
public:
AccessibilityFeaturePodController(
explicit AccessibilityFeaturePodController(
UnifiedSystemTrayController* tray_controller);
AccessibilityFeaturePodController(const AccessibilityFeaturePodController&) =
@ -32,6 +35,9 @@ class ASH_EXPORT AccessibilityFeaturePodController
~AccessibilityFeaturePodController() override;
// AccessibilityObserver:
void OnAccessibilityStatusChanged() override;
// FeaturePodControllerBase:
FeaturePodButton* CreateButton() override;
std::unique_ptr<FeatureTile> CreateTile(bool compact = false) override;
@ -39,9 +45,17 @@ class ASH_EXPORT AccessibilityFeaturePodController
void OnIconPressed() override;
private:
// Updates `tile_` state to reflect the current accessibility features state.
// The `tile_` is toggled if any features are enabled and a sublabel is
// displayed with details for the enabled features.
void UpdateTileStateIfExists();
// Unowned.
const raw_ptr<UnifiedSystemTrayController, ExperimentalAsh> tray_controller_;
// Owned by views hierarchy.
raw_ptr<FeatureTile, ExperimentalAsh> tile_ = nullptr;
base::WeakPtrFactory<AccessibilityFeaturePodController> weak_ptr_factory_{
this};
};

@ -4,14 +4,18 @@
#include "ash/system/accessibility/accessibility_feature_pod_controller.h"
#include "ash/accessibility/a11y_feature_type.h"
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/quick_settings_catalogs.h"
#include "ash/shell.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/unified_system_tray.h"
#include "ash/system/unified/unified_system_tray_bubble.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "ui/views/view.h"
@ -62,6 +66,11 @@ class AccessibilityFeaturePodControllerTest
}
}
AccessibilityControllerImpl* GetAccessibilityController() {
return Shell::Get()->accessibility_controller();
}
FeatureTile* GetFeatureTile() { return tile_.get(); }
UnifiedSystemTrayController* tray_controller() {
return GetPrimaryUnifiedSystemTray()
->bubble()
@ -164,4 +173,100 @@ TEST_P(AccessibilityFeaturePodControllerTest, LabelUMATracking) {
/*expected_count=*/1);
}
TEST_P(AccessibilityFeaturePodControllerTest, FeatureTileBasicToggleBehavior) {
if (!IsQsRevampEnabled()) {
return;
}
SetUpButton();
EXPECT_FALSE(GetFeatureTile()->IsToggled());
// Enable an accessibility feature and expect the feature tile to be toggled
// and the sublabel to be visible.
GetAccessibilityController()
->GetFeature(A11yFeatureType::kHighContrast)
.SetEnabled(true);
EXPECT_TRUE(GetFeatureTile()->IsToggled());
EXPECT_TRUE(GetFeatureTile()->sub_label()->GetVisible());
// Disable an accessibility feature and expect the feature tile to be
// untoggled and the sublabel to be invisible.
GetAccessibilityController()
->GetFeature(A11yFeatureType::kHighContrast)
.SetEnabled(false);
EXPECT_FALSE(GetFeatureTile()->IsToggled());
EXPECT_FALSE(GetFeatureTile()->sub_label()->GetVisible());
}
// Toggle all accessibility features one by one and make sure the feature tile
// is updated appropriately.
TEST_P(AccessibilityFeaturePodControllerTest, FeatureTileAllFeaturesToggled) {
if (!IsQsRevampEnabled()) {
return;
}
SetUpButton();
for (int type = 0; type != static_cast<int>(A11yFeatureType::kFeatureCount);
type++) {
auto& feature = GetAccessibilityController()->GetFeature(
static_cast<A11yFeatureType>(type));
feature.SetEnabled(true);
if (!feature.enabled()) {
continue;
}
if (feature.toggleable_in_quicksettings()) {
EXPECT_TRUE(GetFeatureTile()->IsToggled());
} else {
EXPECT_FALSE(GetFeatureTile()->IsToggled());
}
feature.SetEnabled(false);
}
}
// Enable accessibility features one by one until we have double digits in the
// count shown in the `sub_label`.
TEST_P(AccessibilityFeaturePodControllerTest,
FeatureTileSubLabelCounterBehavior) {
if (!IsQsRevampEnabled()) {
return;
}
SetUpButton();
GetAccessibilityController()
->GetFeature(A11yFeatureType::kLargeCursor)
.SetEnabled(true);
int expected_count = 0;
auto feature_types = {
A11yFeatureType::kCaretHighlight, A11yFeatureType::kCursorHighlight,
A11yFeatureType::kDictation, A11yFeatureType::kFocusHighlight,
A11yFeatureType::kHighContrast, A11yFeatureType::kMonoAudio,
A11yFeatureType::kLiveCaption, A11yFeatureType::kFullscreenMagnifier,
A11yFeatureType::kStickyKeys, A11yFeatureType::kSwitchAccess};
for (A11yFeatureType type : feature_types) {
auto& feature = GetAccessibilityController()->GetFeature(
static_cast<A11yFeatureType>(type));
feature.SetEnabled(true);
expected_count++;
EXPECT_TRUE(base::EndsWith(GetFeatureTile()->sub_label()->GetText(),
base::NumberToString16(expected_count)));
}
for (A11yFeatureType type : feature_types) {
EXPECT_TRUE(base::EndsWith(GetFeatureTile()->sub_label()->GetText(),
base::NumberToString16(expected_count)));
auto& feature = GetAccessibilityController()->GetFeature(
static_cast<A11yFeatureType>(type));
expected_count--;
feature.SetEnabled(false);
}
}
} // namespace ash

@ -267,8 +267,9 @@ void FeatureTile::UpdateColors() {
}
void FeatureTile::SetToggled(bool toggled) {
if (!is_togglable_ || toggled_ == toggled)
if (!is_togglable_ || toggled_ == toggled) {
return;
}
toggled_ = toggled;
UpdateColors();
@ -302,6 +303,10 @@ void FeatureTile::SetLabel(const std::u16string& label) {
label_->SetText(label);
}
int FeatureTile::GetSubLabelMaxWidth() const {
return kTitlesContainerSize.width();
}
void FeatureTile::SetSubLabel(const std::u16string& sub_label) {
sub_label_->SetText(sub_label);
}

@ -110,6 +110,9 @@ class ASH_EXPORT FeatureTile : public views::Button {
// Sets the text of `label_`.
void SetLabel(const std::u16string& label);
// Returns the maximum width for `sub_label_`.
int GetSubLabelMaxWidth() const;
// Sets the text of the `sub_label_`.
void SetSubLabel(const std::u16string& sub_label);