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