bluetooth_device_list_controller_impl.cc 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright 2021 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. #include "ash/system/bluetooth/bluetooth_device_list_controller_impl.h"
  5. #include "ash/constants/ash_features.h"
  6. #include "ash/strings/grit/ash_strings.h"
  7. #include "ash/system/bluetooth/bluetooth_detailed_view.h"
  8. #include "ash/system/bluetooth/bluetooth_device_list_item_view.h"
  9. #include "ash/system/tray/tray_popup_utils.h"
  10. #include "ash/system/tray/tri_view.h"
  11. #include "base/check.h"
  12. #include "ui/gfx/paint_vector_icon.h"
  13. #include "ui/views/controls/separator.h"
  14. #include "ui/views/view.h"
  15. namespace ash {
  16. namespace {
  17. using chromeos::bluetooth_config::mojom::PairedBluetoothDevicePropertiesPtr;
  18. // Helper function to remove |*view| from its view hierarchy, delete the view,
  19. // and reset the value of |*view| to be |nullptr|.
  20. template <class T>
  21. void RemoveAndResetViewIfExists(T** view) {
  22. DCHECK(view);
  23. if (!*view)
  24. return;
  25. views::View* parent = (*view)->parent();
  26. if (parent) {
  27. parent->RemoveChildViewT(*view);
  28. *view = nullptr;
  29. }
  30. }
  31. } // namespace
  32. BluetoothDeviceListControllerImpl::BluetoothDeviceListControllerImpl(
  33. BluetoothDetailedView* bluetooth_detailed_view)
  34. : bluetooth_detailed_view_(bluetooth_detailed_view) {
  35. DCHECK(ash::features::IsBluetoothRevampEnabled());
  36. }
  37. BluetoothDeviceListControllerImpl::~BluetoothDeviceListControllerImpl() =
  38. default;
  39. void BluetoothDeviceListControllerImpl::UpdateBluetoothEnabledState(
  40. bool enabled) {
  41. if (is_bluetooth_enabled_ && !enabled) {
  42. device_id_to_view_map_.clear();
  43. device_list_separator_ = nullptr;
  44. connected_sub_header_ = nullptr;
  45. no_device_connected_sub_header_ = nullptr;
  46. previously_connected_sub_header_ = nullptr;
  47. bluetooth_detailed_view_->device_list()->RemoveAllChildViews();
  48. }
  49. is_bluetooth_enabled_ = enabled;
  50. }
  51. void BluetoothDeviceListControllerImpl::UpdateDeviceList(
  52. const PairedBluetoothDevicePropertiesPtrs& connected,
  53. const PairedBluetoothDevicePropertiesPtrs& previously_connected) {
  54. DCHECK(is_bluetooth_enabled_);
  55. // This function will create views for new devices, re-use views for existing
  56. // devices, and remove views for devices that no longer exist. To do this, we
  57. // keep track of all the preexisting views in |previous_views|, removing a
  58. // view from this map when the corresponding device is found in |connected| or
  59. // |previously_connected|. Before returning, any view remaining in
  60. // |previous_views| is no longer needed and is deleted.
  61. base::flat_map<std::string, BluetoothDeviceListItemView*> previous_views =
  62. std::move(device_id_to_view_map_);
  63. device_id_to_view_map_.clear();
  64. // Since we re-use views when possible, we need to re-order them to match the
  65. // order of the devices we are provided with. We use |index| to keep track of
  66. // the next index within the device list where a view should be placed, i.e.
  67. // all views before |index| are in their final position.
  68. size_t index = 0;
  69. // The list of connected devices.
  70. if (!connected.empty()) {
  71. connected_sub_header_ = CreateSubHeaderIfMissingAndReorder(
  72. connected_sub_header_,
  73. IDS_ASH_STATUS_TRAY_BLUETOOTH_CURRENTLY_CONNECTED_DEVICES, index);
  74. // Increment |index| since this position was taken by
  75. // |connected_sub_header_|.
  76. index++;
  77. index = CreateViewsIfMissingAndReorder(connected, &previous_views, index);
  78. } else {
  79. RemoveAndResetViewIfExists(&connected_sub_header_);
  80. }
  81. // The separator between the connected and previously connected devices.
  82. if (!connected.empty() && !previously_connected.empty()) {
  83. if (!device_list_separator_) {
  84. device_list_separator_ =
  85. bluetooth_detailed_view_->device_list()->AddChildView(
  86. TrayPopupUtils::CreateListSubHeaderSeparator());
  87. }
  88. bluetooth_detailed_view_->device_list()->ReorderChildView(
  89. device_list_separator_, index);
  90. // Increment |index| since this position was taken by
  91. // |device_list_separator_|.
  92. index++;
  93. } else {
  94. RemoveAndResetViewIfExists(&device_list_separator_);
  95. }
  96. // The previously connected devices.
  97. if (!previously_connected.empty()) {
  98. previously_connected_sub_header_ = CreateSubHeaderIfMissingAndReorder(
  99. previously_connected_sub_header_,
  100. IDS_ASH_STATUS_TRAY_BLUETOOTH_PREVIOUSLY_CONNECTED_DEVICES, index);
  101. // Increment |index| since this position was taken by
  102. // |previously_connected_sub_header_|.
  103. index++;
  104. // Ignore the returned index since we are now done re-ordering the list.
  105. CreateViewsIfMissingAndReorder(previously_connected, &previous_views,
  106. index);
  107. } else {
  108. RemoveAndResetViewIfExists(&previously_connected_sub_header_);
  109. }
  110. // The header when there are no connected or previously connected devices.
  111. if (device_id_to_view_map_.empty()) {
  112. no_device_connected_sub_header_ = CreateSubHeaderIfMissingAndReorder(
  113. no_device_connected_sub_header_,
  114. IDS_ASH_STATUS_TRAY_BLUETOOTH_NO_DEVICE_CONNECTED, index);
  115. } else {
  116. RemoveAndResetViewIfExists(&no_device_connected_sub_header_);
  117. }
  118. for (const auto& id_and_view : previous_views) {
  119. bluetooth_detailed_view_->device_list()->RemoveChildViewT(
  120. id_and_view.second);
  121. }
  122. bluetooth_detailed_view_->NotifyDeviceListChanged();
  123. }
  124. TriView* BluetoothDeviceListControllerImpl::CreateSubHeaderIfMissingAndReorder(
  125. TriView* sub_header,
  126. int text_id,
  127. size_t index) {
  128. if (!sub_header) {
  129. sub_header = bluetooth_detailed_view_->AddDeviceListSubHeader(
  130. gfx::kNoneIcon, text_id);
  131. }
  132. bluetooth_detailed_view_->device_list()->ReorderChildView(sub_header, index);
  133. return sub_header;
  134. }
  135. size_t BluetoothDeviceListControllerImpl::CreateViewsIfMissingAndReorder(
  136. const PairedBluetoothDevicePropertiesPtrs& device_property_list,
  137. base::flat_map<std::string, BluetoothDeviceListItemView*>* previous_views,
  138. size_t index) {
  139. DCHECK(previous_views);
  140. BluetoothDeviceListItemView* device_view = nullptr;
  141. const size_t device_count = device_property_list.size();
  142. for (size_t i = 0; i < device_count; ++i) {
  143. const PairedBluetoothDevicePropertiesPtr& device_properties =
  144. device_property_list.at(i);
  145. const std::string& device_id = device_properties->device_properties->id;
  146. auto it = previous_views->find(device_id);
  147. if (it == previous_views->end()) {
  148. device_view = bluetooth_detailed_view_->AddDeviceListItem();
  149. } else {
  150. device_view = it->second;
  151. previous_views->erase(it);
  152. }
  153. device_id_to_view_map_.emplace(device_id, device_view);
  154. device_view->UpdateDeviceProperties(i, device_count, device_properties);
  155. bluetooth_detailed_view_->device_list()->ReorderChildView(device_view,
  156. index);
  157. // Increment |index| since this position was taken by |device_view|.
  158. index++;
  159. }
  160. return index;
  161. }
  162. } // namespace ash