Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(595)

Side by Side Diff: chrome/browser/ui/webui/ntp/android/promo_handler.cc

Issue 10882024: Add webui handler for promotions on Android NTP. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698