privet_notifications.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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 "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/rand_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/local_discovery/privet_device_lister_impl.h"
12#include "chrome/browser/notifications/notification.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/browser_finder.h"
16#include "chrome/browser/ui/host_desktop.h"
17#include "chrome/browser/ui/tabs/tab_strip_model.h"
18#include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
19#include "content/public/browser/browser_context.h"
20#include "content/public/browser/navigation_controller.h"
21#include "content/public/browser/web_contents.h"
22#include "content/public/common/page_transition_types.h"
23#include "grit/generated_resources.h"
24#include "grit/theme_resources.h"
25#include "ui/base/l10n/l10n_util.h"
26#include "ui/base/resource/resource_bundle.h"
27#include "ui/message_center/notifier_settings.h"
28
29namespace local_discovery {
30
31namespace {
32const int kTenMinutesInSeconds = 600;
33const char kPrivetInfoKeyUptime[] = "uptime";
34const char kPrivetNotificationIDPrefix[] = "privet_notification:";
35const char kPrivetNotificationOriginUrl[] = "chrome://devices";
36const int kStartDelaySeconds = 30;
37}
38
39PrivetNotificationsListener::PrivetNotificationsListener(
40    scoped_ptr<PrivetHTTPAsynchronousFactory> privet_http_factory,
41    Delegate* delegate) : delegate_(delegate) {
42  privet_http_factory_.swap(privet_http_factory);
43}
44
45PrivetNotificationsListener::~PrivetNotificationsListener() {
46}
47
48void PrivetNotificationsListener::DeviceChanged(
49    bool added,
50    const std::string& name,
51    const DeviceDescription& description) {
52  DeviceContextMap::iterator found = devices_seen_.find(name);
53  if (found != devices_seen_.end()) {
54    if (!description.id.empty() &&  // Device is registered
55        found->second->notification_may_be_active) {
56      found->second->notification_may_be_active = false;
57      delegate_->PrivetRemoveNotification(name);
58    }
59    return;  // Already saw this device.
60  }
61
62  linked_ptr<DeviceContext> device_context(new DeviceContext);
63
64  device_context->notification_may_be_active = false;
65  device_context->registered = !description.id.empty();
66  device_context->human_readable_name = description.name;
67  device_context->description = description.description;
68
69  devices_seen_.insert(make_pair(name, device_context));
70
71  if (!device_context->registered) {
72    device_context->privet_http_resolution =
73        privet_http_factory_->CreatePrivetHTTP(
74            name,
75            description.address,
76            base::Bind(&PrivetNotificationsListener::CreateInfoOperation,
77                       base::Unretained(this)));
78
79    device_context->privet_http_resolution->Start();
80  }
81}
82
83void PrivetNotificationsListener::CreateInfoOperation(
84    scoped_ptr<PrivetHTTPClient> http_client) {
85  std::string name = http_client->GetName();
86  DeviceContextMap::iterator device_iter = devices_seen_.find(name);
87  DCHECK(device_iter != devices_seen_.end());
88  DeviceContext* device = device_iter->second.get();
89  device->privet_http.swap(http_client);
90  device->info_operation =
91       device->privet_http->CreateInfoOperation(this);
92  device->info_operation->Start();
93}
94
95void PrivetNotificationsListener::OnPrivetInfoDone(
96      PrivetInfoOperation* operation,
97      int http_code,
98      const base::DictionaryValue* json_value) {
99  std::string name = operation->GetHTTPClient()->GetName();
100  DeviceContextMap::iterator device_iter = devices_seen_.find(name);
101  DCHECK(device_iter != devices_seen_.end());
102  DeviceContext* device = device_iter->second.get();
103
104  int uptime;
105
106  if (!json_value ||
107      !json_value->GetInteger(kPrivetInfoKeyUptime, &uptime) ||
108      uptime > kTenMinutesInSeconds) {
109    return;
110  }
111
112  DCHECK(!device->notification_may_be_active);
113  device->notification_may_be_active = true;
114  delegate_->PrivetNotify(name, device->human_readable_name,
115                          device->description);
116}
117
118void PrivetNotificationsListener::DeviceRemoved(const std::string& name) {
119  DCHECK_EQ(1u, devices_seen_.count(name));
120  DeviceContextMap::iterator device_iter = devices_seen_.find(name);
121  DCHECK(device_iter != devices_seen_.end());
122  DeviceContext* device = device_iter->second.get();
123
124  device->info_operation.reset();
125  device->privet_http_resolution.reset();
126  device->notification_may_be_active = false;
127  delegate_->PrivetRemoveNotification(name);
128}
129
130void PrivetNotificationsListener::DeviceCacheFlushed() {
131  for (DeviceContextMap::iterator i = devices_seen_.begin();
132       i != devices_seen_.end(); ++i) {
133    DeviceContext* device = i->second.get();
134
135    device->info_operation.reset();
136    device->privet_http_resolution.reset();
137    if (device->notification_may_be_active) {
138      device->notification_may_be_active = false;
139      delegate_->PrivetRemoveNotification(i->first);
140    }
141  }
142}
143
144PrivetNotificationsListener::DeviceContext::DeviceContext() {
145}
146
147PrivetNotificationsListener::DeviceContext::~DeviceContext() {
148}
149
150PrivetNotificationService::PrivetNotificationService(
151    content::BrowserContext* profile,
152    NotificationUIManager* notification_manager)
153    : profile_(profile),
154      notification_manager_(notification_manager) {
155  base::MessageLoop::current()->PostDelayedTask(
156      FROM_HERE,
157      base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
158      base::TimeDelta::FromSeconds(kStartDelaySeconds +
159                                   base::RandInt(0, kStartDelaySeconds/4)));
160}
161
162PrivetNotificationService::~PrivetNotificationService() {
163  ServiceDiscoveryHostClientFactory::ReleaseClient();
164}
165
166void PrivetNotificationService::DeviceChanged(
167    bool added,
168    const std::string& name,
169    const DeviceDescription& description) {
170  privet_notifications_listener_->DeviceChanged(added, name, description);
171}
172
173void PrivetNotificationService::DeviceRemoved(const std::string& name) {
174  privet_notifications_listener_->DeviceRemoved(name);
175}
176
177void PrivetNotificationService::DeviceCacheFlushed() {
178  privet_notifications_listener_->DeviceCacheFlushed();
179}
180
181void PrivetNotificationService::PrivetNotify(
182    const std::string& device_name,
183    const std::string& human_readable_name,
184    const std::string& description) {
185  if (!LocalDiscoveryUIHandler::GetHasVisible()) {
186    Profile* profile_object = Profile::FromBrowserContext(profile_);
187    message_center::RichNotificationData rich_notification_data;
188
189    rich_notification_data.buttons.push_back(
190        message_center::ButtonInfo(l10n_util::GetStringUTF16(
191            IDS_LOCAL_DISOCVERY_NOTIFICATION_BUTTON_PRINTER)));
192
193    Notification notification(
194        message_center::NOTIFICATION_TYPE_SIMPLE,
195        GURL(kPrivetNotificationOriginUrl),
196        l10n_util::GetStringUTF16(
197            IDS_LOCAL_DISOCVERY_NOTIFICATION_TITLE_PRINTER),
198        l10n_util::GetStringFUTF16(
199            IDS_LOCAL_DISOCVERY_NOTIFICATION_CONTENTS_PRINTER,
200            UTF8ToUTF16(human_readable_name)),
201        ui::ResourceBundle::GetSharedInstance().GetImageNamed(
202            IDR_LOCAL_DISCOVERY_CLOUDPRINT_ICON),
203        WebKit::WebTextDirectionDefault,
204        message_center::NotifierId(
205            message_center::NotifierId::SYSTEM_COMPONENT),
206        l10n_util::GetStringUTF16(
207            IDS_LOCAL_DISOCVERY_NOTIFICATION_DISPLAY_SOURCE_PRINTER),
208        UTF8ToUTF16(kPrivetNotificationIDPrefix +
209                    device_name),
210        rich_notification_data,
211        new PrivetNotificationDelegate(device_name, profile_));
212
213    notification_manager_->Add(notification, profile_object);
214  }
215}
216
217void PrivetNotificationService::PrivetRemoveNotification(
218    const std::string& device_name) {
219  notification_manager_->CancelById(kPrivetNotificationIDPrefix + device_name);
220}
221
222void PrivetNotificationService::Start() {
223  service_discovery_client_ = ServiceDiscoveryHostClientFactory::GetClient();
224  device_lister_.reset(new PrivetDeviceListerImpl(service_discovery_client_,
225                                                  this));
226  device_lister_->Start();
227
228  scoped_ptr<PrivetHTTPAsynchronousFactory> http_factory(
229      new PrivetHTTPAsynchronousFactoryImpl(service_discovery_client_.get(),
230                                            profile_->GetRequestContext()));
231
232  privet_notifications_listener_.reset(new PrivetNotificationsListener(
233      http_factory.Pass(), this));
234}
235
236PrivetNotificationDelegate::PrivetNotificationDelegate(
237    const std::string& device_id, content::BrowserContext* profile)
238    : device_id_(device_id), profile_(profile) {
239}
240
241PrivetNotificationDelegate::~PrivetNotificationDelegate() {
242}
243
244std::string PrivetNotificationDelegate::id() const {
245  return kPrivetNotificationIDPrefix + device_id_;
246}
247
248content::RenderViewHost* PrivetNotificationDelegate::GetRenderViewHost() const {
249  return NULL;
250}
251
252void PrivetNotificationDelegate::Display() {
253}
254
255void PrivetNotificationDelegate::Error() {
256  LOG(ERROR) << "Error displaying privet notification " << device_id_;
257}
258
259void PrivetNotificationDelegate::Close(bool by_user) {
260}
261
262void PrivetNotificationDelegate::Click() {
263}
264
265void PrivetNotificationDelegate::ButtonClick(int button_index) {
266  if (button_index == 0) {
267    // TODO(noamsml): Direct-to-register URL
268    OpenTab(GURL(kPrivetNotificationOriginUrl));
269  }
270}
271
272void PrivetNotificationDelegate::OpenTab(const GURL& url) {
273  Profile* profile_obj = Profile::FromBrowserContext(profile_);
274
275  Browser* browser = FindOrCreateTabbedBrowser(profile_obj,
276                                               chrome::GetActiveDesktop());
277  content::WebContents::CreateParams create_params(profile_obj);
278
279  scoped_ptr<content::WebContents> contents(
280      content::WebContents::Create(create_params));
281  contents->GetController().LoadURL(url,
282                                    content::Referrer(),
283                                    content::PAGE_TRANSITION_AUTO_TOPLEVEL,
284                                    "");
285
286  browser->tab_strip_model()->AppendWebContents(contents.release(), true);
287}
288
289
290}  // namespace local_discovery
291