0

birch: Add privacy nudge encouraging opening the context menu

The nudge encourages the user to open the context menu on a suggestion
chip, which will allow the user to control which data types are used
for suggestions.

The nudge shows a maximum of 3 times, with a 24 hour interview between
shows.

I put the nudge logic in a small controller class as BirchBarView has
a lot of existing logic.

Bug: 353354153
Test: added to ash_unittests
Change-Id: I82d7be9a9bca298468f9e32c7836d7087f763688
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5714776
Auto-Submit: James Cook <jamescook@chromium.org>
Reviewed-by: Michele Fan <michelefan@chromium.org>
Commit-Queue: Michele Fan <michelefan@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: Matthew Mourgos <mmourgos@chromium.org>
Commit-Queue: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1329135}
This commit is contained in:
James Cook
2024-07-17 21:22:47 +00:00
committed by Chromium LUCI CQ
parent d7a4f9e76f
commit fc8eae28b4
14 changed files with 304 additions and 1 deletions

@ -2900,6 +2900,8 @@ component("ash") {
"wm/overview/birch/birch_chip_context_menu_model.h",
"wm/overview/birch/birch_chip_loader_view.cc",
"wm/overview/birch/birch_chip_loader_view.h",
"wm/overview/birch/birch_privacy_nudge_controller.cc",
"wm/overview/birch/birch_privacy_nudge_controller.h",
"wm/overview/cleanup_animation_observer.cc",
"wm/overview/cleanup_animation_observer.h",
"wm/overview/delayed_animation_observer.h",
@ -4318,6 +4320,7 @@ test("ash_unittests") {
"wm/overlay_layout_manager_unittest.cc",
"wm/overview/birch/birch_bar_context_menu_model_unittest.cc",
"wm/overview/birch/birch_bar_unittest.cc",
"wm/overview/birch/birch_privacy_nudge_controller_unittest.cc",
"wm/overview/cleanup_animation_observer_unittest.cc",
"wm/overview/delayed_animation_observer_impl_unittest.cc",
"wm/overview/overview_controller_unittest.cc",

@ -92,6 +92,7 @@
#include "ash/wm/float/tablet_mode_tuck_education.h"
#include "ash/wm/lock_state_controller.h"
#include "ash/wm/overview/birch/birch_bar_controller.h"
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/wm/window_cycle/window_cycle_controller.h"
#include "ash/wm/window_util.h"
#include "chromeos/ash/services/assistant/public/cpp/assistant_prefs.h"
@ -124,6 +125,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry,
BirchBarController::RegisterProfilePrefs(registry);
BirchItem::RegisterProfilePrefs(registry);
BirchModel::RegisterProfilePrefs(registry);
BirchPrivacyNudgeController::RegisterProfilePrefs(registry);
CalendarController::RegisterProfilePrefs(registry);
camera_app_prefs::RegisterProfilePrefs(registry);
CameraEffectsController::RegisterProfilePrefs(registry);

@ -2342,6 +2342,18 @@ inline constexpr char kGameDashboardShowToolbar[] =
static constexpr char kSoftwareScanningEnabled[] =
"ash.nearby.software_scanning_enabled";
// A boolean pref that tracks whether the Birch context menu has been shown.
inline constexpr char kBirchContextMenuShown[] = "ash.birch.context_menu_shown";
// An integer pref that tracks the number of times the Birch privacy nudge has
// been shown.
inline constexpr char kBirchPrivacyNudgeShownCount[] =
"ash.birch.privacy_nudge_shown_count";
// A time pref that stores the time the Birch privacy nudge was last shown.
inline constexpr char kBirchPrivacyNudgeLastShownTime[] =
"ash.birch.privacy_nudge_last_shown";
// A boolean pref indicating whether to show Birch suggestions in Overview mode.
inline constexpr char kBirchShowSuggestions[] = "ash.birch.show_suggestions";

@ -246,7 +246,8 @@ enum class NudgeCatalogName {
kSixPackRemappingPressed = 32,
kCapsLockShortcutPressed = 33,
kMahi = 34,
kMaxValue = kMahi
kBirchPrivacy = 35,
kMaxValue = kBirchPrivacy
};
// A living catalog that registers toasts.

@ -63,6 +63,7 @@
#include "ash/wm/overview/birch/birch_bar_context_menu_model.h"
#include "ash/wm/overview/birch/birch_bar_controller.h"
#include "ash/wm/overview/birch/birch_bar_menu_model_adapter.h"
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/root_window_layout_manager.h"
@ -845,6 +846,7 @@ void RootWindowController::ShowContextMenu(const gfx::Point& location_in_screen,
OverviewController::Get()->InOverviewSession() &&
!split_view_overview_session_) {
root_window_menu_model_adapter_ = BuildBirchMenuModelAdapter(source_type);
BirchPrivacyNudgeController::DidShowContextMenu();
} else {
root_window_menu_model_adapter_ = BuildShelfMenuModelAdapter(source_type);
}

@ -219,6 +219,7 @@
#include "ash/wm/multi_display/multi_display_metrics_controller.h"
#include "ash/wm/multi_display/persistent_window_controller.h"
#include "ash/wm/native_cursor_manager_ash.h"
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/pip/pip_controller.h"
#include "ash/wm/raster_scale/raster_scale_controller.h"
@ -833,6 +834,7 @@ Shell::~Shell() {
shelf_controller_->Shutdown();
shelf_config_->Shutdown();
birch_privacy_nudge_controller_.reset();
birch_model_.reset();
// Depends on `app_list_controller_` and `tablet_mode_controller_`.
@ -1609,6 +1611,8 @@ void Shell::Init(
if (features::IsForestFeatureEnabled()) {
birch_model_ = std::make_unique<BirchModel>();
birch_privacy_nudge_controller_ =
std::make_unique<BirchPrivacyNudgeController>();
}
autoclick_controller_ = std::make_unique<AutoclickController>();

@ -122,6 +122,7 @@ class BackGestureEventHandler;
class BacklightsForcedOffSetter;
class BatterySaverController;
class BirchModel;
class BirchPrivacyNudgeController;
class BluetoothDeviceStatusUiHandler;
class BluetoothNotificationController;
class BluetoothStateCache;
@ -462,6 +463,9 @@ class ASH_EXPORT Shell : public SessionObserver,
return battery_saver_controller_.get();
}
BirchModel* birch_model() { return birch_model_.get(); }
BirchPrivacyNudgeController* birch_privacy_nudge_controller() {
return birch_privacy_nudge_controller_.get();
}
BluetoothStateCache* bluetooth_state_cache() {
return bluetooth_state_cache_.get();
}
@ -1031,6 +1035,7 @@ class ASH_EXPORT Shell : public SessionObserver,
std::unique_ptr<AutozoomControllerImpl> autozoom_controller_;
std::unique_ptr<BacklightsForcedOffSetter> backlights_forced_off_setter_;
std::unique_ptr<BirchModel> birch_model_;
std::unique_ptr<BirchPrivacyNudgeController> birch_privacy_nudge_controller_;
std::unique_ptr<BrightnessControlDelegate> brightness_control_delegate_;
std::unique_ptr<CalendarController> calendar_controller_;
std::unique_ptr<CameraEffectsController> camera_effects_controller_;

@ -12,6 +12,7 @@
#include "ash/wm/overview/birch/birch_bar_menu_model_adapter.h"
#include "ash/wm/overview/birch/birch_bar_view.h"
#include "ash/wm/overview/birch/birch_chip_context_menu_model.h"
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/wm/overview/overview_grid.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/overview/overview_utils.h"
@ -152,6 +153,7 @@ void BirchBarController::ShowChipContextMenu(BirchChipButton* chip,
weak_ptr_factory_.GetWeakPtr()),
Shell::Get()->IsInTabletMode());
chip_menu_model_adapter_->set_close_menu_on_customizing_suggestions(true);
BirchPrivacyNudgeController::DidShowContextMenu();
chip_menu_model_adapter_->Run(gfx::Rect(point, gfx::Size()),
views::MenuAnchorPosition::kBubbleTopRight,
views::MenuRunner::CONTEXT_MENU |

@ -15,7 +15,9 @@
#include "ash/public/cpp/window_properties.h"
#include "ash/root_window_settings.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/wm/overview/birch/birch_chip_loader_view.h"
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/wm/window_properties.h"
#include "base/containers/contains.h"
#include "base/containers/fixed_flat_set.h"
@ -595,9 +597,23 @@ void BirchBarView::OnFadeOutAborted() {
}
void BirchBarView::OnSetupEnded() {
if (state_ == State::kLoading ||
state_ == State::kLoadingForInformedRestore) {
// Loading is finished, so possibly show a privacy nudge.
MaybeShowPrivacyNudge();
}
SetState(State::kNormal);
}
void BirchBarView::MaybeShowPrivacyNudge() {
if (chips_.empty()) {
return;
}
// The nudge is anchored on the first suggestion chip.
views::View* anchor_view = chips_[0];
Shell::Get()->birch_privacy_nudge_controller()->MaybeShowNudge(anchor_view);
}
BEGIN_METADATA(BirchBarView)
END_METADATA

@ -163,6 +163,10 @@ class ASH_EXPORT BirchBarView : public views::BoxLayoutView {
// Called after chips fading-out animations are done during shutting down.
void OnShutdownEnded();
// Possibly show the privacy nudge about context menu options for
// controlling suggestion types.
void MaybeShowPrivacyNudge();
// Cached chip size.
const gfx::Size chip_size_;

@ -0,0 +1,90 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/system/anchored_nudge_data.h"
#include "ash/shell.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "base/command_line.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace ash {
namespace {
// Maximum number of times to show the nudge.
constexpr int kMaxShownCount = 3;
// Minimum time between shows.
constexpr base::TimeDelta kTimeBetweenShown = base::Hours(24);
PrefService* GetPrefService() {
return Shell::Get()->session_controller()->GetActivePrefService();
}
} // namespace
BirchPrivacyNudgeController::BirchPrivacyNudgeController() = default;
BirchPrivacyNudgeController::~BirchPrivacyNudgeController() = default;
// static
void BirchPrivacyNudgeController::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(prefs::kBirchContextMenuShown, false);
registry->RegisterIntegerPref(prefs::kBirchPrivacyNudgeShownCount, 0);
registry->RegisterTimePref(prefs::kBirchPrivacyNudgeLastShownTime,
base::Time());
}
// static
void BirchPrivacyNudgeController::DidShowContextMenu() {
GetPrefService()->SetBoolean(prefs::kBirchContextMenuShown, true);
}
void BirchPrivacyNudgeController::MaybeShowNudge(views::View* anchor_view) {
auto* prefs = GetPrefService();
// Don't show nudge if the user has already opened the context menu.
if (prefs->GetBoolean(prefs::kBirchContextMenuShown)) {
return;
}
// Nudge has been shown three times. No need to educate anymore.
const int shown_count =
prefs->GetInteger(prefs::kBirchPrivacyNudgeShownCount);
if (shown_count >= kMaxShownCount) {
return;
}
// Don't show nudge if it was shown within the last 24 hours.
const base::Time last_shown =
prefs->GetTime(prefs::kBirchPrivacyNudgeLastShownTime);
if (base::Time::Now() - last_shown < kTimeBetweenShown) {
return;
}
// TODO(b/353354153): Localize the string after UX writing provides final
// text.
AnchoredNudgeData nudge_data(
"BirchPrivacyId", NudgeCatalogName::kBirchPrivacy,
u"To customize suggestions, tap or press with two fingers (touchpad) or "
u"right-click (mouse) when hovering over a suggestion",
anchor_view);
nudge_data.arrow = views::BubbleBorder::BOTTOM_LEFT;
Shell::Get()->anchored_nudge_manager()->Show(nudge_data);
// Update nudge prefs.
prefs->SetInteger(prefs::kBirchPrivacyNudgeShownCount, shown_count + 1);
prefs->SetTime(prefs::kBirchPrivacyNudgeLastShownTime, base::Time::Now());
}
} // namespace ash

@ -0,0 +1,42 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_WM_OVERVIEW_BIRCH_BIRCH_PRIVACY_NUDGE_CONTROLLER_H_
#define ASH_WM_OVERVIEW_BIRCH_BIRCH_PRIVACY_NUDGE_CONTROLLER_H_
#include "ash/ash_export.h"
class PrefRegistrySimple;
namespace views {
class View;
}
namespace ash {
// Controller for showing the privacy nudge for Birch suggestion chips. It
// encourages users to open the context menu to control suggestion data types.
class ASH_EXPORT BirchPrivacyNudgeController {
public:
BirchPrivacyNudgeController();
BirchPrivacyNudgeController(const BirchPrivacyNudgeController&) = delete;
BirchPrivacyNudgeController& operator=(const BirchPrivacyNudgeController&) =
delete;
~BirchPrivacyNudgeController();
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Records that the context menu was shown (and hence the nudge doesn't need
// to be shown any more).
static void DidShowContextMenu();
// Attempts to show the nudge. The nudge will show if it hasn't been shown in
// the past 24 hours, or if it has been shown less than three times. The
// nudge will be anchored to `anchor_view` if provided.
void MaybeShowNudge(views::View* anchor_view);
};
} // namespace ash
#endif // ASH_WM_OVERVIEW_BIRCH_BIRCH_PRIVACY_NUDGE_CONTROLLER_H_

@ -0,0 +1,118 @@
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/wm/overview/birch/birch_privacy_nudge_controller.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/toast/anchored_nudge_manager_impl.h"
#include "ash/test/ash_test_base.h"
#include "base/time/time.h"
#include "base/time/time_override.h"
#include "components/prefs/pref_service.h"
namespace ash {
namespace {
constexpr char kNudgeId[] = "BirchPrivacyId";
bool IsNudgeShown() {
return Shell::Get()->anchored_nudge_manager()->IsNudgeShown(kNudgeId);
}
class BirchPrivacyNudgeControllerTest : public AshTestBase {
public:
BirchPrivacyNudgeControllerTest()
: nudge_controller_(std::make_unique<BirchPrivacyNudgeController>()) {}
BirchPrivacyNudgeController* nudge_controller() {
return nudge_controller_.get();
}
AnchoredNudgeManagerImpl* nudge_manager() {
return Shell::Get()->anchored_nudge_manager();
}
static base::Time GetTestTime() { return test_time_; }
static void SetTestTime(base::Time test_time) { test_time_ = test_time; }
private:
std::unique_ptr<BirchPrivacyNudgeController> nudge_controller_;
static base::Time test_time_;
};
// static
base::Time BirchPrivacyNudgeControllerTest::test_time_;
// Tests that the nudge shows by default.
TEST_F(BirchPrivacyNudgeControllerTest, NudgeShows_ByDefault) {
EXPECT_FALSE(IsNudgeShown());
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_TRUE(IsNudgeShown());
}
// Tests that the nudge does not show if the birch context menu has been
// opened.
TEST_F(BirchPrivacyNudgeControllerTest, NudgeDoesNotShow_WhenMenuWasOpened) {
BirchPrivacyNudgeController::DidShowContextMenu();
EXPECT_FALSE(IsNudgeShown());
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_FALSE(IsNudgeShown());
}
// Tests that the nudge won't show if the time between shown threshold hasn't
// passed since it was last shown.
TEST_F(BirchPrivacyNudgeControllerTest, NudgeDoesNotShow_IfRecentlyShown) {
SetTestTime(base::Time::Now());
base::subtle::ScopedTimeClockOverrides clock_override(
/*time_override=*/&BirchPrivacyNudgeControllerTest::GetTestTime,
/*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
// Show the nudge once and close it.
EXPECT_FALSE(IsNudgeShown());
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_TRUE(IsNudgeShown());
nudge_manager()->Cancel(kNudgeId);
// Attempt showing the nudge again immediately. It should not show.
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_FALSE(IsNudgeShown());
// Attempt showing the nudge after some time but before its threshold time has
// fully passed. It should not show.
SetTestTime(GetTestTime() + base::Hours(23));
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_FALSE(IsNudgeShown());
// Attempt showing the nudge after its "time between shown" threshold has
// passed. It should show.
SetTestTime(GetTestTime() + base::Hours(24));
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_TRUE(IsNudgeShown());
}
TEST_F(BirchPrivacyNudgeControllerTest, NudgeDoesNotShow_IfMaxTimesShown) {
SetTestTime(base::Time::Now());
base::subtle::ScopedTimeClockOverrides clock_override(
/*time_override=*/&BirchPrivacyNudgeControllerTest::GetTestTime,
/*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr);
// Show the nudge its max number of times.
for (int i = 0; i < 3; i++) {
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_TRUE(IsNudgeShown());
nudge_manager()->Cancel(kNudgeId);
SetTestTime(GetTestTime() + base::Hours(24) + base::Minutes(5));
}
// Attempt showing the nudge once more. It should not show.
nudge_controller()->MaybeShowNudge(nullptr);
EXPECT_FALSE(IsNudgeShown());
}
} // namespace
} // namespace ash

@ -1500,6 +1500,8 @@ ash/webui/personalization_app/mojom/personalization_app.mojom -->
<int value="31" label="Search Plus Top Row Key Pressed Nudge"/>
<int value="32" label="Six Pack Key Pressed Nudge"/>
<int value="33" label="Switch Caps Lock Key Pressed Nudge"/>
<int value="34" label="Help Me Read Nudge"/>
<int value="35" label="Birch Privacy Nudge"/>
</enum>
<enum name="OnDeviceToServerSpeechRecognitionFallbackReason">