123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/controls/contextual_tooltip.h"
- #include "ash/constants/ash_features.h"
- #include "ash/constants/ash_pref_names.h"
- #include "ash/constants/ash_switches.h"
- #include "ash/public/cpp/shelf_config.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "base/json/values_util.h"
- #include "base/no_destructor.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_util.h"
- #include "base/time/time.h"
- #include "components/prefs/scoped_user_pref_update.h"
- namespace ash {
- namespace contextual_tooltip {
- namespace {
- // Keys for tooltip sub-preferences for shown count and last time shown.
- constexpr char kShownCount[] = "shown_count";
- constexpr char kLastTimeShown[] = "last_time_shown";
- // Keys for tooltip sub-preferences of how many times a gesture has been
- // successfully performed by the user.
- constexpr char kSuccessCount[] = "success_count";
- // Whether the drag handle nudge cannot be shown because the shelf is currently
- // hidden - used to unblock showing back gesture when shelf is hidden (the back
- // gesture will normally show only if the drag handle nudge has already been
- // shown within the last nudge show interval).
- bool g_drag_handle_nudge_disabled_for_hidden_shelf = false;
- // Whether the back gesture nudge is currently being shown.
- bool g_back_gesture_nudge_showing = false;
- base::Clock* g_clock_override = nullptr;
- base::Time GetTime() {
- if (g_clock_override)
- return g_clock_override->Now();
- return base::Time::Now();
- }
- std::string TooltipTypeToString(TooltipType type) {
- switch (type) {
- case TooltipType::kBackGesture:
- return "back_gesture";
- case TooltipType::kHomeToOverview:
- return "home_to_overview";
- case TooltipType::kInAppToHome:
- return "in_app_to_home";
- case TooltipType::kKeyboardBacklightColor:
- return "keyboard_backlight_color";
- case TooltipType::kKeyboardBacklightWallpaperColor:
- return "keyboard_backlight_wallpaper_color";
- }
- return "invalid";
- }
- // Creates the path to the dictionary value from the contextual tooltip type and
- // the sub-preference.
- std::string GetPath(TooltipType type, const std::string& sub_pref) {
- return base::JoinString({TooltipTypeToString(type), sub_pref}, ".");
- }
- base::Time GetLastShownTime(PrefService* prefs, TooltipType type) {
- const base::Value* last_shown_time =
- prefs->GetValueDict(prefs::kContextualTooltips)
- .FindByDottedPath(GetPath(type, kLastTimeShown));
- if (!last_shown_time)
- return base::Time();
- return *base::ValueToTime(last_shown_time);
- }
- int GetSuccessCount(PrefService* prefs, TooltipType type) {
- absl::optional<int> success_count =
- prefs->GetValueDict(prefs::kContextualTooltips)
- .FindIntByDottedPath(GetPath(type, kSuccessCount));
- return success_count.value_or(0);
- }
- const absl::optional<base::TimeDelta>& GetMinIntervalOverride() {
- // Overridden minimum time between showing contextual nudges to the user.
- static absl::optional<base::TimeDelta> min_interval_override;
- if (!min_interval_override) {
- min_interval_override = switches::ContextualNudgesInterval();
- }
- return min_interval_override;
- }
- } // namespace
- void RegisterProfilePrefs(PrefRegistrySimple* registry) {
- if (features::AreContextualNudgesEnabled())
- registry->RegisterDictionaryPref(prefs::kContextualTooltips);
- }
- bool ShouldShowNudge(PrefService* prefs,
- TooltipType type,
- base::TimeDelta* recheck_delay) {
- auto set_recheck_delay = [&recheck_delay](base::TimeDelta delay) {
- if (recheck_delay)
- *recheck_delay = delay;
- };
- if (!features::AreContextualNudgesEnabled()) {
- set_recheck_delay(base::TimeDelta());
- return false;
- }
- if (type == TooltipType::kInAppToHome &&
- g_drag_handle_nudge_disabled_for_hidden_shelf) {
- set_recheck_delay(base::TimeDelta());
- return false;
- }
- const int success_count = GetSuccessCount(prefs, type);
- if ((type == TooltipType::kHomeToOverview &&
- success_count >= kSuccessLimitHomeToOverview) ||
- (type == TooltipType::kBackGesture &&
- success_count >= kSuccessLimitBackGesture) ||
- (type == TooltipType::kInAppToHome &&
- success_count >= kSuccessLimitInAppToHome) ||
- (type == TooltipType::kKeyboardBacklightColor &&
- success_count >= kSuccessLimitKeyboardBacklightColor)) {
- set_recheck_delay(base::TimeDelta());
- return false;
- }
- const int shown_count = GetShownCount(prefs, type);
- if (shown_count >= kNotificationLimit) {
- set_recheck_delay(base::TimeDelta());
- return false;
- }
- // Before showing back gesture nudge, do not show it if in-app to shelf nudge
- // should be shown (to prevent two nudges from showing up at the same time).
- // Verify that the in-app to home nudge was shown within the last show
- // interval.
- if (type == TooltipType::kBackGesture) {
- if (!g_drag_handle_nudge_disabled_for_hidden_shelf &&
- ShouldShowNudge(prefs, TooltipType::kInAppToHome, nullptr)) {
- set_recheck_delay(kMinIntervalBetweenBackAndDragHandleNudge);
- return false;
- }
- // Verify that drag handle nudge has been shown at least a minute ago.
- const base::Time drag_handle_nudge_last_shown_time =
- GetLastShownTime(prefs, TooltipType::kInAppToHome);
- if (!drag_handle_nudge_last_shown_time.is_null()) {
- const base::TimeDelta time_since_drag_handle_nudge =
- GetTime() - drag_handle_nudge_last_shown_time;
- if (time_since_drag_handle_nudge <
- kMinIntervalBetweenBackAndDragHandleNudge) {
- set_recheck_delay(kMinIntervalBetweenBackAndDragHandleNudge -
- time_since_drag_handle_nudge);
- return false;
- }
- }
- }
- // Make sure that drag handle nudge is not shown within a minute of back
- // gesture nudge.
- if (type == TooltipType::kInAppToHome) {
- if (g_back_gesture_nudge_showing) {
- set_recheck_delay(kMinIntervalBetweenBackAndDragHandleNudge);
- return false;
- }
- const base::Time back_nudge_last_shown_time =
- GetLastShownTime(prefs, TooltipType::kBackGesture);
- if (!back_nudge_last_shown_time.is_null()) {
- const base::TimeDelta time_since_back_nudge =
- GetTime() - back_nudge_last_shown_time;
- if (time_since_back_nudge < kMinIntervalBetweenBackAndDragHandleNudge) {
- set_recheck_delay(kMinIntervalBetweenBackAndDragHandleNudge -
- time_since_back_nudge);
- return false;
- }
- }
- }
- if (shown_count == 0)
- return true;
- const base::Time last_shown_time = GetLastShownTime(prefs, type);
- const base::TimeDelta min_interval =
- GetMinIntervalOverride().value_or(kMinInterval);
- const base::TimeDelta time_since_last_nudge = GetTime() - last_shown_time;
- if (time_since_last_nudge < min_interval) {
- set_recheck_delay(min_interval - time_since_last_nudge);
- return false;
- }
- return true;
- }
- base::TimeDelta GetNudgeTimeout(PrefService* prefs, TooltipType type) {
- const int shown_count = GetShownCount(prefs, type);
- if (shown_count == 0)
- return base::TimeDelta();
- return kNudgeShowDuration;
- }
- int GetShownCount(PrefService* prefs, TooltipType type) {
- absl::optional<int> shown_count =
- prefs->GetValueDict(prefs::kContextualTooltips)
- .FindIntByDottedPath(GetPath(type, kShownCount));
- return shown_count.value_or(0);
- }
- void HandleNudgeShown(PrefService* prefs, TooltipType type) {
- const int shown_count = GetShownCount(prefs, type);
- DictionaryPrefUpdate update(prefs, prefs::kContextualTooltips);
- update->SetIntPath(GetPath(type, kShownCount), shown_count + 1);
- update->SetPath(GetPath(type, kLastTimeShown), base::TimeToValue(GetTime()));
- }
- void HandleGesturePerformed(PrefService* prefs, TooltipType type) {
- const int success_count = GetSuccessCount(prefs, type);
- DictionaryPrefUpdate update(prefs, prefs::kContextualTooltips);
- update->SetIntPath(GetPath(type, kSuccessCount), success_count + 1);
- }
- void SetDragHandleNudgeDisabledForHiddenShelf(bool nudge_disabled) {
- g_drag_handle_nudge_disabled_for_hidden_shelf = nudge_disabled;
- }
- void SetBackGestureNudgeShowing(bool showing) {
- g_back_gesture_nudge_showing = showing;
- }
- void ClearPrefs() {
- DCHECK(Shell::Get()->session_controller()->GetLastActiveUserPrefService());
- DictionaryPrefUpdate update(
- Shell::Get()->session_controller()->GetLastActiveUserPrefService(),
- prefs::kContextualTooltips);
- base::Value* nudges_dict = update.Get();
- if (nudges_dict && !nudges_dict->DictEmpty())
- nudges_dict->DictClear();
- }
- void OverrideClockForTesting(base::Clock* test_clock) {
- DCHECK(!g_clock_override);
- g_clock_override = test_clock;
- }
- void ClearClockOverrideForTesting() {
- DCHECK(g_clock_override);
- g_clock_override = nullptr;
- }
- } // namespace contextual_tooltip
- } // namespace ash
|