Index: components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..aaf6b72a46d5fbb96ae0f729a6f9776992b042df |
--- /dev/null |
+++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
@@ -0,0 +1,329 @@ |
+// Copyright 2016 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 "components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h" |
+ |
+#include <string> |
+#include <utility> |
+ |
+#include "components/ntp_snippets/features.h" |
+#include "components/ntp_snippets/pref_names.h" |
+#include "components/ntp_snippets/remote/persistent_scheduler.h" |
+#include "components/ntp_snippets/status.h" |
+#include "components/ntp_snippets/user_classifier.h" |
+#include "components/prefs/pref_registry_simple.h" |
+#include "components/prefs/pref_service.h" |
+#include "components/variations/variations_associated_data.h" |
+ |
+namespace ntp_snippets { |
+ |
+namespace { |
+ |
+// Default values for fetching intervals, fallback and wifi. |
+const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0}; |
+const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0}; |
+const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0}; |
+ |
+// Variation parameters than can the default fetching intervals. |
+const char* kFetchingIntervalParamNameRareNtpUser[] = { |
+ "fetching_interval_hours-fallback-rare_ntp_user", |
+ "fetching_interval_hours-wifi-rare_ntp_user"}; |
+const char* kFetchingIntervalParamNameActiveNtpUser[] = { |
+ "fetching_interval_hours-fallback-active_ntp_user", |
+ "fetching_interval_hours-wifi-active_ntp_user"}; |
+const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = { |
+ "fetching_interval_hours-fallback-active_suggestions_consumer", |
+ "fetching_interval_hours-wifi-active_suggestions_consumer"}; |
+ |
+base::TimeDelta GetDesiredUpdateInterval( |
+ bool is_wifi, |
+ UserClassifier::UserClass user_class) { |
+ double default_value_hours = 0.0; |
+ |
+ const int index = is_wifi ? 1 : 0; |
+ const char* param_name = nullptr; |
+ switch (user_class) { |
+ case UserClassifier::UserClass::RARE_NTP_USER: |
+ default_value_hours = kDefaultFetchingIntervalRareNtpUser[index]; |
+ param_name = kFetchingIntervalParamNameRareNtpUser[index]; |
+ break; |
+ case UserClassifier::UserClass::ACTIVE_NTP_USER: |
+ default_value_hours = kDefaultFetchingIntervalActiveNtpUser[index]; |
+ param_name = kFetchingIntervalParamNameActiveNtpUser[index]; |
+ break; |
+ case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: |
+ default_value_hours = |
+ kDefaultFetchingIntervalActiveSuggestionsConsumer[index]; |
+ param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index]; |
+ break; |
+ } |
+ |
+ double value_hours = variations::GetVariationParamByFeatureAsDouble( |
+ ntp_snippets::kArticleSuggestionsFeature, param_name, |
+ default_value_hours); |
+ |
+ return base::TimeDelta::FromSecondsD(value_hours * 3600.0); |
+} |
+ |
+} // namespace |
+ |
+struct SchedulingRemoteSuggestionsProvider::FetchingSchedule { |
+ base::TimeDelta interval_wifi; |
+ base::TimeDelta interval_fallback; |
+ |
+ static FetchingSchedule Empty() { |
+ return FetchingSchedule{base::TimeDelta(), |
+ base::TimeDelta()}; |
+ } |
+ |
+ bool operator==(const FetchingSchedule& other) const { |
+ return interval_wifi == other.interval_wifi && |
+ interval_fallback == other.interval_fallback; |
+ } |
+ |
+ bool operator!=(const FetchingSchedule& other) const { |
+ return !operator==(other); |
+ } |
+ |
+ bool is_empty() const { |
+ return interval_wifi.is_zero() && interval_fallback.is_zero(); |
+ } |
+}; |
+ |
+SchedulingRemoteSuggestionsProvider::SchedulingRemoteSuggestionsProvider( |
+ Observer* observer, |
+ std::unique_ptr<RemoteSuggestionsProvider> provider, |
+ PersistentScheduler* persistent_scheduler, |
+ const UserClassifier* user_classifier, |
+ PrefService* pref_service) |
+ : RemoteSuggestionsProvider(observer), |
+ RemoteSuggestionsScheduler(), |
+ provider_(std::move(provider)), |
+ persistent_scheduler_(persistent_scheduler), |
+ user_classifier_(user_classifier), |
+ pref_service_(pref_service) { |
+ DCHECK(user_classifier); |
+ DCHECK(pref_service); |
+ |
+ provider_->SetProviderStatusCallback( |
+ base::MakeUnique<RemoteSuggestionsProvider::ProviderStatusCallback>( |
+ base::BindRepeating( |
+ &SchedulingRemoteSuggestionsProvider::OnProviderStatusChanged, |
+ base::Unretained(this)))); |
+} |
+ |
+SchedulingRemoteSuggestionsProvider::~SchedulingRemoteSuggestionsProvider() = |
+ default; |
+ |
+// static |
+void SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs( |
+ PrefRegistrySimple* registry) { |
+ registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); |
+ registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, |
+ 0); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::RescheduleFetching() { |
+ // Force the reschedule by stopping and starting it again. |
+ StopScheduling(); |
+ StartScheduling(); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::OnPersistentSchedulerWakeUp() { |
+ provider_->RefetchInTheBackground( |
+ base::MakeUnique<RemoteSuggestionsProvider::FetchStatusCallback>( |
+ base::Bind(&SchedulingRemoteSuggestionsProvider::OnFetchCompleted, |
+ base::Unretained(this)))); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::SetProviderStatusCallback( |
+ std::unique_ptr<ProviderStatusCallback> callback) { |
+ provider_->SetProviderStatusCallback(std::move(callback)); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::RefetchInTheBackground( |
+ std::unique_ptr<FetchStatusCallback> callback) { |
+ provider_->RefetchInTheBackground(std::move(callback)); |
+} |
+ |
+const NTPSnippetsFetcher* SchedulingRemoteSuggestionsProvider:: |
+ snippets_fetcher_for_testing_and_debugging() const { |
+ return provider_->snippets_fetcher_for_testing_and_debugging(); |
+} |
+ |
+CategoryStatus SchedulingRemoteSuggestionsProvider::GetCategoryStatus( |
+ Category category) { |
+ return provider_->GetCategoryStatus(category); |
+} |
+ |
+CategoryInfo SchedulingRemoteSuggestionsProvider::GetCategoryInfo( |
+ Category category) { |
+ return provider_->GetCategoryInfo(category); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::DismissSuggestion( |
+ const ContentSuggestion::ID& suggestion_id) { |
+ provider_->DismissSuggestion(suggestion_id); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::FetchSuggestionImage( |
+ const ContentSuggestion::ID& suggestion_id, |
+ const ImageFetchedCallback& callback) { |
+ provider_->FetchSuggestionImage(suggestion_id, callback); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::Fetch( |
+ const Category& category, |
+ const std::set<std::string>& known_suggestion_ids, |
+ const FetchDoneCallback& callback) { |
+ provider_->Fetch( |
+ category, known_suggestion_ids, |
+ base::Bind(&SchedulingRemoteSuggestionsProvider::FetchFinished, |
+ base::Unretained(this), callback)); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::ReloadSuggestions() { |
+ provider_->ReloadSuggestions(); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::ClearHistory( |
+ base::Time begin, |
+ base::Time end, |
+ const base::Callback<bool(const GURL& url)>& filter) { |
+ provider_->ClearHistory(begin, end, filter); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::ClearCachedSuggestions( |
+ Category category) { |
+ provider_->ClearCachedSuggestions(category); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::OnSignInStateChanged() { |
+ provider_->OnSignInStateChanged(); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging( |
+ Category category, |
+ const DismissedSuggestionsCallback& callback) { |
+ provider_->GetDismissedSuggestionsForDebugging(category, callback); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging( |
+ Category category) { |
+ provider_->ClearDismissedSuggestionsForDebugging(category); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::OnProviderStatusChanged( |
+ RemoteSuggestionsProvider::ProviderStatus status) { |
+ switch (status) { |
+ case RemoteSuggestionsProvider::ProviderStatus::ACTIVE: |
+ StartScheduling(); |
+ return; |
+ case RemoteSuggestionsProvider::ProviderStatus::INACTIVE: |
+ StopScheduling(); |
+ return; |
+ } |
+ NOTREACHED(); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::StartScheduling() { |
+ // The scheduler only exists on Android so far, it's null on other platforms. |
+ if (!persistent_scheduler_) { |
+ return; |
+ } |
+ |
+ FetchingSchedule last_schedule = GetLastFetchingSchedule(); |
+ FetchingSchedule schedule = GetDesiredFetchingSchedule(); |
+ |
+ // Reset the schedule only if the parameters have changed. |
+ if (last_schedule != schedule) { |
+ ApplyFetchingSchedule(schedule); |
+ } |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::StopScheduling() { |
+ // The scheduler only exists on Android so far, it's null on other platforms. |
+ if (!persistent_scheduler_) { |
+ return; |
+ } |
+ |
+ // Do not unschedule if already switched off |
+ FetchingSchedule last_schedule = GetLastFetchingSchedule(); |
+ if (last_schedule.is_empty()) { |
+ return; |
+ } |
+ |
+ persistent_scheduler_->Unschedule(); |
+ |
+ StoreLastFetchingSchedule(FetchingSchedule::Empty()); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::ApplyFetchingSchedule( |
+ const FetchingSchedule& schedule) { |
+ persistent_scheduler_->Schedule(schedule.interval_wifi, |
+ schedule.interval_fallback); |
+ |
+ StoreLastFetchingSchedule(schedule); |
+} |
+ |
+SchedulingRemoteSuggestionsProvider::FetchingSchedule |
+SchedulingRemoteSuggestionsProvider::GetDesiredFetchingSchedule() const { |
+ UserClassifier::UserClass user_class = user_classifier_->GetUserClass(); |
+ |
+ FetchingSchedule schedule; |
+ schedule.interval_wifi = |
+ GetDesiredUpdateInterval(/*is_wifi=*/true, user_class); |
+ schedule.interval_fallback = |
+ GetDesiredUpdateInterval(/*is_wifi=*/false, user_class); |
+ return schedule; |
+} |
+ |
+SchedulingRemoteSuggestionsProvider::FetchingSchedule |
+SchedulingRemoteSuggestionsProvider::GetLastFetchingSchedule() const { |
+ FetchingSchedule schedule; |
+ schedule.interval_wifi = base::TimeDelta::FromInternalValue( |
+ pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi)); |
+ schedule.interval_fallback = |
+ base::TimeDelta::FromInternalValue(pref_service_->GetInt64( |
+ prefs::kSnippetBackgroundFetchingIntervalFallback)); |
+ return schedule; |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::StoreLastFetchingSchedule( |
+ const FetchingSchedule& schedule) { |
+ pref_service_->SetInt64( |
+ prefs::kSnippetBackgroundFetchingIntervalWifi, |
+ schedule.interval_wifi.ToInternalValue()); |
+ pref_service_->SetInt64( |
+ prefs::kSnippetBackgroundFetchingIntervalFallback, |
+ schedule.interval_fallback.ToInternalValue()); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::FetchFinished( |
+ const FetchDoneCallback& callback, |
+ Status status_code, |
+ std::vector<ContentSuggestion> suggestions) { |
+ OnFetchCompleted(status_code); |
+ callback.Run(status_code, std::move(suggestions)); |
+} |
+ |
+void SchedulingRemoteSuggestionsProvider::OnFetchCompleted( |
+ Status fetch_status) { |
+ // The scheduler only exists on Android so far, it's null on other platforms. |
+ if (!persistent_scheduler_) { |
+ return; |
+ } |
+ |
+ if (fetch_status.code != StatusCode::SUCCESS) { |
+ return; |
+ } |
+ |
+ // Reschedule after a successful fetch. This resets all currently scheduled |
+ // fetches, to make sure the fallback interval triggers only if no wifi fetch |
+ // succeeded, and also that we don't do a background fetch immediately after |
+ // a user-initiated one. |
+ ApplyFetchingSchedule(GetLastFetchingSchedule()); |
+} |
+ |
+} // namespace ntp_snippets |