1 // Copyright (c) 2012 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/bluetooth/tray_bluetooth.h"
7 #include "ash/session/session_state_delegate.h"
9 #include "ash/system/tray/fixed_sized_scroll_view.h"
10 #include "ash/system/tray/hover_highlight_view.h"
11 #include "ash/system/tray/system_tray.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "ash/system/tray/system_tray_notifier.h"
14 #include "ash/system/tray/throbber_view.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "ash/system/tray/tray_details_view.h"
17 #include "ash/system/tray/tray_item_more.h"
18 #include "ash/system/tray/tray_popup_header_button.h"
19 #include "grit/ash_resources.h"
20 #include "grit/ash_strings.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/image/image.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/layout/box_layout.h"
32 // Updates bluetooth device |device| in the |list|. If it is new, append to the
33 // end of the |list|; otherwise, keep it at the same place, but update the data
34 // with new device info provided by |device|.
35 void UpdateBluetoothDeviceListHelper(BluetoothDeviceList
* list
,
36 const BluetoothDeviceInfo
& device
) {
37 for (BluetoothDeviceList::iterator it
= list
->begin(); it
!= list
->end();
39 if ((*it
).address
== device
.address
) {
45 list
->push_back(device
);
48 // Removes the obsolete BluetoothDevices from |list|, if they are not in the
50 void RemoveObsoleteBluetoothDevicesFromList(
51 BluetoothDeviceList
* list
,
52 const std::set
<std::string
>& new_list
) {
53 for (BluetoothDeviceList::iterator it
= list
->begin(); it
!= list
->end();
55 if (new_list
.find((*it
).address
) == new_list
.end()) {
57 if (it
== list
->end())
65 class BluetoothDefaultView
: public TrayItemMore
{
67 BluetoothDefaultView(SystemTrayItem
* owner
, bool show_more
)
68 : TrayItemMore(owner
, show_more
) {
69 ui::ResourceBundle
& bundle
= ui::ResourceBundle::GetSharedInstance();
70 SetImage(bundle
.GetImageNamed(IDR_AURA_UBER_TRAY_BLUETOOTH
).ToImageSkia());
74 ~BluetoothDefaultView() override
{}
77 ash::SystemTrayDelegate
* delegate
=
78 ash::Shell::GetInstance()->system_tray_delegate();
79 if (delegate
->GetBluetoothAvailable()) {
80 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
81 const base::string16 label
=
82 rb
.GetLocalizedString(delegate
->GetBluetoothEnabled() ?
83 IDS_ASH_STATUS_TRAY_BLUETOOTH_ENABLED
:
84 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISABLED
);
86 SetAccessibleName(label
);
94 DISALLOW_COPY_AND_ASSIGN(BluetoothDefaultView
);
97 class BluetoothDetailedView
: public TrayDetailsView
,
98 public ViewClickListener
,
99 public views::ButtonListener
{
101 BluetoothDetailedView(SystemTrayItem
* owner
, user::LoginStatus login
)
102 : TrayDetailsView(owner
),
104 manage_devices_(NULL
),
105 toggle_bluetooth_(NULL
),
106 enable_bluetooth_(NULL
) {
110 ~BluetoothDetailedView() override
{
111 // Stop discovering bluetooth devices when exiting BT detailed view.
112 BluetoothStopDiscovering();
116 BluetoothStartDiscovering();
117 UpdateBluetoothDeviceList();
120 UpdateDeviceScrollList();
127 CreateScrollableList();
128 AppendSettingsEntries();
132 void BluetoothStartDiscovering() {
133 ash::SystemTrayDelegate
* delegate
=
134 ash::Shell::GetInstance()->system_tray_delegate();
135 bool bluetooth_enabled
= delegate
->GetBluetoothEnabled();
136 bool bluetooth_discovering
= delegate
->GetBluetoothDiscovering();
137 if (bluetooth_discovering
) {
142 if (bluetooth_enabled
) {
143 delegate
->BluetoothStartDiscovering();
147 void BluetoothStopDiscovering() {
148 ash::SystemTrayDelegate
* delegate
=
149 ash::Shell::GetInstance()->system_tray_delegate();
150 if (delegate
&& delegate
->GetBluetoothDiscovering()) {
151 delegate
->BluetoothStopDiscovering();
156 void UpdateBluetoothDeviceList() {
157 std::set
<std::string
> new_connecting_devices
;
158 std::set
<std::string
> new_connected_devices
;
159 std::set
<std::string
> new_paired_not_connected_devices
;
160 std::set
<std::string
> new_discovered_not_paired_devices
;
162 BluetoothDeviceList list
;
163 Shell::GetInstance()->system_tray_delegate()->
164 GetAvailableBluetoothDevices(&list
);
165 for (size_t i
= 0; i
< list
.size(); ++i
) {
166 if (list
[i
].connecting
) {
167 list
[i
].display_name
= l10n_util::GetStringFUTF16(
168 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONNECTING
, list
[i
].display_name
);
169 new_connecting_devices
.insert(list
[i
].address
);
170 UpdateBluetoothDeviceListHelper(&connecting_devices_
, list
[i
]);
171 } else if (list
[i
].connected
&& list
[i
].paired
) {
172 new_connected_devices
.insert(list
[i
].address
);
173 UpdateBluetoothDeviceListHelper(&connected_devices_
, list
[i
]);
174 } else if (list
[i
].paired
) {
175 new_paired_not_connected_devices
.insert(list
[i
].address
);
176 UpdateBluetoothDeviceListHelper(
177 &paired_not_connected_devices_
, list
[i
]);
179 new_discovered_not_paired_devices
.insert(list
[i
].address
);
180 UpdateBluetoothDeviceListHelper(
181 &discovered_not_paired_devices_
, list
[i
]);
184 RemoveObsoleteBluetoothDevicesFromList(&connecting_devices_
,
185 new_connecting_devices
);
186 RemoveObsoleteBluetoothDevicesFromList(&connected_devices_
,
187 new_connected_devices
);
188 RemoveObsoleteBluetoothDevicesFromList(&paired_not_connected_devices_
,
189 new_paired_not_connected_devices
);
190 RemoveObsoleteBluetoothDevicesFromList(&discovered_not_paired_devices_
,
191 new_discovered_not_paired_devices
);
194 void AppendHeaderEntry() {
195 CreateSpecialRow(IDS_ASH_STATUS_TRAY_BLUETOOTH
, this);
197 if (login_
== user::LOGGED_IN_LOCKED
)
200 throbber_
= new ThrobberView
;
201 throbber_
->SetTooltipText(
202 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING
));
203 footer()->AddView(throbber_
, false /* separator */);
205 // Do not allow toggling bluetooth in the lock screen.
206 ash::SystemTrayDelegate
* delegate
=
207 ash::Shell::GetInstance()->system_tray_delegate();
208 toggle_bluetooth_
= new TrayPopupHeaderButton(this,
209 IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED
,
210 IDR_AURA_UBER_TRAY_BLUETOOTH_DISABLED
,
211 IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED_HOVER
,
212 IDR_AURA_UBER_TRAY_BLUETOOTH_DISABLED_HOVER
,
213 IDS_ASH_STATUS_TRAY_BLUETOOTH
);
214 toggle_bluetooth_
->SetToggled(!delegate
->GetBluetoothEnabled());
215 toggle_bluetooth_
->SetTooltipText(
216 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_BLUETOOTH
));
217 toggle_bluetooth_
->SetToggledTooltipText(
218 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH
));
219 toggle_bluetooth_
->EnableCanvasFlippingForRTLUI(false);
220 footer()->AddButton(toggle_bluetooth_
);
223 void UpdateHeaderEntry() {
224 if (toggle_bluetooth_
) {
225 toggle_bluetooth_
->SetToggled(
226 !ash::Shell::GetInstance()->system_tray_delegate()->
227 GetBluetoothEnabled());
231 void UpdateDeviceScrollList() {
233 scroll_content()->RemoveAllChildViews(true);
234 enable_bluetooth_
= NULL
;
236 ash::SystemTrayDelegate
* delegate
=
237 ash::Shell::GetInstance()->system_tray_delegate();
238 bool bluetooth_enabled
= delegate
->GetBluetoothEnabled();
239 bool blueooth_available
= delegate
->GetBluetoothAvailable();
240 if (blueooth_available
&& !bluetooth_enabled
&&
242 enable_bluetooth_
= AddScrollListItem(
243 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH
),
244 false /* highlight */, false /* checked */, true /* enabled */);
247 AppendSameTypeDevicesToScrollList(
248 connected_devices_
, true, true, bluetooth_enabled
);
249 AppendSameTypeDevicesToScrollList(
250 connecting_devices_
, true, false, bluetooth_enabled
);
251 AppendSameTypeDevicesToScrollList(
252 paired_not_connected_devices_
, false, false, bluetooth_enabled
);
253 if (discovered_not_paired_devices_
.size() > 0)
254 AddScrollSeparator();
255 AppendSameTypeDevicesToScrollList(
256 discovered_not_paired_devices_
, false, false, bluetooth_enabled
);
258 // Show user Bluetooth state if there is no bluetooth devices in list.
259 if (device_map_
.size() == 0) {
260 if (blueooth_available
&& bluetooth_enabled
) {
261 AddScrollListItem(l10n_util::GetStringUTF16(
262 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING
),
263 false /* highlight */, false /* checked */,
268 scroll_content()->SizeToPreferredSize();
271 void AppendSameTypeDevicesToScrollList(const BluetoothDeviceList
& list
,
275 for (size_t i
= 0; i
< list
.size(); ++i
) {
276 HoverHighlightView
* container
=
277 AddScrollListItem(list
[i
].display_name
, highlight
, checked
, enabled
);
278 device_map_
[container
] = list
[i
].address
;
282 HoverHighlightView
* AddScrollListItem(const base::string16
& text
,
286 HoverHighlightView
* container
= new HoverHighlightView(this);
287 views::Label
* label
=
288 container
->AddCheckableLabel(text
, highlight
, checked
);
289 label
->SetEnabled(enabled
);
290 scroll_content()->AddChildView(container
);
294 // Add settings entries.
295 void AppendSettingsEntries() {
296 if (!ash::Shell::GetInstance()->
297 system_tray_delegate()->ShouldShowSettings()) {
301 // Add bluetooth device requires a browser window, hide it for non logged in
303 bool userAddingRunning
= ash::Shell::GetInstance()
304 ->session_state_delegate()
305 ->IsInSecondaryLoginScreen();
307 if (login_
== user::LOGGED_IN_NONE
|| login_
== user::LOGGED_IN_LOCKED
||
311 ash::SystemTrayDelegate
* delegate
=
312 ash::Shell::GetInstance()->system_tray_delegate();
313 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
314 HoverHighlightView
* container
= new HoverHighlightView(this);
316 rb
.GetLocalizedString(IDS_ASH_STATUS_TRAY_BLUETOOTH_MANAGE_DEVICES
),
317 gfx::ALIGN_LEFT
, false /* highlight */);
318 container
->SetEnabled(delegate
->GetBluetoothAvailable());
319 AddChildView(container
);
320 manage_devices_
= container
;
323 // Returns true if the device with |device_id| is found in |device_list|,
324 // and the display_name of the device will be returned in |display_name| if
326 bool FoundDevice(const std::string
& device_id
,
327 const BluetoothDeviceList
& device_list
,
328 base::string16
* display_name
) {
329 for (size_t i
= 0; i
< device_list
.size(); ++i
) {
330 if (device_list
[i
].address
== device_id
) {
332 *display_name
= device_list
[i
].display_name
;
339 // Updates UI of the clicked bluetooth device to show it is being connected
340 // or disconnected if such an operation is going to be performed underway.
341 void UpdateClickedDevice(std::string device_id
, views::View
* item_container
) {
342 base::string16 display_name
;
343 if (FoundDevice(device_id
, paired_not_connected_devices_
,
345 display_name
= l10n_util::GetStringFUTF16(
346 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONNECTING
, display_name
);
348 item_container
->RemoveAllChildViews(true);
349 static_cast<HoverHighlightView
*>(item_container
)
350 ->AddCheckableLabel(display_name
, true /* highlight */, false);
351 scroll_content()->SizeToPreferredSize();
352 static_cast<views::View
*>(scroller())->Layout();
356 // Overridden from ViewClickListener.
357 void OnViewClicked(views::View
* sender
) override
{
358 ash::SystemTrayDelegate
* delegate
=
359 ash::Shell::GetInstance()->system_tray_delegate();
360 if (sender
== footer()->content()) {
361 TransitionToDefaultView();
362 } else if (sender
== manage_devices_
) {
363 delegate
->ManageBluetoothDevices();
364 } else if (sender
== enable_bluetooth_
) {
365 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
366 delegate
->GetBluetoothEnabled() ?
367 ash::UMA_STATUS_AREA_BLUETOOTH_DISABLED
:
368 ash::UMA_STATUS_AREA_BLUETOOTH_ENABLED
);
369 delegate
->ToggleBluetooth();
371 if (!delegate
->GetBluetoothEnabled())
373 std::map
<views::View
*, std::string
>::iterator find
;
374 find
= device_map_
.find(sender
);
375 if (find
== device_map_
.end())
377 std::string device_id
= find
->second
;
378 if (FoundDevice(device_id
, connecting_devices_
, NULL
))
380 UpdateClickedDevice(device_id
, sender
);
381 delegate
->ConnectToBluetoothDevice(device_id
);
385 // Overridden from ButtonListener.
386 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
{
387 ash::SystemTrayDelegate
* delegate
=
388 ash::Shell::GetInstance()->system_tray_delegate();
389 if (sender
== toggle_bluetooth_
)
390 delegate
->ToggleBluetooth();
395 user::LoginStatus login_
;
397 std::map
<views::View
*, std::string
> device_map_
;
398 views::View
* manage_devices_
;
399 ThrobberView
* throbber_
;
400 TrayPopupHeaderButton
* toggle_bluetooth_
;
401 HoverHighlightView
* enable_bluetooth_
;
402 BluetoothDeviceList connected_devices_
;
403 BluetoothDeviceList connecting_devices_
;
404 BluetoothDeviceList paired_not_connected_devices_
;
405 BluetoothDeviceList discovered_not_paired_devices_
;
407 DISALLOW_COPY_AND_ASSIGN(BluetoothDetailedView
);
412 TrayBluetooth::TrayBluetooth(SystemTray
* system_tray
)
413 : SystemTrayItem(system_tray
),
416 Shell::GetInstance()->system_tray_notifier()->AddBluetoothObserver(this);
419 TrayBluetooth::~TrayBluetooth() {
420 Shell::GetInstance()->system_tray_notifier()->RemoveBluetoothObserver(this);
423 views::View
* TrayBluetooth::CreateTrayView(user::LoginStatus status
) {
427 views::View
* TrayBluetooth::CreateDefaultView(user::LoginStatus status
) {
428 CHECK(default_
== NULL
);
429 default_
= new tray::BluetoothDefaultView(
430 this, status
!= user::LOGGED_IN_LOCKED
);
434 views::View
* TrayBluetooth::CreateDetailedView(user::LoginStatus status
) {
435 if (!Shell::GetInstance()->system_tray_delegate()->GetBluetoothAvailable())
437 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
438 ash::UMA_STATUS_AREA_DETAILED_BLUETOOTH_VIEW
);
439 CHECK(detailed_
== NULL
);
440 detailed_
= new tray::BluetoothDetailedView(this, status
);
445 void TrayBluetooth::DestroyTrayView() {
448 void TrayBluetooth::DestroyDefaultView() {
452 void TrayBluetooth::DestroyDetailedView() {
456 void TrayBluetooth::UpdateAfterLoginStatusChange(user::LoginStatus status
) {
459 void TrayBluetooth::OnBluetoothRefresh() {
461 default_
->UpdateLabel();
466 void TrayBluetooth::OnBluetoothDiscoveringChanged() {