Componentize AccountReconcilor.
[chromium-blink-merge.git] / chrome / browser / local_discovery / privet_notifications.cc
blobf242d1b90665dc48e343ac2e46bd2baa296394ae
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/service_discovery_shared_client.h"
18 #include "chrome/browser/notifications/notification.h"
19 #include "chrome/browser/notifications/notification_ui_manager.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/signin/signin_manager_factory.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 "components/signin/core/browser/signin_manager_base.h"
31 #include "content/public/browser/browser_context.h"
32 #include "content/public/browser/navigation_controller.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/common/page_transition_types.h"
35 #include "grit/generated_resources.h"
36 #include "grit/theme_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/message_center/message_center_util.h"
40 #include "ui/message_center/notifier_settings.h"
42 #if defined(ENABLE_MDNS)
43 #include "chrome/browser/local_discovery/privet_traffic_detector.h"
44 #endif
46 namespace local_discovery {
48 namespace {
50 const int kTenMinutesInSeconds = 600;
51 const char kPrivetInfoKeyUptime[] = "uptime";
52 const char kPrivetNotificationID[] = "privet_notification";
53 const char kPrivetNotificationOriginUrl[] = "chrome://devices";
54 const int kStartDelaySeconds = 5;
56 enum PrivetNotificationsEvent {
57 PRIVET_SERVICE_STARTED,
58 PRIVET_LISTER_STARTED,
59 PRIVET_DEVICE_CHANGED,
60 PRIVET_INFO_DONE,
61 PRIVET_NOTIFICATION_SHOWN,
62 PRIVET_NOTIFICATION_CANCELED,
63 PRIVET_NOTIFICATION_CLICKED,
64 PRIVET_DISABLE_NOTIFICATIONS_CLICKED,
65 PRIVET_EVENT_MAX,
68 void ReportPrivetUmaEvent(PrivetNotificationsEvent privet_event) {
69 UMA_HISTOGRAM_ENUMERATION("LocalDiscovery.PrivetNotificationsEvent",
70 privet_event, PRIVET_EVENT_MAX);
73 } // namespace
75 PrivetNotificationsListener::PrivetNotificationsListener(
76 scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory,
77 Delegate* delegate) : delegate_(delegate), devices_active_(0) {
78 privet_http_factory_.swap(privet_http_factory);
81 PrivetNotificationsListener::~PrivetNotificationsListener() {
84 void PrivetNotificationsListener::DeviceChanged(
85 bool added,
86 const std::string& name,
87 const DeviceDescription& description) {
88 ReportPrivetUmaEvent(PRIVET_DEVICE_CHANGED);
89 DeviceContextMap::iterator found = devices_seen_.find(name);
90 if (found != devices_seen_.end()) {
91 if (!description.id.empty() && // Device is registered
92 found->second->notification_may_be_active) {
93 found->second->notification_may_be_active = false;
94 NotifyDeviceRemoved();
96 return; // Already saw this device.
99 linked_ptr<DeviceContext> device_context(new DeviceContext);
101 device_context->notification_may_be_active = false;
102 device_context->registered = !description.id.empty();
104 devices_seen_.insert(make_pair(name, device_context));
106 if (!device_context->registered) {
107 device_context->privet_http_resolution =
108 privet_http_factory_->CreatePrivetHTTP(
109 name,
110 description.address,
111 base::Bind(&PrivetNotificationsListener::CreateInfoOperation,
112 base::Unretained(this)));
114 device_context->privet_http_resolution->Start();
118 void PrivetNotificationsListener::CreateInfoOperation(
119 scoped_ptr<PrivetHTTPClient> http_client) {
120 std::string name = http_client->GetName();
121 DeviceContextMap::iterator device_iter = devices_seen_.find(name);
122 DCHECK(device_iter != devices_seen_.end());
123 DeviceContext* device = device_iter->second.get();
124 device->privet_http.swap(http_client);
125 device->info_operation = device->privet_http->CreateInfoOperation(
126 base::Bind(&PrivetNotificationsListener::OnPrivetInfoDone,
127 base::Unretained(this),
128 device));
129 device->info_operation->Start();
132 void PrivetNotificationsListener::OnPrivetInfoDone(
133 DeviceContext* device,
134 const base::DictionaryValue* json_value) {
135 int uptime;
137 if (!json_value ||
138 !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) ||
139 uptime > kTenMinutesInSeconds) {
140 return;
143 DCHECK(!device->notification_may_be_active);
144 device->notification_may_be_active = true;
145 devices_active_++;
146 delegate_->PrivetNotify(devices_active_ > 1, true);
149 void PrivetNotificationsListener::DeviceRemoved(const std::string& name) {
150 DCHECK_EQ(1u, devices_seen_.count(name));
151 DeviceContextMap::iterator device_iter = devices_seen_.find(name);
152 DCHECK(device_iter != devices_seen_.end());
153 DeviceContext* device = device_iter->second.get();
155 device->info_operation.reset();
156 device->privet_http_resolution.reset();
157 device->notification_may_be_active = false;
158 NotifyDeviceRemoved();
161 void PrivetNotificationsListener::DeviceCacheFlushed() {
162 for (DeviceContextMap::iterator i = devices_seen_.begin();
163 i != devices_seen_.end(); ++i) {
164 DeviceContext* device = i->second.get();
166 device->info_operation.reset();
167 device->privet_http_resolution.reset();
168 if (device->notification_may_be_active) {
169 device->notification_may_be_active = false;
173 devices_active_ = 0;
174 delegate_->PrivetRemoveNotification();
177 void PrivetNotificationsListener::NotifyDeviceRemoved() {
178 devices_active_--;
179 if (devices_active_ == 0) {
180 delegate_->PrivetRemoveNotification();
181 } else {
182 delegate_->PrivetNotify(devices_active_ > 1, false);
186 PrivetNotificationsListener::DeviceContext::DeviceContext() {
189 PrivetNotificationsListener::DeviceContext::~DeviceContext() {
192 PrivetNotificationService::PrivetNotificationService(
193 content::BrowserContext* profile)
194 : profile_(profile) {
195 base::MessageLoop::current()->PostDelayedTask(
196 FROM_HERE,
197 base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
198 base::TimeDelta::FromSeconds(kStartDelaySeconds +
199 base::RandInt(0, kStartDelaySeconds/4)));
202 PrivetNotificationService::~PrivetNotificationService() {
205 void PrivetNotificationService::DeviceChanged(
206 bool added,
207 const std::string& name,
208 const DeviceDescription& description) {
209 privet_notifications_listener_->DeviceChanged(added, name, description);
212 void PrivetNotificationService::DeviceRemoved(const std::string& name) {
213 privet_notifications_listener_->DeviceRemoved(name);
216 void PrivetNotificationService::DeviceCacheFlushed() {
217 privet_notifications_listener_->DeviceCacheFlushed();
220 // static
221 bool PrivetNotificationService::IsEnabled() {
222 CommandLine* command_line = CommandLine::ForCurrentProcess();
223 return !command_line->HasSwitch(switches::kDisableDeviceDiscovery) &&
224 !command_line->HasSwitch(
225 switches::kDisableDeviceDiscoveryNotifications) &&
226 message_center::IsRichNotificationEnabled();
229 // static
230 bool PrivetNotificationService::IsForced() {
231 CommandLine* command_line = CommandLine::ForCurrentProcess();
232 return command_line->HasSwitch(switches::kEnableDeviceDiscoveryNotifications);
235 void PrivetNotificationService::PrivetNotify(bool has_multiple,
236 bool added) {
237 base::string16 product_name = l10n_util::GetStringUTF16(
238 IDS_LOCAL_DISOCVERY_SERVICE_NAME_PRINTER);
240 int title_resource = has_multiple ?
241 IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER_MULTIPLE :
242 IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER;
244 int body_resource = has_multiple ?
245 IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER_MULTIPLE :
246 IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER;
248 base::string16 title = l10n_util::GetStringUTF16(title_resource);
249 base::string16 body = l10n_util::GetStringUTF16(body_resource);
251 Profile* profile_object = Profile::FromBrowserContext(profile_);
252 message_center::RichNotificationData rich_notification_data;
254 rich_notification_data.buttons.push_back(
255 message_center::ButtonInfo(l10n_util::GetStringUTF16(
256 IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
258 rich_notification_data.buttons.push_back(
259 message_center::ButtonInfo(l10n_util::GetStringUTF16(
260 IDS_LOCAL_DISCOVERY_NOTIFICATIONS_DISABLE_BUTTON_LABEL)));
262 Notification notification(
263 message_center::NOTIFICATION_TYPE_SIMPLE,
264 GURL(kPrivetNotificationOriginUrl),
265 title,
266 body,
267 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
268 IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
269 blink::WebTextDirectionDefault,
270 message_center::NotifierId(GURL(kPrivetNotificationOriginUrl)),
271 product_name,
272 base::UTF8ToUTF16(kPrivetNotificationID),
273 rich_notification_data,
274 new PrivetNotificationDelegate(profile_));
276 bool updated = g_browser_process->notification_ui_manager()->Update(
277 notification, profile_object);
278 if (!updated && added && !LocalDiscoveryUIHandler::GetHasVisible()) {
279 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_SHOWN);
280 g_browser_process->notification_ui_manager()->Add(notification,
281 profile_object);
285 void PrivetNotificationService::PrivetRemoveNotification() {
286 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CANCELED);
287 g_browser_process->notification_ui_manager()->CancelById(
288 kPrivetNotificationID);
291 void PrivetNotificationService::Start() {
292 #if defined(CHROMEOS)
293 SigninManagerBase* signin_manager =
294 SigninManagerFactory::GetForProfileIfExists(
295 Profile::FromBrowserContext(profile_));
297 if (!signin_manager || signin_manager->GetAuthenticatedUsername().empty())
298 return;
299 #endif
301 enable_privet_notification_member_.Init(
302 prefs::kLocalDiscoveryNotificationsEnabled,
303 Profile::FromBrowserContext(profile_)->GetPrefs(),
304 base::Bind(&PrivetNotificationService::OnNotificationsEnabledChanged,
305 base::Unretained(this)));
306 OnNotificationsEnabledChanged();
309 void PrivetNotificationService::OnNotificationsEnabledChanged() {
310 #if defined(ENABLE_MDNS)
311 if (IsForced()) {
312 StartLister();
313 } else if (*enable_privet_notification_member_) {
314 ReportPrivetUmaEvent(PRIVET_SERVICE_STARTED);
315 traffic_detector_ =
316 new PrivetTrafficDetector(
317 net::ADDRESS_FAMILY_IPV4,
318 base::Bind(&PrivetNotificationService::StartLister, AsWeakPtr()));
319 traffic_detector_->Start();
320 } else {
321 traffic_detector_ = NULL;
322 device_lister_.reset();
323 service_discovery_client_ = NULL;
324 privet_notifications_listener_.reset();
326 #else
327 if (IsForced() || *enable_privet_notification_member_) {
328 StartLister();
329 } else {
330 device_lister_.reset();
331 service_discovery_client_ = NULL;
332 privet_notifications_listener_.reset();
334 #endif
337 void PrivetNotificationService::StartLister() {
338 ReportPrivetUmaEvent(PRIVET_LISTER_STARTED);
339 #if defined(ENABLE_MDNS)
340 traffic_detector_ = NULL;
341 #endif // ENABLE_MDNS
342 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
343 device_lister_.reset(new PrivetDeviceListerImpl(service_discovery_client_,
344 this));
345 device_lister_->Start();
346 device_lister_->DiscoverNewDevices(false);
348 scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory(
349 PrivetHTTPAsynchronousFactory::CreateInstance(
350 service_discovery_client_.get(), profile_->GetRequestContext()));
352 privet_notifications_listener_.reset(new PrivetNotificationsListener(
353 http_factory.Pass(), this));
356 PrivetNotificationDelegate::PrivetNotificationDelegate(
357 content::BrowserContext* profile)
358 : profile_(profile) {
361 PrivetNotificationDelegate::~PrivetNotificationDelegate() {
364 std::string PrivetNotificationDelegate::id() const {
365 return kPrivetNotificationID;
368 content::RenderViewHost* PrivetNotificationDelegate::GetRenderViewHost() const {
369 return NULL;
372 void PrivetNotificationDelegate::Display() {
375 void PrivetNotificationDelegate::Error() {
376 LOG(ERROR) << "Error displaying privet notification";
379 void PrivetNotificationDelegate::Close(bool by_user) {
382 void PrivetNotificationDelegate::Click() {
385 void PrivetNotificationDelegate::ButtonClick(int button_index) {
386 if (button_index == 0) {
387 ReportPrivetUmaEvent(PRIVET_NOTIFICATION_CLICKED);
388 OpenTab(GURL(kPrivetNotificationOriginUrl));
389 } else if (button_index == 1) {
390 ReportPrivetUmaEvent(PRIVET_DISABLE_NOTIFICATIONS_CLICKED);
391 DisableNotifications();
395 void PrivetNotificationDelegate::OpenTab(const GURL& url) {
396 Profile* profile_obj = Profile::FromBrowserContext(profile_);
398 chrome::NavigateParams params(profile_obj,
399 url,
400 content::PAGE_TRANSITION_AUTO_TOPLEVEL);
401 params.disposition = NEW_FOREGROUND_TAB;
402 chrome::Navigate(&params);
405 void PrivetNotificationDelegate::DisableNotifications() {
406 Profile* profile_obj = Profile::FromBrowserContext(profile_);
408 profile_obj->GetPrefs()->SetBoolean(
409 prefs::kLocalDiscoveryNotificationsEnabled,
410 false);
413 } // namespace local_discovery