| // Copyright 2021 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 "chrome/browser/ui/user_education/browser_feature_promo_snooze_service.h" |
| |
| #include <ostream> |
| |
| #include "base/feature_list.h" |
| #include "base/json/values_util.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| |
| namespace { |
| // Snooze data will be saved as a dictionary in the PrefService of a profile. |
| |
| // PrefService path. |
| const char kIPHSnoozeDataPath[] = "in_product_help.snoozed_feature"; |
| |
| // Path to the boolean indicates if an IPH was dismissed. |
| // in_product_help.snoozed_feature.[iph_name].is_dismissed |
| constexpr char kIPHIsDismissedPath[] = "is_dismissed"; |
| // Path to the timestamp an IPH was last shown. |
| // in_product_help.snoozed_feature.[iph_name].last_show_time |
| constexpr char kIPHLastShowTimePath[] = "last_show_time"; |
| // Path to the timestamp an IPH was last snoozed. |
| // in_product_help.snoozed_feature.[iph_name].last_snooze_time |
| constexpr char kIPHLastSnoozeTimePath[] = "last_snooze_time"; |
| // Path to the duration of snooze. |
| // in_product_help.snoozed_feature.[iph_name].last_snooze_duration |
| constexpr char kIPHLastSnoozeDurationPath[] = "last_snooze_duration"; |
| // Path to the count of how many times this IPH has been snoozed. |
| // in_product_help.snoozed_feature.[iph_name].snooze_count |
| constexpr char kIPHSnoozeCountPath[] = "snooze_count"; |
| // Path to the count of how many times this IPH has been shown. |
| // in_product_help.snoozed_feature.[iph_name].show_count |
| constexpr char kIPHShowCountPath[] = "show_count"; |
| |
| } // namespace |
| |
| BrowserFeaturePromoSnoozeService::BrowserFeaturePromoSnoozeService( |
| Profile* profile) |
| : profile_(profile) {} |
| BrowserFeaturePromoSnoozeService::~BrowserFeaturePromoSnoozeService() = default; |
| |
| // static |
| void BrowserFeaturePromoSnoozeService::RegisterProfilePrefs( |
| PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(kIPHSnoozeDataPath); |
| } |
| |
| void BrowserFeaturePromoSnoozeService::Reset(const base::Feature& iph_feature) { |
| DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath); |
| auto* const pref_data = update.Get(); |
| pref_data->RemovePath(iph_feature.name); |
| } |
| |
| absl::optional<user_education::FeaturePromoSnoozeService::SnoozeData> |
| BrowserFeaturePromoSnoozeService::ReadSnoozeData( |
| const base::Feature& iph_feature) { |
| std::string path_prefix = std::string(iph_feature.name) + "."; |
| |
| const auto& pref_data = |
| profile_->GetPrefs()->GetValueDict(kIPHSnoozeDataPath); |
| absl::optional<bool> is_dismissed = |
| pref_data.FindBoolByDottedPath(path_prefix + kIPHIsDismissedPath); |
| absl::optional<base::Time> show_time = base::ValueToTime( |
| pref_data.FindByDottedPath(path_prefix + kIPHLastShowTimePath)); |
| absl::optional<base::Time> snooze_time = base::ValueToTime( |
| pref_data.FindByDottedPath(path_prefix + kIPHLastSnoozeTimePath)); |
| absl::optional<base::TimeDelta> snooze_duration = base::ValueToTimeDelta( |
| pref_data.FindByDottedPath(path_prefix + kIPHLastSnoozeDurationPath)); |
| absl::optional<int> snooze_count = |
| pref_data.FindIntByDottedPath(path_prefix + kIPHSnoozeCountPath); |
| absl::optional<int> show_count = |
| pref_data.FindIntByDottedPath(path_prefix + kIPHShowCountPath); |
| |
| absl::optional<SnoozeData> snooze_data; |
| |
| if (!is_dismissed) |
| return snooze_data; |
| |
| if (!snooze_time || !snooze_count || !snooze_duration) { |
| // IPH snooze data is corrupt. Clear data of this feature. |
| Reset(iph_feature); |
| return snooze_data; |
| } |
| |
| if (!show_time || !show_count) { |
| // This feature was shipped before without handling |
| // non-clickers. Assume previous displays were all snooozed. |
| show_time = *snooze_time - base::Seconds(1); |
| show_count = *snooze_count; |
| } |
| |
| snooze_data = SnoozeData(); |
| snooze_data->is_dismissed = *is_dismissed; |
| snooze_data->last_show_time = *show_time; |
| snooze_data->last_snooze_time = *snooze_time; |
| snooze_data->last_snooze_duration = *snooze_duration; |
| snooze_data->snooze_count = *snooze_count; |
| snooze_data->show_count = *show_count; |
| |
| return snooze_data; |
| } |
| |
| void BrowserFeaturePromoSnoozeService::SaveSnoozeData( |
| const base::Feature& iph_feature, |
| const user_education::FeaturePromoSnoozeService::SnoozeData& snooze_data) { |
| std::string path_prefix = std::string(iph_feature.name) + "."; |
| |
| DictionaryPrefUpdate update(profile_->GetPrefs(), kIPHSnoozeDataPath); |
| auto* const pref_data = update.Get(); |
| |
| pref_data->SetBoolPath(path_prefix + kIPHIsDismissedPath, |
| snooze_data.is_dismissed); |
| pref_data->SetPath(path_prefix + kIPHLastShowTimePath, |
| base::TimeToValue(snooze_data.last_show_time)); |
| pref_data->SetPath(path_prefix + kIPHLastSnoozeTimePath, |
| base::TimeToValue(snooze_data.last_snooze_time)); |
| pref_data->SetPath(path_prefix + kIPHLastSnoozeDurationPath, |
| base::TimeDeltaToValue(snooze_data.last_snooze_duration)); |
| pref_data->SetIntPath(path_prefix + kIPHSnoozeCountPath, |
| snooze_data.snooze_count); |
| pref_data->SetIntPath(path_prefix + kIPHShowCountPath, |
| snooze_data.show_count); |
| } |