network_list_view.cc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846
  1. // Copyright 2016 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/network/network_list_view.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "ash/public/cpp/ash_view_ids.h"
  8. #include "ash/resources/vector_icons/vector_icons.h"
  9. #include "ash/session/session_controller_impl.h"
  10. #include "ash/shell.h"
  11. #include "ash/strings/grit/ash_strings.h"
  12. #include "ash/style/ash_color_provider.h"
  13. #include "ash/system/model/system_tray_model.h"
  14. #include "ash/system/network/network_icon.h"
  15. #include "ash/system/network/network_icon_animation.h"
  16. #include "ash/system/network/network_info.h"
  17. #include "ash/system/network/network_section_header_view.h"
  18. #include "ash/system/network/network_state_list_detailed_view.h"
  19. #include "ash/system/network/network_utils.h"
  20. #include "ash/system/network/tray_network_state_model.h"
  21. #include "ash/system/power/power_status.h"
  22. #include "ash/system/tray/hover_highlight_view.h"
  23. #include "ash/system/tray/tray_info_label.h"
  24. #include "ash/system/tray/tray_popup_utils.h"
  25. #include "ash/system/tray/tray_utils.h"
  26. #include "ash/system/tray/tri_view.h"
  27. #include "base/bind.h"
  28. #include "base/i18n/number_formatting.h"
  29. #include "base/strings/utf_string_conversions.h"
  30. #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
  31. #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
  32. #include "components/device_event_log/device_event_log.h"
  33. #include "ui/base/l10n/l10n_util.h"
  34. #include "ui/base/ui_base_features.h"
  35. #include "ui/gfx/image/image_skia_operations.h"
  36. #include "ui/gfx/paint_vector_icon.h"
  37. #include "ui/views/accessibility/view_accessibility.h"
  38. #include "ui/views/background.h"
  39. #include "ui/views/border.h"
  40. #include "ui/views/controls/image_view.h"
  41. #include "ui/views/controls/scroll_view.h"
  42. #include "ui/views/controls/separator.h"
  43. using chromeos::network_config::IsInhibited;
  44. using chromeos::network_config::NetworkTypeMatchesType;
  45. using chromeos::network_config::StateIsConnected;
  46. using chromeos::network_config::mojom::ActivationStateType;
  47. using chromeos::network_config::mojom::ConnectionStateType;
  48. using chromeos::network_config::mojom::DeviceStatePropertiesPtr;
  49. using chromeos::network_config::mojom::DeviceStateType;
  50. using chromeos::network_config::mojom::FilterType;
  51. using chromeos::network_config::mojom::NetworkFilter;
  52. using chromeos::network_config::mojom::NetworkStateProperties;
  53. using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
  54. using chromeos::network_config::mojom::NetworkType;
  55. using chromeos::network_config::mojom::OncSource;
  56. using chromeos::network_config::mojom::ProxyMode;
  57. namespace ash {
  58. namespace {
  59. const int kMobileNetworkBatteryIconSize = 20;
  60. const int kPowerStatusPaddingRight = 10;
  61. const double kAlphaValueForInhibitedIconOpacity = 0.3;
  62. bool IsSecondaryUser() {
  63. SessionControllerImpl* session_controller =
  64. Shell::Get()->session_controller();
  65. return session_controller->IsActiveUserSessionStarted() &&
  66. !session_controller->IsUserPrimary();
  67. }
  68. SkColor GetIconColor() {
  69. return AshColorProvider::Get()->GetContentLayerColor(
  70. AshColorProvider::ContentLayerType::kIconColorPrimary);
  71. }
  72. bool IsManagedByPolicy(const NetworkInfo& info) {
  73. return info.source == OncSource::kDevicePolicy ||
  74. info.source == OncSource::kUserPolicy;
  75. }
  76. bool ShouldShowActivateCellularNetwork(const NetworkInfo& info) {
  77. return NetworkTypeMatchesType(info.type, NetworkType::kCellular) &&
  78. info.activation_state == ActivationStateType::kNotActivated &&
  79. info.sim_eid.empty();
  80. }
  81. bool ShouldShowContactCarrier(const NetworkInfo& info) {
  82. return NetworkTypeMatchesType(info.type, NetworkType::kCellular) &&
  83. info.activation_state == ActivationStateType::kNotActivated &&
  84. !info.sim_eid.empty();
  85. }
  86. gfx::ImageSkia GetNetworkImageForNetwork(const NetworkInfo& info) {
  87. gfx::ImageSkia network_image;
  88. if (NetworkTypeMatchesType(info.type, NetworkType::kMobile) &&
  89. info.connection_state == ConnectionStateType::kNotConnected) {
  90. // Mobile icons which are not connecting or connected should display a small
  91. // "X" icon superimposed so that it is clear that they are disconnected.
  92. network_image = gfx::ImageSkiaOperations::CreateSuperimposedImage(
  93. info.image, gfx::CreateVectorIcon(kNetworkMobileNotConnectedXIcon,
  94. info.image.height(), GetIconColor()));
  95. } else {
  96. network_image = info.image;
  97. }
  98. // When the network is disabled, its appearance should be grayed out to
  99. // indicate users that these networks are unavailable. We must change the
  100. // image before we add it to the view, and then alter the label and sub-label
  101. // if they exist after it is added to the view.
  102. if (info.disable) {
  103. network_image = gfx::ImageSkiaOperations::CreateTransparentImage(
  104. network_image, kAlphaValueForInhibitedIconOpacity);
  105. }
  106. return network_image;
  107. }
  108. bool ShouldShowUnlockCellularNetwork(const NetworkInfo& info) {
  109. return NetworkTypeMatchesType(info.type, NetworkType::kCellular) &&
  110. info.sim_locked;
  111. }
  112. // returns 0 if there is no cellular subtext
  113. int GetCellularNetworkSubText(const NetworkInfo& info) {
  114. if (ShouldShowActivateCellularNetwork(info))
  115. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_ACTIVATE;
  116. if (ShouldShowContactCarrier(info))
  117. return IDS_ASH_STATUS_TRAY_NETWORK_UNAVAILABLE_SIM_NETWORK;
  118. if (!ShouldShowUnlockCellularNetwork(info))
  119. return 0;
  120. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted())
  121. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_UNLOCK;
  122. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SIGN_IN_TO_UNLOCK;
  123. }
  124. // Returns color for cellular network item text label.
  125. SkColor GetCellularNetworkPrimaryTextColor(const NetworkInfo& info) {
  126. return AshColorProvider::Get()->GetContentLayerColor(
  127. AshColorProvider::ContentLayerType::kTextColorPrimary);
  128. }
  129. // Returns color for cellular network item sub text label.
  130. SkColor GetCellularNetworkSubTextColor(const NetworkInfo& info) {
  131. return AshColorProvider::Get()->GetContentLayerColor(
  132. AshColorProvider::ContentLayerType::kTextColorWarning);
  133. }
  134. // Updates the disabled network item's label text colors to grey if the item
  135. // is disabled.
  136. void UpdateDisabledListItemTextColor(HoverHighlightView* view,
  137. const NetworkInfo& info) {
  138. // The network row is disabled if blocked by policy, device is inhibited or
  139. // when SIM is locked and user is not logged in.
  140. if (!info.disable) {
  141. return;
  142. }
  143. if (view->text_label()) {
  144. SkColor primary_text_color = view->text_label()->GetEnabledColor();
  145. view->text_label()->SetEnabledColor(
  146. AshColorProvider::GetDisabledColor(primary_text_color));
  147. }
  148. if (view->sub_text_label()) {
  149. SkColor sub_text_color = view->sub_text_label()->GetEnabledColor();
  150. view->sub_text_label()->SetEnabledColor(
  151. AshColorProvider::GetDisabledColor(sub_text_color));
  152. }
  153. }
  154. void SetupCellularListItemWithSubtext(HoverHighlightView* view,
  155. const NetworkInfo& info,
  156. int cellular_subtext_message_id) {
  157. if (view->text_label()) {
  158. view->text_label()->SetEnabledColor(
  159. GetCellularNetworkPrimaryTextColor(info));
  160. }
  161. view->SetSubText(l10n_util::GetStringUTF16(cellular_subtext_message_id));
  162. view->sub_text_label()->SetEnabledColor(GetCellularNetworkSubTextColor(info));
  163. }
  164. bool ComputeNetworkDisabledProperty(const NetworkStatePropertiesPtr& network,
  165. const NetworkInfo& info,
  166. ActivationStateType activation_state,
  167. bool inhibited) {
  168. // If user is not logged in and SIM is locked disable the row.
  169. if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
  170. ShouldShowUnlockCellularNetwork(info)) {
  171. return info.sim_locked;
  172. }
  173. // If the device is inhibited or network is blocked by policy, the network
  174. // row should be disabled.
  175. return activation_state == ActivationStateType::kActivating ||
  176. network->prohibited_by_policy || inhibited;
  177. }
  178. } // namespace
  179. // NetworkListView:
  180. NetworkListView::NetworkListView(DetailedViewDelegate* delegate,
  181. LoginStatus login)
  182. : NetworkStateListDetailedView(delegate, LIST_TYPE_NETWORK, login) {}
  183. NetworkListView::~NetworkListView() {
  184. network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
  185. }
  186. void NetworkListView::UpdateNetworkList() {
  187. CHECK(scroll_content());
  188. model()->cros_network_config()->GetNetworkStateList(
  189. NetworkFilter::New(FilterType::kVisible, NetworkType::kAll,
  190. chromeos::network_config::mojom::kNoLimit),
  191. base::BindOnce(&NetworkListView::OnGetNetworkStateList,
  192. weak_ptr_factory_.GetWeakPtr()));
  193. }
  194. bool NetworkListView::IsNetworkEntry(views::View* view,
  195. std::string* guid) const {
  196. std::map<views::View*, std::string>::const_iterator found =
  197. network_map_.find(view);
  198. if (found == network_map_.end())
  199. return false;
  200. *guid = found->second;
  201. return true;
  202. }
  203. const char* NetworkListView::GetClassName() const {
  204. return "NetworkListView";
  205. }
  206. void NetworkListView::OnGetNetworkStateList(
  207. std::vector<NetworkStatePropertiesPtr> networks) {
  208. // |network_list_| contains all the info and is going to be cleared and
  209. // recreated. Save them to |last_network_info_map_|.
  210. last_network_info_map_.clear();
  211. for (auto& info : network_list_)
  212. last_network_info_map_[info->guid] = std::move(info);
  213. bool animating = false;
  214. network_list_.clear();
  215. vpn_connected_ = false;
  216. wifi_has_networks_ = false;
  217. mobile_has_networks_ = false;
  218. tether_has_networks_ = false;
  219. for (auto& network : networks) {
  220. ConnectionStateType connection_state = network->connection_state;
  221. if (network->type == NetworkType::kVPN) {
  222. if (chromeos::network_config::StateIsConnected(connection_state))
  223. vpn_connected_ = true;
  224. continue;
  225. }
  226. auto info = std::make_unique<NetworkInfo>(network->guid);
  227. bool inhibited = false;
  228. ActivationStateType activation_state = ActivationStateType::kUnknown;
  229. const chromeos::network_config::mojom::DeviceStateProperties*
  230. cellular_device = model()->GetDevice(NetworkType::kCellular);
  231. switch (network->type) {
  232. case NetworkType::kCellular:
  233. mobile_has_networks_ = true;
  234. activation_state =
  235. network->type_state->get_cellular()->activation_state;
  236. info->activation_state = activation_state;
  237. info->sim_locked = network->type_state->get_cellular()->sim_locked;
  238. info->sim_eid = network->type_state->get_cellular()->eid;
  239. if (cellular_device && IsInhibited(cellular_device))
  240. inhibited = true;
  241. // If cellular is not enabled, skip cellular networks with no service.
  242. if (model()->GetDeviceState(NetworkType::kCellular) !=
  243. DeviceStateType::kEnabled &&
  244. activation_state == ActivationStateType::kNoService) {
  245. continue;
  246. }
  247. break;
  248. case NetworkType::kWiFi:
  249. wifi_has_networks_ = true;
  250. info->secured = network->type_state->get_wifi()->security !=
  251. chromeos::network_config::mojom::SecurityType::kNone;
  252. break;
  253. case NetworkType::kTether:
  254. mobile_has_networks_ = true;
  255. tether_has_networks_ = true;
  256. info->battery_percentage =
  257. network->type_state->get_tether()->battery_percentage;
  258. break;
  259. default:
  260. break;
  261. }
  262. info->label = network_icon::GetLabelForNetworkList(network.get());
  263. // |network_list_| only contains non virtual networks.
  264. info->image = network_icon::GetImageForNonVirtualNetwork(
  265. network.get(), network_icon::ICON_TYPE_LIST, false /* badge_vpn */);
  266. info->type = network->type;
  267. info->disable = ComputeNetworkDisabledProperty(network, *info,
  268. activation_state, inhibited);
  269. // If the device state is inhibited, we want to have the cellular network
  270. // rows not connectable.
  271. info->connectable = network->connectable && !inhibited && !info->sim_locked;
  272. if (network->prohibited_by_policy) {
  273. info->tooltip =
  274. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED);
  275. }
  276. info->connection_state = connection_state;
  277. info->signal_strength =
  278. chromeos::network_config::GetWirelessSignalStrength(network.get());
  279. info->source = network->source;
  280. if (!animating && connection_state == ConnectionStateType::kConnecting)
  281. animating = true;
  282. network_list_.push_back(std::move(info));
  283. }
  284. if (animating)
  285. network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
  286. else
  287. network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
  288. UpdateNetworkListInternal();
  289. }
  290. void NetworkListView::UpdateNetworkListInternal() {
  291. // Get the updated list entries.
  292. needs_relayout_ = false;
  293. network_map_.clear();
  294. std::unique_ptr<std::set<std::string>> new_guids = UpdateNetworkListEntries();
  295. // Remove old children.
  296. std::set<std::string> remove_guids;
  297. for (const auto& iter : network_guid_map_) {
  298. if (new_guids->find(iter.first) == new_guids->end()) {
  299. remove_guids.insert(iter.first);
  300. network_map_.erase(iter.second);
  301. delete iter.second;
  302. needs_relayout_ = true;
  303. }
  304. }
  305. for (const auto& remove_iter : remove_guids)
  306. network_guid_map_.erase(remove_iter);
  307. if (!needs_relayout_)
  308. return;
  309. views::View* selected_view = nullptr;
  310. for (const auto& iter : network_guid_map_) {
  311. // The within_bounds check is necessary when the network list goes beyond
  312. // the visible area (i.e. scrolling) and the mouse is below the tray pop-up.
  313. // The items not in view in the tray pop-up keep going down and have
  314. // View::GetVisibility() == true but they are masked and not seen by the
  315. // user. When the mouse is below the list where the item would be if the
  316. // list continued downward, IsMouseHovered() is true and this will trigger
  317. // an incorrect programmatic scroll if we don't stop it. The bounds check
  318. // ensures the view is actually visible within the tray pop-up.
  319. bool within_bounds =
  320. this->GetBoundsInScreen().Intersects(iter.second->GetBoundsInScreen());
  321. if (within_bounds && iter.second->IsMouseHovered()) {
  322. selected_view = iter.second;
  323. break;
  324. }
  325. }
  326. scroll_content()->SizeToPreferredSize();
  327. scroller()->Layout();
  328. if (selected_view)
  329. scroll_content()->ScrollRectToVisible(selected_view->bounds());
  330. }
  331. std::unique_ptr<std::set<std::string>>
  332. NetworkListView::UpdateNetworkListEntries() {
  333. // Keep an index where the next child should be inserted.
  334. size_t index = 0;
  335. const NetworkStateProperties* default_network = model()->default_network();
  336. bool using_proxy =
  337. default_network && default_network->proxy_mode != ProxyMode::kDirect;
  338. // Show a warning that the connection might be monitored if connected to a VPN
  339. // or if the default network has a proxy installed.
  340. if (vpn_connected_ || using_proxy) {
  341. if (!connection_warning_)
  342. connection_warning_ = CreateConnectionWarning();
  343. PlaceViewAtIndex(connection_warning_, index++);
  344. }
  345. // First add Ethernet networks.
  346. std::unique_ptr<std::set<std::string>> new_guids =
  347. UpdateNetworkChildren(NetworkType::kEthernet, index);
  348. index += new_guids->size();
  349. if (ShouldMobileDataSectionBeShown()) {
  350. if (!mobile_header_view_) {
  351. RecordDetailedViewSection(DetailedViewSection::kMobileSection);
  352. mobile_header_view_ = new MobileSectionHeaderView();
  353. }
  354. index = UpdateNetworkSectionHeader(
  355. NetworkType::kMobile, false /* enabled */, index, mobile_header_view_,
  356. &mobile_separator_view_);
  357. std::unique_ptr<std::set<std::string>> new_cellular_guids =
  358. UpdateNetworkChildren(NetworkType::kMobile, index);
  359. int mobile_status_message =
  360. mobile_header_view_->UpdateToggleAndGetStatusMessage(
  361. mobile_has_networks_, tether_has_networks_);
  362. // |mobile_status_message| may be zero. Passing zero to UpdateInfoLabel
  363. // clears the label.
  364. UpdateInfoLabel(mobile_status_message, index, &mobile_status_message_);
  365. if (mobile_status_message)
  366. ++index;
  367. index += new_cellular_guids->size();
  368. new_guids->insert(new_cellular_guids->begin(), new_cellular_guids->end());
  369. } else if (mobile_header_view_) {
  370. scroll_content()->RemoveChildView(mobile_header_view_);
  371. delete mobile_header_view_;
  372. mobile_header_view_ = nullptr;
  373. needs_relayout_ = true;
  374. }
  375. if (!wifi_header_view_) {
  376. RecordDetailedViewSection(DetailedViewSection::kWifiSection);
  377. wifi_header_view_ = new WifiSectionHeaderView();
  378. }
  379. bool wifi_enabled =
  380. model()->GetDeviceState(NetworkType::kWiFi) == DeviceStateType::kEnabled;
  381. index = UpdateNetworkSectionHeader(NetworkType::kWiFi, wifi_enabled, index,
  382. wifi_header_view_, &wifi_separator_view_);
  383. if (!wifi_enabled) {
  384. UpdateInfoLabel(IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED, index,
  385. &wifi_status_message_);
  386. return new_guids;
  387. }
  388. bool should_clear_info_label = true;
  389. if (!wifi_has_networks_) {
  390. UpdateInfoLabel(IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED, index,
  391. &wifi_status_message_);
  392. ++index;
  393. should_clear_info_label = false;
  394. }
  395. // Add Wi-Fi networks.
  396. std::unique_ptr<std::set<std::string>> new_wifi_guids =
  397. UpdateNetworkChildren(NetworkType::kWiFi, index);
  398. index += new_wifi_guids->size();
  399. new_guids->insert(new_wifi_guids->begin(), new_wifi_guids->end());
  400. // No networks or other messages (fallback).
  401. if (index == 0) {
  402. UpdateInfoLabel(IDS_ASH_STATUS_TRAY_NO_NETWORKS, index,
  403. &wifi_status_message_);
  404. } else if (should_clear_info_label) {
  405. // Update the label to show nothing.
  406. UpdateInfoLabel(0, index, &wifi_status_message_);
  407. }
  408. return new_guids;
  409. }
  410. bool NetworkListView::ShouldMobileDataSectionBeShown() {
  411. // The section should always be shown if Cellular networks are available.
  412. if (model()->GetDeviceState(NetworkType::kCellular) !=
  413. DeviceStateType::kUnavailable) {
  414. return true;
  415. }
  416. DeviceStateType tether_state = model()->GetDeviceState(NetworkType::kTether);
  417. // Hide the section if both Cellular and Tether are UNAVAILABLE.
  418. if (tether_state == DeviceStateType::kUnavailable)
  419. return false;
  420. // Hide the section if Tether is PROHIBITED.
  421. if (tether_state == DeviceStateType::kProhibited)
  422. return false;
  423. // Secondary users cannot enable Bluetooth, and Tether is only UNINITIALIZED
  424. // if Bluetooth is disabled. Hide the section in this case.
  425. if (tether_state == DeviceStateType::kUninitialized && IsSecondaryUser())
  426. return false;
  427. return true;
  428. }
  429. void NetworkListView::UpdateViewForNetwork(HoverHighlightView* view,
  430. const NetworkInfo& info) {
  431. view->Reset();
  432. view->AddIconAndLabel(GetNetworkImageForNetwork(info), info.label);
  433. int cellular_subtext_message_id = GetCellularNetworkSubText(info);
  434. if (cellular_subtext_message_id) {
  435. SetupCellularListItemWithSubtext(view, info, cellular_subtext_message_id);
  436. } else {
  437. if (StateIsConnected(info.connection_state)) {
  438. SetupConnectedScrollListItem(view);
  439. } else if (info.connection_state == ConnectionStateType::kConnecting) {
  440. SetupConnectingScrollListItem(view);
  441. }
  442. }
  443. UpdateDisabledListItemTextColor(view, info);
  444. view->SetTooltipText(info.tooltip);
  445. // Add an additional icon to the right of the label for networks
  446. // that require it (e.g. Tether, controlled by extension).
  447. views::View* icon = CreatePowerStatusView(info);
  448. if (icon) {
  449. view->AddRightView(icon, views::CreateEmptyBorder(gfx::Insets::TLBR(
  450. 0, 0, 0, kPowerStatusPaddingRight)));
  451. } else {
  452. icon = CreatePolicyView(info);
  453. if (icon)
  454. view->AddRightView(icon);
  455. }
  456. view->SetAccessibleName(GenerateAccessibilityLabel(info));
  457. view->GetViewAccessibility().OverrideDescription(
  458. GenerateAccessibilityDescription(info));
  459. needs_relayout_ = true;
  460. }
  461. std::u16string NetworkListView::GenerateAccessibilityLabel(
  462. const NetworkInfo& info) {
  463. if (CanNetworkConnect(info.connection_state, info.type, info.activation_state,
  464. info.connectable, info.sim_eid)) {
  465. return l10n_util::GetStringFUTF16(
  466. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_CONNECT, info.label);
  467. }
  468. if (ShouldShowActivateCellularNetwork(info)) {
  469. return l10n_util::GetStringFUTF16(
  470. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_ACTIVATE, info.label);
  471. }
  472. if (ShouldShowContactCarrier(info)) {
  473. return l10n_util::GetStringFUTF16(
  474. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_UNAVAILABLE_SIM_NETWORK, info.label);
  475. }
  476. return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_OPEN,
  477. info.label);
  478. }
  479. std::u16string NetworkListView::GenerateAccessibilityDescription(
  480. const NetworkInfo& info) {
  481. std::u16string connection_status;
  482. if (StateIsConnected(info.connection_state) ||
  483. info.connection_state == ConnectionStateType::kConnecting) {
  484. connection_status = l10n_util::GetStringUTF16(
  485. StateIsConnected(info.connection_state)
  486. ? IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED
  487. : IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING);
  488. }
  489. switch (info.type) {
  490. case NetworkType::kEthernet:
  491. if (!connection_status.empty()) {
  492. if (IsManagedByPolicy(info)) {
  493. return l10n_util::GetStringFUTF16(
  494. IDS_ASH_STATUS_TRAY_ETHERNET_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  495. connection_status);
  496. }
  497. return connection_status;
  498. }
  499. if (IsManagedByPolicy(info)) {
  500. return l10n_util::GetStringUTF16(
  501. IDS_ASH_STATUS_TRAY_ETHERNET_A11Y_DESC_MANAGED);
  502. }
  503. return info.label;
  504. case NetworkType::kWiFi: {
  505. std::u16string security_label = l10n_util::GetStringUTF16(
  506. info.secured ? IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SECURED
  507. : IDS_ASH_STATUS_TRAY_NETWORK_STATUS_UNSECURED);
  508. if (!connection_status.empty()) {
  509. if (IsManagedByPolicy(info)) {
  510. return l10n_util::GetStringFUTF16(
  511. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  512. security_label, connection_status,
  513. base::FormatPercent(info.signal_strength));
  514. }
  515. return l10n_util::GetStringFUTF16(
  516. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  517. security_label, connection_status,
  518. base::FormatPercent(info.signal_strength));
  519. }
  520. if (IsManagedByPolicy(info)) {
  521. return l10n_util::GetStringFUTF16(
  522. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_MANAGED, security_label,
  523. base::FormatPercent(info.signal_strength));
  524. }
  525. return l10n_util::GetStringFUTF16(
  526. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC, security_label,
  527. base::FormatPercent(info.signal_strength));
  528. }
  529. case NetworkType::kCellular:
  530. if (ShouldShowActivateCellularNetwork(info)) {
  531. return l10n_util::GetStringUTF16(
  532. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_ACTIVATE);
  533. }
  534. if (ShouldShowContactCarrier(info)) {
  535. return l10n_util::GetStringUTF16(
  536. IDS_ASH_STATUS_TRAY_NETWORK_UNAVAILABLE_SIM_NETWORK);
  537. }
  538. if (info.sim_locked) {
  539. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
  540. return l10n_util::GetStringUTF16(
  541. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_UNLOCK);
  542. }
  543. return l10n_util::GetStringUTF16(
  544. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SIGN_IN_TO_UNLOCK);
  545. }
  546. if (!connection_status.empty()) {
  547. if (IsManagedByPolicy(info)) {
  548. return l10n_util::GetStringFUTF16(
  549. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  550. connection_status, base::FormatPercent(info.signal_strength));
  551. }
  552. return l10n_util::GetStringFUTF16(
  553. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  554. connection_status, base::FormatPercent(info.signal_strength));
  555. }
  556. if (IsManagedByPolicy(info)) {
  557. return l10n_util::GetStringFUTF16(
  558. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_MANAGED,
  559. base::FormatPercent(info.signal_strength));
  560. }
  561. return l10n_util::GetStringFUTF16(
  562. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC,
  563. base::FormatPercent(info.signal_strength));
  564. case NetworkType::kTether:
  565. if (!connection_status.empty()) {
  566. return l10n_util::GetStringFUTF16(
  567. IDS_ASH_STATUS_TRAY_TETHER_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  568. connection_status, base::FormatPercent(info.signal_strength),
  569. base::FormatPercent(info.battery_percentage));
  570. }
  571. return l10n_util::GetStringFUTF16(
  572. IDS_ASH_STATUS_TRAY_TETHER_NETWORK_A11Y_DESC,
  573. base::FormatPercent(info.signal_strength),
  574. base::FormatPercent(info.battery_percentage));
  575. default:
  576. return u"";
  577. }
  578. }
  579. views::View* NetworkListView::CreatePowerStatusView(const NetworkInfo& info) {
  580. // Mobile can be Cellular or Tether.
  581. if (!NetworkTypeMatchesType(info.type, NetworkType::kMobile))
  582. return nullptr;
  583. // Only return a battery icon for Tether network type.
  584. if (info.type != NetworkType::kTether)
  585. return nullptr;
  586. views::ImageView* icon = new views::ImageView;
  587. const SkColor icon_color = GetIconColor();
  588. icon->SetPreferredSize(gfx::Size(kMenuIconSize, kMenuIconSize));
  589. icon->SetFlipCanvasOnPaintForRTLUI(true);
  590. PowerStatus::BatteryImageInfo icon_info;
  591. icon_info.charge_percent = info.battery_percentage;
  592. icon->SetImage(PowerStatus::GetBatteryImage(
  593. icon_info, kMobileNetworkBatteryIconSize,
  594. AshColorProvider::GetSecondToneColor(icon_color), icon_color));
  595. // Show the numeric battery percentage on hover.
  596. icon->SetTooltipText(base::FormatPercent(info.battery_percentage));
  597. return icon;
  598. }
  599. views::View* NetworkListView::CreatePolicyView(const NetworkInfo& info) {
  600. // Check if the network is managed by policy.
  601. OncSource source = info.source;
  602. if (source != OncSource::kDevicePolicy && source != OncSource::kUserPolicy)
  603. return nullptr;
  604. views::ImageView* controlled_icon = TrayPopupUtils::CreateMainImageView();
  605. controlled_icon->SetImage(
  606. gfx::CreateVectorIcon(kSystemMenuBusinessIcon, GetIconColor()));
  607. return controlled_icon;
  608. }
  609. std::unique_ptr<std::set<std::string>> NetworkListView::UpdateNetworkChildren(
  610. NetworkType type,
  611. size_t index) {
  612. std::unique_ptr<std::set<std::string>> new_guids(new std::set<std::string>);
  613. for (const auto& info : network_list_) {
  614. if (!NetworkTypeMatchesType(info->type, type))
  615. continue;
  616. UpdateNetworkChild(index++, info.get());
  617. new_guids->insert(info->guid);
  618. }
  619. return new_guids;
  620. }
  621. void NetworkListView::UpdateNetworkChild(size_t index,
  622. const NetworkInfo* info) {
  623. HoverHighlightView* network_view = nullptr;
  624. NetworkGuidMap::const_iterator found = network_guid_map_.find(info->guid);
  625. // This value is used to determine whether at least one network of |type| type
  626. // already existed prior to this method.
  627. bool has_reordered_a_network = false;
  628. if (found == network_guid_map_.end()) {
  629. network_view = new HoverHighlightView(this);
  630. UpdateViewForNetwork(network_view, *info);
  631. } else {
  632. has_reordered_a_network = true;
  633. network_view = found->second;
  634. if (NeedUpdateViewForNetwork(*info))
  635. UpdateViewForNetwork(network_view, *info);
  636. }
  637. // Only emit ethernet metric each time we show Ethernet section
  638. // for the first time. We use |has_reordered_a_network| to determine
  639. // if Ethernet networks already exist in network detailed list.
  640. if (NetworkTypeMatchesType(info->type, NetworkType::kEthernet) &&
  641. !has_reordered_a_network) {
  642. RecordDetailedViewSection(DetailedViewSection::kEthernetSection);
  643. }
  644. PlaceViewAtIndex(network_view, index);
  645. network_view->SetEnabled(!info->disable);
  646. network_map_[network_view] = info->guid;
  647. network_guid_map_[info->guid] = network_view;
  648. }
  649. void NetworkListView::PlaceViewAtIndex(views::View* view, size_t index) {
  650. if (view->parent() != scroll_content()) {
  651. scroll_content()->AddChildViewAt(view, index);
  652. } else if (index > 0 && index < scroll_content()->children().size() &&
  653. scroll_content()->children()[index] == view) {
  654. // ReorderChildView() would no-op in this case, but we still want to avoid
  655. // setting |needs_relayout_|.
  656. return;
  657. } else {
  658. scroll_content()->ReorderChildView(view, index);
  659. }
  660. needs_relayout_ = true;
  661. }
  662. void NetworkListView::UpdateInfoLabel(int message_id,
  663. size_t insertion_index,
  664. TrayInfoLabel** info_label_ptr) {
  665. TrayInfoLabel* info_label = *info_label_ptr;
  666. if (!message_id) {
  667. if (info_label) {
  668. needs_relayout_ = true;
  669. delete info_label;
  670. *info_label_ptr = nullptr;
  671. }
  672. return;
  673. }
  674. if (!info_label)
  675. info_label = new TrayInfoLabel(message_id);
  676. else
  677. info_label->Update(message_id);
  678. PlaceViewAtIndex(info_label, insertion_index);
  679. *info_label_ptr = info_label;
  680. }
  681. size_t NetworkListView::UpdateNetworkSectionHeader(
  682. chromeos::network_config::mojom::NetworkType type,
  683. bool enabled,
  684. size_t child_index,
  685. NetworkSectionHeaderView* view,
  686. views::Separator** separator_view) {
  687. // Show or hide a separator above the header. The separator should only be
  688. // visible when the header row is not at the top of the list.
  689. if (child_index > 0) {
  690. if (!*separator_view)
  691. *separator_view = TrayPopupUtils::CreateListSubHeaderSeparator();
  692. PlaceViewAtIndex(*separator_view, child_index++);
  693. } else {
  694. if (*separator_view)
  695. delete *separator_view;
  696. *separator_view = nullptr;
  697. }
  698. // Mobile updates its toggle state independently.
  699. if (!NetworkTypeMatchesType(type, NetworkType::kMobile))
  700. view->SetToggleState(true /* toggle_enabled */, enabled /* is_on */);
  701. PlaceViewAtIndex(view, child_index++);
  702. return child_index;
  703. }
  704. void NetworkListView::NetworkIconChanged() {
  705. Update();
  706. }
  707. bool NetworkListView::NeedUpdateViewForNetwork(const NetworkInfo& info) const {
  708. NetworkInfoMap::const_iterator found = last_network_info_map_.find(info.guid);
  709. if (found == last_network_info_map_.end()) {
  710. // If we cannot find |info| in |last_network_info_map_|, just return true
  711. // since this is a new network so we have nothing to compare.
  712. return true;
  713. } else {
  714. return *found->second != info;
  715. }
  716. }
  717. TriView* NetworkListView::CreateConnectionWarning() {
  718. // Set up layout and apply sticky row property.
  719. TriView* connection_warning = TrayPopupUtils::CreateDefaultRowView();
  720. TrayPopupUtils::ConfigureAsStickyHeader(connection_warning);
  721. // Set 'info' icon on left side.
  722. views::ImageView* image_view = TrayPopupUtils::CreateMainImageView();
  723. image_view->SetImage(
  724. gfx::CreateVectorIcon(kSystemMenuInfoIcon, GetIconColor()));
  725. image_view->SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
  726. connection_warning->AddView(TriView::Container::START, image_view);
  727. // Set message label in middle of row.
  728. views::Label* label = TrayPopupUtils::CreateDefaultLabel();
  729. label->SetText(
  730. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_MONITORED_WARNING));
  731. label->SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
  732. label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
  733. AshColorProvider::ContentLayerType::kTextColorPrimary));
  734. TrayPopupUtils::SetLabelFontList(
  735. label, TrayPopupUtils::FontStyle::kDetailedViewLabel);
  736. connection_warning->AddView(TriView::Container::CENTER, label);
  737. connection_warning->SetContainerBorder(
  738. TriView::Container::CENTER, views::CreateEmptyBorder(gfx::Insets::TLBR(
  739. 0, 0, 0, kTrayPopupLabelRightPadding)));
  740. // Nothing to the right of the text.
  741. connection_warning->SetContainerVisible(TriView::Container::END, false);
  742. return connection_warning;
  743. }
  744. } // namespace ash