OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014 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 "ash/system/chromeos/bluetooth/bluetooth_notification_controller.h" |
| 6 |
| 7 #include "ash/system/system_notifier.h" |
| 8 #include "base/bind.h" |
| 9 #include "base/callback.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/memory/scoped_ptr.h" |
| 12 #include "base/strings/stringprintf.h" |
| 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "device/bluetooth/bluetooth_adapter_factory.h" |
| 15 #include "device/bluetooth/bluetooth_device.h" |
| 16 #include "grit/ash_resources.h" |
| 17 #include "grit/ash_strings.h" |
| 18 #include "ui/base/l10n/l10n_util.h" |
| 19 #include "ui/base/resource/resource_bundle.h" |
| 20 #include "ui/message_center/message_center.h" |
| 21 #include "ui/message_center/notification.h" |
| 22 #include "ui/message_center/notification_delegate.h" |
| 23 #include "ui/message_center/notification_types.h" |
| 24 |
| 25 using device::BluetoothAdapter; |
| 26 using device::BluetoothAdapterFactory; |
| 27 using device::BluetoothDevice; |
| 28 using message_center::Notification; |
| 29 |
| 30 namespace { |
| 31 |
| 32 // Identifier for the pairing notification; the Bluetooth code ensures we |
| 33 // only receive one pairing request at a time, so a single id is sufficient and |
| 34 // means we "update" one notification if not handled rather than continually |
| 35 // bugging the user. |
| 36 const char kBluetoothDevicePairingNotificationId[] = |
| 37 "chrome://settings/bluetooth/pairing"; |
| 38 |
| 39 // The BluetoothPairingNotificationDelegate handles user interaction with the |
| 40 // pairing notification and sending the confirmation, rejection or cancellation |
| 41 // back to the underlying device. |
| 42 class BluetoothPairingNotificationDelegate |
| 43 : public message_center::NotificationDelegate { |
| 44 public: |
| 45 BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter, |
| 46 const std::string& address); |
| 47 |
| 48 protected: |
| 49 virtual ~BluetoothPairingNotificationDelegate(); |
| 50 |
| 51 // message_center::NotificationDelegate overrides. |
| 52 virtual void Display() OVERRIDE; |
| 53 virtual void Error() OVERRIDE; |
| 54 virtual void Close(bool by_user) OVERRIDE; |
| 55 virtual bool HasClickedListener() OVERRIDE; |
| 56 virtual void Click() OVERRIDE; |
| 57 virtual void ButtonClick(int button_index) OVERRIDE; |
| 58 |
| 59 private: |
| 60 // Buttons that appear in notifications. |
| 61 enum Button { |
| 62 BUTTON_ACCEPT, |
| 63 BUTTON_REJECT |
| 64 }; |
| 65 |
| 66 // Reference to the underlying Bluetooth Adapter, holding onto this |
| 67 // reference ensures the adapter object doesn't go out of scope while we have |
| 68 // a pending request and user interaction. |
| 69 scoped_refptr<BluetoothAdapter> adapter_; |
| 70 |
| 71 // Address of the device being paired. |
| 72 const std::string address_; |
| 73 |
| 74 DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate); |
| 75 }; |
| 76 |
| 77 BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate( |
| 78 scoped_refptr<BluetoothAdapter> adapter, |
| 79 const std::string& address) |
| 80 : adapter_(adapter), |
| 81 address_(address) { |
| 82 } |
| 83 |
| 84 BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() { |
| 85 } |
| 86 |
| 87 void BluetoothPairingNotificationDelegate::Display() { |
| 88 } |
| 89 |
| 90 void BluetoothPairingNotificationDelegate::Error() { |
| 91 } |
| 92 |
| 93 void BluetoothPairingNotificationDelegate::Close(bool by_user) { |
| 94 VLOG(1) << "Pairing notification closed. by_user = " << by_user; |
| 95 // Ignore notification closes generated as a result of pairing completion. |
| 96 if (!by_user) |
| 97 return; |
| 98 |
| 99 // Cancel the pairing of the device, if the object still exists. |
| 100 BluetoothDevice* device = adapter_->GetDevice(address_); |
| 101 if (device) |
| 102 device->CancelPairing(); |
| 103 } |
| 104 |
| 105 bool BluetoothPairingNotificationDelegate::HasClickedListener() { |
| 106 return false; |
| 107 } |
| 108 |
| 109 void BluetoothPairingNotificationDelegate::Click() { |
| 110 } |
| 111 |
| 112 void BluetoothPairingNotificationDelegate::ButtonClick(int button_index) { |
| 113 VLOG(1) << "Pairing notification, button click: " << button_index; |
| 114 // If the device object still exists, send the appropriate response either |
| 115 // confirming or rejecting the pairing. |
| 116 BluetoothDevice* device = adapter_->GetDevice(address_); |
| 117 if (device) { |
| 118 switch (button_index) { |
| 119 case BUTTON_ACCEPT: |
| 120 device->ConfirmPairing(); |
| 121 break; |
| 122 case BUTTON_REJECT: |
| 123 device->RejectPairing(); |
| 124 break; |
| 125 } |
| 126 } |
| 127 |
| 128 // In any case, remove this pairing notification. |
| 129 message_center::MessageCenter::Get()->RemoveNotification( |
| 130 kBluetoothDevicePairingNotificationId, false /* by_user */); |
| 131 } |
| 132 |
| 133 } // namespace |
| 134 |
| 135 |
| 136 namespace ash { |
| 137 namespace internal { |
| 138 |
| 139 BluetoothNotificationController::BluetoothNotificationController() |
| 140 : weak_ptr_factory_(this) { |
| 141 BluetoothAdapterFactory::GetAdapter( |
| 142 base::Bind(&BluetoothNotificationController::OnGetAdapter, |
| 143 weak_ptr_factory_.GetWeakPtr())); |
| 144 } |
| 145 |
| 146 BluetoothNotificationController::~BluetoothNotificationController() { |
| 147 if (adapter_.get()) { |
| 148 adapter_->RemoveObserver(this); |
| 149 adapter_->RemovePairingDelegate(this); |
| 150 adapter_ = NULL; |
| 151 } |
| 152 } |
| 153 |
| 154 |
| 155 void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter, |
| 156 BluetoothDevice* device) { |
| 157 if (device->IsPaired()) { |
| 158 paired_devices_.insert(device->GetAddress()); |
| 159 NotifyPairedDevice(device); |
| 160 } |
| 161 } |
| 162 |
| 163 void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter, |
| 164 BluetoothDevice* device) { |
| 165 if (paired_devices_.find(device->GetAddress()) != paired_devices_.end()) |
| 166 return; |
| 167 |
| 168 if (device->IsPaired()) { |
| 169 paired_devices_.insert(device->GetAddress()); |
| 170 NotifyPairedDevice(device); |
| 171 } |
| 172 } |
| 173 |
| 174 void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter, |
| 175 BluetoothDevice* device) { |
| 176 paired_devices_.erase(device->GetAddress()); |
| 177 } |
| 178 |
| 179 |
| 180 void BluetoothNotificationController::RequestPinCode(BluetoothDevice* device) { |
| 181 // Cannot provide keyboard entry in a notification; these devices (old car |
| 182 // audio systems for the most part) will need pairing to be initiated from |
| 183 // the Chromebook. |
| 184 device->CancelPairing(); |
| 185 } |
| 186 |
| 187 void BluetoothNotificationController::RequestPasskey(BluetoothDevice* device) { |
| 188 // Cannot provide keyboard entry in a notification; fortunately the spec |
| 189 // doesn't allow for this to be an option when we're receiving the pairing |
| 190 // request anyway. |
| 191 device->CancelPairing(); |
| 192 } |
| 193 |
| 194 void BluetoothNotificationController::DisplayPinCode( |
| 195 BluetoothDevice* device, |
| 196 const std::string& pincode) { |
| 197 base::string16 message = l10n_util::GetStringFUTF16( |
| 198 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE, |
| 199 device->GetName(), base::UTF8ToUTF16(pincode)); |
| 200 |
| 201 NotifyPairing(device, message, false); |
| 202 } |
| 203 |
| 204 void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device, |
| 205 uint32 passkey) { |
| 206 base::string16 message = l10n_util::GetStringFUTF16( |
| 207 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY, |
| 208 device->GetName(), base::UTF8ToUTF16( |
| 209 base::StringPrintf("%06i", passkey))); |
| 210 |
| 211 NotifyPairing(device, message, false); |
| 212 } |
| 213 |
| 214 void BluetoothNotificationController::KeysEntered(BluetoothDevice* device, |
| 215 uint32 entered) { |
| 216 // Ignored since we don't have CSS in the notification to update. |
| 217 } |
| 218 |
| 219 void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device, |
| 220 uint32 passkey) { |
| 221 base::string16 message = l10n_util::GetStringFUTF16( |
| 222 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY, |
| 223 device->GetName(), base::UTF8ToUTF16( |
| 224 base::StringPrintf("%06i", passkey))); |
| 225 |
| 226 NotifyPairing(device, message, true); |
| 227 } |
| 228 |
| 229 void BluetoothNotificationController::AuthorizePairing( |
| 230 BluetoothDevice* device) { |
| 231 base::string16 message = l10n_util::GetStringFUTF16( |
| 232 IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING, |
| 233 device->GetName()); |
| 234 |
| 235 NotifyPairing(device, message, true); |
| 236 } |
| 237 |
| 238 |
| 239 void BluetoothNotificationController::OnGetAdapter( |
| 240 scoped_refptr<BluetoothAdapter> adapter) { |
| 241 DCHECK(!adapter_.get()); |
| 242 adapter_ = adapter; |
| 243 adapter_->AddObserver(this); |
| 244 adapter_->AddPairingDelegate(this, |
| 245 BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); |
| 246 |
| 247 // Build a list of the currently paired devices; these don't receive |
| 248 // notifications since it's assumed they were previously notified. |
| 249 BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); |
| 250 for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin(); |
| 251 iter != devices.end(); ++iter) { |
| 252 const BluetoothDevice* device = *iter; |
| 253 if (device->IsPaired()) |
| 254 paired_devices_.insert(device->GetAddress()); |
| 255 } |
| 256 } |
| 257 |
| 258 |
| 259 void BluetoothNotificationController::NotifyPairing( |
| 260 BluetoothDevice* device, |
| 261 const base::string16& message, |
| 262 bool with_buttons) { |
| 263 message_center::RichNotificationData optional; |
| 264 if (with_buttons) { |
| 265 optional.buttons.push_back(message_center::ButtonInfo( |
| 266 l10n_util::GetStringUTF16( |
| 267 IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT))); |
| 268 optional.buttons.push_back(message_center::ButtonInfo( |
| 269 l10n_util::GetStringUTF16( |
| 270 IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT))); |
| 271 } |
| 272 |
| 273 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| 274 |
| 275 scoped_ptr<Notification> notification(new Notification( |
| 276 message_center::NOTIFICATION_TYPE_SIMPLE, |
| 277 kBluetoothDevicePairingNotificationId, |
| 278 base::string16() /* title */, |
| 279 message, |
| 280 bundle.GetImageNamed(IDR_AURA_UBER_TRAY_BLUETOOTH), |
| 281 base::string16() /* display source */, |
| 282 message_center::NotifierId( |
| 283 message_center::NotifierId::SYSTEM_COMPONENT, |
| 284 system_notifier::kNotifierBluetooth), |
| 285 optional, |
| 286 new BluetoothPairingNotificationDelegate(adapter_, |
| 287 device->GetAddress()))); |
| 288 message_center::MessageCenter::Get()->AddNotification(notification.Pass()); |
| 289 } |
| 290 |
| 291 void BluetoothNotificationController::NotifyPairedDevice( |
| 292 BluetoothDevice* device) { |
| 293 // Remove the currently presented pairing notification; since only one |
| 294 // pairing request is queued at a time, this is guaranteed to be the device |
| 295 // that just became paired. |
| 296 message_center::MessageCenter::Get()->RemoveNotification( |
| 297 kBluetoothDevicePairingNotificationId, false /* by_user */); |
| 298 } |
| 299 |
| 300 } // namespace internal |
| 301 } // namespace ash |
OLD | NEW |