OLD | NEW |
| (Empty) |
1 // Copyright 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/common/system/chromeos/bluetooth/bluetooth_notification_controller
.h" | |
6 | |
7 #include <memory> | |
8 #include <utility> | |
9 | |
10 #include "ash/common/system/system_notifier.h" | |
11 #include "ash/resources/grit/ash_resources.h" | |
12 #include "ash/strings/grit/ash_strings.h" | |
13 #include "base/bind.h" | |
14 #include "base/callback.h" | |
15 #include "base/logging.h" | |
16 #include "base/strings/stringprintf.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "device/bluetooth/bluetooth_adapter_factory.h" | |
19 #include "device/bluetooth/bluetooth_device.h" | |
20 #include "ui/base/l10n/l10n_util.h" | |
21 #include "ui/base/resource/resource_bundle.h" | |
22 #include "ui/message_center/message_center.h" | |
23 #include "ui/message_center/notification.h" | |
24 #include "ui/message_center/notification_delegate.h" | |
25 #include "ui/message_center/notification_types.h" | |
26 | |
27 using device::BluetoothAdapter; | |
28 using device::BluetoothAdapterFactory; | |
29 using device::BluetoothDevice; | |
30 using message_center::Notification; | |
31 | |
32 namespace { | |
33 | |
34 // Identifier for the discoverable notification. | |
35 const char kBluetoothDeviceDiscoverableNotificationId[] = | |
36 "chrome://settings/bluetooth/discoverable"; | |
37 | |
38 // Identifier for the pairing notification; the Bluetooth code ensures we | |
39 // only receive one pairing request at a time, so a single id is sufficient and | |
40 // means we "update" one notification if not handled rather than continually | |
41 // bugging the user. | |
42 const char kBluetoothDevicePairingNotificationId[] = | |
43 "chrome://settings/bluetooth/pairing"; | |
44 | |
45 // Identifier for the notification that a device has been paired with the | |
46 // system. | |
47 const char kBluetoothDevicePairedNotificationId[] = | |
48 "chrome://settings/bluetooth/paired"; | |
49 | |
50 // The BluetoothPairingNotificationDelegate handles user interaction with the | |
51 // pairing notification and sending the confirmation, rejection or cancellation | |
52 // back to the underlying device. | |
53 class BluetoothPairingNotificationDelegate | |
54 : public message_center::NotificationDelegate { | |
55 public: | |
56 BluetoothPairingNotificationDelegate(scoped_refptr<BluetoothAdapter> adapter, | |
57 const std::string& address); | |
58 | |
59 protected: | |
60 ~BluetoothPairingNotificationDelegate() override; | |
61 | |
62 // message_center::NotificationDelegate overrides. | |
63 void Close(bool by_user) override; | |
64 void ButtonClick(int button_index) override; | |
65 | |
66 private: | |
67 // Buttons that appear in notifications. | |
68 enum Button { BUTTON_ACCEPT, BUTTON_REJECT }; | |
69 | |
70 // Reference to the underlying Bluetooth Adapter, holding onto this | |
71 // reference ensures the adapter object doesn't go out of scope while we have | |
72 // a pending request and user interaction. | |
73 scoped_refptr<BluetoothAdapter> adapter_; | |
74 | |
75 // Address of the device being paired. | |
76 const std::string address_; | |
77 | |
78 DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate); | |
79 }; | |
80 | |
81 BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate( | |
82 scoped_refptr<BluetoothAdapter> adapter, | |
83 const std::string& address) | |
84 : adapter_(adapter), address_(address) {} | |
85 | |
86 BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {} | |
87 | |
88 void BluetoothPairingNotificationDelegate::Close(bool by_user) { | |
89 VLOG(1) << "Pairing notification closed. by_user = " << by_user; | |
90 // Ignore notification closes generated as a result of pairing completion. | |
91 if (!by_user) | |
92 return; | |
93 | |
94 // Cancel the pairing of the device, if the object still exists. | |
95 BluetoothDevice* device = adapter_->GetDevice(address_); | |
96 if (device) | |
97 device->CancelPairing(); | |
98 } | |
99 | |
100 void BluetoothPairingNotificationDelegate::ButtonClick(int button_index) { | |
101 VLOG(1) << "Pairing notification, button click: " << button_index; | |
102 // If the device object still exists, send the appropriate response either | |
103 // confirming or rejecting the pairing. | |
104 BluetoothDevice* device = adapter_->GetDevice(address_); | |
105 if (device) { | |
106 switch (button_index) { | |
107 case BUTTON_ACCEPT: | |
108 device->ConfirmPairing(); | |
109 break; | |
110 case BUTTON_REJECT: | |
111 device->RejectPairing(); | |
112 break; | |
113 } | |
114 } | |
115 | |
116 // In any case, remove this pairing notification. | |
117 message_center::MessageCenter::Get()->RemoveNotification( | |
118 kBluetoothDevicePairingNotificationId, false /* by_user */); | |
119 } | |
120 | |
121 } // namespace | |
122 | |
123 namespace ash { | |
124 | |
125 BluetoothNotificationController::BluetoothNotificationController() | |
126 : weak_ptr_factory_(this) { | |
127 BluetoothAdapterFactory::GetAdapter( | |
128 base::Bind(&BluetoothNotificationController::OnGetAdapter, | |
129 weak_ptr_factory_.GetWeakPtr())); | |
130 } | |
131 | |
132 BluetoothNotificationController::~BluetoothNotificationController() { | |
133 if (adapter_.get()) { | |
134 adapter_->RemoveObserver(this); | |
135 adapter_->RemovePairingDelegate(this); | |
136 adapter_ = NULL; | |
137 } | |
138 } | |
139 | |
140 void BluetoothNotificationController::AdapterDiscoverableChanged( | |
141 BluetoothAdapter* adapter, | |
142 bool discoverable) { | |
143 if (discoverable) { | |
144 NotifyAdapterDiscoverable(); | |
145 } else { | |
146 // Clear any previous discoverable notification. | |
147 message_center::MessageCenter::Get()->RemoveNotification( | |
148 kBluetoothDeviceDiscoverableNotificationId, false /* by_user */); | |
149 } | |
150 } | |
151 | |
152 void BluetoothNotificationController::DeviceAdded(BluetoothAdapter* adapter, | |
153 BluetoothDevice* device) { | |
154 // Add the new device to the list of currently paired devices; it doesn't | |
155 // receive a notification since it's assumed it was previously notified. | |
156 if (device->IsPaired()) | |
157 paired_devices_.insert(device->GetAddress()); | |
158 } | |
159 | |
160 void BluetoothNotificationController::DeviceChanged(BluetoothAdapter* adapter, | |
161 BluetoothDevice* device) { | |
162 // If the device is already in the list of paired devices, then don't | |
163 // notify. | |
164 if (paired_devices_.find(device->GetAddress()) != paired_devices_.end()) | |
165 return; | |
166 | |
167 // Otherwise if it's marked as paired then it must be newly paired, so | |
168 // notify the user about that. | |
169 if (device->IsPaired()) { | |
170 paired_devices_.insert(device->GetAddress()); | |
171 NotifyPairedDevice(device); | |
172 } | |
173 } | |
174 | |
175 void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter* adapter, | |
176 BluetoothDevice* device) { | |
177 paired_devices_.erase(device->GetAddress()); | |
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->GetNameForDisplay(), base::UTF8ToUTF16(pincode)); | |
200 | |
201 NotifyPairing(device, message, false); | |
202 } | |
203 | |
204 void BluetoothNotificationController::DisplayPasskey(BluetoothDevice* device, | |
205 uint32_t passkey) { | |
206 base::string16 message = l10n_util::GetStringFUTF16( | |
207 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY, | |
208 device->GetNameForDisplay(), | |
209 base::UTF8ToUTF16(base::StringPrintf("%06i", passkey))); | |
210 | |
211 NotifyPairing(device, message, false); | |
212 } | |
213 | |
214 void BluetoothNotificationController::KeysEntered(BluetoothDevice* device, | |
215 uint32_t entered) { | |
216 // Ignored since we don't have CSS in the notification to update. | |
217 } | |
218 | |
219 void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice* device, | |
220 uint32_t passkey) { | |
221 base::string16 message = l10n_util::GetStringFUTF16( | |
222 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY, | |
223 device->GetNameForDisplay(), | |
224 base::UTF8ToUTF16(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->GetNameForDisplay()); | |
234 | |
235 NotifyPairing(device, message, true); | |
236 } | |
237 | |
238 void BluetoothNotificationController::OnGetAdapter( | |
239 scoped_refptr<BluetoothAdapter> adapter) { | |
240 DCHECK(!adapter_.get()); | |
241 adapter_ = adapter; | |
242 adapter_->AddObserver(this); | |
243 adapter_->AddPairingDelegate(this, | |
244 BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW); | |
245 | |
246 // Notify a user if the adapter is already in the discoverable state. | |
247 if (adapter_->IsDiscoverable()) | |
248 NotifyAdapterDiscoverable(); | |
249 | |
250 // Build a list of the currently paired devices; these don't receive | |
251 // notifications since it's assumed they were previously notified. | |
252 BluetoothAdapter::DeviceList devices = adapter_->GetDevices(); | |
253 for (BluetoothAdapter::DeviceList::const_iterator iter = devices.begin(); | |
254 iter != devices.end(); ++iter) { | |
255 const BluetoothDevice* device = *iter; | |
256 if (device->IsPaired()) | |
257 paired_devices_.insert(device->GetAddress()); | |
258 } | |
259 } | |
260 | |
261 void BluetoothNotificationController::NotifyAdapterDiscoverable() { | |
262 message_center::RichNotificationData optional; | |
263 | |
264 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
265 | |
266 std::unique_ptr<Notification> notification(new Notification( | |
267 message_center::NOTIFICATION_TYPE_SIMPLE, | |
268 kBluetoothDeviceDiscoverableNotificationId, base::string16() /* title */, | |
269 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE, | |
270 base::UTF8ToUTF16(adapter_->GetName()), | |
271 base::UTF8ToUTF16(adapter_->GetAddress())), | |
272 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH), | |
273 base::string16() /* display source */, GURL(), | |
274 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, | |
275 system_notifier::kNotifierBluetooth), | |
276 optional, NULL)); | |
277 message_center::MessageCenter::Get()->AddNotification( | |
278 std::move(notification)); | |
279 } | |
280 | |
281 void BluetoothNotificationController::NotifyPairing( | |
282 BluetoothDevice* device, | |
283 const base::string16& message, | |
284 bool with_buttons) { | |
285 message_center::RichNotificationData optional; | |
286 if (with_buttons) { | |
287 optional.buttons.push_back(message_center::ButtonInfo( | |
288 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT))); | |
289 optional.buttons.push_back(message_center::ButtonInfo( | |
290 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT))); | |
291 } | |
292 | |
293 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
294 | |
295 std::unique_ptr<Notification> notification(new Notification( | |
296 message_center::NOTIFICATION_TYPE_SIMPLE, | |
297 kBluetoothDevicePairingNotificationId, base::string16() /* title */, | |
298 message, bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH), | |
299 base::string16() /* display source */, GURL(), | |
300 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, | |
301 system_notifier::kNotifierBluetooth), | |
302 optional, new BluetoothPairingNotificationDelegate( | |
303 adapter_, device->GetAddress()))); | |
304 message_center::MessageCenter::Get()->AddNotification( | |
305 std::move(notification)); | |
306 } | |
307 | |
308 void BluetoothNotificationController::NotifyPairedDevice( | |
309 BluetoothDevice* device) { | |
310 // Remove the currently presented pairing notification; since only one | |
311 // pairing request is queued at a time, this is guaranteed to be the device | |
312 // that just became paired. | |
313 message_center::MessageCenter::Get()->RemoveNotification( | |
314 kBluetoothDevicePairingNotificationId, false /* by_user */); | |
315 | |
316 message_center::RichNotificationData optional; | |
317 | |
318 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
319 | |
320 std::unique_ptr<Notification> notification(new Notification( | |
321 message_center::NOTIFICATION_TYPE_SIMPLE, | |
322 kBluetoothDevicePairedNotificationId, base::string16() /* title */, | |
323 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED, | |
324 device->GetNameForDisplay()), | |
325 bundle.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH), | |
326 base::string16() /* display source */, GURL(), | |
327 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT, | |
328 system_notifier::kNotifierBluetooth), | |
329 optional, NULL)); | |
330 message_center::MessageCenter::Get()->AddNotification( | |
331 std::move(notification)); | |
332 } | |
333 | |
334 } // namespace ash | |
OLD | NEW |