0

SC Education: Add Arm 3 and fix tests

Add Arm 3 for Screen Capture Education, a system nudge that is
anchored to the unified system tray button and does not contain a
keyboard shortcut view. Also fixes the behaviour of some unit tests.

Bug: b/302368917
Test: manual + unit
Change-Id: I8100e9d0254ba620e29dff5d61f37887a4ffa044
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5059882
Commit-Queue: Elijah Hewer <hewer@chromium.org>
Reviewed-by: Michele Fan <michelefan@chromium.org>
Reviewed-by: Ahmed Fakhry <afakhry@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1230970}
This commit is contained in:
Elijah Hewer
2023-11-29 22:46:08 +00:00
committed by Chromium LUCI CQ
parent 515a00238e
commit 7e2d446210
10 changed files with 152 additions and 21 deletions

@ -6136,6 +6136,9 @@ Here are some things you can try to get started.
<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>
<message name="IDS_ASH_SCREEN_CAPTURE_EDUCATION_SETTINGS_NUDGE_LABEL" desc="The label of the Screen Capture Education settings nudge (Arm 3) for using the quick settings menu.">
Use the screen capture tool in quick settings to take screenshots
</message>
<!-- Snap Group -->
<message name="IDS_ASH_SNAP_GROUP_CLICK_TO_LOCK_WINDOWS" desc="Click to lock the windows.">

@ -0,0 +1 @@
8610c616aa79506fc599fed631f635e5d67ec039

@ -1112,6 +1112,10 @@ void CaptureModeController::StartInternal(
},
weak_ptr_factory_.GetWeakPtr(), std::move(callback), IsActive()));
if (education_controller_) {
education_controller_->CloseAllEducationNudgesAndTutorials();
}
if (capture_mode_session_ || pending_dlp_check_) {
return;
}

