OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/local_discovery/privet_notifications.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/command_line.h" | |
11 #include "base/location.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/prefs/pref_service.h" | |
14 #include "base/rand_util.h" | |
15 #include "base/single_thread_task_runner.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/thread_task_runner_handle.h" | |
18 #include "chrome/browser/browser_process.h" | |
19 #include "chrome/browser/local_discovery/privet_device_lister_impl.h" | |
20 #include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h" | |
21 #include "chrome/browser/local_discovery/service_discovery_shared_client.h" | |
22 #include "chrome/browser/notifications/notification.h" | |
23 #include "chrome/browser/notifications/notification_ui_manager.h" | |
24 #include "chrome/browser/profiles/profile.h" | |
25 #include "chrome/browser/signin/signin_manager_factory.h" | |
26 #include "chrome/browser/ui/browser.h" | |
27 #include "chrome/browser/ui/browser_finder.h" | |
28 #include "chrome/browser/ui/browser_navigator_params.h" | |
29 #include "chrome/browser/ui/browser_window.h" | |
30 #include "chrome/browser/ui/host_desktop.h" | |
31 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
32 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h" | |
33 #include "chrome/common/chrome_switches.h" | |
34 #include "chrome/common/pref_names.h" | |
35 #include "chrome/grit/generated_resources.h" | |
36 #include "components/signin/core/browser/signin_manager_base.h" | |
37 #include "content/public/browser/browser_context.h" | |
38 #include "content/public/browser/navigation_controller.h" | |
39 #include "content/public/browser/web_contents.h" | |
40 #include "grit/theme_resources.h" | |
41 #include "ui/base/l10n/l10n_util.h" | |
42 #include "ui/base/page_transition_types.h" | |
43 #include "ui/base/resource/resource_bundle.h" | |
44 #include "ui/message_center/notifier_settings.h" | |
45 | |
46 #if defined(ENABLE_MDNS) | |
47 #include "chrome/browser/local_discovery/privet_traffic_detector.h" | |
48 #endif | |
49 | |
50 namespace local_discovery { | |
51 | |
52 namespace { | |
53 | |
54 const int kTenMinutesInSeconds = 600; | |
55 const char kPrivetInfoKeyUptime[] = "uptime"; | |
56 const char kPrivetNotificationID[] = "privet_notification"; | |
57 const char kPrivetNotificationOriginUrl[] = "chrome://devices"; | |
58 const int kStartDelaySeconds = 5; | |
59 | |
60 enum PrivetNotificationsEvent { | |
61 PRIVET_SERVICE_STARTED, | |
62 PRIVET_LISTER_STARTED, | |
63 PRIVET_DEVICE_CHANGED, | |
64 PRIVET_INFO_DONE, | |
65 PRIVET_NOTIFICATION_SHOWN, | |
66 PRIVET_NOTIFICATION_CANCELED, | |
67 PRIVET_NOTIFICATION_CLICKED, | |
68 PRIVET_DISABLE_NOTIFICATIONS_CLICKED, | |
69 PRIVET_EVENT_MAX, | |
70 }; | |
71 | |
72 void ReportPrivetUmaEvent(PrivetNotificationsEvent privet_event) { | |
73 UMA_HISTOGRAM_ENUMERATION("LocalDiscovery.PrivetNotificationsEvent", | |
74 privet_event, PRIVET_EVENT_MAX); | |
75 } | |
76 | |
77 } // namespace | |
78 | |
79 PrivetNotificationsListener::PrivetNotificationsListener( | |
80 scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory, | |
81 Delegate* delegate) | |
82 : delegate_(delegate), devices_active_(0) { | |
83 privet_http_factory_.swap(privet_http_factory); | |
84 } | |
85 | |
86 PrivetNotificationsListener::~PrivetNotificationsListener() { | |
87 } | |
88 | |
89 void PrivetNotificationsListener::DeviceChanged( | |
90 bool added, | |
91 const std::string& name, | |
92 const DeviceDescription& description) { | |
93 ReportPrivetUmaEvent(PRIVET_DEVICE_CHANGED); | |
94 DeviceContextMap::iterator found = devices_seen_.find(name); | |
95 if (found != devices_seen_.end()) { | |
96 if (!description.id.empty() && // Device is registered | |
97 found->second->notification_may_be_active) { | |
98 found->second->notification_may_be_active = false; | |
99 NotifyDeviceRemoved(); | |
100 } | |
101 return; // Already saw this device. | |
102 } | |
103 | |
104 linked_ptr<DeviceContext> device_context(new DeviceContext); | |
105 | |
106 device_context->notification_may_be_active = false; | |
107 device_context->registered = !description.id.empty(); | |
108 | |
109 devices_seen_.insert(make_pair(name, device_context)); | |
110 | |
111 if (!device_context->registered) { | |
112 device_context->privet_http_resolution = | |
113 privet_http_factory_->CreatePrivetHTTP(name); | |
114 device_context->privet_http_resolution->Start( | |
115 description.address, | |
116 base::Bind(&PrivetNotificationsListener::CreateInfoOperation, | |
117 base::Unretained(this))); | |
118 } | |
119 } | |
120 | |
121 void PrivetNotificationsListener::CreateInfoOperation( | |
122 scoped_ptr<PrivetHTTPClient> http_client) { | |
123 if (!http_client) { | |
124 // Do nothing if resolution fails. | |
125 return; | |
126 } | |
127 | |
128 std::string name = http_client->GetName(); | |
129 DeviceContextMap::iterator device_iter = devices_seen_.find(name); | |
130 if (device_iter == devices_seen_.end()) | |
131 return; | |
132 DeviceContext* device = device_iter->second.get(); | |
133 device->privet_http.swap(http_client); | |
134 device->info_operation = device->privet_http->CreateInfoOperation( | |
135 base::Bind(&PrivetNotificationsListener::OnPrivetInfoDone, | |
136 base::Unretained(this), | |
137 device)); | |
138 device->info_operation->Start(); | |
139 } | |
140 | |
141 void PrivetNotificationsListener::OnPrivetInfoDone( | |
142 DeviceContext* device, | |
143 const base::DictionaryValue* json_value) { | |
144 int uptime; | |
145 | |
146 if (!json_value || | |
147 !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) || | |
148 uptime > kTenMinutesInSeconds) { | |
149 return; | |
150 } | |
151 | |
152 DCHECK(!device->notification_may_be_active); | |
153 device->notification_may_be_active = true; | |
154 devices_active_++; | |
155 delegate_->PrivetNotify(devices_active_, true); | |
156 } | |
157 | |
158 void PrivetNotificationsListener::DeviceRemoved(const std::string& name) { | |
159 DeviceContextMap::iterator device_iter = devices_seen_.find(name); | |
160 if (device_iter == devices_seen_.end()) | |
161 return; | |
162 DeviceContext* device = device_iter->second.get(); | |
163 | |
164 device->info_operation.reset(); | |
165 device->privet_http_resolution.reset(); | |
166 device->notification_may_be_active = false; | |
167 NotifyDeviceRemoved(); | |
168 } | |
169 | |
170 void PrivetNotificationsListener::DeviceCacheFlushed() { | |
171 for (DeviceContextMap::iterator i = devices_seen_.begin(); | |
172 i != devices_seen_.end(); ++i) { | |
173 DeviceContext* device = i->second.get(); | |
174 | |
175 device->info_operation.reset(); | |
176 device->privet_http_resolution.reset(); | |
177 if (device->notification_may_be_active) { | |
178 device->notification_may_be_active = false; | |
179 } | |
180 } | |
181 | |
182 devices_active_ = 0; | |
183 delegate_->PrivetRemoveNotification(); | |
184 } | |
185 | |
186 void PrivetNotificationsListener::NotifyDeviceRemoved() { | |
187 devices_active_--; | |
188 if (devices_active_ == 0) { | |
189 delegate_->PrivetRemoveNotification(); | |
190 } else { | |
191 delegate_->PrivetNotify(devices_active_, false); | |
192 } | |
193 } | |
194 | |
195 PrivetNotificationsListener::DeviceContext::DeviceContext() { | |
196 } | |
197 | |
198 PrivetNotificationsListener::DeviceContext::~DeviceContext() { | |
199 } | |
200 | |
201 PrivetNotificationService::PrivetNotificationService( | |
202 content::BrowserContext* profile) | |
203 : profile_(profile) { | |
204 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
205 FROM_HERE, base::Bind(&PrivetNotificationService::Start, AsWeakPtr()), | |
206 base::TimeDelta::FromSeconds(kStartDelaySeconds + | |
207 base::RandInt(0, kStartDelaySeconds / 4))); | |
208 } | |
209 | |
210 PrivetNotificationService::~PrivetNotificationService() { | |
211 } | |
212 | |
213 void PrivetNotificationService::DeviceChanged( | |
214 bool added, | |
215 const std::string& name, | |
216 const DeviceDescription& description) { | |
217 privet_notifications_listener_->DeviceChanged(added, name, description); | |
218 } | |
219 | |
220 void PrivetNotificationService::DeviceRemoved(const std::string& name) { | |
221 privet_notifications_listener_->DeviceRemoved(name); | |
222 } | |
223 | |
224 void PrivetNotificationService::DeviceCacheFlushed() { | |
225 privet_notifications_listener_->DeviceCacheFlushed(); | |
226 } | |
227 | |
228 // static | |
229 bool PrivetNotificationService::IsEnabled() { | |
230 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
231 return !command_line->HasSwitch( | |
232 switches::kDisableDeviceDiscoveryNotifications); | |
233 } | |
234 | |
235 // static | |
236 bool PrivetNotificationService::IsForced() { | |
237 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
238 return command_line->HasSwitch(switches::kEnableDeviceDiscoveryNotifications); | |
239 } | |
240 | |
241 void PrivetNotificationService::PrivetNotify(int devices_active, | |
242 bool added) { | |
243 base::string16 product_name = l10n_util::GetStringUTF16( | |
244 IDS_LOCAL_DISCOVERY_SERVICE_NAME_PRINTER); | |
245 | |
246 base::string16 title = l10n_util::GetPluralStringFUTF16( | |
247 IDS_LOCAL_DISCOVERY_NOTIFICATION_TITLE_PRINTER, devices_active); | |
248 base::string16 body = l10n_util::GetPluralStringFUTF16( | |
249 IDS_LOCAL_DISCOVERY_NOTIFICATION_CONTENTS_PRINTER, devices_active); | |
250 | |
251 Profile* profile_object = Profile::FromBrowserContext(profile_); | |
252 message_center::RichNotificationData rich_notification_data; | |
253 | |
254 rich_notification_data.buttons.push_back( | |
255 message_center::ButtonInfo(l10n_util::GetStringUTF16( | |
256 IDS_LOCAL_DISCOVERY_NOTIFICATION_BUTTON_PRINTER))); | |
257 | |
258 rich_notification_data.buttons.push_back( | |
259 message_center::ButtonInfo(l10n_util::GetStringUTF16( | |
260 IDS_LOCAL_DISCOVERY_NOTIFICATIONS_DISABLE_BUTTON_LABEL))); | |
261 | |
262 Notification notification( | |
263 message_center::NOTIFICATION_TYPE_SIMPLE, title, body, | |
264 ui::ResourceBundle::GetSharedInstance().GetImageNamed( | |
265 IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON), | |
266 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, | |
267 kPrivetNotificationID), | |
268 product_name, GURL(kPrivetNotificationOriginUrl), kPrivetNotificationID, | |
269 rich_notification_data, new PrivetNotificationDelegate(profile_)); | |
270 | |
271 bool updated = g_browser_process->notification_ui_manager()->Update( | |
272 notification, profile_object); | |
273 if (!updated && added && !LocalDiscoveryUIHandler::GetHasVisible()) { | |
274 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_SHOWN); | |
275 g_browser_process->notification_ui_manager()->Add(notification, | |
276 profile_object); | |
277 } | |
278 } | |
279 | |
280 void PrivetNotificationService::PrivetRemoveNotification() { | |
281 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CANCELED); | |
282 Profile* profile_object = Profile::FromBrowserContext(profile_); | |
283 g_browser_process->notification_ui_manager()->CancelById( | |
284 kPrivetNotificationID, | |
285 NotificationUIManager::GetProfileID(profile_object)); | |
286 } | |
287 | |
288 void PrivetNotificationService::Start() { | |
289 #if defined(CHROMEOS) | |
290 SigninManagerBase* signin_manager = | |
291 SigninManagerFactory::GetForProfileIfExists( | |
292 Profile::FromBrowserContext(profile_)); | |
293 | |
294 if (!signin_manager || !signin_manager->IsAuthenticated()) | |
295 return; | |
296 #endif | |
297 | |
298 enable_privet_notification_member_.Init( | |
299 prefs::kLocalDiscoveryNotificationsEnabled, | |
300 Profile::FromBrowserContext(profile_)->GetPrefs(), | |
301 base::Bind(&PrivetNotificationService::OnNotificationsEnabledChanged, | |
302 base::Unretained(this))); | |
303 OnNotificationsEnabledChanged(); | |
304 } | |
305 | |
306 void PrivetNotificationService::OnNotificationsEnabledChanged() { | |
307 #if defined(ENABLE_MDNS) | |
308 if (IsForced()) { | |
309 StartLister(); | |
310 } else if (*enable_privet_notification_member_) { | |
311 ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED); | |
312 traffic_detector_ = | |
313 new PrivetTrafficDetector( | |
314 net::ADDRESS_FAMILY_IPV4, | |
315 base::Bind(&PrivetNotificationService::StartLister, AsWeakPtr())); | |
316 traffic_detector_->Start(); | |
317 } else { | |
318 traffic_detector_ = NULL; | |
319 device_lister_.reset(); | |
320 service_discovery_client_ = NULL; | |
321 privet_notifications_listener_.reset(); | |
322 } | |
323 #else | |
324 if (IsForced() || *enable_privet_notification_member_) { | |
325 StartLister(); | |
326 } else { | |
327 device_lister_.reset(); | |
328 service_discovery_client_ = NULL; | |
329 privet_notifications_listener_.reset(); | |
330 } | |
331 #endif | |
332 } | |
333 | |
334 void PrivetNotificationService::StartLister() { | |
335 ReportPrivetUmaEvent(PRIVET_LISTER_STARTED); | |
336 #if defined(ENABLE_MDNS) | |
337 traffic_detector_ = NULL; | |
338 #endif // ENABLE_MDNS | |
339 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance(); | |
340 device_lister_.reset( | |
341 new PrivetDeviceListerImpl(service_discovery_client_.get(), this)); | |
342 device_lister_->Start(); | |
343 device_lister_->DiscoverNewDevices(false); | |
344 | |
345 scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory( | |
346 PrivetHTTPAsynchronousFactory::CreateInstance( | |
347 profile_->GetRequestContext())); | |
348 | |
349 privet_notifications_listener_.reset( | |
350 new PrivetNotificationsListener(std::move(http_factory), this)); | |
351 } | |
352 | |
353 PrivetNotificationDelegate::PrivetNotificationDelegate( | |
354 content::BrowserContext* profile) | |
355 : profile_(profile) { | |
356 } | |
357 | |
358 PrivetNotificationDelegate::~PrivetNotificationDelegate() { | |
359 } | |
360 | |
361 std::string PrivetNotificationDelegate::id() const { | |
362 return kPrivetNotificationID; | |
363 } | |
364 | |
365 void PrivetNotificationDelegate::ButtonClick(int button_index) { | |
366 if (button_index == 0) { | |
367 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CLICKED); | |
368 OpenTab(GURL(kPrivetNotificationOriginUrl)); | |
369 } else if (button_index == 1) { | |
370 ReportPrivetUmaEvent(PRIVET_DISABLE_NOTIFICATIONS_CLICKED); | |
371 DisableNotifications(); | |
372 } | |
373 } | |
374 | |
375 void PrivetNotificationDelegate::OpenTab(const GURL& url) { | |
376 Profile* profile_obj = Profile::FromBrowserContext(profile_); | |
377 | |
378 chrome::NavigateParams params(profile_obj, | |
379 url, | |
380 ui::PAGE_TRANSITION_AUTO_TOPLEVEL); | |
381 params.disposition = NEW_FOREGROUND_TAB; | |
382 chrome::Navigate(¶ms); | |
383 } | |
384 | |
385 void PrivetNotificationDelegate::DisableNotifications() { | |
386 Profile* profile_obj = Profile::FromBrowserContext(profile_); | |
387 | |
388 profile_obj->GetPrefs()->SetBoolean( | |
389 prefs::kLocalDiscoveryNotificationsEnabled, | |
390 false); | |
391 } | |
392 | |
393 } // namespace local_discovery | |
OLD | NEW |