OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/android/ntp/content_suggestions_notifier_service.h" | 5 #include "chrome/browser/android/ntp/content_suggestions_notifier_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/android/application_status_listener.h" | 9 #include "base/android/application_status_listener.h" |
10 #include "base/memory/ptr_util.h" | 10 #include "base/memory/ptr_util.h" |
11 #include "chrome/browser/android/ntp/content_suggestions_notification_helper.h" | 11 #include "chrome/browser/android/ntp/content_suggestions_notification_helper.h" |
12 #include "chrome/browser/notifications/notification.h" | 12 #include "chrome/browser/notifications/notification.h" |
13 #include "chrome/browser/notifications/notification_handler.h" | 13 #include "chrome/browser/notifications/notification_handler.h" |
14 #include "chrome/browser/ntp_snippets/ntp_snippets_features.h" | 14 #include "chrome/browser/ntp_snippets/ntp_snippets_features.h" |
| 15 #include "chrome/browser/ntp_snippets/ntp_snippets_metrics.h" |
15 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/common/pref_names.h" |
16 #include "components/ntp_snippets/content_suggestions_service.h" | 18 #include "components/ntp_snippets/content_suggestions_service.h" |
17 #include "components/pref_registry/pref_registry_syncable.h" | 19 #include "components/pref_registry/pref_registry_syncable.h" |
18 #include "components/prefs/pref_service.h" | 20 #include "components/prefs/pref_service.h" |
19 #include "components/variations/variations_associated_data.h" | 21 #include "components/variations/variations_associated_data.h" |
20 #include "ui/gfx/geometry/rect.h" | 22 #include "ui/gfx/geometry/rect.h" |
21 #include "ui/gfx/image/image_skia.h" | 23 #include "ui/gfx/image/image_skia.h" |
22 #include "ui/gfx/image/image_skia_operations.h" | 24 #include "ui/gfx/image/image_skia_operations.h" |
23 | 25 |
24 using ntp_snippets::Category; | 26 using ntp_snippets::Category; |
25 using ntp_snippets::CategoryStatus; | 27 using ntp_snippets::CategoryStatus; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
61 | 63 |
62 } // namespace | 64 } // namespace |
63 | 65 |
64 class ContentSuggestionsNotifierService::NotifyingObserver | 66 class ContentSuggestionsNotifierService::NotifyingObserver |
65 : public ContentSuggestionsService::Observer { | 67 : public ContentSuggestionsService::Observer { |
66 public: | 68 public: |
67 NotifyingObserver(ContentSuggestionsService* service, | 69 NotifyingObserver(ContentSuggestionsService* service, |
68 Profile* profile, | 70 Profile* profile, |
69 PrefService* prefs) | 71 PrefService* prefs) |
70 : service_(service), | 72 : service_(service), |
| 73 profile_(profile), |
71 prefs_(prefs), | 74 prefs_(prefs), |
72 app_status_listener_(base::Bind(&NotifyingObserver::AppStatusChanged, | 75 app_status_listener_(base::Bind(&NotifyingObserver::AppStatusChanged, |
73 base::Unretained(this))), | 76 base::Unretained(this))), |
74 weak_ptr_factory_(this) {} | 77 weak_ptr_factory_(this) {} |
75 | 78 |
76 void OnNewSuggestions(Category category) override { | 79 void OnNewSuggestions(Category category) override { |
77 if (!ShouldNotifyInState(app_status_listener_.GetState())) { | 80 if (!ShouldNotifyInState(app_status_listener_.GetState()) || |
| 81 ContentSuggestionsNotificationHelper::IsDisabledForProfile(profile_)) { |
| 82 DVLOG(1) << "notification suppressed"; |
78 return; | 83 return; |
79 } | 84 } |
80 const ContentSuggestion* suggestion = GetSuggestionToNotifyAbout(category); | 85 const ContentSuggestion* suggestion = GetSuggestionToNotifyAbout(category); |
81 if (!suggestion) { | 86 if (!suggestion) { |
82 return; | 87 return; |
83 } | 88 } |
84 base::Time timeout_at = suggestion->notification_extra() | 89 base::Time timeout_at = suggestion->notification_extra() |
85 ? suggestion->notification_extra()->deadline | 90 ? suggestion->notification_extra()->deadline |
86 : base::Time::Max(); | 91 : base::Time::Max(); |
87 service_->FetchSuggestionImage( | 92 service_->FetchSuggestionImage( |
(...skipping 13 matching lines...) Expand all Loading... |
101 case CategoryStatus::AVAILABLE: | 106 case CategoryStatus::AVAILABLE: |
102 case CategoryStatus::AVAILABLE_LOADING: | 107 case CategoryStatus::AVAILABLE_LOADING: |
103 break; // nothing to do | 108 break; // nothing to do |
104 case CategoryStatus::INITIALIZING: | 109 case CategoryStatus::INITIALIZING: |
105 case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED: | 110 case CategoryStatus::ALL_SUGGESTIONS_EXPLICITLY_DISABLED: |
106 case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED: | 111 case CategoryStatus::CATEGORY_EXPLICITLY_DISABLED: |
107 case CategoryStatus::LOADING_ERROR: | 112 case CategoryStatus::LOADING_ERROR: |
108 case CategoryStatus::NOT_PROVIDED: | 113 case CategoryStatus::NOT_PROVIDED: |
109 case CategoryStatus::SIGNED_OUT: | 114 case CategoryStatus::SIGNED_OUT: |
110 ContentSuggestionsNotificationHelper::HideAllNotifications(); | 115 ContentSuggestionsNotificationHelper::HideAllNotifications(); |
| 116 RecordContentSuggestionsNotificationAction( |
| 117 CONTENT_SUGGESTIONS_HIDE_DISABLED); |
111 break; | 118 break; |
112 } | 119 } |
113 } | 120 } |
114 | 121 |
115 void OnSuggestionInvalidated( | 122 void OnSuggestionInvalidated( |
116 const ContentSuggestion::ID& suggestion_id) override { | 123 const ContentSuggestion::ID& suggestion_id) override { |
| 124 // TODO(sfiera): handle concurrent notifications and non-articles properly. |
117 if (suggestion_id.category().IsKnownCategory(KnownCategories::ARTICLES) && | 125 if (suggestion_id.category().IsKnownCategory(KnownCategories::ARTICLES) && |
118 (suggestion_id.id_within_category() == | 126 (suggestion_id.id_within_category() == |
119 prefs_->GetString(kNotificationIDWithinCategory))) { | 127 prefs_->GetString(kNotificationIDWithinCategory))) { |
120 ContentSuggestionsNotificationHelper::HideAllNotifications(); | 128 ContentSuggestionsNotificationHelper::HideAllNotifications(); |
| 129 RecordContentSuggestionsNotificationAction( |
| 130 CONTENT_SUGGESTIONS_HIDE_EXPIRY); |
121 } | 131 } |
122 } | 132 } |
123 | 133 |
124 void OnFullRefreshRequired() override { | 134 void OnFullRefreshRequired() override { |
125 ContentSuggestionsNotificationHelper::HideAllNotifications(); | 135 ContentSuggestionsNotificationHelper::HideAllNotifications(); |
| 136 RecordContentSuggestionsNotificationAction(CONTENT_SUGGESTIONS_HIDE_EXPIRY); |
126 } | 137 } |
127 | 138 |
128 void ContentSuggestionsServiceShutdown() override { | 139 void ContentSuggestionsServiceShutdown() override { |
129 ContentSuggestionsNotificationHelper::HideAllNotifications(); | 140 ContentSuggestionsNotificationHelper::HideAllNotifications(); |
| 141 RecordContentSuggestionsNotificationAction( |
| 142 CONTENT_SUGGESTIONS_HIDE_SHUTDOWN); |
130 } | 143 } |
131 | 144 |
132 private: | 145 private: |
133 const ContentSuggestion* GetSuggestionToNotifyAbout(Category category) { | 146 const ContentSuggestion* GetSuggestionToNotifyAbout(Category category) { |
134 const auto& suggestions = service_->GetSuggestionsForCategory(category); | 147 const auto& suggestions = service_->GetSuggestionsForCategory(category); |
135 // TODO(sfiera): replace with AlwaysNotifyAboutContentSuggestions(). | 148 // TODO(sfiera): replace with AlwaysNotifyAboutContentSuggestions(). |
136 if (variations::GetVariationParamByFeatureAsBool( | 149 if (variations::GetVariationParamByFeatureAsBool( |
137 kContentSuggestionsNotificationsFeature, | 150 kContentSuggestionsNotificationsFeature, |
138 kContentSuggestionsNotificationsAlwaysNotifyParam, false)) { | 151 kContentSuggestionsNotificationsAlwaysNotifyParam, false)) { |
139 if (category.IsKnownCategory(KnownCategories::ARTICLES) && | 152 if (category.IsKnownCategory(KnownCategories::ARTICLES) && |
140 !suggestions.empty()) { | 153 !suggestions.empty()) { |
141 return &suggestions[0]; | 154 return &suggestions[0]; |
142 } | 155 } |
143 return nullptr; | 156 return nullptr; |
144 } | 157 } |
145 | 158 |
146 for (const ContentSuggestion& suggestion : suggestions) { | 159 for (const ContentSuggestion& suggestion : suggestions) { |
147 if (suggestion.notification_extra()) { | 160 if (suggestion.notification_extra()) { |
148 return &suggestion; | 161 return &suggestion; |
149 } | 162 } |
150 } | 163 } |
151 return nullptr; | 164 return nullptr; |
152 } | 165 } |
153 | 166 |
154 void AppStatusChanged(base::android::ApplicationState state) { | 167 void AppStatusChanged(base::android::ApplicationState state) { |
155 if (!ShouldNotifyInState(state)) { | 168 if (!ShouldNotifyInState(state)) { |
156 ContentSuggestionsNotificationHelper::HideAllNotifications(); | 169 ContentSuggestionsNotificationHelper::HideAllNotifications(); |
| 170 RecordContentSuggestionsNotificationAction( |
| 171 CONTENT_SUGGESTIONS_HIDE_FRONTMOST); |
157 } | 172 } |
158 } | 173 } |
159 | 174 |
160 void ImageFetched(const ContentSuggestion::ID& id, | 175 void ImageFetched(const ContentSuggestion::ID& id, |
161 const GURL& url, | 176 const GURL& url, |
162 const base::string16& title, | 177 const base::string16& title, |
163 const base::string16& publisher, | 178 const base::string16& publisher, |
164 base::Time timeout_at, | 179 base::Time timeout_at, |
165 const gfx::Image& image) { | 180 const gfx::Image& image) { |
166 if (!ShouldNotifyInState(app_status_listener_.GetState())) { | 181 if (!ShouldNotifyInState(app_status_listener_.GetState())) { |
167 return; // Became foreground while we were fetching the image; forget it. | 182 return; // Became foreground while we were fetching the image; forget it. |
168 } | 183 } |
169 // check if suggestion is still valid. | 184 // check if suggestion is still valid. |
170 DVLOG(1) << "Fetched " << image.Size().width() << "x" | 185 DVLOG(1) << "Fetched " << image.Size().width() << "x" |
171 << image.Size().height() << " image for " << url.spec(); | 186 << image.Size().height() << " image for " << url.spec(); |
172 prefs_->SetString(kNotificationIDWithinCategory, id.id_within_category()); | 187 prefs_->SetString(kNotificationIDWithinCategory, id.id_within_category()); |
173 ContentSuggestionsNotificationHelper::SendNotification( | 188 ContentSuggestionsNotificationHelper::SendNotification( |
174 url, title, publisher, CropSquare(image), timeout_at); | 189 url, title, publisher, CropSquare(image), timeout_at); |
| 190 RecordContentSuggestionsNotificationImpression( |
| 191 id.category().IsKnownCategory(KnownCategories::ARTICLES) |
| 192 ? CONTENT_SUGGESTIONS_ARTICLE |
| 193 : CONTENT_SUGGESTIONS_NONARTICLE); |
175 } | 194 } |
176 | 195 |
177 ContentSuggestionsService* const service_; | 196 ContentSuggestionsService* const service_; |
| 197 Profile* const profile_; |
178 PrefService* const prefs_; | 198 PrefService* const prefs_; |
179 base::android::ApplicationStatusListener app_status_listener_; | 199 base::android::ApplicationStatusListener app_status_listener_; |
180 | 200 |
181 base::WeakPtrFactory<NotifyingObserver> weak_ptr_factory_; | 201 base::WeakPtrFactory<NotifyingObserver> weak_ptr_factory_; |
182 | 202 |
183 DISALLOW_COPY_AND_ASSIGN(NotifyingObserver); | 203 DISALLOW_COPY_AND_ASSIGN(NotifyingObserver); |
184 }; | 204 }; |
185 | 205 |
186 ContentSuggestionsNotifierService::ContentSuggestionsNotifierService( | 206 ContentSuggestionsNotifierService::ContentSuggestionsNotifierService( |
187 Profile* profile, | 207 Profile* profile, |
188 ContentSuggestionsService* suggestions, | 208 ContentSuggestionsService* suggestions, |
189 PrefService* prefs) | 209 PrefService* prefs) |
190 : observer_(base::MakeUnique<NotifyingObserver>(suggestions, | 210 : observer_(base::MakeUnique<NotifyingObserver>(suggestions, |
191 profile, | 211 profile, |
192 profile->GetPrefs())) { | 212 profile->GetPrefs())) { |
| 213 ContentSuggestionsNotificationHelper::FlushCachedMetrics(); |
193 suggestions->AddObserver(observer_.get()); | 214 suggestions->AddObserver(observer_.get()); |
194 } | 215 } |
195 | 216 |
196 ContentSuggestionsNotifierService::~ContentSuggestionsNotifierService() = | 217 ContentSuggestionsNotifierService::~ContentSuggestionsNotifierService() = |
197 default; | 218 default; |
198 | 219 |
199 void ContentSuggestionsNotifierService::RegisterProfilePrefs( | 220 void ContentSuggestionsNotifierService::RegisterProfilePrefs( |
200 user_prefs::PrefRegistrySyncable* registry) { | 221 user_prefs::PrefRegistrySyncable* registry) { |
201 registry->RegisterStringPref(kNotificationIDWithinCategory, std::string()); | 222 registry->RegisterStringPref(kNotificationIDWithinCategory, std::string()); |
| 223 registry->RegisterIntegerPref( |
| 224 prefs::kContentSuggestionsConsecutiveIgnoredPrefName, 0); |
202 } | 225 } |
OLD | NEW |