@ -14,11 +14,15 @@
#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/root_window_controller.h"
#include "ash/shelf/shelf.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 "ash/system/status_area_widget.h"
#include "ash/system/unified/unified_system_tray.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"
@ -59,6 +63,7 @@ base::Time GetTime() {
return g_clock_override ? g_clock_override->Now() : base::Time::Now();
}
// Creates nudge data common to Arms 1 and 2.
AnchoredNudgeData CreateBaseNudgeData(NudgeCatalogName catalog_name) {
AnchoredNudgeData nudge_data(
kCaptureModeNudgeId, catalog_name,
@ -171,10 +176,10 @@ bool CaptureModeEducationController::IsArm2ShortcutTutorialEnabled() {
}
// static
bool CaptureModeEducationController::IsArm3SettingsNudgeEnabled() {
bool CaptureModeEducationController::IsArm3QuickSettingsNudgeEnabled() {
return features::IsCaptureModeEducationEnabled() &&
features::kCaptureModeEducationParam.Get() ==
features::CaptureModeEducationParam::kSettingsNudge;
features::CaptureModeEducationParam::kQuickSettingsNudge;
}
void CaptureModeEducationController::MaybeShowEducation() {
@ -208,9 +213,7 @@ void CaptureModeEducationController::MaybeShowEducation() {
pref_service->SetTime(prefs::kCaptureModeEducationLastShown, now);
}
// Close any existing forms of education.
AnchoredNudgeManager::Get()->Cancel(kCaptureModeNudgeId);
tutorial_widget_.reset();
CloseAllEducationNudgesAndTutorials();
if (IsArm1ShortcutNudgeEnabled()) {
ShowShortcutNudge();
@ -219,6 +222,15 @@ void CaptureModeEducationController::MaybeShowEducation() {
if (IsArm2ShortcutTutorialEnabled()) {
ShowTutorialNudge();
}
if (IsArm3QuickSettingsNudgeEnabled()) {
ShowQuickSettingsNudge();
}
}
void CaptureModeEducationController::CloseAllEducationNudgesAndTutorials() {
AnchoredNudgeManager::Get()->Cancel(kCaptureModeNudgeId);
tutorial_widget_.reset();
}
// static
@ -228,10 +240,6 @@ void CaptureModeEducationController::SetOverrideClockForTesting(
}
void CaptureModeEducationController::ShowShortcutNudge() {
// Close the nudge if it already exists.
auto* nudge_manager = AnchoredNudgeManager::Get();
nudge_manager->Cancel(kCaptureModeNudgeId);
AnchoredNudgeData nudge_data =
CreateBaseNudgeData(NudgeCatalogName::kCaptureModeEducationShortcutNudge);
@ -251,6 +259,24 @@ void CaptureModeEducationController::ShowTutorialNudge() {
AnchoredNudgeManager::Get()->Show(nudge_data);
}
void CaptureModeEducationController::ShowQuickSettingsNudge() {
AnchoredNudgeData nudge_data(
kCaptureModeNudgeId,
NudgeCatalogName::kCaptureModeEducationQuickSettingsNudge,
l10n_util::GetStringUTF16(
IDS_ASH_SCREEN_CAPTURE_EDUCATION_SETTINGS_NUDGE_LABEL));
nudge_data.image_model = ui::ImageModel::FromVectorIcon(
kCaptureModeIcon, kColorAshIconColorPrimary, kShortcutIconSize);
nudge_data.SetAnchorView(
RootWindowController::ForWindow(Shell::GetRootWindowForNewWindows())
->shelf()
->status_area_widget()
->unified_system_tray());
AnchoredNudgeManager::Get()->Show(nudge_data);
}
void CaptureModeEducationController::CreateAndShowTutorialDialog() {
// As we are creating a system modal dialog, it will automatically be parented
// to `kShellWindowId_SystemModalContainer`.

@ -6,7 +6,6 @@
#define ASH_CAPTURE_MODE_CAPTURE_MODE_EDUCATION_CONTROLLER_H_
#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"
@ -24,7 +23,7 @@ namespace ash {
// - Arm 2: Shortcut Tutorial. Similar to Arm 1, but the nudge also appears
// with a button that opens a new popup, showing the keyboard layout of where
// the shortcut keys are found.
// - Arm 3: Settings Nudge. A system nudge anchored to the quick settings
// - Arm 3: Quick Settings Nudge. A system nudge anchored to the quick settings
// button in the shelf, with text alerting users to the Screen Capture tile
// in the quick settings menu.
class ASH_EXPORT CaptureModeEducationController {
@ -48,14 +47,17 @@ class ASH_EXPORT CaptureModeEducationController {
static bool IsArm2ShortcutTutorialEnabled();
// Returns true if the feature flag 'kCaptureModeEducation' is enabled and
// the associated param is 'kSettingsNudge';
static bool IsArm3SettingsNudgeEnabled();
// the associated param is 'kQuickSettingsNudge';
static bool IsArm3QuickSettingsNudgeEnabled();
// If a form of user education has already been shown 3 times or once in the
// past 24 hours, returns. Otherwise, shows the appropriate form of user
// education based on the enabled arm/feature param.
void MaybeShowEducation();
// Closes any Screen Capture nudges or tutorials that may be open.
void CloseAllEducationNudgesAndTutorials();
views::Widget* tutorial_widget_for_test() { return tutorial_widget_.get(); }
private:
@ -72,6 +74,11 @@ class ASH_EXPORT CaptureModeEducationController {
// take a screenshot, with a button to open a new tutorial widget.
void ShowTutorialNudge();
// Shows Arm 3, a system nudge anchored to the unified system tray button,
// indicating the location of the screen capture tool in the quick settings
// menu.
void ShowQuickSettingsNudge();
// Creates and shows the system dialog displaying the keyboard shortcut and
// illustration for taking a screenshot.
void CreateAndShowTutorialDialog();

@ -10,6 +10,7 @@
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/ash_view_ids.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/style/keyboard_shortcut_view.h"
#include "ash/system/toast/anchored_nudge.h"
@ -37,6 +38,7 @@ constexpr char kNudgeTimeToActionWithinSession[] =
"Ash.NotifierFramework.Nudge.TimeToAction.WithinSession";
constexpr float kKeyboardImageWidth = 448;
constexpr int kNudgeWorkAreaSpacing = 8;
PrefService* GetPrefService() {
return Shell::Get()->session_controller()->GetActivePrefService();
@ -71,9 +73,13 @@ class CaptureModeEducationControllerTest : public AshTestBase {
CaptureModeEducationController::SetOverrideClockForTesting(test_clock);
}
void ActivateNudgeAndCheckVisibility() {
// By default, attempts to use the Windows Snipping Tool (capture bar)
// shortcut to activate the nudge.
void ActivateNudgeAndCheckVisibility(ui::KeyboardCode key_code = ui::VKEY_S,
int flags = ui::EF_COMMAND_DOWN |
ui::EF_SHIFT_DOWN) {
// Attempt to use the Windows Snipping Tool (capture bar) shortcut.
PressAndReleaseKey(ui::VKEY_S, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN);
PressAndReleaseKey(key_code, flags);
// Get the list of visible nudges from the nudge manager and make sure our
// education nudge is in the list and visible.
@ -84,6 +90,32 @@ class CaptureModeEducationControllerTest : public AshTestBase {
EXPECT_TRUE(nudge->GetVisible());
}
// Starts a capture session using the screenshort shortcut, and verifies that
// the nudge and tutorial are both closed. Stops the session afterwards.
void StartSessionAndCheckEducationClosed() {
// Use the screenshot shortcut to start a capture session.
PressAndReleaseKey(ui::VKEY_MEDIA_LAUNCH_APP1,
ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
auto* capture_mode_controller = CaptureModeController::Get();
ASSERT_TRUE(capture_mode_controller->IsActive());
// Get the list of visible nudges from the nudge manager and make sure our
// education nudge is no longer in the list.
const AnchoredNudge* nudge =
Shell::Get()->anchored_nudge_manager()->GetNudgeIfShown(
kCaptureModeNudgeId);
EXPECT_FALSE(nudge);
// The tutorial should also be closed.
EXPECT_FALSE(capture_mode_controller->education_controller()
->tutorial_widget_for_test());
// Stop the session in case we start a new one later.
capture_mode_controller->Stop();
ASSERT_FALSE(capture_mode_controller->IsActive());
}
// Skip the 3 times/24 hours show limit for testing.
void SkipNudgePrefs() {
CaptureModeController::Get()->education_controller()->skip_prefs_for_test_ =
@ -115,7 +147,7 @@ TEST_F(CaptureModeEducationControllerTest, NudgeAppearsOnAcceleratorPressed) {
continue;
}
ActivateNudgeAndCheckVisibility();
ActivateNudgeAndCheckVisibility(tracker_data.key_code, tracker_data.flags);
// Close nudge to get ready for the next input.
CancelNudge(kCaptureModeNudgeId);
@ -135,7 +167,7 @@ class CaptureModeEducationShortcutNudgeTest
~CaptureModeEducationShortcutNudgeTest() override = default;
};
TEST_F(CaptureModeEducationShortcutNudgeTest, CorrectStyling) {
TEST_F(CaptureModeEducationShortcutNudgeTest, KeyboardShortcutVisible) {
base::SimpleTestClock test_clock;
CaptureModeEducationControllerTest::SetOverrideClock(&test_clock);
@ -160,6 +192,8 @@ TEST_F(CaptureModeEducationShortcutNudgeTest, CorrectStyling) {
ASSERT_TRUE(shortcut_view);
EXPECT_TRUE(shortcut_view->GetVisible());
StartSessionAndCheckEducationClosed();
CaptureModeEducationControllerTest::SetOverrideClock(nullptr);
}
@ -331,7 +365,7 @@ class CaptureModeEducationShortcutTutorialTest
~CaptureModeEducationShortcutTutorialTest() override = default;
};
TEST_F(CaptureModeEducationShortcutTutorialTest, DialogShowsOnButtonPressed) {
TEST_F(CaptureModeEducationShortcutTutorialTest, DialogOpensAndCloses) {
base::SimpleTestClock test_clock;
CaptureModeEducationControllerTest::SetOverrideClock(&test_clock);
@ -360,6 +394,60 @@ TEST_F(CaptureModeEducationShortcutTutorialTest, DialogShowsOnButtonPressed) {
VIEW_ID_SCREEN_CAPTURE_EDUCATION_KEYBOARD_IMAGE);
ASSERT_TRUE(image_view);
EXPECT_EQ(kKeyboardImageWidth, image_view->width());
StartSessionAndCheckEducationClosed();
}
// Test fixture to verify the behaviour of Arm 3, the settings nudge.
class CaptureModeEducationQuickSettingsNudgeTest
: public CaptureModeEducationControllerTest {
public:
CaptureModeEducationQuickSettingsNudgeTest()
: CaptureModeEducationControllerTest("QuickSettingsNudge") {}
CaptureModeEducationQuickSettingsNudgeTest(
const CaptureModeEducationQuickSettingsNudgeTest&) = delete;
CaptureModeEducationQuickSettingsNudgeTest& operator=(
const CaptureModeEducationQuickSettingsNudgeTest&) = delete;
~CaptureModeEducationQuickSettingsNudgeTest() override = default;
};
TEST_F(CaptureModeEducationQuickSettingsNudgeTest, NudgeLocation) {
UpdateDisplay("800x600");
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);
// Get the list of visible nudges from the nudge manager and make sure our
// education nudge is in the list and visible.
AnchoredNudge* nudge =
Shell::Get()->anchored_nudge_manager()->GetNudgeIfShown(
kCaptureModeNudgeId);
ASSERT_TRUE(nudge);
EXPECT_TRUE(nudge->GetVisible());
// Test the shelf in horizontal (bottom) alignment.
ASSERT_TRUE(Shell::GetPrimaryRootWindowController()
->shelf()
->IsHorizontalAlignment());
// The nudge should appear on the bottom right side of the screen. Compare the
// positions of the bottom right corners of the nudge and the work area.
const auto nudge_corner = nudge->GetBoundsInScreen().bottom_right();
auto work_area_corner =
nudge->GetWidget()->GetWorkAreaBoundsInScreen().bottom_right();
EXPECT_EQ(work_area_corner.x() - kNudgeWorkAreaSpacing, nudge_corner.x());
EXPECT_EQ(work_area_corner.y() - kNudgeWorkAreaSpacing, nudge_corner.y());
StartSessionAndCheckEducationClosed();
// TODO(hewer): Test shelf in vertical positions.
CaptureModeEducationControllerTest::SetOverrideClock(nullptr);
}
} // namespace ash

@ -387,7 +387,7 @@ constexpr base::FeatureParam<CaptureModeEducationParam>::Option
capture_mode_education_type_options[] = {
{CaptureModeEducationParam::kShortcutNudge, "ShortcutNudge"},
{CaptureModeEducationParam::kShortcutTutorial, "ShortcutTutorial"},
{CaptureModeEducationParam::kSettingsNudge, "SettingsNudge"}};
{CaptureModeEducationParam::kQuickSettingsNudge, "QuickSettingsNudge"}};
const base::FeatureParam<CaptureModeEducationParam> kCaptureModeEducationParam{
&kCaptureModeEducation, "CaptureModeEducationParam",
CaptureModeEducationParam::kShortcutNudge,

@ -108,7 +108,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kCaptureModeEducation);
enum class CaptureModeEducationParam {
kShortcutNudge,
kShortcutTutorial,
kSettingsNudge
kQuickSettingsNudge
};
COMPONENT_EXPORT(ASH_CONSTANTS)
extern const base::FeatureParam<CaptureModeEducationParam>

@ -226,7 +226,8 @@ enum class NudgeCatalogName {
kMultitaskMenuTablet = 21,
kCaptureModeEducationShortcutNudge = 22,
kCaptureModeEducationShortcutTutorial = 23,
kMaxValue = kCaptureModeEducationShortcutTutorial
kCaptureModeEducationQuickSettingsNudge = 24,
kMaxValue = kCaptureModeEducationQuickSettingsNudge
};
// A living catalog that registers toasts.

@ -1255,6 +1255,7 @@ chromium-metrics-reviews@google.com.
<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"/>
<int value="24" label="Capture Mode Education Quick Settings Nudge"/>
</enum>
<enum name="OnDeviceToServerSpeechRecognitionFallbackReason">