SC Education: Add Arm 2, Shortcut Tutorial
Add Arm 2 for Screen Capture Education, an extension of Arm 1's system nudge that opens a SystemDialogDelegateView when the primary button is clicked. The dialog widget includes a keyboard shortcut view and a themed image of the location of the shortcut keys on a keyboard. Bug: b/302368855 Test: manual + unit Change-Id: I7b88e984fcd1146611c5f3fe8b47f9585860ed6c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5015111 Reviewed-by: Alex Newcomer <newcomer@chromium.org> Reviewed-by: Ahmed Fakhry <afakhry@chromium.org> Commit-Queue: Elijah Hewer <hewer@chromium.org> Reviewed-by: Michele Fan <michelefan@chromium.org> Cr-Commit-Position: refs/heads/main@{#1230184}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
976affef18
commit
956f2334de
ash
ash_strings.grd
ash_strings_grd
IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_BUTTON.png.sha1IDS_ASH_SCREEN_CAPTURE_EDUCATION_TUTORIAL_TITLE.png.sha1
capture_mode
capture_mode_education_controller.cccapture_mode_education_controller.hcapture_mode_education_controller_unittest.cc
constants
public
cpp
ash_view_ids.h
resources
style
tools/metrics/histograms/metadata/ash
@ -6130,6 +6130,12 @@ Here are some things you can try to get started.
|
||||
<message name="IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_LABEL" desc="The label of the Screen Capture Education shortcut nudge (Arm 1) for using the screenshot accelerator.">
|
||||
To take a screenshot, press
|
||||
</message>
|
||||
<message name="IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_BUTTON" desc="The label of the button in the Screen Capture Education shortcut nudge (Arm 2).">
|
||||
Show me how
|
||||
</message>
|
||||
<message name="IDS_ASH_SCREEN_CAPTURE_EDUCATION_TUTORIAL_TITLE" desc="The title of the tutorial dialog for Screen Capture Education (Arm 2).">
|
||||
Press the screenshot shortcut
|
||||
</message>
|
||||
|
||||
<!-- Snap Group -->
|
||||
<message name="IDS_ASH_SNAP_GROUP_CLICK_TO_LOCK_WINDOWS" desc="Click to lock the windows.">
|
||||
|
@ -0,0 +1 @@
|
||||
ee4cc20e712f317e42bda0793dd4973c07ffc4d6
|
@ -0,0 +1 @@
|
||||
429ed526bbc9522910969a18a0af4da869a06b7d
|
@ -4,18 +4,29 @@
|
||||
|
||||
#include "ash/capture_mode/capture_mode_education_controller.h"
|
||||
|
||||
#include "ash/capture_mode/capture_mode_util.h"
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/constants/notifier_catalogs.h"
|
||||
#include "ash/public/cpp/ash_view_ids.h"
|
||||
#include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
|
||||
#include "ash/public/cpp/shell_window_ids.h"
|
||||
#include "ash/public/cpp/system/anchored_nudge_data.h"
|
||||
#include "ash/public/cpp/system/anchored_nudge_manager.h"
|
||||
#include "ash/resources/vector_icons/vector_icons.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/ash_color_id.h"
|
||||
#include "ash/style/keyboard_shortcut_view.h"
|
||||
#include "ash/style/system_dialog_delegate_view.h"
|
||||
#include "components/prefs/pref_registry_simple.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/events/keycodes/keyboard_codes_posix.h"
|
||||
#include "ui/views/controls/image_view.h"
|
||||
#include "ui/views/layout/table_layout.h"
|
||||
#include "ui/views/layout/table_layout_view.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace ash {
|
||||
|
||||
@ -31,6 +42,12 @@ constexpr char kCaptureModeNudgeId[] = "kCaptureModeNudge";
|
||||
// Nudge styling values.
|
||||
constexpr int kShortcutIconSize = 60;
|
||||
|
||||
// Tutorial styling values.
|
||||
constexpr int kRowSpacing = 30;
|
||||
constexpr int kTitleShortcutSpacing = 8;
|
||||
constexpr int kImageButtonSpacing = 20;
|
||||
constexpr int kKeyboardImageWidth = 448;
|
||||
|
||||
// Clock that can be overridden for testing.
|
||||
base::Clock* g_clock_override = nullptr;
|
||||
|
||||
@ -42,6 +59,89 @@ base::Time GetTime() {
|
||||
return g_clock_override ? g_clock_override->Now() : base::Time::Now();
|
||||
}
|
||||
|
||||
AnchoredNudgeData CreateBaseNudgeData(NudgeCatalogName catalog_name) {
|
||||
AnchoredNudgeData nudge_data(
|
||||
kCaptureModeNudgeId, catalog_name,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_LABEL));
|
||||
|
||||
nudge_data.image_model = ui::ImageModel::FromVectorIcon(
|
||||
kCaptureModeIcon, kColorAshIconColorPrimary, kShortcutIconSize);
|
||||
nudge_data.keyboard_codes = {ui::VKEY_CONTROL, ui::VKEY_SHIFT,
|
||||
ui::VKEY_MEDIA_LAUNCH_APP1};
|
||||
|
||||
return nudge_data;
|
||||
}
|
||||
|
||||
// Creates a view containing a keyboard illustration that indicates the
|
||||
// location of the keys in the screenshot keyboard shortcut.
|
||||
std::unique_ptr<views::ImageView> CreateImageView() {
|
||||
auto image_view = std::make_unique<views::ImageView>();
|
||||
image_view->SetImage(
|
||||
ui::ResourceBundle::GetSharedInstance().GetThemedLottieImageNamed(
|
||||
IDR_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE));
|
||||
// Rescale the image size to properly take up the width of the container.
|
||||
auto image_bounds = image_view->GetImageBounds();
|
||||
const float image_scale =
|
||||
static_cast<float>(kKeyboardImageWidth) / image_bounds.width();
|
||||
image_view->SetImageSize(
|
||||
gfx::Size(kKeyboardImageWidth, image_bounds.height() * image_scale));
|
||||
image_view->SetHorizontalAlignment(views::ImageViewBase::Alignment::kCenter);
|
||||
image_view->SetVerticalAlignment(views::ImageViewBase::Alignment::kCenter);
|
||||
image_view->SetID(VIEW_ID_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE);
|
||||
return image_view;
|
||||
}
|
||||
|
||||
// Creates a view containing a keyboard shortcut view and a keyboard
|
||||
// illustration. To be used as the middle content in a
|
||||
// `SystemDialogDelegateView`.
|
||||
std::unique_ptr<views::TableLayoutView> CreateContentView() {
|
||||
// Use a vertical table with two rows, so we can choose which cells to
|
||||
// left-align.
|
||||
auto content_view = std::make_unique<views::TableLayoutView>();
|
||||
content_view->AddColumn(
|
||||
views::LayoutAlignment::kStretch, views::LayoutAlignment::kStretch,
|
||||
/*horizontal_resize=*/1.0f, views::TableLayout::ColumnSize::kUsePreferred,
|
||||
/*fixed_width=*/0, /*min_width=*/0);
|
||||
content_view->AddRows(1, views::TableLayout::kFixedSize);
|
||||
content_view->AddPaddingRow(views::TableLayout::kFixedSize, kRowSpacing);
|
||||
content_view->AddRows(1, views::TableLayout::kFixedSize);
|
||||
// If the middle content of `SystemDialogDelegateView` has no top margin, it
|
||||
// will automatically insert a default content padding. We want to avoid this,
|
||||
// so we will set the margin ourselves.
|
||||
content_view->SetProperty(
|
||||
views::kMarginsKey,
|
||||
gfx::Insets::TLBR(kTitleShortcutSpacing, 0, kImageButtonSpacing, 0));
|
||||
|
||||
// The shortcut view should be left-aligned with the title text.
|
||||
const std::vector<ui::KeyboardCode> key_codes{
|
||||
ui::VKEY_CONTROL, ui::VKEY_SHIFT, ui::VKEY_MEDIA_LAUNCH_APP1};
|
||||
auto* shortcut_view = content_view->AddChildView(
|
||||
std::make_unique<KeyboardShortcutView>(key_codes));
|
||||
shortcut_view->SetProperty(views::kTableHorizAlignKey,
|
||||
views::LayoutAlignment::kStart);
|
||||
|
||||
// Add the keyboard illustration below the keyboard shortcut.
|
||||
content_view->AddChildView(CreateImageView());
|
||||
|
||||
return content_view;
|
||||
}
|
||||
|
||||
// Creates a `SystemDialogDelegateView` to be used as the `WidgetDelegate` for
|
||||
// the Arm 2 tutorial widget.
|
||||
std::unique_ptr<SystemDialogDelegateView> CreateDialogView() {
|
||||
auto dialog = std::make_unique<SystemDialogDelegateView>();
|
||||
dialog->SetTitleText(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_SCREEN_CAPTURE_EDUCATION_TUTORIAL_TITLE));
|
||||
dialog->SetMiddleContentView(CreateContentView());
|
||||
dialog->SetMiddleContentAlignment(views::LayoutAlignment::kStretch);
|
||||
// Override the title margins to be zero, as the space between the title and
|
||||
// the shortcut view has already been set by the `content_view` margins.
|
||||
dialog->SetTitleMargins(gfx::Insets());
|
||||
dialog->SetAcceptButtonVisible(false);
|
||||
dialog->SetModalType(ui::ModalType::MODAL_TYPE_SYSTEM);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CaptureModeEducationController::CaptureModeEducationController() = default;
|
||||
@ -108,9 +208,17 @@ void CaptureModeEducationController::MaybeShowEducation() {
|
||||
pref_service->SetTime(prefs::kCaptureModeEducationLastShown, now);
|
||||
}
|
||||
|
||||
// Close any existing forms of education.
|
||||
AnchoredNudgeManager::Get()->Cancel(kCaptureModeNudgeId);
|
||||
tutorial_widget_.reset();
|
||||
|
||||
if (IsArm1ShortcutNudgeEnabled()) {
|
||||
ShowShortcutNudge();
|
||||
}
|
||||
|
||||
if (IsArm2ShortcutTutorialEnabled()) {
|
||||
ShowTutorialNudge();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
@ -124,18 +232,39 @@ void CaptureModeEducationController::ShowShortcutNudge() {
|
||||
auto* nudge_manager = AnchoredNudgeManager::Get();
|
||||
nudge_manager->Cancel(kCaptureModeNudgeId);
|
||||
|
||||
AnchoredNudgeData nudge_data(
|
||||
kCaptureModeNudgeId, NudgeCatalogName::kCaptureModeEducationShortcutNudge,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_LABEL));
|
||||
AnchoredNudgeData nudge_data =
|
||||
CreateBaseNudgeData(NudgeCatalogName::kCaptureModeEducationShortcutNudge);
|
||||
|
||||
nudge_data.image_model = ui::ImageModel::FromVectorIcon(
|
||||
kCaptureModeIcon, kColorAshIconColorPrimary, kShortcutIconSize);
|
||||
nudge_data.keyboard_codes = {ui::VKEY_CONTROL, ui::VKEY_SHIFT,
|
||||
ui::VKEY_MEDIA_LAUNCH_APP1};
|
||||
AnchoredNudgeManager::Get()->Show(nudge_data);
|
||||
}
|
||||
|
||||
// TODO(b/302368860): Add a new view to display keyboard shortcuts in the same
|
||||
// style as the launcher and the new keyboard shortcut app.
|
||||
nudge_manager->Show(nudge_data);
|
||||
void CaptureModeEducationController::ShowTutorialNudge() {
|
||||
AnchoredNudgeData nudge_data = CreateBaseNudgeData(
|
||||
NudgeCatalogName::kCaptureModeEducationShortcutTutorial);
|
||||
|
||||
nudge_data.primary_button_text =
|
||||
l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_EDUCATION_NUDGE_BUTTON);
|
||||
nudge_data.primary_button_callback = base::BindRepeating(
|
||||
&CaptureModeEducationController::OnShowMeHowButtonPressed,
|
||||
weak_ptr_factory_.GetWeakPtr());
|
||||
|
||||
AnchoredNudgeManager::Get()->Show(nudge_data);
|
||||
}
|
||||
|
||||
void CaptureModeEducationController::CreateAndShowTutorialDialog() {
|
||||
// As we are creating a system modal dialog, it will automatically be parented
|
||||
// to `kShellWindowId_SystemModalContainer`.
|
||||
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
|
||||
params.delegate = CreateDialogView().release();
|
||||
params.name = "CaptureModeEducationTutorialWidget";
|
||||
tutorial_widget_ = std::make_unique<views::Widget>();
|
||||
tutorial_widget_->Init(std::move(params));
|
||||
tutorial_widget_->Show();
|
||||
}
|
||||
|
||||
void CaptureModeEducationController::OnShowMeHowButtonPressed() {
|
||||
CHECK(!tutorial_widget_);
|
||||
CreateAndShowTutorialDialog();
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -7,12 +7,16 @@
|
||||
|
||||
#include "ash/ash_export.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/time/clock.h"
|
||||
#include "ui/views/widget/unique_widget_ptr.h"
|
||||
|
||||
class PrefRegistrySimple;
|
||||
|
||||
namespace ash {
|
||||
|
||||
// TODO(hewer): Close tutorial when a capture session is started.
|
||||
|
||||
// Controller for showing the different forms of user education for Screen
|
||||
// Capture entry points. Education is split into three different arms:
|
||||
// - Arm 1: Shortcut Nudge. A simple system nudge appears with text indicating
|
||||
@ -52,6 +56,8 @@ class ASH_EXPORT CaptureModeEducationController {
|
||||
// education based on the enabled arm/feature param.
|
||||
void MaybeShowEducation();
|
||||
|
||||
views::Widget* tutorial_widget_for_test() { return tutorial_widget_.get(); }
|
||||
|
||||
private:
|
||||
friend class CaptureModeEducationControllerTest;
|
||||
|
||||
@ -62,8 +68,24 @@ class ASH_EXPORT CaptureModeEducationController {
|
||||
// take a screenshot.
|
||||
void ShowShortcutNudge();
|
||||
|
||||
// Shows Arm 2, an unanchored system nudge indicating the keyboard shortcut to
|
||||
// take a screenshot, with a button to open a new tutorial widget.
|
||||
void ShowTutorialNudge();
|
||||
|
||||
// Creates and shows the system dialog displaying the keyboard shortcut and
|
||||
// illustration for taking a screenshot.
|
||||
void CreateAndShowTutorialDialog();
|
||||
|
||||
// Closes the nudge and shows the tutorial dialog for Arm 2.
|
||||
void OnShowMeHowButtonPressed();
|
||||
|
||||
// If set to true, ignores the 3 times/24 hours show limit for testing.
|
||||
bool skip_prefs_for_test_ = false;
|
||||
|
||||
// The widget that contains the tutorial dialog view for Arm 2.
|
||||
views::UniqueWidgetPtr tutorial_widget_;
|
||||
|
||||
base::WeakPtrFactory<CaptureModeEducationController> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
@ -36,6 +36,8 @@ constexpr char kNudgeTimeToActionWithin1h[] =
|
||||
constexpr char kNudgeTimeToActionWithinSession[] =
|
||||
"Ash.NotifierFramework.Nudge.TimeToAction.WithinSession";
|
||||
|
||||
constexpr float kKeyboardImageWidth = 448;
|
||||
|
||||
PrefService* GetPrefService() {
|
||||
return Shell::Get()->session_controller()->GetActivePrefService();
|
||||
}
|
||||
@ -316,4 +318,48 @@ TEST_F(CaptureModeEducationShortcutNudgeTest, ShortcutNudgeMetrics) {
|
||||
NudgeCatalogName::kCaptureModeEducationShortcutNudge, 1);
|
||||
}
|
||||
|
||||
// Test fixture to verify the behaviour of Arm 2, the shortcut tutorial.
|
||||
class CaptureModeEducationShortcutTutorialTest
|
||||
: public CaptureModeEducationControllerTest {
|
||||
public:
|
||||
CaptureModeEducationShortcutTutorialTest()
|
||||
: CaptureModeEducationControllerTest("ShortcutTutorial") {}
|
||||
CaptureModeEducationShortcutTutorialTest(
|
||||
const CaptureModeEducationShortcutTutorialTest&) = delete;
|
||||
CaptureModeEducationShortcutTutorialTest& operator=(
|
||||
const CaptureModeEducationShortcutTutorialTest&) = delete;
|
||||
~CaptureModeEducationShortcutTutorialTest() override = default;
|
||||
};
|
||||
|
||||
TEST_F(CaptureModeEducationShortcutTutorialTest, DialogShowsOnButtonPressed) {
|
||||
base::SimpleTestClock test_clock;
|
||||
CaptureModeEducationControllerTest::SetOverrideClock(&test_clock);
|
||||
|
||||
// Advance clock so we aren't at zero time.
|
||||
test_clock.Advance(base::Hours(25));
|
||||
|
||||
// Attempt to use the Windows Snipping Tool (capture bar) shortcut.
|
||||
PressAndReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
|
||||
|
||||
// Click the "Show me how" button on the nudge.
|
||||
AnchoredNudgeManagerImpl* nudge_manager =
|
||||
Shell::Get()->anchored_nudge_manager();
|
||||
LeftClickOn(nudge_manager->GetNudgePrimaryButtonForTest(kCaptureModeNudgeId));
|
||||
|
||||
// The nudge should not be visible.
|
||||
EXPECT_FALSE(nudge_manager->GetNudgeIfShown(kCaptureModeNudgeId));
|
||||
|
||||
// The tutorial widget should be visible.
|
||||
auto* tutorial_widget = CaptureModeController::Get()
|
||||
->education_controller()
|
||||
->tutorial_widget_for_test();
|
||||
ASSERT_TRUE(tutorial_widget);
|
||||
|
||||
// The keyboard image should be scaled to the the correct width.
|
||||
auto* image_view = tutorial_widget->GetContentsView()->GetViewByID(
|
||||
VIEW_ID_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE);
|
||||
ASSERT_TRUE(image_view);
|
||||
EXPECT_EQ(kKeyboardImageWidth, image_view->width());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -225,7 +225,8 @@ enum class NudgeCatalogName {
|
||||
kMultitaskMenuClamshell = 20,
|
||||
kMultitaskMenuTablet = 21,
|
||||
kCaptureModeEducationShortcutNudge = 22,
|
||||
kMaxValue = kCaptureModeEducationShortcutNudge
|
||||
kCaptureModeEducationShortcutTutorial = 23,
|
||||
kMaxValue = kCaptureModeEducationShortcutTutorial
|
||||
};
|
||||
|
||||
// A living catalog that registers toasts.
|
||||
|
@ -101,6 +101,9 @@ enum ViewID {
|
||||
VIEW_ID_SA_NOTIFICATION_TRAY,
|
||||
VIEW_ID_SA_MAX = VIEW_ID_SA_NOTIFICATION_TRAY,
|
||||
|
||||
// Screen capture:
|
||||
VIEW_ID_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE,
|
||||
|
||||
// System dialog delegate view:
|
||||
VIEW_ID_STYLE_SYSTEM_DIALOG_DELEGATE_ACCEPT_BUTTON,
|
||||
VIEW_ID_STYLE_SYSTEM_DIALOG_DELEGATE_CANCEL_BUTTON,
|
||||
|
@ -49,6 +49,8 @@
|
||||
<structure type="lottie" name="IDR_WELCOME_TOUR_DIALOG_IMAGE" file="unscaled_resources/welcome_tour_dialog_image.json" compress="gzip" />
|
||||
<!-- VC Nudge -->
|
||||
<structure type="lottie" name="IDR_SPEAK_ON_MUTE_OPT_IN_NUDGE_IMAGE" file="unscaled_resources/speak_on_mute_opt_in_nudge_image.json" compress="gzip" />
|
||||
<!-- Screen Capture Education -->
|
||||
<structure type="lottie" name="IDR_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE" file="unscaled_resources/screen_capture_education_keyboard.json" compress="gzip" />
|
||||
</structures>
|
||||
</release>
|
||||
</grit>
|
||||
|
File diff suppressed because one or more lines are too long
@ -328,6 +328,14 @@ void SystemDialogDelegateView::SetMiddleContentAlignment(
|
||||
SetViewCrossAxisAlignment(contents_[ContentType::kMiddle], alignment);
|
||||
}
|
||||
|
||||
void SystemDialogDelegateView::SetAcceptButtonVisible(bool visible) {
|
||||
button_container_->accept_button()->SetVisible(visible);
|
||||
}
|
||||
|
||||
void SystemDialogDelegateView::SetTitleMargins(const gfx::Insets& margins) {
|
||||
SetViewLayoutSpecs(title_, margins);
|
||||
}
|
||||
|
||||
gfx::Size SystemDialogDelegateView::CalculatePreferredSize() const {
|
||||
auto* host_window = GetDialogHostWindow(GetWidget());
|
||||
// If the delegate view is not added to a widget or parented to a host window,
|
||||
|
@ -126,6 +126,12 @@ class ASH_EXPORT SystemDialogDelegateView : public views::WidgetDelegateView {
|
||||
void SetTopContentAlignment(views::LayoutAlignment alignment);
|
||||
void SetMiddleContentAlignment(views::LayoutAlignment alignment);
|
||||
|
||||
// If true, hides the accept button in `button_container_`.
|
||||
void SetAcceptButtonVisible(bool visible);
|
||||
|
||||
// Sets the margins for the title label view.
|
||||
void SetTitleMargins(const gfx::Insets& margins);
|
||||
|
||||
// views::WidgetDelegateView:
|
||||
gfx::Size CalculatePreferredSize() const override;
|
||||
gfx::Size GetMinimumSize() const override;
|
||||
@ -204,6 +210,8 @@ VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, MiddleContentView)
|
||||
VIEW_BUILDER_VIEW_TYPE_PROPERTY(views::View, AdditionalViewInButtonRow)
|
||||
VIEW_BUILDER_PROPERTY(views::LayoutAlignment, TopContentAlignment)
|
||||
VIEW_BUILDER_PROPERTY(views::LayoutAlignment, MiddleContentAlignment)
|
||||
VIEW_BUILDER_PROPERTY(bool, AcceptButtonVisible)
|
||||
VIEW_BUILDER_PROPERTY(const gfx::Insets&, TitleMargins)
|
||||
VIEW_BUILDER_PROPERTY(ui::ModalType, ModalType)
|
||||
END_VIEW_BUILDER
|
||||
|
||||
|
@ -1250,6 +1250,7 @@ chromium-metrics-reviews@google.com.
|
||||
<int value="20" label="Multitask Menu Clamshell"/>
|
||||
<int value="21" label="Multitask Menu Tablet"/>
|
||||
<int value="22" label="Capture Mode Education Shortcut Nudge"/>
|
||||
<int value="23" label="Capture Mode Education Shortcut Tutorial"/>
|
||||
</enum>
|
||||
|
||||
<enum name="OnDeviceToServerSpeechRecognitionFallbackReason">
|
||||
|
Reference in New Issue
Block a user