OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/notifications/extension_welcome_notification.h" | |
6 | |
7 #include "base/guid.h" | |
8 #include "base/lazy_instance.h" | |
9 #include "base/message_loop/message_loop.h" | |
10 #include "base/prefs/pref_service.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "chrome/browser/browser_process.h" | |
13 #include "chrome/browser/notifications/notification.h" | |
14 #include "chrome/browser/prefs/pref_service_syncable.h" | |
15 #include "chrome/browser/profiles/profile.h" | |
16 #include "chrome/browser/ui/browser_navigator.h" | |
17 #include "chrome/common/pref_names.h" | |
18 #include "chrome/common/url_constants.h" | |
19 #include "chrome/grit/generated_resources.h" | |
20 #include "components/pref_registry/pref_registry_syncable.h" | |
21 #include "grit/theme_resources.h" | |
22 #include "ui/base/l10n/l10n_util.h" | |
23 #include "ui/base/resource/resource_bundle.h" | |
24 #include "ui/message_center/message_center.h" | |
25 #include "ui/message_center/notification.h" | |
26 #include "ui/message_center/notification_delegate.h" | |
27 #include "ui/message_center/notification_types.h" | |
28 | |
29 const int ExtensionWelcomeNotification::kRequestedShowTimeDays = 14; | |
30 | |
31 namespace { | |
32 | |
33 class NotificationCallbacks | |
34 : public message_center::NotificationDelegate { | |
35 public: | |
36 NotificationCallbacks( | |
37 Profile* profile, | |
38 const message_center::NotifierId notifier_id, | |
39 const std::string& welcome_notification_id, | |
40 ExtensionWelcomeNotification::Delegate* delegate) | |
41 : profile_(profile), | |
42 notifier_id_(notifier_id.type, notifier_id.id), | |
43 welcome_notification_id_(welcome_notification_id), | |
44 delegate_(delegate) { | |
45 } | |
46 | |
47 // Overridden from NotificationDelegate: | |
48 virtual void Display() OVERRIDE {} | |
49 virtual void Error() OVERRIDE {} | |
50 | |
51 virtual void Close(bool by_user) OVERRIDE { | |
52 if (by_user) { | |
53 // Setting the preference here may cause the notification erasing | |
54 // to reenter. Posting a task avoids this issue. | |
55 delegate_->PostTask( | |
56 FROM_HERE, | |
57 base::Bind(&NotificationCallbacks::MarkAsDismissed, this)); | |
58 } | |
59 } | |
60 | |
61 virtual void Click() OVERRIDE {} | |
62 virtual void ButtonClick(int index) OVERRIDE { | |
63 if (index == 0) { | |
64 OpenNotificationLearnMoreTab(); | |
65 } else if (index == 1) { | |
66 DisableNotificationProvider(); | |
67 Close(true); | |
68 } else { | |
69 NOTREACHED(); | |
70 } | |
71 } | |
72 | |
73 private: | |
74 void MarkAsDismissed() { | |
75 profile_->GetPrefs()->SetBoolean(prefs::kWelcomeNotificationDismissedLocal, | |
76 true); | |
77 } | |
78 | |
79 void OpenNotificationLearnMoreTab() { | |
80 chrome::NavigateParams params( | |
81 profile_, | |
82 GURL(chrome::kNotificationWelcomeLearnMoreURL), | |
83 content::PAGE_TRANSITION_LINK); | |
84 params.disposition = NEW_FOREGROUND_TAB; | |
85 params.window_action = chrome::NavigateParams::SHOW_WINDOW; | |
86 chrome::Navigate(¶ms); | |
87 } | |
88 | |
89 void DisableNotificationProvider() { | |
90 message_center::Notifier notifier(notifier_id_, base::string16(), true); | |
91 message_center::MessageCenter* message_center = | |
92 delegate_->GetMessageCenter(); | |
93 message_center->DisableNotificationsByNotifier(notifier_id_); | |
94 message_center->RemoveNotification(welcome_notification_id_, false); | |
95 message_center->GetNotifierSettingsProvider()->SetNotifierEnabled( | |
96 notifier, false); | |
97 } | |
98 | |
99 virtual ~NotificationCallbacks() {} | |
100 | |
101 Profile* const profile_; | |
102 | |
103 const message_center::NotifierId notifier_id_; | |
104 | |
105 std::string welcome_notification_id_; | |
106 | |
107 // Weak ref owned by ExtensionWelcomeNotification. | |
108 ExtensionWelcomeNotification::Delegate* const delegate_; | |
109 | |
110 DISALLOW_COPY_AND_ASSIGN(NotificationCallbacks); | |
111 }; | |
112 | |
113 class DefaultDelegate : public ExtensionWelcomeNotification::Delegate { | |
114 public: | |
115 DefaultDelegate() {} | |
116 | |
117 virtual message_center::MessageCenter* GetMessageCenter() OVERRIDE { | |
118 return g_browser_process->message_center(); | |
119 } | |
120 | |
121 virtual base::Time GetCurrentTime() OVERRIDE { | |
122 return base::Time::Now(); | |
123 } | |
124 | |
125 virtual void PostTask( | |
126 const tracked_objects::Location& from_here, | |
127 const base::Closure& task) OVERRIDE { | |
128 base::MessageLoop::current()->PostTask(from_here, task); | |
129 } | |
130 | |
131 private: | |
132 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate); | |
133 }; | |
134 | |
135 } // namespace | |
136 | |
137 ExtensionWelcomeNotification::ExtensionWelcomeNotification( | |
138 const std::string& extension_id, | |
139 Profile* const profile, | |
140 ExtensionWelcomeNotification::Delegate* const delegate) | |
141 : notifier_id_(message_center::NotifierId::APPLICATION, extension_id), | |
142 profile_(profile), | |
143 delegate_(delegate) { | |
144 welcome_notification_dismissed_pref_.Init( | |
145 prefs::kWelcomeNotificationDismissed, | |
146 profile_->GetPrefs(), | |
147 base::Bind( | |
148 &ExtensionWelcomeNotification::OnWelcomeNotificationDismissedChanged, | |
149 base::Unretained(this))); | |
150 welcome_notification_dismissed_local_pref_.Init( | |
151 prefs::kWelcomeNotificationDismissedLocal, | |
152 profile_->GetPrefs()); | |
153 } | |
154 | |
155 // static | |
156 scoped_ptr<ExtensionWelcomeNotification> ExtensionWelcomeNotification::Create( | |
157 const std::string& extension_id, | |
158 Profile* const profile) { | |
159 return Create(extension_id, profile, new DefaultDelegate()).Pass(); | |
160 } | |
161 | |
162 // static | |
163 scoped_ptr<ExtensionWelcomeNotification> ExtensionWelcomeNotification::Create( | |
164 const std::string& extension_id, | |
165 Profile* const profile, | |
166 Delegate* const delegate) { | |
167 return scoped_ptr<ExtensionWelcomeNotification>( | |
168 new ExtensionWelcomeNotification(extension_id, profile, delegate)).Pass(); | |
169 } | |
170 | |
171 ExtensionWelcomeNotification::~ExtensionWelcomeNotification() { | |
172 if (delayed_notification_) { | |
173 delayed_notification_.reset(); | |
174 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this); | |
175 } else { | |
176 HideWelcomeNotification(); | |
177 } | |
178 } | |
179 | |
180 void ExtensionWelcomeNotification::OnIsSyncingChanged() { | |
181 DCHECK(delayed_notification_); | |
182 PrefServiceSyncable* const pref_service_syncable = | |
183 PrefServiceSyncable::FromProfile(profile_); | |
184 if (pref_service_syncable->IsSyncing()) { | |
185 pref_service_syncable->RemoveObserver(this); | |
186 scoped_ptr<Notification> previous_notification( | |
187 delayed_notification_.release()); | |
188 ShowWelcomeNotificationIfNecessary(*(previous_notification.get())); | |
189 } | |
190 } | |
191 | |
192 void ExtensionWelcomeNotification::ShowWelcomeNotificationIfNecessary( | |
193 const Notification& notification) { | |
194 if ((notification.notifier_id() == notifier_id_) && !delayed_notification_) { | |
195 PrefServiceSyncable* const pref_service_syncable = | |
196 PrefServiceSyncable::FromProfile(profile_); | |
197 if (pref_service_syncable->IsSyncing()) { | |
198 PrefService* const pref_service = profile_->GetPrefs(); | |
199 if (!UserHasDismissedWelcomeNotification()) { | |
200 const PopUpRequest pop_up_request = | |
201 pref_service->GetBoolean( | |
202 prefs::kWelcomeNotificationPreviouslyPoppedUp) | |
203 ? POP_UP_HIDDEN | |
204 : POP_UP_SHOWN; | |
205 if (pop_up_request == POP_UP_SHOWN) { | |
206 pref_service->SetBoolean( | |
207 prefs::kWelcomeNotificationPreviouslyPoppedUp, true); | |
208 } | |
209 | |
210 if (IsWelcomeNotificationExpired()) { | |
211 ExpireWelcomeNotification(); | |
212 } else { | |
213 ShowWelcomeNotification( | |
214 notification.display_source(), pop_up_request); | |
215 } | |
216 } | |
217 } else { | |
218 delayed_notification_.reset(new Notification(notification)); | |
219 pref_service_syncable->AddObserver(this); | |
220 } | |
221 } | |
222 } | |
223 | |
224 // static | |
225 void ExtensionWelcomeNotification::RegisterProfilePrefs( | |
226 user_prefs::PrefRegistrySyncable* prefs) { | |
227 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationDismissed, | |
228 false, | |
229 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); | |
230 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationDismissedLocal, | |
231 false, | |
232 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
233 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationPreviouslyPoppedUp, | |
234 false, | |
235 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
236 prefs->RegisterInt64Pref(prefs::kWelcomeNotificationExpirationTimestamp, | |
237 0, | |
238 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); | |
239 } | |
240 | |
241 message_center::MessageCenter* | |
242 ExtensionWelcomeNotification::GetMessageCenter() const { | |
243 return delegate_->GetMessageCenter(); | |
244 } | |
245 | |
246 void ExtensionWelcomeNotification::ShowWelcomeNotification( | |
247 const base::string16& display_source, | |
248 const PopUpRequest pop_up_request) { | |
249 message_center::ButtonInfo learn_more( | |
250 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_BUTTON_LEARN_MORE)); | |
251 learn_more.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
252 IDR_NOTIFICATION_WELCOME_LEARN_MORE); | |
253 message_center::ButtonInfo disable( | |
254 l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BUTTON)); | |
255 disable.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
256 IDR_NOTIFIER_BLOCK_BUTTON); | |
257 | |
258 message_center::RichNotificationData rich_notification_data; | |
259 rich_notification_data.priority = 2; | |
260 rich_notification_data.buttons.push_back(learn_more); | |
261 rich_notification_data.buttons.push_back(disable); | |
262 | |
263 if (welcome_notification_id_.empty()) | |
264 welcome_notification_id_ = base::GenerateGUID(); | |
265 | |
266 if (!welcome_notification_id_.empty()) { | |
267 scoped_ptr<message_center::Notification> message_center_notification( | |
268 new message_center::Notification( | |
269 message_center::NOTIFICATION_TYPE_BASE_FORMAT, | |
270 welcome_notification_id_, | |
271 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_TITLE), | |
272 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_BODY), | |
273 ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
274 IDR_NOTIFICATION_WELCOME_ICON), | |
275 display_source, | |
276 notifier_id_, | |
277 rich_notification_data, | |
278 new NotificationCallbacks( | |
279 profile_, notifier_id_, welcome_notification_id_, | |
280 delegate_.get()))); | |
281 | |
282 if (pop_up_request == POP_UP_HIDDEN) | |
283 message_center_notification->set_shown_as_popup(true); | |
284 | |
285 GetMessageCenter()->AddNotification(message_center_notification.Pass()); | |
286 StartExpirationTimer(); | |
287 } | |
288 } | |
289 | |
290 void ExtensionWelcomeNotification::HideWelcomeNotification() { | |
291 if (!welcome_notification_id_.empty() && | |
292 GetMessageCenter()->FindVisibleNotificationById( | |
293 welcome_notification_id_) != NULL) { | |
294 GetMessageCenter()->RemoveNotification(welcome_notification_id_, false); | |
295 StopExpirationTimer(); | |
296 } | |
297 } | |
298 | |
299 bool ExtensionWelcomeNotification::UserHasDismissedWelcomeNotification() const { | |
300 // This was previously a syncable preference; now it's per-machine. | |
301 // Only the local pref will be written moving forward, but check for both so | |
302 // users won't be double-toasted. | |
303 bool shown_synced = profile_->GetPrefs()->GetBoolean( | |
304 prefs::kWelcomeNotificationDismissed); | |
305 bool shown_local = profile_->GetPrefs()->GetBoolean( | |
306 prefs::kWelcomeNotificationDismissedLocal); | |
307 return (shown_synced || shown_local); | |
308 } | |
309 | |
310 void ExtensionWelcomeNotification::OnWelcomeNotificationDismissedChanged() { | |
311 if (UserHasDismissedWelcomeNotification()) { | |
312 HideWelcomeNotification(); | |
313 } | |
314 } | |
315 | |
316 void ExtensionWelcomeNotification::StartExpirationTimer() { | |
317 if (!expiration_timer_ && !IsWelcomeNotificationExpired()) { | |
318 base::Time expiration_timestamp = GetExpirationTimestamp(); | |
319 if (expiration_timestamp.is_null()) { | |
320 SetExpirationTimestampFromNow(); | |
321 expiration_timestamp = GetExpirationTimestamp(); | |
322 DCHECK(!expiration_timestamp.is_null()); | |
323 } | |
324 expiration_timer_.reset( | |
325 new base::OneShotTimer<ExtensionWelcomeNotification>()); | |
326 expiration_timer_->Start( | |
327 FROM_HERE, | |
328 expiration_timestamp - delegate_->GetCurrentTime(), | |
329 this, | |
330 &ExtensionWelcomeNotification::ExpireWelcomeNotification); | |
331 } | |
332 } | |
333 | |
334 void ExtensionWelcomeNotification::StopExpirationTimer() { | |
335 if (expiration_timer_) { | |
336 expiration_timer_->Stop(); | |
337 expiration_timer_.reset(); | |
338 } | |
339 } | |
340 | |
341 void ExtensionWelcomeNotification::ExpireWelcomeNotification() { | |
342 DCHECK(IsWelcomeNotificationExpired()); | |
343 profile_->GetPrefs()->SetBoolean( | |
344 prefs::kWelcomeNotificationDismissedLocal, true); | |
345 HideWelcomeNotification(); | |
346 } | |
347 | |
348 base::Time ExtensionWelcomeNotification::GetExpirationTimestamp() const { | |
349 PrefService* const pref_service = profile_->GetPrefs(); | |
350 const int64 expiration_timestamp = | |
351 pref_service->GetInt64(prefs::kWelcomeNotificationExpirationTimestamp); | |
352 return (expiration_timestamp == 0) | |
353 ? base::Time() | |
354 : base::Time::FromInternalValue(expiration_timestamp); | |
355 } | |
356 | |
357 void ExtensionWelcomeNotification::SetExpirationTimestampFromNow() { | |
358 PrefService* const pref_service = profile_->GetPrefs(); | |
359 pref_service->SetInt64( | |
360 prefs::kWelcomeNotificationExpirationTimestamp, | |
361 (delegate_->GetCurrentTime() + | |
362 base::TimeDelta::FromDays(kRequestedShowTimeDays)).ToInternalValue()); | |
363 } | |
364 | |
365 bool ExtensionWelcomeNotification::IsWelcomeNotificationExpired() const { | |
366 const base::Time expiration_timestamp = GetExpirationTimestamp(); | |
367 return !expiration_timestamp.is_null() && | |
368 (expiration_timestamp <= delegate_->GetCurrentTime()); | |
369 } | |
OLD | NEW |