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.
5 #include "ash/system/chromeos/bluetooth/bluetooth_notification_controller.h"
7 #include "ash/system/system_notifier.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"
25 using device::BluetoothAdapter
;
26 using device::BluetoothAdapterFactory
;
27 using device::BluetoothDevice
;
28 using message_center::Notification
;
32 // Identifier for the discoverable notification.
33 const char kBluetoothDeviceDiscoverableNotificationId
[] =
34 "chrome://settings/bluetooth/discoverable";
36 // Identifier for the pairing notification; the Bluetooth code ensures we
37 // only receive one pairing request at a time, so a single id is sufficient and
38 // means we "update" one notification if not handled rather than continually
40 const char kBluetoothDevicePairingNotificationId
[] =
41 "chrome://settings/bluetooth/pairing";
43 // Identifier for the notification that a device has been paired with the
45 const char kBluetoothDevicePairedNotificationId
[] =
46 "chrome://settings/bluetooth/paired";
48 // The BluetoothPairingNotificationDelegate handles user interaction with the
49 // pairing notification and sending the confirmation, rejection or cancellation
50 // back to the underlying device.
51 class BluetoothPairingNotificationDelegate
52 : public message_center::NotificationDelegate
{
54 BluetoothPairingNotificationDelegate(scoped_refptr
<BluetoothAdapter
> adapter
,
55 const std::string
& address
);
58 ~BluetoothPairingNotificationDelegate() override
;
60 // message_center::NotificationDelegate overrides.
61 void Close(bool by_user
) override
;
62 void ButtonClick(int button_index
) override
;
65 // Buttons that appear in notifications.
71 // Reference to the underlying Bluetooth Adapter, holding onto this
72 // reference ensures the adapter object doesn't go out of scope while we have
73 // a pending request and user interaction.
74 scoped_refptr
<BluetoothAdapter
> adapter_
;
76 // Address of the device being paired.
77 const std::string address_
;
79 DISALLOW_COPY_AND_ASSIGN(BluetoothPairingNotificationDelegate
);
82 BluetoothPairingNotificationDelegate::BluetoothPairingNotificationDelegate(
83 scoped_refptr
<BluetoothAdapter
> adapter
,
84 const std::string
& address
)
89 BluetoothPairingNotificationDelegate::~BluetoothPairingNotificationDelegate() {
92 void BluetoothPairingNotificationDelegate::Close(bool by_user
) {
93 VLOG(1) << "Pairing notification closed. by_user = " << by_user
;
94 // Ignore notification closes generated as a result of pairing completion.
98 // Cancel the pairing of the device, if the object still exists.
99 BluetoothDevice
* device
= adapter_
->GetDevice(address_
);
101 device
->CancelPairing();
104 void BluetoothPairingNotificationDelegate::ButtonClick(int button_index
) {
105 VLOG(1) << "Pairing notification, button click: " << button_index
;
106 // If the device object still exists, send the appropriate response either
107 // confirming or rejecting the pairing.
108 BluetoothDevice
* device
= adapter_
->GetDevice(address_
);
110 switch (button_index
) {
112 device
->ConfirmPairing();
115 device
->RejectPairing();
120 // In any case, remove this pairing notification.
121 message_center::MessageCenter::Get()->RemoveNotification(
122 kBluetoothDevicePairingNotificationId
, false /* by_user */);
130 BluetoothNotificationController::BluetoothNotificationController()
131 : weak_ptr_factory_(this) {
132 BluetoothAdapterFactory::GetAdapter(
133 base::Bind(&BluetoothNotificationController::OnGetAdapter
,
134 weak_ptr_factory_
.GetWeakPtr()));
137 BluetoothNotificationController::~BluetoothNotificationController() {
138 if (adapter_
.get()) {
139 adapter_
->RemoveObserver(this);
140 adapter_
->RemovePairingDelegate(this);
146 void BluetoothNotificationController::AdapterDiscoverableChanged(
147 BluetoothAdapter
* adapter
,
150 NotifyAdapterDiscoverable();
152 // Clear any previous discoverable notification.
153 message_center::MessageCenter::Get()->RemoveNotification(
154 kBluetoothDeviceDiscoverableNotificationId
, false /* by_user */);
158 void BluetoothNotificationController::DeviceAdded(BluetoothAdapter
* adapter
,
159 BluetoothDevice
* device
) {
160 // Add the new device to the list of currently paired devices; it doesn't
161 // receive a notification since it's assumed it was previously notified.
162 if (device
->IsPaired())
163 paired_devices_
.insert(device
->GetAddress());
166 void BluetoothNotificationController::DeviceChanged(BluetoothAdapter
* adapter
,
167 BluetoothDevice
* device
) {
168 // If the device is already in the list of paired devices, then don't
170 if (paired_devices_
.find(device
->GetAddress()) != paired_devices_
.end())
173 // Otherwise if it's marked as paired then it must be newly paired, so
174 // notify the user about that.
175 if (device
->IsPaired()) {
176 paired_devices_
.insert(device
->GetAddress());
177 NotifyPairedDevice(device
);
181 void BluetoothNotificationController::DeviceRemoved(BluetoothAdapter
* adapter
,
182 BluetoothDevice
* device
) {
183 paired_devices_
.erase(device
->GetAddress());
187 void BluetoothNotificationController::RequestPinCode(BluetoothDevice
* device
) {
188 // Cannot provide keyboard entry in a notification; these devices (old car
189 // audio systems for the most part) will need pairing to be initiated from
191 device
->CancelPairing();
194 void BluetoothNotificationController::RequestPasskey(BluetoothDevice
* device
) {
195 // Cannot provide keyboard entry in a notification; fortunately the spec
196 // doesn't allow for this to be an option when we're receiving the pairing
198 device
->CancelPairing();
201 void BluetoothNotificationController::DisplayPinCode(
202 BluetoothDevice
* device
,
203 const std::string
& pincode
) {
204 base::string16 message
= l10n_util::GetStringFUTF16(
205 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PINCODE
,
206 device
->GetName(), base::UTF8ToUTF16(pincode
));
208 NotifyPairing(device
, message
, false);
211 void BluetoothNotificationController::DisplayPasskey(BluetoothDevice
* device
,
213 base::string16 message
= l10n_util::GetStringFUTF16(
214 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISPLAY_PASSKEY
,
215 device
->GetName(), base::UTF8ToUTF16(
216 base::StringPrintf("%06i", passkey
)));
218 NotifyPairing(device
, message
, false);
221 void BluetoothNotificationController::KeysEntered(BluetoothDevice
* device
,
223 // Ignored since we don't have CSS in the notification to update.
226 void BluetoothNotificationController::ConfirmPasskey(BluetoothDevice
* device
,
228 base::string16 message
= l10n_util::GetStringFUTF16(
229 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONFIRM_PASSKEY
,
230 device
->GetName(), base::UTF8ToUTF16(
231 base::StringPrintf("%06i", passkey
)));
233 NotifyPairing(device
, message
, true);
236 void BluetoothNotificationController::AuthorizePairing(
237 BluetoothDevice
* device
) {
238 base::string16 message
= l10n_util::GetStringFUTF16(
239 IDS_ASH_STATUS_TRAY_BLUETOOTH_AUTHORIZE_PAIRING
,
242 NotifyPairing(device
, message
, true);
246 void BluetoothNotificationController::OnGetAdapter(
247 scoped_refptr
<BluetoothAdapter
> adapter
) {
248 DCHECK(!adapter_
.get());
250 adapter_
->AddObserver(this);
251 adapter_
->AddPairingDelegate(this,
252 BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_LOW
);
254 // Notify a user if the adapter is already in the discoverable state.
255 if (adapter_
->IsDiscoverable())
256 NotifyAdapterDiscoverable();
258 // Build a list of the currently paired devices; these don't receive
259 // notifications since it's assumed they were previously notified.
260 BluetoothAdapter::DeviceList devices
= adapter_
->GetDevices();
261 for (BluetoothAdapter::DeviceList::const_iterator iter
= devices
.begin();
262 iter
!= devices
.end(); ++iter
) {
263 const BluetoothDevice
* device
= *iter
;
264 if (device
->IsPaired())
265 paired_devices_
.insert(device
->GetAddress());
270 void BluetoothNotificationController::NotifyAdapterDiscoverable() {
271 message_center::RichNotificationData optional
;
273 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
275 scoped_ptr
<Notification
> notification(new Notification(
276 message_center::NOTIFICATION_TYPE_SIMPLE
,
277 kBluetoothDeviceDiscoverableNotificationId
, base::string16() /* title */,
278 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERABLE
,
279 base::UTF8ToUTF16(adapter_
->GetName()),
280 base::UTF8ToUTF16(adapter_
->GetAddress())),
281 bundle
.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH
),
282 base::string16() /* display source */, GURL(),
283 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
284 system_notifier::kNotifierBluetooth
),
286 message_center::MessageCenter::Get()->AddNotification(notification
.Pass());
289 void BluetoothNotificationController::NotifyPairing(
290 BluetoothDevice
* device
,
291 const base::string16
& message
,
293 message_center::RichNotificationData optional
;
295 optional
.buttons
.push_back(message_center::ButtonInfo(
296 l10n_util::GetStringUTF16(
297 IDS_ASH_STATUS_TRAY_BLUETOOTH_ACCEPT
)));
298 optional
.buttons
.push_back(message_center::ButtonInfo(
299 l10n_util::GetStringUTF16(
300 IDS_ASH_STATUS_TRAY_BLUETOOTH_REJECT
)));
303 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
305 scoped_ptr
<Notification
> notification(new Notification(
306 message_center::NOTIFICATION_TYPE_SIMPLE
,
307 kBluetoothDevicePairingNotificationId
, base::string16() /* title */,
308 message
, bundle
.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH
),
309 base::string16() /* display source */, GURL(),
310 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
311 system_notifier::kNotifierBluetooth
),
312 optional
, new BluetoothPairingNotificationDelegate(
313 adapter_
, device
->GetAddress())));
314 message_center::MessageCenter::Get()->AddNotification(notification
.Pass());
317 void BluetoothNotificationController::NotifyPairedDevice(
318 BluetoothDevice
* device
) {
319 // Remove the currently presented pairing notification; since only one
320 // pairing request is queued at a time, this is guaranteed to be the device
321 // that just became paired.
322 message_center::MessageCenter::Get()->RemoveNotification(
323 kBluetoothDevicePairingNotificationId
, false /* by_user */);
325 message_center::RichNotificationData optional
;
327 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
329 scoped_ptr
<Notification
> notification(new Notification(
330 message_center::NOTIFICATION_TYPE_SIMPLE
,
331 kBluetoothDevicePairedNotificationId
, base::string16() /* title */,
332 l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_PAIRED
,
334 bundle
.GetImageNamed(IDR_AURA_NOTIFICATION_BLUETOOTH
),
335 base::string16() /* display source */, GURL(),
336 message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT
,
337 system_notifier::kNotifierBluetooth
),
339 message_center::MessageCenter::Get()->AddNotification(notification
.Pass());