welcome_tour: Add A11y support
Enable ChromeVox will not prevent tour to show. This cl adds a different set of string for accessible labels in the help bubble view. Bug: b:323363476 Test: Added unittest and Tested on the device Change-Id: I28e7e62622a69b9912d18f5abfe09ee23a9213d1 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5315597 Reviewed-by: David Black <dmblack@google.com> Commit-Queue: Tao Wu <wutao@chromium.org> Cr-Commit-Position: refs/heads/main@{#1269979}
This commit is contained in:
ash
BUILD.gnash_strings.grd
ash_strings_grd
IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME.png.sha1IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK.png.sha1IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES.png.sha1IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME.png.sha1IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME.png.sha1IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_ACCNAME.png.sha1IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT.png.sha1IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME.png.sha1IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT.png.sha1
constants
user_education
chrome
tools/metrics/histograms/metadata/ash_user_education
@ -3114,6 +3114,7 @@ component("ash") {
|
||||
"//chromeos/components/sensors:sensors",
|
||||
"//chromeos/components/sensors/mojom",
|
||||
"//chromeos/components/webauthn",
|
||||
"//chromeos/constants",
|
||||
"//chromeos/crosapi/cpp",
|
||||
"//chromeos/dbus/constants",
|
||||
"//chromeos/dbus/init",
|
||||
|
@ -7443,25 +7443,46 @@ To shut down the device, press and hold the power button on the device again.
|
||||
No thanks
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT" desc="Text shown in the body of the dialog as part of the System UI Welcome Tour.">
|
||||
Take a quick tour to learn how to get around your Chromebook. Get up and running in 6 steps.
|
||||
Take a quick tour to learn how to get around your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>. Get up and running in 5 steps.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT" desc="Text shown as the Welcome Tour dialog title.">
|
||||
Hi there. Chromebook is a little different.
|
||||
Hi there. <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> is a little different.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the Explore app as part of the System UI Welcome Tour.">
|
||||
Those were the basics! Continue in Explore, our built-in app for tips and help. You’ll find tips for getting started, recommended apps, special offers, and the newest Chromebook features.
|
||||
Those were the basics! Continue in Explore, our built-in app for tips and help. You’ll find tips for getting started, recommended apps, special offers, and the newest <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> features.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the home button as part of the System UI Welcome Tour.">
|
||||
Your Chromebook comes with built-in apps to help you get stuff done. Find your apps in the Launcher.
|
||||
<message name="IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the home button as part of the System UI Welcome Tour.">
|
||||
Tour step 3 of 5. Use apps to do everything you need on your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>. You can find your apps in the Launcher. Press Alt + Shift + L to focus on the Launcher button.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK" desc="Text shown in the body of a help bubble anchored to the home button as part of the System UI Welcome Tour.">
|
||||
Use apps to do everything you need on your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>. You can find your apps in the Launcher. You can also press the Launcher key (above the left Shift key) on the keyboard.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES" desc="Text shown in the body of a help bubble anchored to the home button as part of the System UI Welcome Tour.">
|
||||
Use apps to do everything you need on your <ph name="PRODUCT_NAME">$1<ex>Chromebox</ex></ph>. You can find your apps in the Launcher.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT" desc="Text which is overridden and so not shown in the body of a help bubble as part of the System UI Welcome Tour but which is still required to be provided to satisfy API requirements." translateable="false">
|
||||
''' '''
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the search box as part of the System UI Welcome Tour.">
|
||||
Tour step 4 of 5. Once Launcher is activated, you’ll get an enhanced search bar. You can start typing to search for your files, apps, and more. You can also get answers to questions about your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the search box as part of the System UI Welcome Tour.">
|
||||
Search for your files, apps, and more in the Launcher. You can also get answers to questions about your Chromebook.
|
||||
Search for your files, apps, and more in the Launcher. You can also get answers to questions about your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph>.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the Settings app as part of the System UI Welcome Tour">
|
||||
Tour step 5 of 5. You can find your device Settings in Launcher. Try customizing your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> in Settings like changing your wallpaper or setting a screen saver.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the Settings app as part of the System UI Welcome Tour">
|
||||
Customize and personalize your Chromebook in Settings. Try changing your wallpaper or setting a screen saver.
|
||||
Customize and personalize your <ph name="PRODUCT_NAME">$1<ex>Chromebook</ex></ph> in Settings. Try changing your wallpaper or setting a screen saver.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the shelf as part of the System UI Welcome Tour.">
|
||||
Tour step 1 of 5. Your pinned and open apps are on the shelf located at the bottom of your screen. Press Alt + Shift + L then tab to focus on shelf items.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the shelf as part of the System UI Welcome Tour.">
|
||||
Your pinned and open apps are on the Shelf. To pin an app to the Shelf, right-click an app or tap your touchpad with two fingers.
|
||||
Your pinned and open apps are on the shelf. To pin an app to the shelf, right-click an app or tap your touchpad with two fingers.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME" desc="The accessible name of a help bubble anchored to the status area as part of the System UI Welcome Tour.">
|
||||
Tour step 2 of 5. Frequently used controls like Wi-Fi, Bluetooth and volume are in Quick Settings. You can also go here to take screenshots. Press Alt + Shift + S to open Quick Settings.
|
||||
</message>
|
||||
<message name="IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT" desc="Text shown in the body of a help bubble anchored to the status area as part of the System UI Welcome Tour.">
|
||||
Frequently used controls like Wi-Fi, Bluetooth and volume are in Quick Settings. You can also go here to take screenshots.
|
||||
|
@ -1 +1 @@
|
||||
91f005bedaacf5f2aad71e6d14d785516e314450
|
||||
b08c55b3ed3576880e8392b6a93f0d87be670d1d
|
@ -1 +1 @@
|
||||
91f005bedaacf5f2aad71e6d14d785516e314450
|
||||
abdd1b8775319d9448142fdc111a167cf7d188e1
|
@ -1 +1 @@
|
||||
4d26e1f3f321fe1847588fbde7485635fad23855
|
||||
3e66510f6e69fdb7d266853f790053f92bb45bf3
|
@ -0,0 +1 @@
|
||||
50db39a2b7ba065eccee1cf516f22df2eb6b3fd2
|
@ -1 +0,0 @@
|
||||
d3f894789567c27afce7f35ce6df99cdcdd771fc
|
1
ash/ash_strings_grd/IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
a766a35a25a15ca955f256bba66c10f7e8e6a2ff
|
1
ash/ash_strings_grd/IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES.png.sha1
Normal file
1
ash/ash_strings_grd/IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES.png.sha1
Normal file
@ -0,0 +1 @@
|
||||
50f131a2b0a93fc71e26b1300967be48744f3e77
|
@ -0,0 +1 @@
|
||||
e8894f733d95248c62b26dab3d4105e930c811ea
|
@ -1 +1 @@
|
||||
af458f270c6b49e0451e8cd61843150f2ae57f21
|
||||
72af7447d4ad45234d7c88f495871cb5b3477b3e
|
@ -0,0 +1 @@
|
||||
5802fb73976b6c11ecc58d80caa101e8bf0fe1ca
|
@ -1 +1 @@
|
||||
671909ea0a3c0362619d421860c3d964f5a454e1
|
||||
c17dd9de3294d5e389772f8ed1146bcc85a4bb88
|
@ -0,0 +1 @@
|
||||
26f96d0e6ac397230594df7cb2b33be2bd3c0ef1
|
@ -1 +1 @@
|
||||
67d12523136eb3cdd936b127e4fcc9791ecbd4cd
|
||||
305612f7a60680c1683681b3a5a7ef912c8f86a6
|
@ -0,0 +1 @@
|
||||
6ec4e1ca2a23bb080ed7480fc3c1f0f284cd4530
|
@ -1 +1 @@
|
||||
1f223010c05927e508e24924da37f601a9065089
|
||||
6e4ef87469c088cbbe1ff97a0f2593b0ff3d8ff0
|
@ -2882,6 +2882,12 @@ BASE_FEATURE(kWelcomeTour, "WelcomeTour", base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
const base::FeatureParam<bool> kWelcomeTourEnabledCounterfactually{
|
||||
&kWelcomeTour, "is-counterfactual", false};
|
||||
|
||||
// Whether ChromeVox is supported in the Welcome Tour that walks new users
|
||||
// through ChromeOS System UI.
|
||||
BASE_FEATURE(kWelcomeTourChromeVoxSupported,
|
||||
"WelcomeTourChromeVoxSupported",
|
||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||
|
||||
// Forces user eligibility for the Welcome Tour that walks new users through
|
||||
// ChromeOS System UI. Enabling this flag has no effect unless `kWelcomeTour` is
|
||||
// also enabled.
|
||||
@ -4417,6 +4423,11 @@ bool IsWallpaperPerDeskEnabled() {
|
||||
return base::FeatureList::IsEnabled(kWallpaperPerDesk);
|
||||
}
|
||||
|
||||
bool IsWelcomeTourChromeVoxSupported() {
|
||||
return IsWelcomeTourEnabled() &&
|
||||
base::FeatureList::IsEnabled(kWelcomeTourChromeVoxSupported);
|
||||
}
|
||||
|
||||
bool IsWelcomeTourEnabled() {
|
||||
return base::FeatureList::IsEnabled(kWelcomeTour);
|
||||
}
|
||||
|
@ -874,6 +874,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kWelcomeTour);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kWelcomeTourChromeVoxSupported);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kWelcomeTourForceUserEligibility);
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
BASE_DECLARE_FEATURE(kWifiConnectMacAddressRandomization);
|
||||
@ -1284,6 +1286,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWallpaperFastRefreshEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS)
|
||||
bool IsWallpaperGooglePhotosSharedAlbumsEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWallpaperPerDeskEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourChromeVoxSupported();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourEnabled();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourEnabledCounterfactually();
|
||||
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsWelcomeTourForceUserEligibilityEnabled();
|
||||
|
@ -31,7 +31,9 @@ namespace ash::user_education_util {
|
||||
namespace {
|
||||
|
||||
// Keys used in `user_education::HelpBubbleParams::ExtendedProperties`.
|
||||
constexpr char kHelpBubbleAccessibleNameKey[] = "helpBubbleAccessibleName";
|
||||
constexpr char kHelpBubbleBodyIconKey[] = "helpBubbleBodyIcon";
|
||||
constexpr char kHelpBubbleBodyTextKey[] = "helpBubbleBodyText";
|
||||
constexpr char kHelpBubbleIdKey[] = "helpBubbleId";
|
||||
constexpr char kHelpBubbleModalTypeKey[] = "helpBubbleModalType";
|
||||
constexpr char kHelpBubbleStyleKey[] = "helpBubbleStyle";
|
||||
@ -117,10 +119,36 @@ user_education::HelpBubbleParams::ExtendedProperties CreateExtendedProperties(
|
||||
return extended_properties;
|
||||
}
|
||||
|
||||
user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateExtendedPropertiesWithAccessibleName(const std::string& accessible_name) {
|
||||
user_education::HelpBubbleParams::ExtendedProperties extended_properties;
|
||||
extended_properties.values().Set(kHelpBubbleAccessibleNameKey,
|
||||
accessible_name);
|
||||
return extended_properties;
|
||||
}
|
||||
|
||||
user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateExtendedPropertiesWithBodyText(const std::string& body_text) {
|
||||
user_education::HelpBubbleParams::ExtendedProperties extended_properties;
|
||||
extended_properties.values().Set(kHelpBubbleBodyTextKey, body_text);
|
||||
return extended_properties;
|
||||
}
|
||||
|
||||
const AccountId& GetAccountId(const UserSession* user_session) {
|
||||
return user_session ? user_session->user_info.account_id : EmptyAccountId();
|
||||
}
|
||||
|
||||
std::optional<std::string> GetHelpBubbleAccessibleName(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties) {
|
||||
if (const std::string* help_bubble_accessible_name =
|
||||
extended_properties.values().FindString(
|
||||
kHelpBubbleAccessibleNameKey)) {
|
||||
return *help_bubble_accessible_name;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const gfx::VectorIcon>>
|
||||
GetHelpBubbleBodyIcon(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
@ -135,6 +163,16 @@ GetHelpBubbleBodyIcon(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> GetHelpBubbleBodyText(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties) {
|
||||
if (const std::string* help_bubble_body_text =
|
||||
extended_properties.values().FindString(kHelpBubbleBodyTextKey)) {
|
||||
return *help_bubble_body_text;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
HelpBubbleId GetHelpBubbleId(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties) {
|
||||
|
@ -57,6 +57,14 @@ CreateExtendedProperties(HelpBubbleStyle help_bubble_style);
|
||||
ASH_EXPORT user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateExtendedProperties(ui::ModalType modal_type);
|
||||
|
||||
// Returns extended properties for a help bubble having set `accessible_name`.
|
||||
ASH_EXPORT user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateExtendedPropertiesWithAccessibleName(const std::string& accessible_name);
|
||||
|
||||
// Returns extended properties for a help bubble having set `body_text`.
|
||||
ASH_EXPORT user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateExtendedPropertiesWithBodyText(const std::string& body_text);
|
||||
|
||||
/*
|
||||
Creates an extended properties instance by merging `properties`.
|
||||
|
||||
@ -79,6 +87,13 @@ CreateExtendedProperties(Properties&&... properties) {
|
||||
// `user_session` is `nullptr`, `EmptyAccountId()` is returned.
|
||||
ASH_EXPORT const AccountId& GetAccountId(const UserSession* user_session);
|
||||
|
||||
// Returns help bubble accessible name from the specified `extended_properties`.
|
||||
// If the specified `extended_properties` does not contain help bubble
|
||||
// accessible name, an absent value is returned.
|
||||
ASH_EXPORT std::optional<std::string> GetHelpBubbleAccessibleName(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties);
|
||||
|
||||
// Returns help bubble body icon from the specified `external_properties`. If
|
||||
// the specified `external_properties` does not contain a help bubble body icon,
|
||||
// an absent value is returned.
|
||||
@ -87,6 +102,13 @@ GetHelpBubbleBodyIcon(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties);
|
||||
|
||||
// Returns help bubble body text from the specified `extended_properties`.
|
||||
// If the specified `extended_properties` does not contain help bubble
|
||||
// body text, an absent value is returned.
|
||||
ASH_EXPORT std::optional<std::string> GetHelpBubbleBodyText(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
extended_properties);
|
||||
|
||||
// Returns help bubble ID from the specified `extended_properties`.
|
||||
ASH_EXPORT HelpBubbleId GetHelpBubbleId(
|
||||
const user_education::HelpBubbleParams::ExtendedProperties&
|
||||
|
@ -121,6 +121,36 @@ TEST_F(UserEducationUtilTest, ExtendedPropertiesWithStyle) {
|
||||
std::nullopt);
|
||||
}
|
||||
|
||||
// Verifies that `CreateExtendedPropertiesWithAccessibleName()` can be used to
|
||||
// create extended properties for a help bubble having set accessible name, and
|
||||
// that `GetHelpBubbleAccessibleName()` can be used to retrieve help bubble
|
||||
// accessible name from extended properties.
|
||||
TEST_F(UserEducationUtilTest, ExtendedPropertiesWithAccessibleName) {
|
||||
std::string accessible_name = "Accessible Name";
|
||||
EXPECT_EQ(GetHelpBubbleAccessibleName(
|
||||
CreateExtendedPropertiesWithAccessibleName(accessible_name)),
|
||||
accessible_name);
|
||||
|
||||
// It is permissible to query help bubble accessible name even when absent.
|
||||
EXPECT_EQ(GetHelpBubbleAccessibleName(HelpBubbleParams::ExtendedProperties()),
|
||||
std::nullopt);
|
||||
}
|
||||
|
||||
// Verifies that `CreateExtendedPropertiesWithBodyText()` can be used to create
|
||||
// extended properties for a help bubble having set body text, and that
|
||||
// `GetHelpBubbleBodyText()` can be used to retrieve help bubble body text from
|
||||
// extended properties.
|
||||
TEST_F(UserEducationUtilTest, ExtendedPropertiesWithBodyText) {
|
||||
std::string body_text = "Body Text";
|
||||
EXPECT_EQ(
|
||||
GetHelpBubbleBodyText(CreateExtendedPropertiesWithBodyText(body_text)),
|
||||
body_text);
|
||||
|
||||
// It is permissible to query help bubble body text even when absent.
|
||||
EXPECT_EQ(GetHelpBubbleBodyText(HelpBubbleParams::ExtendedProperties()),
|
||||
std::nullopt);
|
||||
}
|
||||
|
||||
// Verifies that `ToString()` is working as intended.
|
||||
TEST_F(UserEducationUtilTest, ToString) {
|
||||
std::set<std::string> tutorial_id_strs;
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@ -22,6 +23,7 @@
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/metrics/user_metrics.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/types/pass_key.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
@ -300,13 +302,37 @@ HelpBubbleViewAsh::HelpBubbleViewAsh(
|
||||
}
|
||||
SetCancelCallback(std::move(params.dismiss_callback));
|
||||
|
||||
accessible_name_ = params.title_text;
|
||||
if (!accessible_name_.empty()) {
|
||||
accessible_name_ += u". ";
|
||||
// A body text provided from extended properties should take precedence
|
||||
// over the default body text provided from help bubble `params` since
|
||||
// extended properties are the ChromeOS-specific mechanism for overriding
|
||||
// platform agnostic behaviors.
|
||||
std::u16string body_text;
|
||||
if (auto body_text_from_extended_properties =
|
||||
user_education_util::GetHelpBubbleBodyText(
|
||||
params.extended_properties)) {
|
||||
body_text = base::UTF8ToUTF16(body_text_from_extended_properties.value());
|
||||
} else {
|
||||
body_text = params.body_text;
|
||||
}
|
||||
accessible_name_ += params.screenreader_text.empty()
|
||||
? params.body_text
|
||||
: params.screenreader_text;
|
||||
|
||||
// An accessible name provided from extended properties should take precedence
|
||||
// over the default accessible name provided from help bubble `params` since
|
||||
// extended properties are the ChromeOS-specific mechanism for overriding
|
||||
// platform agnostic behaviors.
|
||||
if (auto accessible_name_from_extended_properties =
|
||||
user_education_util::GetHelpBubbleAccessibleName(
|
||||
params.extended_properties)) {
|
||||
accessible_name_ =
|
||||
base::UTF8ToUTF16(accessible_name_from_extended_properties.value());
|
||||
} else {
|
||||
accessible_name_ = params.title_text;
|
||||
if (!accessible_name_.empty()) {
|
||||
accessible_name_ += u". ";
|
||||
}
|
||||
accessible_name_ +=
|
||||
params.screenreader_text.empty() ? body_text : params.screenreader_text;
|
||||
}
|
||||
|
||||
screenreader_hint_text_ = params.keyboard_navigation_hint;
|
||||
|
||||
// Since we don't have any controls for the user to interact with (we're just
|
||||
@ -380,16 +406,14 @@ HelpBubbleViewAsh::HelpBubbleViewAsh(
|
||||
labels_.push_back(
|
||||
top_text_container->AddChildView(bubble_utils::CreateLabel(
|
||||
TypographyToken::kCrosBody1, params.title_text)));
|
||||
views::Label* label =
|
||||
AddChildViewAt(bubble_utils::CreateLabel(TypographyToken::kCrosBody1,
|
||||
params.body_text),
|
||||
GetIndexOf(button_container).value());
|
||||
views::Label* label = AddChildViewAt(
|
||||
bubble_utils::CreateLabel(TypographyToken::kCrosBody1, body_text),
|
||||
GetIndexOf(button_container).value());
|
||||
labels_.push_back(label);
|
||||
label->SetProperty(views::kElementIdentifierKey, kBodyTextIdForTesting);
|
||||
} else {
|
||||
views::Label* label =
|
||||
top_text_container->AddChildView(bubble_utils::CreateLabel(
|
||||
TypographyToken::kCrosBody1, params.body_text));
|
||||
views::Label* label = top_text_container->AddChildView(
|
||||
bubble_utils::CreateLabel(TypographyToken::kCrosBody1, body_text));
|
||||
labels_.push_back(label);
|
||||
label->SetProperty(views::kElementIdentifierKey, kBodyTextIdForTesting);
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "ash/user_education/welcome_tour/welcome_tour_accelerator_handler.h"
|
||||
|
||||
#include "ash/accelerators/ash_accelerator_configuration.h"
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/shell.h"
|
||||
#include "base/ranges/algorithm.h"
|
||||
#include "base/task/sequenced_task_runner.h"
|
||||
@ -43,6 +44,11 @@ void WelcomeTourAcceleratorHandler::OnKeyEvent(ui::KeyEvent* event) {
|
||||
// Block `event` if `action` is not allowed.
|
||||
event->StopPropagation();
|
||||
} else if (action_it->aborts_tour) {
|
||||
if (action_it->action == AcceleratorAction::kToggleSpokenFeedback &&
|
||||
features::IsWelcomeTourChromeVoxSupported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Aborting the Welcome Tour could affect the enabling of `action`.
|
||||
// Therefore, abort the Welcome Tour asynchronously.
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
|
||||
|
@ -34,7 +34,9 @@
|
||||
#include "base/check_op.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/timer/elapsed_timer.h"
|
||||
#include "chromeos/constants/devicetype.h"
|
||||
#include "components/user_education/common/events.h"
|
||||
#include "components/user_education/common/help_bubble.h"
|
||||
#include "components/user_education/common/tutorial_description.h"
|
||||
@ -42,7 +44,9 @@
|
||||
#include "ui/base/interaction/element_identifier.h"
|
||||
#include "ui/base/interaction/element_tracker.h"
|
||||
#include "ui/base/interaction/interaction_sequence.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/ui_base_types.h"
|
||||
#include "ui/chromeos/devicetype_utils.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/display/tablet_state.h"
|
||||
@ -68,6 +72,24 @@ CreateHelpBubbleExtendedProperties(HelpBubbleId help_bubble_id) {
|
||||
/*body_icon=*/gfx::kNoneIcon));
|
||||
}
|
||||
|
||||
user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateHelpBubbleExtendedProperties(HelpBubbleId help_bubble_id,
|
||||
const std::string& body_text) {
|
||||
return user_education_util::CreateExtendedProperties(
|
||||
CreateHelpBubbleExtendedProperties(help_bubble_id),
|
||||
user_education_util::CreateExtendedPropertiesWithBodyText(body_text));
|
||||
}
|
||||
|
||||
user_education::HelpBubbleParams::ExtendedProperties
|
||||
CreateHelpBubbleExtendedProperties(HelpBubbleId help_bubble_id,
|
||||
const std::string& accessible_name,
|
||||
const std::string& body_text) {
|
||||
return user_education_util::CreateExtendedProperties(
|
||||
CreateHelpBubbleExtendedProperties(help_bubble_id, body_text),
|
||||
user_education_util::CreateExtendedPropertiesWithAccessibleName(
|
||||
accessible_name));
|
||||
}
|
||||
|
||||
base::RepeatingCallback<void(ui::TrackedElement*)> DefaultNextButtonCallback() {
|
||||
return base::BindRepeating([](ui::TrackedElement* current_anchor) {
|
||||
ui::ElementTracker::GetFrameworkDelegate()->NotifyCustomEvent(
|
||||
@ -154,6 +176,8 @@ ui::ElementContext WelcomeTourController::GetInitialElementContext() const {
|
||||
|
||||
user_education::TutorialDescription
|
||||
WelcomeTourController::GetTutorialDescription() const {
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
|
||||
user_education::TutorialDescription tutorial_description;
|
||||
tutorial_description.complete_button_text_id =
|
||||
IDS_ASH_WELCOME_TOUR_COMPLETE_BUTTON_TEXT;
|
||||
@ -177,6 +201,7 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
user_education::TutorialDescription::BubbleStep(kShelfViewElementId)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomCenter)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleScreenreaderText(IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_ACCNAME)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourShelf))
|
||||
.AddCustomNextButton(DefaultNextButtonCallback().Then(
|
||||
@ -201,6 +226,8 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
kUnifiedSystemTrayElementName)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomRight)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleScreenreaderText(
|
||||
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_ACCNAME)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourStatusArea))
|
||||
.AddCustomNextButton(DefaultNextButtonCallback().Then(
|
||||
@ -224,9 +251,20 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
tutorial_description.steps.emplace_back(
|
||||
user_education::TutorialDescription::BubbleStep(kHomeButtonElementName)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourHomeButton))
|
||||
HelpBubbleId::kWelcomeTourHomeButton,
|
||||
/*accessible_name=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME,
|
||||
product_name),
|
||||
/*body_text=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
(chromeos::GetDeviceType() ==
|
||||
chromeos::DeviceType::kChromebook)
|
||||
? IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK
|
||||
: IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES,
|
||||
product_name)))
|
||||
.AddCustomNextButton(base::BindRepeating([](ui::TrackedElement*) {
|
||||
Shell::Get()->app_list_controller()->Show(
|
||||
GetPrimaryDisplayId(),
|
||||
@ -244,9 +282,16 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
tutorial_description.steps.emplace_back(
|
||||
user_education::TutorialDescription::BubbleStep(kSearchBoxViewElementId)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kTopCenter)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourSearchBox))
|
||||
HelpBubbleId::kWelcomeTourSearchBox,
|
||||
/*accessible_name=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME, product_name),
|
||||
/*body_text=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT,
|
||||
product_name)))
|
||||
.AddCustomNextButton(DefaultNextButtonCallback().Then(
|
||||
base::BindRepeating(&WelcomeTourController::SetCurrentStep,
|
||||
weak_ptr_factory_.GetMutableWeakPtr(),
|
||||
@ -264,9 +309,17 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
tutorial_description.steps.emplace_back(
|
||||
user_education::TutorialDescription::BubbleStep(kSettingsAppElementId)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourSettingsApp))
|
||||
HelpBubbleId::kWelcomeTourSettingsApp,
|
||||
/*accessible_name=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME,
|
||||
product_name),
|
||||
/*body_text=*/
|
||||
l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT,
|
||||
product_name)))
|
||||
.AddCustomNextButton(DefaultNextButtonCallback().Then(
|
||||
base::BindRepeating(&WelcomeTourController::SetCurrentStep,
|
||||
weak_ptr_factory_.GetMutableWeakPtr(),
|
||||
@ -281,12 +334,16 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
.InSameContext());
|
||||
|
||||
// Step 6: Explore app.
|
||||
// NOTE: The accessible name is the same as the body text.
|
||||
tutorial_description.steps.emplace_back(
|
||||
user_education::TutorialDescription::BubbleStep(kExploreAppElementId)
|
||||
.SetBubbleArrow(user_education::HelpBubbleArrow::kBottomLeft)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT)
|
||||
.SetBubbleBodyText(IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT)
|
||||
.SetExtendedProperties(CreateHelpBubbleExtendedProperties(
|
||||
HelpBubbleId::kWelcomeTourExploreApp))
|
||||
HelpBubbleId::kWelcomeTourExploreApp,
|
||||
/*body_text=*/l10n_util::GetStringFUTF8(
|
||||
IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT,
|
||||
product_name)))
|
||||
.InSameContext());
|
||||
|
||||
// Step 7: Explore app window.
|
||||
@ -296,14 +353,27 @@ WelcomeTourController::GetTutorialDescription() const {
|
||||
}
|
||||
|
||||
void WelcomeTourController::OnAccessibilityControllerShutdown() {
|
||||
if (features::IsWelcomeTourChromeVoxSupported()) {
|
||||
accessibility_observation_.Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
MaybeAbortWelcomeTour(welcome_tour_metrics::AbortedReason::kShutdown);
|
||||
}
|
||||
|
||||
void WelcomeTourController::OnAccessibilityStatusChanged() {
|
||||
if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled()) {
|
||||
MaybeAbortWelcomeTour(
|
||||
welcome_tour_metrics::AbortedReason::kChromeVoxEnabled);
|
||||
if (!Shell::Get()->accessibility_controller()->spoken_feedback().enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the usage of ChromeVox in Welcome Tour.
|
||||
if (features::IsWelcomeTourChromeVoxSupported()) {
|
||||
welcome_tour_metrics::RecordChromeVoxEnabled(
|
||||
welcome_tour_metrics::ChromeVoxEnabled::kDuringTour);
|
||||
return;
|
||||
}
|
||||
|
||||
MaybeAbortWelcomeTour(welcome_tour_metrics::AbortedReason::kChromeVoxEnabled);
|
||||
}
|
||||
|
||||
void WelcomeTourController::OnActiveUserSessionChanged(
|
||||
@ -400,8 +470,9 @@ void WelcomeTourController::MaybeStartWelcomeTour() {
|
||||
: base::BindOnce(&LaunchExploreAppAsync,
|
||||
UserEducationPrivateApiKey()));
|
||||
|
||||
// Welcome Tour is not supported with ChromeVox enabled.
|
||||
if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled()) {
|
||||
// Welcome Tour is only conditionally supported with ChromeVox enabled.
|
||||
if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled() &&
|
||||
!features::IsWelcomeTourChromeVoxSupported()) {
|
||||
welcome_tour_metrics::RecordTourPrevented(
|
||||
welcome_tour_metrics::PreventedReason::kChromeVoxEnabled);
|
||||
return;
|
||||
@ -462,6 +533,12 @@ void WelcomeTourController::MaybeAbortWelcomeTour(
|
||||
}
|
||||
|
||||
void WelcomeTourController::OnWelcomeTourStarted() {
|
||||
if (Shell::Get()->accessibility_controller()->spoken_feedback().enabled()) {
|
||||
CHECK(features::IsWelcomeTourChromeVoxSupported());
|
||||
welcome_tour_metrics::RecordChromeVoxEnabled(
|
||||
welcome_tour_metrics::ChromeVoxEnabled::kBeforeTour);
|
||||
}
|
||||
|
||||
aborted_reason_ = welcome_tour_metrics::AbortedReason::kUnknown;
|
||||
accelerator_handler_ = std::make_unique<WelcomeTourAcceleratorHandler>(
|
||||
base::BindRepeating(&WelcomeTourController::MaybeAbortWelcomeTour,
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "base/test/metrics/histogram_tester.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "chromeos/constants/devicetype.h"
|
||||
#include "components/account_id/account_id.h"
|
||||
#include "components/services/app_service/public/cpp/app_launch_util.h"
|
||||
#include "components/user_education/common/tutorial_description.h"
|
||||
@ -54,6 +55,8 @@
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/abseil-cpp/absl/types/variant.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/chromeos/devicetype_utils.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/events/types/event_type.h"
|
||||
@ -68,15 +71,18 @@ namespace ash {
|
||||
namespace {
|
||||
|
||||
// Aliases.
|
||||
using ::ash::welcome_tour_metrics::ChromeVoxEnabled;
|
||||
using ::ash::welcome_tour_metrics::PreventedReason;
|
||||
using ::base::test::RunOnceClosure;
|
||||
using ::session_manager::SessionState;
|
||||
using ::testing::_;
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Conditional;
|
||||
using ::testing::Contains;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::IsTrue;
|
||||
using ::testing::Matches;
|
||||
using ::testing::Mock;
|
||||
@ -110,6 +116,10 @@ auto MoveArgs(T*... out) {
|
||||
|
||||
// Matchers --------------------------------------------------------------------
|
||||
|
||||
MATCHER_P2(StringFUT8Eq, message_id, sub, "") {
|
||||
return Matches(l10n_util::GetStringFUTF8(message_id, sub))(arg);
|
||||
}
|
||||
|
||||
MATCHER_P(ElementSpecifierEq, element_specifier, "") {
|
||||
return absl::visit(base::Overloaded{
|
||||
[&](const ui::ElementIdentifier& element_id) {
|
||||
@ -144,6 +154,53 @@ MATCHER_P6(BubbleStep,
|
||||
&util::GetHelpBubbleBodyIcon(ext_props)->get() == &gfx::kNoneIcon;
|
||||
}
|
||||
|
||||
MATCHER_P7(BubbleStep,
|
||||
element_specifier,
|
||||
context_mode,
|
||||
help_bubble_id,
|
||||
body_text_id,
|
||||
body_text_matcher,
|
||||
arrow,
|
||||
has_next_button,
|
||||
"") {
|
||||
namespace util = user_education_util;
|
||||
const auto& ext_props = arg.extended_properties();
|
||||
return arg.step_type() == ui::InteractionSequence::StepType::kShown &&
|
||||
Matches(ElementSpecifierEq(element_specifier))(arg) &&
|
||||
arg.context_mode() == context_mode &&
|
||||
util::GetHelpBubbleId(ext_props) == help_bubble_id &&
|
||||
arg.body_text_id() == body_text_id && arg.arrow() == arrow &&
|
||||
Matches(body_text_matcher)(util::GetHelpBubbleBodyText(ext_props)) &&
|
||||
arg.next_button_callback().is_null() != has_next_button &&
|
||||
&util::GetHelpBubbleBodyIcon(ext_props)->get() == &gfx::kNoneIcon &&
|
||||
util::GetHelpBubbleModalType(ext_props) == ui::MODAL_TYPE_SYSTEM;
|
||||
}
|
||||
|
||||
MATCHER_P8(BubbleStep,
|
||||
element_specifier,
|
||||
context_mode,
|
||||
help_bubble_id,
|
||||
accessible_name_matcher,
|
||||
body_text_id,
|
||||
body_text_matcher,
|
||||
arrow,
|
||||
has_next_button,
|
||||
"") {
|
||||
namespace util = user_education_util;
|
||||
const auto& ext_props = arg.extended_properties();
|
||||
return arg.step_type() == ui::InteractionSequence::StepType::kShown &&
|
||||
Matches(ElementSpecifierEq(element_specifier))(arg) &&
|
||||
arg.context_mode() == context_mode &&
|
||||
util::GetHelpBubbleId(ext_props) == help_bubble_id &&
|
||||
Matches(accessible_name_matcher)(
|
||||
util::GetHelpBubbleAccessibleName(ext_props)) &&
|
||||
arg.body_text_id() == body_text_id && arg.arrow() == arrow &&
|
||||
Matches(body_text_matcher)(util::GetHelpBubbleBodyText(ext_props)) &&
|
||||
arg.next_button_callback().is_null() != has_next_button &&
|
||||
&util::GetHelpBubbleBodyIcon(ext_props)->get() == &gfx::kNoneIcon &&
|
||||
util::GetHelpBubbleModalType(ext_props) == ui::MODAL_TYPE_SYSTEM;
|
||||
}
|
||||
|
||||
MATCHER_P2(HiddenStep, element_specifier, context_mode, "") {
|
||||
return arg.step_type() == ui::InteractionSequence::StepType::kHidden &&
|
||||
Matches(ElementSpecifierEq(element_specifier))(arg) &&
|
||||
@ -303,6 +360,8 @@ TEST_F(WelcomeTourControllerTest, GetTutorialDescription) {
|
||||
auto* welcome_tour_controller = WelcomeTourController::Get();
|
||||
ASSERT_TRUE(welcome_tour_controller);
|
||||
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
|
||||
EXPECT_THAT(
|
||||
welcome_tour_controller->GetTutorialDescription(),
|
||||
AllOf(
|
||||
@ -333,36 +392,62 @@ TEST_F(WelcomeTourControllerTest, GetTutorialDescription) {
|
||||
EventStep(ElementSpecifier(kUnifiedSystemTrayElementName),
|
||||
ContextMode::kFromPreviousStep,
|
||||
/*has_name_elements_callback=*/true),
|
||||
BubbleStep(ElementSpecifier(kHomeButtonElementName),
|
||||
ContextMode::kAny,
|
||||
HelpBubbleId::kWelcomeTourHomeButton,
|
||||
IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT,
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/true),
|
||||
BubbleStep(ElementSpecifier(kSearchBoxViewElementId),
|
||||
ContextMode::kAny,
|
||||
HelpBubbleId::kWelcomeTourSearchBox,
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT,
|
||||
HelpBubbleArrow::kTopCenter,
|
||||
/*has_next_button=*/true),
|
||||
BubbleStep(
|
||||
ElementSpecifier(kHomeButtonElementName),
|
||||
ContextMode::kAny, HelpBubbleId::kWelcomeTourHomeButton,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_ACCNAME,
|
||||
product_name),
|
||||
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
|
||||
StringFUT8Eq(
|
||||
(chromeos::GetDeviceType() ==
|
||||
chromeos::DeviceType::kChromebook)
|
||||
? IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK
|
||||
: IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES,
|
||||
product_name),
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/true),
|
||||
BubbleStep(
|
||||
ElementSpecifier(kSearchBoxViewElementId),
|
||||
ContextMode::kAny, HelpBubbleId::kWelcomeTourSearchBox,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_ACCNAME,
|
||||
product_name),
|
||||
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT,
|
||||
product_name),
|
||||
HelpBubbleArrow::kTopCenter,
|
||||
/*has_next_button=*/true),
|
||||
EventStep(ElementSpecifier(kSearchBoxViewElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
/*has_name_elements_callback=*/false),
|
||||
BubbleStep(ElementSpecifier(kSettingsAppElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
HelpBubbleId::kWelcomeTourSettingsApp,
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT,
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/true),
|
||||
BubbleStep(
|
||||
ElementSpecifier(kSettingsAppElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
HelpBubbleId::kWelcomeTourSettingsApp,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_ACCNAME,
|
||||
product_name),
|
||||
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT,
|
||||
product_name),
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/true),
|
||||
EventStep(ElementSpecifier(kSettingsAppElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
/*has_name_elements_callback=*/false),
|
||||
BubbleStep(ElementSpecifier(kExploreAppElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
HelpBubbleId::kWelcomeTourExploreApp,
|
||||
IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT,
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/false)))));
|
||||
BubbleStep(
|
||||
ElementSpecifier(kExploreAppElementId),
|
||||
ContextMode::kFromPreviousStep,
|
||||
HelpBubbleId::kWelcomeTourExploreApp,
|
||||
IDS_ASH_WELCOME_TOUR_OVERRIDDEN_BUBBLE_BODY_TEXT,
|
||||
StringFUT8Eq(
|
||||
IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT,
|
||||
product_name),
|
||||
HelpBubbleArrow::kBottomLeft,
|
||||
/*has_next_button=*/false)))));
|
||||
}
|
||||
|
||||
// Verifies that the Welcome Tour is started when the primary user session is
|
||||
@ -515,8 +600,42 @@ TEST_F(WelcomeTourControllerTest, AbortsTourAndPropagatesEvents) {
|
||||
EXPECT_TRUE(ended_future.Wait());
|
||||
}
|
||||
|
||||
// Verifies the Welcome Tour to be aborted if ChromeVox is enabled during tour.
|
||||
TEST_F(WelcomeTourControllerTest, AbortTourIfChromeVoxEnabledDuringTour) {
|
||||
// WelcomeTourControllerChromeVoxTest -------------------------------------
|
||||
|
||||
// Base class for tests of the `WelcomeTourController` which are concerned with
|
||||
// the behaviors when ChromeVox is supported in the Welcome Tour, parameterized
|
||||
// by whether ChromeVox is supported.
|
||||
class WelcomeTourControllerChromeVoxTest
|
||||
: public WelcomeTourControllerTest,
|
||||
public ::testing::WithParamInterface<
|
||||
/*is_chromevox_supported=*/std::optional<bool>> {
|
||||
public:
|
||||
WelcomeTourControllerChromeVoxTest() {
|
||||
scoped_feature_list_.InitWithFeatureState(
|
||||
features::kWelcomeTourChromeVoxSupported, IsChromeVoxSupported());
|
||||
}
|
||||
|
||||
// Returns whether ChromeVox is supported in the Welcome Tour given test
|
||||
// parameterization.
|
||||
bool IsChromeVoxSupported() const { return GetParam().value_or(false); }
|
||||
|
||||
private:
|
||||
// Used to conditionally enable ChromeVox support in the Welcome Tour
|
||||
// given test parameterization.
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(All,
|
||||
WelcomeTourControllerChromeVoxTest,
|
||||
/*is_chromevox_supported=*/
|
||||
::testing::Values(std::make_optional(true),
|
||||
std::make_optional(false),
|
||||
std::nullopt));
|
||||
|
||||
// Verifies the Welcome Tour is aborted if ChromeVox is not supported but is
|
||||
// enabled during the tour.
|
||||
TEST_P(WelcomeTourControllerChromeVoxTest,
|
||||
MaybeAbortTourIfChromeVoxEnabledDuringTour) {
|
||||
// Start the Welcome Tour by logging in the primary user for the first time.
|
||||
const auto primary_account_id = AccountId::FromUserEmail("primary@test");
|
||||
SimulateNewUserFirstLogin(primary_account_id.GetUserEmail());
|
||||
@ -527,33 +646,48 @@ TEST_F(WelcomeTourControllerTest, AbortTourIfChromeVoxEnabledDuringTour) {
|
||||
observation{&observer};
|
||||
observation.Observe(WelcomeTourController::Get());
|
||||
|
||||
// Satisfy `ended_future` when an end event is received.
|
||||
base::test::TestFuture<void> ended_future;
|
||||
EXPECT_CALL(observer, OnWelcomeTourEnded)
|
||||
.WillOnce(RunOnceClosure(ended_future.GetCallback()));
|
||||
|
||||
// Expect the Welcome Tour to be aborted when enabling ChromeVox during tour.
|
||||
// The Welcome Tour is only expected to abort when ChromeVox is enabled if
|
||||
// ChromeVox is not supported.
|
||||
const bool expect_abort = !IsChromeVoxSupported();
|
||||
EXPECT_CALL(
|
||||
*user_education_delegate(),
|
||||
AbortTutorial(Eq(primary_account_id), Eq(TutorialId::kWelcomeTour)));
|
||||
AbortTutorial(Eq(primary_account_id), Eq(TutorialId::kWelcomeTour)))
|
||||
.Times(expect_abort ? 1 : 0);
|
||||
|
||||
// Expect an attempt to launch the Explore app when the tour is aborted.
|
||||
// Expect the Welcome Tour to end only if it is expected to abort.
|
||||
EXPECT_CALL(observer, OnWelcomeTourEnded).Times(expect_abort ? 1 : 0);
|
||||
|
||||
// Expect an attempt to launch the Explore app only if the Welcome Tour is
|
||||
// aborted.
|
||||
EXPECT_CALL(*user_education_delegate(),
|
||||
LaunchSystemWebAppAsync(
|
||||
Eq(primary_account_id), Eq(ash::SystemWebAppType::HELP),
|
||||
Eq(apps::LaunchSource::kFromWelcomeTour),
|
||||
Eq(display::Screen::GetScreen()->GetPrimaryDisplay().id())));
|
||||
Eq(display::Screen::GetScreen()->GetPrimaryDisplay().id())))
|
||||
.Times(expect_abort ? 1 : 0);
|
||||
|
||||
base::HistogramTester histogram_tester;
|
||||
auto* const a11y_controller = Shell::Get()->accessibility_controller();
|
||||
a11y_controller->SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE);
|
||||
Mock::VerifyAndClearExpectations(user_education_delegate());
|
||||
EXPECT_TRUE(a11y_controller->spoken_feedback().enabled());
|
||||
|
||||
// Verify histograms.
|
||||
EXPECT_THAT(
|
||||
histogram_tester.GetAllSamples("Ash.WelcomeTour.ChromeVoxEnabled.When"),
|
||||
Conditional(IsChromeVoxSupported(),
|
||||
BucketsAre(base::Bucket(ChromeVoxEnabled::kBeforeTour, 0),
|
||||
base::Bucket(ChromeVoxEnabled::kDuringTour, 1)),
|
||||
IsEmpty()));
|
||||
}
|
||||
|
||||
// Checks that the Welcome Tour should NOT start if ChromeVox is enabled.
|
||||
TEST_F(WelcomeTourControllerTest, PreventTourFromStartingIfChromeVoxEnabled) {
|
||||
// Verifies the Welcome Tour is prevented from starting if ChromeVox is not
|
||||
// supported but is enabled.
|
||||
TEST_P(WelcomeTourControllerChromeVoxTest,
|
||||
MaybePreventTourFromStartingIfChromeVoxEnabled) {
|
||||
const auto primary_account_id = AccountId::FromUserEmail("primary@test");
|
||||
|
||||
base::HistogramTester histogram_tester;
|
||||
TestSessionControllerClient* const session = GetSessionControllerClient();
|
||||
session->AddUserSession(
|
||||
primary_account_id.GetUserEmail(), user_manager::UserType::kRegular,
|
||||
@ -567,17 +701,33 @@ TEST_F(WelcomeTourControllerTest, PreventTourFromStartingIfChromeVoxEnabled) {
|
||||
EXPECT_TRUE(a11y_controller->spoken_feedback().enabled());
|
||||
|
||||
// Start the Welcome Tour by activating the user session. Expect that the
|
||||
// Welcome Tour is NOT registered or started but that an attempt is made to
|
||||
// launch the Explore app.
|
||||
EXPECT_CALL(*user_education_delegate(), RegisterTutorial).Times(0);
|
||||
EXPECT_CALL(*user_education_delegate(), StartTutorial).Times(0);
|
||||
// Welcome Tour is NOT registered or started when ChromeVox is enabled if
|
||||
// ChromeVox is not supported.
|
||||
const bool expect_prevent = !IsChromeVoxSupported();
|
||||
EXPECT_CALL(*user_education_delegate(), RegisterTutorial)
|
||||
.Times(expect_prevent ? 0 : 1);
|
||||
EXPECT_CALL(*user_education_delegate(), StartTutorial)
|
||||
.Times(expect_prevent ? 0 : 1);
|
||||
|
||||
// Expect an attempt to launch the Explore app only if the Welcome Tour is
|
||||
// prevented.
|
||||
EXPECT_CALL(*user_education_delegate(),
|
||||
LaunchSystemWebAppAsync(
|
||||
Eq(primary_account_id), Eq(ash::SystemWebAppType::HELP),
|
||||
Eq(apps::LaunchSource::kFromWelcomeTour),
|
||||
Eq(display::Screen::GetScreen()->GetPrimaryDisplay().id())));
|
||||
Eq(display::Screen::GetScreen()->GetPrimaryDisplay().id())))
|
||||
.Times(expect_prevent ? 1 : 0);
|
||||
|
||||
session->SetSessionState(SessionState::ACTIVE);
|
||||
Mock::VerifyAndClearExpectations(user_education_delegate());
|
||||
|
||||
// Verify histograms.
|
||||
EXPECT_THAT(
|
||||
histogram_tester.GetAllSamples("Ash.WelcomeTour.ChromeVoxEnabled.When"),
|
||||
Conditional(IsChromeVoxSupported(),
|
||||
BucketsAre(base::Bucket(ChromeVoxEnabled::kBeforeTour, 1),
|
||||
base::Bucket(ChromeVoxEnabled::kDuringTour, 0)),
|
||||
IsEmpty()));
|
||||
}
|
||||
|
||||
// WelcomeTourControllerCounterfactualTest -------------------------------------
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "ash/user_education/welcome_tour/welcome_tour_dialog.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "ash/ash_element_identifiers.h"
|
||||
@ -16,6 +17,7 @@
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/base/resource/resource_bundle.h"
|
||||
#include "ui/base/ui_base_types.h"
|
||||
#include "ui/chromeos/devicetype_utils.h"
|
||||
#include "ui/views/controls/image_view.h"
|
||||
#include "ui/views/view_class_properties.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
@ -66,6 +68,8 @@ WelcomeTourDialog::WelcomeTourDialog(base::OnceClosure accept_callback,
|
||||
CHECK_EQ(g_instance, nullptr);
|
||||
g_instance = this;
|
||||
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
|
||||
views::Builder<SystemDialogDelegateView>(this)
|
||||
.SetAcceptButtonText(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_ACCEPT_BUTTON_TEXT))
|
||||
@ -74,12 +78,12 @@ WelcomeTourDialog::WelcomeTourDialog(base::OnceClosure accept_callback,
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_CANCEL_BUTTON_TEXT))
|
||||
.SetCancelCallback(std::move(cancel_callback))
|
||||
.SetCloseCallback(std::move(close_callback))
|
||||
.SetDescription(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT))
|
||||
.SetDescription(l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT, product_name))
|
||||
.SetModalType(ui::ModalType::MODAL_TYPE_SYSTEM)
|
||||
.SetProperty(views::kElementIdentifierKey, kWelcomeTourDialogElementId)
|
||||
.SetTitleText(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT))
|
||||
.SetTitleText(l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT, product_name))
|
||||
.SetTopContentView(views::Builder<views::ImageView>()
|
||||
.SetImage(ui::ResourceBundle::GetSharedInstance()
|
||||
.GetThemedLottieImageNamed(
|
||||
|
@ -38,7 +38,7 @@ TEST_F(WelcomeTourDialogPixelTest, Appearance) {
|
||||
// Take a screenshot of the Welcome Tour dialog.
|
||||
EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
|
||||
"welcome_tour_dialog",
|
||||
/*revision_number=*/2, WelcomeTourDialog::Get()));
|
||||
/*revision_number=*/3, WelcomeTourDialog::Get()));
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -28,6 +28,12 @@ PrefService* GetLastActiveUserPrefService() {
|
||||
|
||||
} // namespace
|
||||
|
||||
void RecordChromeVoxEnabled(ChromeVoxEnabled when) {
|
||||
CHECK(features::IsWelcomeTourEnabled());
|
||||
|
||||
base::UmaHistogramEnumeration("Ash.WelcomeTour.ChromeVoxEnabled.When", when);
|
||||
}
|
||||
|
||||
void RecordInteraction(Interaction interaction) {
|
||||
CHECK(features::IsWelcomeTourEnabled());
|
||||
|
||||
|
@ -32,6 +32,16 @@ enum class AbortedReason {
|
||||
kMaxValue = kShutdown,
|
||||
};
|
||||
|
||||
// Enumeration of when ChromeVox is enabled in the Welcome Tour. These values
|
||||
// are persisted to logs. Entries should not be renumbered and numeric values
|
||||
// should never be reused.
|
||||
enum class ChromeVoxEnabled {
|
||||
kMinValue = 0,
|
||||
kBeforeTour = kMinValue,
|
||||
kDuringTour = 1,
|
||||
kMaxValue = kDuringTour,
|
||||
};
|
||||
|
||||
// Enumeration of reasons the Welcome Tour may be prevented. These values are
|
||||
// persisted to logs. Entries should not be renumbered and numeric values should
|
||||
// never be reused. Be sure to update `kAllPreventedReasonsSet` accordingly.
|
||||
@ -106,6 +116,9 @@ static constexpr auto kAllInteractionsSet =
|
||||
|
||||
// Utilities -------------------------------------------------------------------
|
||||
|
||||
// Record the usage of ChromeVox in the Welcome Tour.
|
||||
ASH_EXPORT void RecordChromeVoxEnabled(ChromeVoxEnabled when);
|
||||
|
||||
// Record that a given `interaction` has occurred.
|
||||
ASH_EXPORT void RecordInteraction(Interaction interaction);
|
||||
|
||||
|
@ -22,9 +22,11 @@
|
||||
#include "chrome/browser/web_applications/web_app_id_constants.h"
|
||||
#include "chrome/browser/web_applications/web_app_provider.h"
|
||||
#include "chrome/test/interaction/interactive_browser_test.h"
|
||||
#include "chromeos/constants/devicetype.h"
|
||||
#include "components/strings/grit/components_strings.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/chromeos/devicetype_utils.h"
|
||||
#include "ui/events/event_constants.h"
|
||||
#include "ui/events/keycodes/keyboard_codes_posix.h"
|
||||
#include "ui/events/test/event_generator.h"
|
||||
@ -171,19 +173,22 @@ class WelcomeTourInteractiveUiTest : public InteractiveBrowserTest {
|
||||
// Returns a builder for an interaction step that checks the dialog
|
||||
// description.
|
||||
[[nodiscard]] static auto CheckDialogDescription() {
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
return CheckViewProperty(
|
||||
ash::SystemDialogDelegateView::kDescriptionTextIdForTesting,
|
||||
&views::Label::GetText,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT));
|
||||
l10n_util::GetStringFUTF16(IDS_ASH_WELCOME_TOUR_DIALOG_DESCRIPTION_TEXT,
|
||||
product_name));
|
||||
}
|
||||
|
||||
// Returns a builder for an interaction step that checks the dialog title.
|
||||
[[nodiscard]] static auto CheckDialogTitle() {
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
return CheckViewProperty(
|
||||
ash::SystemDialogDelegateView::kTitleTextIdForTesting,
|
||||
&views::Label::GetText,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT));
|
||||
l10n_util::GetStringFUTF16(IDS_ASH_WELCOME_TOUR_DIALOG_TITLE_TEXT,
|
||||
product_name));
|
||||
}
|
||||
|
||||
// Returns a builder for an interaction step that checks that the anchor of a
|
||||
@ -199,11 +204,11 @@ class WelcomeTourInteractiveUiTest : public InteractiveBrowserTest {
|
||||
}
|
||||
|
||||
// Returns a builder for an interaction step that checks that the body text of
|
||||
// a help bubble matches the specified `message_id`.
|
||||
[[nodiscard]] static auto CheckHelpBubbleBodyText(int message_id) {
|
||||
// a help bubble matches the specified `body_text`.
|
||||
[[nodiscard]] static auto CheckHelpBubbleBodyText(
|
||||
const std::u16string& body_text) {
|
||||
return CheckViewProperty(ash::HelpBubbleViewAsh::kBodyTextIdForTesting,
|
||||
&views::Label::GetText,
|
||||
l10n_util::GetStringUTF16(message_id));
|
||||
&views::Label::GetText, body_text);
|
||||
}
|
||||
|
||||
// Returns a builder for an interaction step that checks whether the help
|
||||
@ -242,6 +247,8 @@ class WelcomeTourInteractiveUiTest : public InteractiveBrowserTest {
|
||||
|
||||
// An interactive UI test that exercises the entire Welcome Tour.
|
||||
IN_PROC_BROWSER_TEST_F(WelcomeTourInteractiveUiTest, WelcomeTour) {
|
||||
const std::u16string product_name = ui::GetChromeOSDeviceName();
|
||||
|
||||
RunTestSequence(
|
||||
// Step 0: Dialog.
|
||||
InAnyContext(WaitForDialogVisibility(true)),
|
||||
@ -252,66 +259,71 @@ IN_PROC_BROWSER_TEST_F(WelcomeTourInteractiveUiTest, WelcomeTour) {
|
||||
|
||||
// Step 1: Shelf.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(Steps(
|
||||
CheckHelpBubbleAnchor(ash::kShelfViewElementId),
|
||||
CheckHelpBubbleBodyText(IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
InSameContext(
|
||||
Steps(CheckHelpBubbleAnchor(ash::kShelfViewElementId),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_SHELF_BUBBLE_BODY_TEXT)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 2: Status area.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(
|
||||
Steps(CheckHelpBubbleAnchor(ash::kUnifiedSystemTrayElementId),
|
||||
CheckHelpBubbleBodyText(
|
||||
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_STATUS_AREA_BUBBLE_BODY_TEXT)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 3: Home button.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(
|
||||
Steps(CheckHelpBubbleAnchor(ash::kHomeButtonElementId),
|
||||
CheckHelpBubbleBodyText(
|
||||
IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
InSameContext(Steps(
|
||||
CheckHelpBubbleAnchor(ash::kHomeButtonElementId),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringFUTF16(
|
||||
(chromeos::GetDeviceType() == chromeos::DeviceType::kChromebook)
|
||||
? IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_CHROMEBOOK
|
||||
: IDS_ASH_WELCOME_TOUR_HOME_BUTTON_BUBBLE_BODY_TEXT_OTHER_DEVICE_TYPES,
|
||||
product_name)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 4: Search box.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(
|
||||
Steps(CheckAppListBubbleVisibility(true),
|
||||
CheckHelpBubbleAnchor(ash::kSearchBoxViewElementId),
|
||||
CheckHelpBubbleBodyText(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
InSameContext(Steps(
|
||||
CheckAppListBubbleVisibility(true),
|
||||
CheckHelpBubbleAnchor(ash::kSearchBoxViewElementId),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_SEARCH_BOX_BUBBLE_BODY_TEXT, product_name)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 5: Settings app.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(
|
||||
Steps(CheckAppListBubbleVisibility(true),
|
||||
CheckHelpBubbleAnchor(ash::kSettingsAppElementId),
|
||||
CheckHelpBubbleBodyText(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_SETTINGS_APP_BUBBLE_BODY_TEXT,
|
||||
product_name)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(IDS_TUTORIAL_NEXT_BUTTON),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 6: Explore app.
|
||||
InAnyContext(WaitForHelpBubble()),
|
||||
InSameContext(
|
||||
Steps(CheckAppListBubbleVisibility(true),
|
||||
CheckHelpBubbleAnchor(ash::kExploreAppElementId),
|
||||
CheckHelpBubbleBodyText(
|
||||
IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(
|
||||
IDS_ASH_WELCOME_TOUR_COMPLETE_BUTTON_TEXT),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
InSameContext(Steps(
|
||||
CheckAppListBubbleVisibility(true),
|
||||
CheckHelpBubbleAnchor(ash::kExploreAppElementId),
|
||||
CheckHelpBubbleBodyText(l10n_util::GetStringFUTF16(
|
||||
IDS_ASH_WELCOME_TOUR_EXPLORE_APP_BUBBLE_BODY_TEXT, product_name)),
|
||||
CheckHelpBubbleDefaultButtonFocus(true),
|
||||
CheckHelpBubbleDefaultButtonText(
|
||||
IDS_ASH_WELCOME_TOUR_COMPLETE_BUTTON_TEXT),
|
||||
PressHelpBubbleDefaultButton(), FlushEvents())),
|
||||
|
||||
// Step 7: Explore app window.
|
||||
InAnyContext(WaitForBrowser()),
|
||||
|
@ -11268,6 +11268,7 @@ if (!is_android && !is_chromeos_device) {
|
||||
"//chromeos/ash/components/standalone_browser",
|
||||
"//components/app_constants",
|
||||
"//device/udev_linux:test_support",
|
||||
"//ui/chromeos",
|
||||
"//ui/events/devices",
|
||||
"//ui/events/devices:test_support",
|
||||
]
|
||||
|
@ -76,6 +76,11 @@ chromium-metrics-reviews@google.com.
|
||||
<int value="5" label="kShutdown"/>
|
||||
</enum>
|
||||
|
||||
<enum name="WelcomeTourChromeVoxEnabled">
|
||||
<int value="0" label="kBeforeTour"/>
|
||||
<int value="1" label="kDuringTour"/>
|
||||
</enum>
|
||||
|
||||
<enum name="WelcomeTourInteraction">
|
||||
<int value="0" label="kFilesApp"/>
|
||||
<int value="1" label="kLauncher"/>
|
||||
|
@ -182,6 +182,16 @@ chromium-metrics-reviews@google.com.
|
||||
</summary>
|
||||
</histogram>
|
||||
|
||||
<histogram name="Ash.WelcomeTour.ChromeVoxEnabled.When"
|
||||
enum="WelcomeTourChromeVoxEnabled" expires_after="2025-02-01">
|
||||
<owner>wutao@chromium.org</owner>
|
||||
<owner>teresachow@google.com</owner>
|
||||
<summary>
|
||||
Records when ChromeVox is enabled in the Welcome Tour. Only logged when the
|
||||
feature flag is turned on and the tour is shown.
|
||||
</summary>
|
||||
</histogram>
|
||||
|
||||
<histogram name="Ash.WelcomeTour.Prevented.Reason"
|
||||
enum="WelcomeTourPreventedReason" expires_after="2024-08-04">
|
||||
<owner>dmblack@google.com</owner>
|
||||
|
Reference in New Issue
Block a user