123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- // Copyright 2022 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/app_list/views/app_list_nudge_controller.h"
- #include <memory>
- #include <string>
- #include "ash/constants/ash_pref_names.h"
- #include "ash/constants/ash_switches.h"
- #include "ash/public/cpp/app_list/app_list_types.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "base/json/values_util.h"
- #include "base/time/time.h"
- #include "components/prefs/pref_registry_simple.h"
- #include "components/prefs/pref_service.h"
- #include "components/prefs/scoped_user_pref_update.h"
- namespace ash {
- namespace {
- // Flags to enable/disable the app list nudges for test.
- bool g_reorder_nudge_disabled_for_test = false;
- bool g_privacy_notice_accepted_for_test = false;
- // Reorder nudge dictionary pref keys.
- constexpr char kReorderNudgeShownCount[] = "shown_count";
- constexpr char kReorderNudgeConfirmed[] = "confirmed";
- // Privacy notice dictionary pref keys.
- const char kPrivacyNoticeAcceptedKey[] = "accepted";
- const char kPrivacyNoticeShownKey[] = "shown";
- // Maximum number of times that the nudge is showing to users.
- constexpr int kMaxShowCount = 3;
- // Returns the last active user pref service.
- PrefService* GetPrefs() {
- if (!Shell::HasInstance())
- return nullptr;
- return Shell::Get()->session_controller()->GetLastActiveUserPrefService();
- }
- // Returns the preference path string that corresponds to nudge type `type`.
- std::string GetPrefPath(AppListNudgeController::NudgeType type) {
- switch (type) {
- case AppListNudgeController::NudgeType::kReorderNudge:
- return prefs::kAppListReorderNudge;
- default:
- NOTREACHED();
- return "";
- }
- }
- // Returns true if the app list has been reordered before.
- bool WasAppListReorderedPreviously(PrefService* prefs) {
- const base::Value::Dict& dictionary =
- prefs->GetValueDict(prefs::kAppListReorderNudge);
- return dictionary.FindBool(kReorderNudgeConfirmed).value_or(false);
- }
- } // namespace
- AppListNudgeController::AppListNudgeController() = default;
- // static
- void AppListNudgeController::RegisterProfilePrefs(
- PrefRegistrySimple* registry) {
- registry->RegisterDictionaryPref(prefs::kAppListReorderNudge);
- registry->RegisterDictionaryPref(prefs::kLauncherFilesPrivacyNotice);
- }
- // static
- void AppListNudgeController::ResetPrefsForNewUserSession(PrefService* prefs) {
- prefs->ClearPref(prefs::kAppListReorderNudge);
- prefs->ClearPref(prefs::kLauncherFilesPrivacyNotice);
- }
- // static
- int AppListNudgeController::GetShownCount(PrefService* prefs, NudgeType type) {
- const base::Value::Dict& dictionary = prefs->GetValueDict(GetPrefPath(type));
- return dictionary.FindIntByDottedPath(kReorderNudgeShownCount).value_or(0);
- }
- // static
- void AppListNudgeController::SetReorderNudgeDisabledForTest(bool is_disabled) {
- g_reorder_nudge_disabled_for_test = is_disabled;
- }
- // static
- void AppListNudgeController::SetPrivacyNoticeAcceptedForTest(bool is_disabled) {
- g_privacy_notice_accepted_for_test = is_disabled;
- }
- bool AppListNudgeController::ShouldShowReorderNudge() const {
- if (g_reorder_nudge_disabled_for_test)
- return false;
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return false;
- // Don't show the reorder nudge if the privacy notice is showing.
- if (current_nudge_ == NudgeType::kPrivacyNotice)
- return false;
- if (GetShownCount(prefs, NudgeType::kReorderNudge) < kMaxShowCount &&
- !WasAppListReorderedPreviously(prefs)) {
- return true;
- }
- return false;
- }
- void AppListNudgeController::OnTemporarySortOrderChanged(
- const absl::optional<AppListSortOrder>& new_order) {
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return;
- // Record the reorder action so that the nudge view won't be showing anymore.
- DictionaryPrefUpdate update(prefs, prefs::kAppListReorderNudge);
- update->SetBoolPath(kReorderNudgeConfirmed, true);
- }
- void AppListNudgeController::SetPrivacyNoticeAcceptedPref(bool accepted) {
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return;
- {
- DictionaryPrefUpdate privacy_pref_update(
- prefs, prefs::kLauncherFilesPrivacyNotice);
- privacy_pref_update->SetBoolKey(kPrivacyNoticeAcceptedKey, accepted);
- }
- }
- void AppListNudgeController::SetPrivacyNoticeShownPref(bool shown) {
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return;
- DictionaryPrefUpdate privacy_pref_update(prefs,
- prefs::kLauncherFilesPrivacyNotice);
- privacy_pref_update->SetBoolKey(kPrivacyNoticeShownKey, shown);
- }
- bool AppListNudgeController::IsPrivacyNoticeAccepted() const {
- if (g_privacy_notice_accepted_for_test)
- return true;
- const PrefService* prefs = GetPrefs();
- if (!prefs)
- return false;
- return prefs->GetValueDict(prefs::kLauncherFilesPrivacyNotice)
- .FindBool(kPrivacyNoticeAcceptedKey)
- .value_or(false);
- }
- bool AppListNudgeController::WasPrivacyNoticeShown() const {
- const PrefService* prefs = GetPrefs();
- if (!prefs)
- return false;
- return prefs->GetValueDict(prefs::kLauncherFilesPrivacyNotice)
- .FindBool(kPrivacyNoticeShownKey)
- .value_or(false);
- }
- void AppListNudgeController::SetPrivacyNoticeShown(bool shown) {
- DCHECK(current_nudge_ != NudgeType::kReorderNudge);
- current_nudge_ = shown ? NudgeType::kPrivacyNotice : NudgeType::kNone;
- }
- void AppListNudgeController::SetNudgeVisible(bool is_nudge_visible,
- NudgeType type) {
- // Do not update the state and prefs if it didn't change.
- if (is_visible_ == is_nudge_visible && is_active_ == is_nudge_visible &&
- current_nudge_ == type) {
- return;
- }
- // All NudgeType transition must start from or end to kNone to make sure the
- // prefs is correctly recorded.
- DCHECK(current_nudge_ == NudgeType::kNone || type == NudgeType::kNone ||
- current_nudge_ == type);
- const bool is_visible_updated = is_visible_ != is_nudge_visible;
- const bool is_active_updated = is_active_ != is_nudge_visible;
- is_visible_ = is_nudge_visible;
- // `is_active_` should be updated along with `is_visible_` if visibility
- // updates.
- is_active_ = is_visible_;
- current_nudge_ = type;
- UpdateCurrentNudgeStateInPrefs(is_visible_updated, is_active_updated);
- }
- void AppListNudgeController::SetNudgeActive(bool is_nudge_active,
- NudgeType type) {
- // Do not update the state and prefs if it didn't change.
- if (is_active_ == is_nudge_active && current_nudge_ == type)
- return;
- // All NudgeType transition must start from or end to kNone to make sure the
- // prefs is correctly recorded.
- DCHECK(current_nudge_ == NudgeType::kNone || type == NudgeType::kNone ||
- current_nudge_ == type);
- // The nudge must be visible to change its active state.
- DCHECK(is_visible_);
- const bool is_active_updated = is_active_ != is_nudge_active;
- current_nudge_ = type;
- is_active_ = is_nudge_active;
- UpdateCurrentNudgeStateInPrefs(false, is_active_updated);
- }
- void AppListNudgeController::OnReorderNudgeConfirmed() {
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return;
- // Record the nudge as confirmed so that it will not show up again.
- DictionaryPrefUpdate update(prefs, prefs::kAppListReorderNudge);
- update->SetBoolPath(kReorderNudgeConfirmed, true);
- }
- void AppListNudgeController::UpdateCurrentNudgeStateInPrefs(
- bool is_visible_updated,
- bool is_active_updated) {
- PrefService* prefs = GetPrefs();
- if (!prefs)
- return;
- // Handle the case where the nudge is active to the users.
- if (is_active_) {
- switch (current_nudge_) {
- case NudgeType::kReorderNudge: {
- // Only reset the timer if the `is_active_` state is updated.
- if (is_active_updated)
- current_nudge_show_timestamp_ = base::Time::Now();
- break;
- }
- case NudgeType::kPrivacyNotice:
- case NudgeType::kNone:
- break;
- }
- return;
- }
- // Handle the case where the nudge is not active to the users.
- switch (current_nudge_) {
- case NudgeType::kReorderNudge: {
- DictionaryPrefUpdate update(prefs, prefs::kAppListReorderNudge);
- base::TimeDelta shown_duration =
- base::Time::Now() - current_nudge_show_timestamp_;
- // Caches that the nudge is considered as shown if:
- // 1. the time threshold is skipped; or
- // 2. the time delta of showing the nudge is long enough.
- if (ash::switches::IsSkipRecorderNudgeShowThresholdDurationEnabled() ||
- shown_duration >= base::Seconds(1)) {
- is_nudge_considered_as_shown_ = true;
- }
- // Update the number of times that the reorder nudge was
- // shown to users if the visibility updates.
- if (is_visible_updated) {
- MaybeIncrementShownCountInPrefs(update, shown_duration);
- is_nudge_considered_as_shown_ = false;
- }
- } break;
- case NudgeType::kPrivacyNotice:
- case NudgeType::kNone:
- break;
- }
- }
- void AppListNudgeController::MaybeIncrementShownCountInPrefs(
- DictionaryPrefUpdate& update,
- base::TimeDelta duration) {
- // Only increment the shown count if the nudge changed to invisible state and
- // the nudge was shown long enough to the user before the nudge became
- // invisible. Note that if the nudge is inactive but visible, it doesn't count
- // as showing once to the user.
- if (!is_visible_ && is_nudge_considered_as_shown_) {
- update->SetIntPath(kReorderNudgeShownCount,
- GetShownCount(GetPrefs(), NudgeType::kReorderNudge) + 1);
- }
- }
- } // namespace ash
|