Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / local_discovery / privet_notifications.cc
blobcb18793d0e256b8247b246d2c127b0a37cf7a118
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.
5 #include "chrome/browser/local_discovery/privet_notifications.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/rand_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/local_discovery/privet_device_lister_impl.h"
16 #include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
17 #include "chrome/browser/local_discovery/privet_traffic_detector.h"
18 #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
19 #include "chrome/browser/notifications/notification.h"
20 #include "chrome/browser/notifications/notification_ui_manager.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/host_desktop.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/pref_names.h"
30 #include "content/public/browser/browser_context.h"
31 #include "content/public/browser/navigation_controller.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/common/page_transition_types.h"
34 #include "grit/generated_resources.h"
35 #include "grit/theme_resources.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/message_center/message_center_util.h"
39 #include "ui/message_center/notifier_settings.h"
41 namespace local_discovery {
43 namespace {
45 const int kTenMinutesInSeconds = 600;
46 const char kPrivetInfoKeyUptime[] = "uptime";
47 const char kPrivetNotificationID[] = "privet_notification";
48 const char kPrivetNotificationOriginUrl[] = "chrome://devices";
49 const int kStartDelaySeconds = 5;
51 enum PrivetNotificationsEvent {
52 PRIVET_SERVICE_STARTED,
53 PRIVET_LISTER_STARTED,
54 PRIVET_DEVICE_CHANGED,
55 PRIVET_INFO_DONE,
56 PRIVET_NOTIFICATION_SHOWN,
57 PRIVET_NOTIFICATION_CANCELED,
58 PRIVET_NOTIFICATION_CLICKED,
59 PRIVET_DISABLE_NOTIFICATIONS_CLICKED,
60 PRIVET_EVENT_MAX,
63 void ReportPrivetUmaEvent(PrivetNotificationsEvent privet_event) {
64 UMA_HISTOGRAM_ENUMERATION("LocalDiscovery.PrivetNotificationsEvent",
65 privet_event, PRIVET_EVENT_MAX);
68 } // namespace
70 PrivetNotificationsListener::PrivetNotificationsListener(
71 scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory,
72 Delegate* delegate) : delegate_(delegate), devices_active_(0) {
73 privet_http_factory_.swap(privet_http_factory);
76 PrivetNotificationsListener::~PrivetNotificationsListener() {
79 void PrivetNotificationsListener::DeviceChanged(
80 bool added,
81 const std::string& name,
82 const DeviceDescription& description) {
83 ReportPrivetUmaEvent(PRIVET_DEVICE_CHANGED);
84 DeviceContextMap::iterator found = devices_seen_.find(name);
85 if (found != devices_seen_.end()) {
86 if (!description.id.empty() && // Device is registered
87 found->second->notification_may_be_active) {
88 found->second->notification_may_be_active = false;
89 NotifyDeviceRemoved();
91 return; // Already saw this device.
94 linked_ptr<DeviceContext> device_context(new DeviceContext);
96 device_context->notification_may_be_active = false;
97 device_context->registered = !description.id.empty();
99 devices_seen_.insert(make_pair(name, device_context));
101 if (!device_context->registered) {
102 device_context->privet_http_resolution =
103 privet_http_factory_->CreatePrivetHTTP(
104 name,
105 description.address,
106 base::Bind(&PrivetNotificationsListener::CreateInfoOperation,
107 base::Unretained(this)));
109 device_context->privet_http_resolution->Start();
113 void PrivetNotificationsListener::CreateInfoOperation(
114 scoped_ptr<PrivetHTTPClient> http_client) {
115 std::string name = http_client->GetName();
116 DeviceContextMap::iterator device_iter = devices_seen_.find(name);
117 DCHECK(device_iter != devices_seen_.end());
118 DeviceContext* device = device_iter->second.get();
119 device->privet_http.swap(http_client);
120 device->info_operation = device->privet_http->CreateInfoOperation(
121 base::Bind(&PrivetNotificationsListener::OnPrivetInfoDone,
122 base::Unretained(this),
123 device));
124 device->info_operation->Start();
127 void PrivetNotificationsListener::OnPrivetInfoDone(
128 DeviceContext* device,
129 const base::DictionaryValue* json_value) {
130 int uptime;
132 if (!json_value ||
133 !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) ||
134 uptime > kTenMinutesInSeconds) {
135 return;
138 DCHECK(!device->notification_may_be_active);
139 device->notification_may_be_active = true;
140 devices_active_++;
141 delegate_->PrivetNotify(devices_active_ > 1, true);
144 void PrivetNotificationsListener::DeviceRemoved(const std::string& name) {
145 DCHECK_EQ(1u, devices_seen_.count(name));
146 DeviceContextMap::iterator device_iter = devices_seen_.find(name);
147 DCHECK(device_iter != devices_seen_.end());
148 DeviceContext* device = device_iter->second.get();
150 device->info_operation.reset();
151 device->privet_http_resolution.reset();
152 device->notification_may_be_active = false;
153 NotifyDeviceRemoved();
156 void PrivetNotificationsListener::DeviceCacheFlushed() {
157 for (DeviceContextMap::iterator i = devices_seen_.begin();
158 i != devices_seen_.end(); ++i) {
159 DeviceContext* device = i->second.get();
161 device->info_operation.reset();
162 device->privet_http_resolution.reset();
163 if (device->notification_may_be_active) {
164 device->notification_may_be_active = false;
168 devices_active_ = 0;
169 delegate_->PrivetRemoveNotification();
172 void PrivetNotificationsListener::NotifyDeviceRemoved() {
173 devices_active_--;
174 if (devices_active_ == 0) {
175 delegate_->PrivetRemoveNotification();
176 } else {
177 delegate_->PrivetNotify(devices_active_ > 1, true);
181 PrivetNotificationsListener::DeviceContext::DeviceContext() {
184 PrivetNotificationsListener::DeviceContext::~DeviceContext() {
187 PrivetNotificationService::PrivetNotificationService(
188 content::BrowserContext* profile)
189 : profile_(profile) {
190 base::MessageLoop::current()->PostDelayedTask(
191 FROM_HERE,
192 base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
193 base::TimeDelta::FromSeconds(kStartDelaySeconds +
194 base::RandInt(0, kStartDelaySeconds/4)));
197 PrivetNotificationService::~PrivetNotificationService() {
200 void PrivetNotificationService::DeviceChanged(
201 bool added,
202 const std::string& name,
203 const DeviceDescription& description) {
204 privet_notifications_listener_->DeviceChanged(added, name, description);
207 void PrivetNotificationService::DeviceRemoved(const std::string& name) {
208 privet_notifications_listener_->DeviceRemoved(name);
211 void PrivetNotificationService::DeviceCacheFlushed() {
212 privet_notifications_listener_->DeviceCacheFlushed();
215 // static
216 bool PrivetNotificationService::IsEnabled() {
217 CommandLine* command_line = CommandLine::ForCurrentProcess();
218 return !command_line->HasSwitch(switches::kDisableDeviceDiscovery) &&
219 !command_line->HasSwitch(
220 switches::kDisableDeviceDiscoveryNotifications) &&
221 message_center::IsRichNotificationEnabled();
224 // static
225 bool PrivetNotificationService::IsForced() {
226 CommandLine* command_line = CommandLine::ForCurrentProcess();
227 return command_line->HasSwitch(switches::kEnableDeviceDiscoveryNotifications);
230 void PrivetNotificationService::PrivetNotify(bool has_multiple,
231 bool added) {
232 base::string16 product_name = l10n_util::GetStringUTF16(
233 IDS_LOCAL_DISOCVERY_SERVICE_NAME_PRINTER);
235 int title_resource = has_multiple ?
236 IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER_MULTIPLE :
237 IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER;
239 int body_resource = has_multiple ?
240 IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER_MULTIPLE :
241 IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER;
243 base::string16 title = l10n_util::GetStringUTF16(title_resource);
244 base::string16 body = l10n_util::GetStringUTF16(body_resource);
246 Profile* profile_object = Profile::FromBrowserContext(profile_);
247 message_center::RichNotificationData rich_notification_data;
249 rich_notification_data.buttons.push_back(
250 message_center::ButtonInfo(l10n_util::GetStringUTF16(
251 IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
253 rich_notification_data.buttons.push_back(
254 message_center::ButtonInfo(l10n_util::GetStringUTF16(
255 IDS_LOCAL_DISCOVERY_NOTIFICATIONS_DISABLE_BUTTON_LABEL)));
257 Notification notification(
258 message_center::NOTIFICATION_TYPE_SIMPLE,
259 GURL(kPrivetNotificationOriginUrl),
260 title,
261 body,
262 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
263 IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
264 blink::WebTextDirectionDefault,
265 message_center::NotifierId(GURL(kPrivetNotificationOriginUrl)),
266 product_name,
267 base::UTF8ToUTF16(kPrivetNotificationID),
268 rich_notification_data,
269 new PrivetNotificationDelegate(profile_));
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);
280 void PrivetNotificationService::PrivetRemoveNotification() {
281 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CANCELED);
282 g_browser_process->notification_ui_manager()->CancelById(
283 kPrivetNotificationID);
286 void PrivetNotificationService::Start() {
287 enable_privet_notification_member_.Init(
288 prefs::kLocalDiscoveryNotificationsEnabled,
289 Profile::FromBrowserContext(profile_)->GetPrefs(),
290 base::Bind(&PrivetNotificationService::OnNotificationsEnabledChanged,
291 base::Unretained(this)));
292 OnNotificationsEnabledChanged();
295 void PrivetNotificationService::OnNotificationsEnabledChanged() {
296 if (IsForced()) {
297 StartLister();
298 } else if (*enable_privet_notification_member_) {
299 ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED);
300 traffic_detector_ =
301 new PrivetTrafficDetector(
302 net::ADDRESS_FAMILY_IPV4,
303 base::Bind(&PrivetNotificationService::StartLister, AsWeakPtr()));
304 traffic_detector_->Start();
305 } else {
306 traffic_detector_ = NULL;
307 device_lister_.reset();
308 service_discovery_client_ = NULL;
309 privet_notifications_listener_.reset();
313 void PrivetNotificationService::StartLister() {
314 ReportPrivetUmaEvent(PRIVET_LISTER_STARTED);
315 traffic_detector_ = NULL;
316 DCHECK(!service_discovery_client_);
317 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
318 device_lister_.reset(new PrivetDeviceListerImpl(service_discovery_client_,
319 this));
320 device_lister_->Start();
321 device_lister_->DiscoverNewDevices(false);
323 scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory(
324 PrivetHTTPAsynchronousFactory::CreateInstance(
325 service_discovery_client_.get(), profile_->GetRequestContext()));
327 privet_notifications_listener_.reset(new PrivetNotificationsListener(
328 http_factory.Pass(), this));
331 PrivetNotificationDelegate::PrivetNotificationDelegate(
332 content::BrowserContext* profile)
333 : profile_(profile) {
336 PrivetNotificationDelegate::~PrivetNotificationDelegate() {
339 std::string PrivetNotificationDelegate::id() const {
340 return kPrivetNotificationID;
343 content::RenderViewHost* PrivetNotificationDelegate::GetRenderViewHost() const {
344 return NULL;
347 void PrivetNotificationDelegate::Display() {
350 void PrivetNotificationDelegate::Error() {
351 LOG(ERROR) << "Error displaying privet notification";
354 void PrivetNotificationDelegate::Close(bool by_user) {
357 void PrivetNotificationDelegate::Click() {
360 void PrivetNotificationDelegate::ButtonClick(int button_index) {
361 if (button_index == 0) {
362 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CLICKED);
363 OpenTab(GURL(kPrivetNotificationOriginUrl));
364 } else if (button_index == 1) {
365 ReportPrivetUmaEvent(PRIVET_DISABLE_NOTIFICATIONS_CLICKED);
366 DisableNotifications();
370 void PrivetNotificationDelegate::OpenTab(const GURL& url) {
371 Profile* profile_obj = Profile::FromBrowserContext(profile_);
373 chrome::NavigateParams params(profile_obj,
374 url,
375 content::PAGE_TRANSITION_AUTO_TOPLEVEL);
376 params.disposition = NEW_FOREGROUND_TAB;
377 chrome::Navigate(&params);
380 void PrivetNotificationDelegate::DisableNotifications() {
381 Profile* profile_obj = Profile::FromBrowserContext(profile_);
383 profile_obj->GetPrefs()->SetBoolean(
384 prefs::kLocalDiscoveryNotificationsEnabled,
385 false);
388 } // namespace local_discovery