OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/ui/webui/ntp/android/promo_handler.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/memory/ref_counted_memory.h" | |
9 #include "base/string_number_conversions.h" | |
10 #include "base/string_util.h" | |
11 #include "chrome/browser/android/intent_helper.h" | |
12 #include "chrome/browser/prefs/pref_service.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/browser/profiles/profile_manager.h" | |
15 #include "chrome/browser/signin/signin_manager.h" | |
16 #include "chrome/browser/sync/glue/session_model_associator.h" | |
17 #include "chrome/browser/sync/glue/synced_session.h" | |
18 #include "chrome/browser/sync/profile_sync_service.h" | |
19 #include "chrome/browser/sync/profile_sync_service_factory.h" | |
20 #include "chrome/browser/web_resource/notification_promo.h" | |
21 #include "chrome/browser/web_resource/notification_promo_mobile_ntp.h" | |
22 #include "chrome/browser/web_resource/promo_resource_service.h" | |
23 #include "chrome/common/chrome_notification_types.h" | |
24 #include "chrome/common/pref_names.h" | |
25 #include "content/public/browser/browser_thread.h" | |
26 #include "content/public/browser/notification_service.h" | |
27 #include "content/public/browser/user_metrics.h" | |
28 #include "content/public/browser/web_contents.h" | |
29 | |
30 using content::BrowserThread; | |
31 | |
32 namespace { | |
Dan Beam
2012/08/25 00:48:20
nit: \n
aruslan
2012/08/27 22:19:51
Done.
| |
33 // Helper to ask whether the promo is active. | |
34 bool CanShowNotificationPromo(Profile* profile) { | |
35 NotificationPromo notification_promo(profile); | |
36 notification_promo.InitFromPrefs(NotificationPromo::MOBILE_NTP_SYNC_PROMO); | |
37 return notification_promo.CanShow(); | |
38 } | |
39 | |
40 // Helper to send out promo resource change notification. | |
41 void Notify(PromoHandler* ph, chrome::NotificationType notification_type) { | |
42 content::NotificationService* service = | |
43 content::NotificationService::current(); | |
44 service->Notify(notification_type, | |
45 content::Source<PromoHandler>(ph), | |
46 content::NotificationService::NoDetails()); | |
47 } | |
48 | |
49 // Replaces all formatting markup in the promo with the corresponding HTML. | |
50 string16 ReplaceSimpleMarkupWithHTML(string16 text) { | |
51 const string16 LINE_BREAK = ASCIIToUTF16("<br/>"); | |
52 const string16 SYNCGRAPHIC_IMAGE = | |
53 ASCIIToUTF16("<div class=\"promo-sync-graphic\"></div>"); | |
54 const string16 BEGIN_HIGHLIGHT = | |
55 ASCIIToUTF16( | |
56 "<div style=\"text-align: center\"><button class=\"promo-button\">"); | |
57 const string16 END_HIGHLIGHT = | |
58 ASCIIToUTF16("</button></div>"); | |
59 const string16 BEGIN_LINK = | |
60 ASCIIToUTF16("<span style=\"color: blue; text-decoration: underline;\">"); | |
61 const string16 END_LINK = | |
62 ASCIIToUTF16("</span>"); | |
63 const string16 BEGIN_PROMO_AREA = | |
64 ASCIIToUTF16("<div class=\"promo-action-target\">"); | |
65 const string16 END_PROMO_AREA = | |
66 ASCIIToUTF16("</div>"); | |
67 | |
68 ReplaceSubstringsAfterOffset( | |
69 &text, 0, ASCIIToUTF16("LINE_BREAK"), LINE_BREAK); | |
70 ReplaceSubstringsAfterOffset( | |
71 &text, 0, ASCIIToUTF16("SYNCGRAPHIC_IMAGE"), SYNCGRAPHIC_IMAGE); | |
72 ReplaceSubstringsAfterOffset( | |
73 &text, 0, ASCIIToUTF16("BEGIN_HIGHLIGHT"), BEGIN_HIGHLIGHT); | |
74 ReplaceSubstringsAfterOffset( | |
75 &text, 0, ASCIIToUTF16("END_HIGHLIGHT"), END_HIGHLIGHT); | |
76 ReplaceSubstringsAfterOffset( | |
77 &text, 0, ASCIIToUTF16("BEGIN_LINK"), BEGIN_LINK); | |
78 ReplaceSubstringsAfterOffset( | |
79 &text, 0, ASCIIToUTF16("END_LINK"), END_LINK); | |
80 return BEGIN_PROMO_AREA + text + END_PROMO_AREA; | |
81 } | |
Dan Beam
2012/08/25 00:48:20
nit: \n
aruslan
2012/08/27 22:19:51
Done.
| |
82 } // namespace | |
83 | |
84 PromoHandler::PromoHandler() { | |
85 } | |
86 | |
87 PromoHandler::~PromoHandler() { | |
88 } | |
89 | |
90 void PromoHandler::RegisterMessages() { | |
91 web_ui()->RegisterMessageCallback("getPromotions", | |
92 base::Bind(&PromoHandler::InjectPromoDecorations, | |
93 base::Unretained(this))); | |
94 web_ui()->RegisterMessageCallback("recordImpression", | |
95 base::Bind(&PromoHandler::RecordImpression, | |
96 base::Unretained(this))); | |
97 web_ui()->RegisterMessageCallback("promoActionTriggered", | |
98 base::Bind(&PromoHandler::HandlePromoActionTriggered, | |
99 base::Unretained(this))); | |
100 web_ui()->RegisterMessageCallback("promoDisabled", | |
101 base::Bind(&PromoHandler::HandlePromoDisabled, | |
102 base::Unretained(this))); | |
103 | |
104 Profile* profile = Profile::FromBrowserContext( | |
105 web_ui()->GetWebContents()->GetBrowserContext()); | |
106 if (profile) { | |
Dan Beam
2012/08/25 00:48:20
if this is the norm, might as well change to DCHEC
aruslan
2012/08/27 22:19:51
Good point; the profile is not even necessary here
| |
107 // Watch for pref changes that cause us to need to re-inject promolines. | |
Dan Beam
2012/08/25 00:48:20
why do you need to do this in registermessages rat
aruslan
2012/08/27 22:19:51
Done.
| |
108 registrar_.Add(this, chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED, | |
109 content::NotificationService::AllSources()); | |
110 // Watch for sync service updates that could cause re-injections | |
111 registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE, | |
112 content::NotificationService::AllSources()); | |
113 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED, | |
114 content::NotificationService::AllSources()); | |
115 } | |
116 } | |
117 | |
118 // static | |
119 void PromoHandler::RegisterUserPrefs(PrefService* prefs) { | |
120 prefs->RegisterBooleanPref(prefs::kNtpPromoDesktopSessionFound, | |
121 false, | |
122 PrefService::UNSYNCABLE_PREF); | |
123 } | |
124 | |
125 void PromoHandler::Observe(int type, | |
126 const content::NotificationSource& source, | |
127 const content::NotificationDetails& details) { | |
128 if (chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED == type || | |
129 chrome::NOTIFICATION_SYNC_CONFIGURE_DONE == type || | |
130 chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED == type) { | |
131 // A change occurred to one of the preferences we care about | |
132 CheckDesktopSessions(); | |
133 InjectPromoDecorations(NULL); | |
134 } | |
Dan Beam
2012/08/25 00:48:20
} else {
NOTREACHED() << "Unknown pref changed."
aruslan
2012/08/27 22:19:51
Done.
| |
135 } | |
136 | |
137 void PromoHandler::HandlePromoSendEmail(const base::ListValue* args) { | |
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
139 Profile* profile = Profile::FromBrowserContext( | |
140 web_ui()->GetWebContents()->GetBrowserContext()); | |
141 if (!profile) | |
142 return; | |
143 | |
144 string16 data_subject, data_body, data_inv; | |
145 if (!args || args->GetSize() < 3) { | |
146 LOG(ERROR) << "promoSendEmail: expected three args, got " | |
Dan Beam
2012/08/25 00:48:20
probably DLOG or DVLOG(##) here, eh?
aruslan
2012/08/27 22:19:51
Done.
| |
147 << (args ? args->GetSize() : 0); | |
148 return; | |
149 } | |
150 args->GetString(0, &data_subject); | |
151 args->GetString(1, &data_body); | |
152 args->GetString(2, &data_inv); | |
153 if (data_inv.empty() || (data_subject.empty() && data_body.empty())) | |
154 return; | |
155 | |
156 std::string data_email; | |
157 ProfileSyncService* service = | |
158 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); | |
159 if (service && service->signin()) | |
160 data_email = service->signin()->GetAuthenticatedUsername(); | |
161 | |
162 chrome::android::SendEmail( | |
Dan Beam
2012/08/25 00:48:20
cool!
aruslan
2012/08/27 22:19:51
:)
| |
163 UTF8ToUTF16(data_email), data_subject, data_body, data_inv); | |
164 RecordPromotionImpression("send_email"); | |
165 } | |
166 | |
167 void PromoHandler::HandlePromoActionTriggered( | |
168 const base::ListValue* /*args*/) { | |
169 Profile* profile = Profile::FromWebUI(web_ui()); | |
170 if (!profile || !CanShowNotificationPromo(profile)) | |
171 return; | |
172 | |
173 NotificationPromoMobileNtp promo(profile); | |
174 if (!promo.InitFromPrefs()) | |
175 return; | |
176 | |
177 if (promo.action_type() == "ACTION_EMAIL") | |
178 HandlePromoSendEmail(promo.action_args()); | |
179 } | |
180 | |
181 void PromoHandler::HandlePromoDisabled( | |
182 const base::ListValue* /*args*/) { | |
183 Profile* profile = Profile::FromWebUI(web_ui()); | |
184 if (!profile || !CanShowNotificationPromo(profile)) | |
185 return; | |
186 | |
187 NotificationPromo::HandleClosed( | |
188 profile, NotificationPromo::MOBILE_NTP_SYNC_PROMO); | |
189 RecordPromotionImpression("disable_promo"); | |
190 | |
191 content::NotificationService* service = | |
192 content::NotificationService::current(); | |
193 service->Notify(chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED, | |
194 content::Source<PromoHandler>(this), | |
195 content::NotificationService::NoDetails()); | |
196 } | |
197 | |
198 void PromoHandler::InjectPromoDecorations( | |
199 const base::ListValue* /*args*/) { | |
200 DictionaryValue result; | |
201 if (FetchPromotion(&result)) | |
202 web_ui()->CallJavascriptFunction("ntp.setPromotions", result); | |
203 else | |
204 web_ui()->CallJavascriptFunction("ntp.clearPromotions"); | |
205 } | |
206 | |
207 void PromoHandler::RecordImpression(const base::ListValue* args) { | |
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
209 if (args && !args->empty()) | |
Dan Beam
2012/08/25 00:48:20
if you expect something be passed to this, perhaps
aruslan
2012/08/27 22:19:51
Done.
| |
210 RecordPromotionImpression(UTF16ToASCII(ExtractStringValue(args))); | |
211 } | |
212 | |
213 void PromoHandler::RecordPromotionImpression(const std::string& id) { | |
214 // Update number of views a promotion has received and trigger refresh | |
215 // if it exceeded max_views set for the promotion. | |
216 Profile* profile = Profile::FromWebUI(web_ui()); | |
217 if (profile && | |
218 NotificationPromo::HandleViewed(profile, | |
219 NotificationPromo::MOBILE_NTP_SYNC_PROMO)) | |
Dan Beam
2012/08/25 00:48:20
nit: curlies
aruslan
2012/08/27 22:19:51
Done.
| |
220 Notify(this, chrome::NOTIFICATION_PROMO_RESOURCE_STATE_CHANGED); | |
221 | |
222 if (!id.compare("most_visited")) | |
Dan Beam
2012/08/25 00:48:20
nit: curlies on every level
aruslan
2012/08/27 22:19:51
Done.
| |
223 content::RecordAction( | |
224 content::UserMetricsAction("MobilePromoShownOnMostVisited")); | |
225 else if (!id.compare("open_tabs")) | |
226 content::RecordAction( | |
227 content::UserMetricsAction("MobilePromoShownOnOpenTabs")); | |
228 else if (!id.compare("sync_promo")) | |
229 content::RecordAction( | |
230 content::UserMetricsAction("MobilePromoShownOnSyncPromo")); | |
231 else if (!id.compare("send_email")) | |
232 content::RecordAction( | |
233 content::UserMetricsAction("MobilePromoEmailLinkPressed")); | |
234 else if (!id.compare("disable_promo")) | |
235 content::RecordAction( | |
236 content::UserMetricsAction("MobilePromoClosePressedOnOpenTabs")); | |
237 else | |
238 NOTREACHED() << "Unknown promotion impression: " << id; | |
239 } | |
240 | |
241 bool PromoHandler::FetchPromotion(DictionaryValue* result) { | |
242 DCHECK(result != NULL); | |
243 Profile* profile = Profile::FromWebUI(web_ui()); | |
244 if (!profile || !CanShowNotificationPromo(profile)) | |
Dan Beam
2012/08/25 00:48:20
same general gist on the if () -> DCHECK() if appl
aruslan
2012/08/27 22:19:51
I'll find out more about circumstances except the
| |
245 return false; | |
246 | |
247 NotificationPromoMobileNtp promo(profile); | |
248 if (!promo.InitFromPrefs()) | |
249 return false; | |
250 if (!DoesChromePromoMatchCurrentSync( | |
251 promo.requires_sync(), promo.requires_mobile_only_sync())) | |
252 return false; | |
253 | |
254 string16 promo_line; | |
255 string16 promo_line_long; | |
256 const std::string utf8 = promo.text(); | |
257 const std::string utf8long = promo.text_long(); | |
258 if (utf8.empty() || | |
259 !UTF8ToUTF16(utf8.c_str(), utf8.length(), &promo_line) || | |
260 !UTF8ToUTF16(utf8long.c_str(), utf8long.length(), &promo_line_long)) | |
Dan Beam
2012/08/25 00:48:20
nit: curlies
aruslan
2012/08/27 22:19:51
Done.
| |
261 return false; | |
262 | |
263 promo_line = ReplaceSimpleMarkupWithHTML(promo_line); | |
Dan Beam
2012/08/25 00:48:20
WithHtml would probably be the preferred capitaliz
aruslan
2012/08/27 22:19:51
Done.
| |
264 promo_line_long = ReplaceSimpleMarkupWithHTML(promo_line_long); | |
265 | |
266 result->SetBoolean("promoIsAllowed", true); | |
267 result->SetBoolean("promoIsAllowedOnMostVisited", | |
268 promo.show_on_most_visited()); | |
269 result->SetBoolean("promoIsAllowedOnOpenTabs", promo.show_on_open_tabs()); | |
270 result->SetBoolean("promoIsAllowedAsVC", promo.show_as_virtual_computer()); | |
271 result->SetString("promoVCTitle", promo.virtual_computer_title()); | |
272 result->SetString("promoVCLastSynced", promo.virtual_computer_lastsync()); | |
273 result->SetString("promoMessage", promo_line); | |
274 result->SetString("promoMessageLong", promo_line_long); | |
275 return true; | |
276 } | |
277 | |
278 bool PromoHandler::DoesChromePromoMatchCurrentSync( | |
279 bool promo_requires_sync, | |
280 bool promo_requires_no_active_desktop_sync_sessions) { | |
281 Profile* profile = Profile::FromWebUI(web_ui()); | |
282 if (!profile) | |
283 return false; | |
284 | |
285 // If the promo doesn't require any sync, we are good to go. | |
Dan Beam
2012/08/25 00:48:20
nit: some prefer not to use "we", "you", or "us" i
aruslan
2012/08/27 22:19:51
Done.
| |
286 if (!promo_requires_sync) | |
287 return true; | |
Dan Beam
2012/08/25 00:48:20
nit: \n after every if that might return from the
aruslan
2012/08/27 22:19:51
Done.
| |
288 ProfileSyncService* service = | |
289 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); | |
290 if (!service || !service->ShouldPushChanges()) | |
291 return false; | |
292 | |
293 // If the promo doesn't have specific requirements for the sync, we are good. | |
294 if (!promo_requires_no_active_desktop_sync_sessions) | |
295 return true; | |
296 PrefService* prefs = profile->GetPrefs(); | |
297 if (!prefs) | |
298 return true; | |
299 if (prefs->GetBoolean(prefs::kNtpPromoDesktopSessionFound)) | |
300 return false; | |
301 | |
302 return true; | |
303 } | |
304 | |
305 void PromoHandler::CheckDesktopSessions() { | |
306 Profile* profile = Profile::FromWebUI(web_ui()); | |
307 if (!profile) | |
308 return; | |
309 | |
310 PrefService* prefs = profile->GetPrefs(); | |
311 if (!prefs) | |
312 return; | |
313 if (prefs->GetBoolean(prefs::kNtpPromoDesktopSessionFound)) | |
314 return; // already got a desktop session. | |
Dan Beam
2012/08/25 00:48:20
nit: Already
aruslan
2012/08/27 22:19:51
Done.
| |
315 | |
316 ProfileSyncService* service = | |
317 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); | |
318 if (!service || !service->ShouldPushChanges()) | |
319 return; | |
320 | |
321 browser_sync::SessionModelAssociator* associator = | |
322 service->GetSessionModelAssociator(); | |
323 // No sessions means no desktop sessions as well, so we are good to go. | |
324 if (!associator) | |
325 return; | |
326 | |
327 // Let's see if there are no desktop sessions. | |
328 std::vector<const browser_sync::SyncedSession*> sessions; | |
329 ListValue session_list; | |
330 if (!associator->GetAllForeignSessions(&sessions)) | |
331 return; | |
332 | |
333 for (size_t i = 0; i < sessions.size(); ++i) { | |
334 const browser_sync::SyncedSession::DeviceType device_type = | |
335 sessions[i]->device_type; | |
336 if (device_type == browser_sync::SyncedSession::TYPE_WIN || | |
337 device_type == browser_sync::SyncedSession::TYPE_MACOSX || | |
338 device_type == browser_sync::SyncedSession::TYPE_LINUX) { | |
339 // Found a desktop session: write out the pref. | |
340 prefs->SetBoolean(prefs::kNtpPromoDesktopSessionFound, true); | |
341 return; | |
342 } | |
343 } | |
344 } | |
OLD | NEW |