| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "ash/system/chromeos/bluetooth/tray_bluetooth.h" | |
| 6 | |
| 7 #include "ash/common/session/session_state_delegate.h" | |
| 8 #include "ash/common/system/tray/fixed_sized_scroll_view.h" | |
| 9 #include "ash/common/system/tray/hover_highlight_view.h" | |
| 10 #include "ash/common/system/tray/system_tray_delegate.h" | |
| 11 #include "ash/common/system/tray/system_tray_notifier.h" | |
| 12 #include "ash/common/system/tray/throbber_view.h" | |
| 13 #include "ash/common/system/tray/tray_constants.h" | |
| 14 #include "ash/common/system/tray/tray_details_view.h" | |
| 15 #include "ash/common/system/tray/tray_item_more.h" | |
| 16 #include "ash/common/system/tray/tray_popup_header_button.h" | |
| 17 #include "ash/common/system/tray/view_click_listener.h" | |
| 18 #include "ash/common/wm_shell.h" | |
| 19 #include "ash/system/tray/system_tray.h" | |
| 20 #include "grit/ash_resources.h" | |
| 21 #include "grit/ash_strings.h" | |
| 22 #include "ui/base/l10n/l10n_util.h" | |
| 23 #include "ui/base/resource/resource_bundle.h" | |
| 24 #include "ui/gfx/image/image.h" | |
| 25 #include "ui/views/controls/image_view.h" | |
| 26 #include "ui/views/controls/label.h" | |
| 27 #include "ui/views/layout/box_layout.h" | |
| 28 | |
| 29 namespace ash { | |
| 30 namespace tray { | |
| 31 namespace { | |
| 32 | |
| 33 // Updates bluetooth device |device| in the |list|. If it is new, append to the | |
| 34 // end of the |list|; otherwise, keep it at the same place, but update the data | |
| 35 // with new device info provided by |device|. | |
| 36 void UpdateBluetoothDeviceListHelper(BluetoothDeviceList* list, | |
| 37 const BluetoothDeviceInfo& device) { | |
| 38 for (BluetoothDeviceList::iterator it = list->begin(); it != list->end(); | |
| 39 ++it) { | |
| 40 if ((*it).address == device.address) { | |
| 41 *it = device; | |
| 42 return; | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 list->push_back(device); | |
| 47 } | |
| 48 | |
| 49 // Removes the obsolete BluetoothDevices from |list|, if they are not in the | |
| 50 // |new_list|. | |
| 51 void RemoveObsoleteBluetoothDevicesFromList( | |
| 52 BluetoothDeviceList* list, | |
| 53 const std::set<std::string>& new_list) { | |
| 54 for (BluetoothDeviceList::iterator it = list->begin(); it != list->end(); | |
| 55 ++it) { | |
| 56 if (new_list.find((*it).address) == new_list.end()) { | |
| 57 it = list->erase(it); | |
| 58 if (it == list->end()) | |
| 59 return; | |
| 60 } | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 } // namespace | |
| 65 | |
| 66 class BluetoothDefaultView : public TrayItemMore { | |
| 67 public: | |
| 68 BluetoothDefaultView(SystemTrayItem* owner, bool show_more) | |
| 69 : TrayItemMore(owner, show_more) { | |
| 70 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
| 71 SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_BLUETOOTH).ToImageSkia()); | |
| 72 UpdateLabel(); | |
| 73 } | |
| 74 | |
| 75 ~BluetoothDefaultView() override {} | |
| 76 | |
| 77 void UpdateLabel() { | |
| 78 SystemTrayDelegate* delegate = WmShell::Get()->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); | |
| 85 SetLabel(label); | |
| 86 SetAccessibleName(label); | |
| 87 SetVisible(true); | |
| 88 } else { | |
| 89 SetVisible(false); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 private: | |
| 94 DISALLOW_COPY_AND_ASSIGN(BluetoothDefaultView); | |
| 95 }; | |
| 96 | |
| 97 class BluetoothDetailedView : public TrayDetailsView, | |
| 98 public ViewClickListener, | |
| 99 public views::ButtonListener { | |
| 100 public: | |
| 101 BluetoothDetailedView(SystemTrayItem* owner, LoginStatus login) | |
| 102 : TrayDetailsView(owner), | |
| 103 login_(login), | |
| 104 manage_devices_(NULL), | |
| 105 toggle_bluetooth_(NULL), | |
| 106 enable_bluetooth_(NULL) { | |
| 107 CreateItems(); | |
| 108 } | |
| 109 | |
| 110 ~BluetoothDetailedView() override { | |
| 111 // Stop discovering bluetooth devices when exiting BT detailed view. | |
| 112 BluetoothStopDiscovering(); | |
| 113 } | |
| 114 | |
| 115 void Update() { | |
| 116 BluetoothStartDiscovering(); | |
| 117 UpdateBluetoothDeviceList(); | |
| 118 | |
| 119 // Update UI. | |
| 120 UpdateDeviceScrollList(); | |
| 121 UpdateHeaderEntry(); | |
| 122 Layout(); | |
| 123 } | |
| 124 | |
| 125 private: | |
| 126 void CreateItems() { | |
| 127 CreateScrollableList(); | |
| 128 AppendSettingsEntries(); | |
| 129 AppendHeaderEntry(); | |
| 130 } | |
| 131 | |
| 132 void BluetoothStartDiscovering() { | |
| 133 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 134 bool bluetooth_enabled = delegate->GetBluetoothEnabled(); | |
| 135 bool bluetooth_discovering = delegate->GetBluetoothDiscovering(); | |
| 136 if (bluetooth_discovering) { | |
| 137 throbber_->Start(); | |
| 138 return; | |
| 139 } | |
| 140 throbber_->Stop(); | |
| 141 if (bluetooth_enabled) { | |
| 142 delegate->BluetoothStartDiscovering(); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 void BluetoothStopDiscovering() { | |
| 147 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 148 if (delegate && delegate->GetBluetoothDiscovering()) { | |
| 149 delegate->BluetoothStopDiscovering(); | |
| 150 throbber_->Stop(); | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 void UpdateBluetoothDeviceList() { | |
| 155 std::set<std::string> new_connecting_devices; | |
| 156 std::set<std::string> new_connected_devices; | |
| 157 std::set<std::string> new_paired_not_connected_devices; | |
| 158 std::set<std::string> new_discovered_not_paired_devices; | |
| 159 | |
| 160 BluetoothDeviceList list; | |
| 161 WmShell::Get()->system_tray_delegate()->GetAvailableBluetoothDevices(&list); | |
| 162 for (size_t i = 0; i < list.size(); ++i) { | |
| 163 if (list[i].connecting) { | |
| 164 list[i].display_name = l10n_util::GetStringFUTF16( | |
| 165 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONNECTING, list[i].display_name); | |
| 166 new_connecting_devices.insert(list[i].address); | |
| 167 UpdateBluetoothDeviceListHelper(&connecting_devices_, list[i]); | |
| 168 } else if (list[i].connected && list[i].paired) { | |
| 169 new_connected_devices.insert(list[i].address); | |
| 170 UpdateBluetoothDeviceListHelper(&connected_devices_, list[i]); | |
| 171 } else if (list[i].paired) { | |
| 172 new_paired_not_connected_devices.insert(list[i].address); | |
| 173 UpdateBluetoothDeviceListHelper(&paired_not_connected_devices_, | |
| 174 list[i]); | |
| 175 } else { | |
| 176 new_discovered_not_paired_devices.insert(list[i].address); | |
| 177 UpdateBluetoothDeviceListHelper(&discovered_not_paired_devices_, | |
| 178 list[i]); | |
| 179 } | |
| 180 } | |
| 181 RemoveObsoleteBluetoothDevicesFromList(&connecting_devices_, | |
| 182 new_connecting_devices); | |
| 183 RemoveObsoleteBluetoothDevicesFromList(&connected_devices_, | |
| 184 new_connected_devices); | |
| 185 RemoveObsoleteBluetoothDevicesFromList(&paired_not_connected_devices_, | |
| 186 new_paired_not_connected_devices); | |
| 187 RemoveObsoleteBluetoothDevicesFromList(&discovered_not_paired_devices_, | |
| 188 new_discovered_not_paired_devices); | |
| 189 } | |
| 190 | |
| 191 void AppendHeaderEntry() { | |
| 192 CreateSpecialRow(IDS_ASH_STATUS_TRAY_BLUETOOTH, this); | |
| 193 | |
| 194 if (login_ == LoginStatus::LOCKED) | |
| 195 return; | |
| 196 | |
| 197 throbber_ = new ThrobberView; | |
| 198 throbber_->SetTooltipText( | |
| 199 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING)); | |
| 200 footer()->AddView(throbber_, false /* separator */); | |
| 201 | |
| 202 // Do not allow toggling bluetooth in the lock screen. | |
| 203 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 204 toggle_bluetooth_ = | |
| 205 new TrayPopupHeaderButton(this, IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED, | |
| 206 IDR_AURA_UBER_TRAY_BLUETOOTH_DISABLED, | |
| 207 IDR_AURA_UBER_TRAY_BLUETOOTH_ENABLED_HOVER, | |
| 208 IDR_AURA_UBER_TRAY_BLUETOOTH_DISABLED_HOVER, | |
| 209 IDS_ASH_STATUS_TRAY_BLUETOOTH); | |
| 210 toggle_bluetooth_->SetToggled(!delegate->GetBluetoothEnabled()); | |
| 211 toggle_bluetooth_->SetTooltipText( | |
| 212 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_BLUETOOTH)); | |
| 213 toggle_bluetooth_->SetToggledTooltipText( | |
| 214 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH)); | |
| 215 toggle_bluetooth_->EnableCanvasFlippingForRTLUI(false); | |
| 216 footer()->AddButton(toggle_bluetooth_); | |
| 217 } | |
| 218 | |
| 219 void UpdateHeaderEntry() { | |
| 220 if (toggle_bluetooth_) { | |
| 221 toggle_bluetooth_->SetToggled( | |
| 222 !WmShell::Get()->system_tray_delegate()->GetBluetoothEnabled()); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 void UpdateDeviceScrollList() { | |
| 227 device_map_.clear(); | |
| 228 scroll_content()->RemoveAllChildViews(true); | |
| 229 enable_bluetooth_ = NULL; | |
| 230 | |
| 231 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 232 bool bluetooth_enabled = delegate->GetBluetoothEnabled(); | |
| 233 bool blueooth_available = delegate->GetBluetoothAvailable(); | |
| 234 if (blueooth_available && !bluetooth_enabled && toggle_bluetooth_) { | |
| 235 enable_bluetooth_ = AddScrollListItem( | |
| 236 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_BLUETOOTH), | |
| 237 false /* highlight */, false /* checked */, true /* enabled */); | |
| 238 } | |
| 239 | |
| 240 AppendSameTypeDevicesToScrollList(connected_devices_, true, true, | |
| 241 bluetooth_enabled); | |
| 242 AppendSameTypeDevicesToScrollList(connecting_devices_, true, false, | |
| 243 bluetooth_enabled); | |
| 244 AppendSameTypeDevicesToScrollList(paired_not_connected_devices_, false, | |
| 245 false, bluetooth_enabled); | |
| 246 if (discovered_not_paired_devices_.size() > 0) | |
| 247 AddScrollSeparator(); | |
| 248 AppendSameTypeDevicesToScrollList(discovered_not_paired_devices_, false, | |
| 249 false, bluetooth_enabled); | |
| 250 | |
| 251 // Show user Bluetooth state if there is no bluetooth devices in list. | |
| 252 if (device_map_.size() == 0) { | |
| 253 if (blueooth_available && bluetooth_enabled) { | |
| 254 AddScrollListItem(l10n_util::GetStringUTF16( | |
| 255 IDS_ASH_STATUS_TRAY_BLUETOOTH_DISCOVERING), | |
| 256 false /* highlight */, false /* checked */, | |
| 257 true /* enabled */); | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 scroll_content()->SizeToPreferredSize(); | |
| 262 } | |
| 263 | |
| 264 void AppendSameTypeDevicesToScrollList(const BluetoothDeviceList& list, | |
| 265 bool highlight, | |
| 266 bool checked, | |
| 267 bool enabled) { | |
| 268 for (size_t i = 0; i < list.size(); ++i) { | |
| 269 HoverHighlightView* container = | |
| 270 AddScrollListItem(list[i].display_name, highlight, checked, enabled); | |
| 271 device_map_[container] = list[i].address; | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 HoverHighlightView* AddScrollListItem(const base::string16& text, | |
| 276 bool highlight, | |
| 277 bool checked, | |
| 278 bool enabled) { | |
| 279 HoverHighlightView* container = new HoverHighlightView(this); | |
| 280 views::Label* label = | |
| 281 container->AddCheckableLabel(text, highlight, checked); | |
| 282 label->SetEnabled(enabled); | |
| 283 scroll_content()->AddChildView(container); | |
| 284 return container; | |
| 285 } | |
| 286 | |
| 287 // Add settings entries. | |
| 288 void AppendSettingsEntries() { | |
| 289 if (!WmShell::Get()->system_tray_delegate()->ShouldShowSettings()) | |
| 290 return; | |
| 291 | |
| 292 // Add bluetooth device requires a browser window, hide it for non logged in | |
| 293 // user. | |
| 294 if (login_ == LoginStatus::NOT_LOGGED_IN || login_ == LoginStatus::LOCKED || | |
| 295 WmShell::Get()->GetSessionStateDelegate()->IsInSecondaryLoginScreen()) { | |
| 296 return; | |
| 297 } | |
| 298 | |
| 299 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 300 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 301 HoverHighlightView* container = new HoverHighlightView(this); | |
| 302 container->AddLabel( | |
| 303 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_BLUETOOTH_MANAGE_DEVICES), | |
| 304 gfx::ALIGN_LEFT, false /* highlight */); | |
| 305 container->SetEnabled(delegate->GetBluetoothAvailable()); | |
| 306 AddChildView(container); | |
| 307 manage_devices_ = container; | |
| 308 } | |
| 309 | |
| 310 // Returns true if the device with |device_id| is found in |device_list|, | |
| 311 // and the display_name of the device will be returned in |display_name| if | |
| 312 // it's not NULL. | |
| 313 bool FoundDevice(const std::string& device_id, | |
| 314 const BluetoothDeviceList& device_list, | |
| 315 base::string16* display_name) { | |
| 316 for (size_t i = 0; i < device_list.size(); ++i) { | |
| 317 if (device_list[i].address == device_id) { | |
| 318 if (display_name) | |
| 319 *display_name = device_list[i].display_name; | |
| 320 return true; | |
| 321 } | |
| 322 } | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 // Updates UI of the clicked bluetooth device to show it is being connected | |
| 327 // or disconnected if such an operation is going to be performed underway. | |
| 328 void UpdateClickedDevice(const std::string& device_id, | |
| 329 views::View* item_container) { | |
| 330 base::string16 display_name; | |
| 331 if (FoundDevice(device_id, paired_not_connected_devices_, &display_name)) { | |
| 332 display_name = l10n_util::GetStringFUTF16( | |
| 333 IDS_ASH_STATUS_TRAY_BLUETOOTH_CONNECTING, display_name); | |
| 334 | |
| 335 item_container->RemoveAllChildViews(true); | |
| 336 static_cast<HoverHighlightView*>(item_container) | |
| 337 ->AddCheckableLabel(display_name, true /* highlight */, false); | |
| 338 scroll_content()->SizeToPreferredSize(); | |
| 339 static_cast<views::View*>(scroller())->Layout(); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 // Overridden from ViewClickListener. | |
| 344 void OnViewClicked(views::View* sender) override { | |
| 345 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 346 if (sender == footer()->content()) { | |
| 347 TransitionToDefaultView(); | |
| 348 } else if (sender == manage_devices_) { | |
| 349 delegate->ManageBluetoothDevices(); | |
| 350 } else if (sender == enable_bluetooth_) { | |
| 351 WmShell::Get()->RecordUserMetricsAction( | |
| 352 delegate->GetBluetoothEnabled() ? UMA_STATUS_AREA_BLUETOOTH_DISABLED | |
| 353 : UMA_STATUS_AREA_BLUETOOTH_ENABLED); | |
| 354 delegate->ToggleBluetooth(); | |
| 355 } else { | |
| 356 if (!delegate->GetBluetoothEnabled()) | |
| 357 return; | |
| 358 std::map<views::View*, std::string>::iterator find; | |
| 359 find = device_map_.find(sender); | |
| 360 if (find == device_map_.end()) | |
| 361 return; | |
| 362 const std::string device_id = find->second; | |
| 363 if (FoundDevice(device_id, connecting_devices_, NULL)) | |
| 364 return; | |
| 365 UpdateClickedDevice(device_id, sender); | |
| 366 delegate->ConnectToBluetoothDevice(device_id); | |
| 367 } | |
| 368 } | |
| 369 | |
| 370 // Overridden from ButtonListener. | |
| 371 void ButtonPressed(views::Button* sender, const ui::Event& event) override { | |
| 372 SystemTrayDelegate* delegate = WmShell::Get()->system_tray_delegate(); | |
| 373 if (sender == toggle_bluetooth_) | |
| 374 delegate->ToggleBluetooth(); | |
| 375 else | |
| 376 NOTREACHED(); | |
| 377 } | |
| 378 | |
| 379 LoginStatus login_; | |
| 380 | |
| 381 std::map<views::View*, std::string> device_map_; | |
| 382 views::View* manage_devices_; | |
| 383 ThrobberView* throbber_; | |
| 384 TrayPopupHeaderButton* toggle_bluetooth_; | |
| 385 HoverHighlightView* enable_bluetooth_; | |
| 386 BluetoothDeviceList connected_devices_; | |
| 387 BluetoothDeviceList connecting_devices_; | |
| 388 BluetoothDeviceList paired_not_connected_devices_; | |
| 389 BluetoothDeviceList discovered_not_paired_devices_; | |
| 390 | |
| 391 DISALLOW_COPY_AND_ASSIGN(BluetoothDetailedView); | |
| 392 }; | |
| 393 | |
| 394 } // namespace tray | |
| 395 | |
| 396 TrayBluetooth::TrayBluetooth(SystemTray* system_tray) | |
| 397 : SystemTrayItem(system_tray), default_(NULL), detailed_(NULL) { | |
| 398 WmShell::Get()->system_tray_notifier()->AddBluetoothObserver(this); | |
| 399 } | |
| 400 | |
| 401 TrayBluetooth::~TrayBluetooth() { | |
| 402 WmShell::Get()->system_tray_notifier()->RemoveBluetoothObserver(this); | |
| 403 } | |
| 404 | |
| 405 views::View* TrayBluetooth::CreateTrayView(LoginStatus status) { | |
| 406 return NULL; | |
| 407 } | |
| 408 | |
| 409 views::View* TrayBluetooth::CreateDefaultView(LoginStatus status) { | |
| 410 CHECK(default_ == NULL); | |
| 411 default_ = | |
| 412 new tray::BluetoothDefaultView(this, status != LoginStatus::LOCKED); | |
| 413 return default_; | |
| 414 } | |
| 415 | |
| 416 views::View* TrayBluetooth::CreateDetailedView(LoginStatus status) { | |
| 417 if (!WmShell::Get()->system_tray_delegate()->GetBluetoothAvailable()) | |
| 418 return NULL; | |
| 419 WmShell::Get()->RecordUserMetricsAction( | |
| 420 UMA_STATUS_AREA_DETAILED_BLUETOOTH_VIEW); | |
| 421 CHECK(detailed_ == NULL); | |
| 422 detailed_ = new tray::BluetoothDetailedView(this, status); | |
| 423 detailed_->Update(); | |
| 424 return detailed_; | |
| 425 } | |
| 426 | |
| 427 void TrayBluetooth::DestroyTrayView() {} | |
| 428 | |
| 429 void TrayBluetooth::DestroyDefaultView() { | |
| 430 default_ = NULL; | |
| 431 } | |
| 432 | |
| 433 void TrayBluetooth::DestroyDetailedView() { | |
| 434 detailed_ = NULL; | |
| 435 } | |
| 436 | |
| 437 void TrayBluetooth::UpdateAfterLoginStatusChange(LoginStatus status) {} | |
| 438 | |
| 439 void TrayBluetooth::OnBluetoothRefresh() { | |
| 440 if (default_) | |
| 441 default_->UpdateLabel(); | |
| 442 else if (detailed_) | |
| 443 detailed_->Update(); | |
| 444 } | |
| 445 | |
| 446 void TrayBluetooth::OnBluetoothDiscoveringChanged() { | |
| 447 if (!detailed_) | |
| 448 return; | |
| 449 detailed_->Update(); | |
| 450 } | |
| 451 | |
| 452 } // namespace ash | |
| OLD | NEW |