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