network_list_network_item_view.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. // Copyright 2022 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_network_item_view.h"
  5. #include <string>
  6. #include "ash/constants/ash_features.h"
  7. #include "ash/resources/vector_icons/vector_icons.h"
  8. #include "ash/session/session_controller_impl.h"
  9. #include "ash/shell.h"
  10. #include "ash/strings/grit/ash_strings.h"
  11. #include "ash/style/ash_color_provider.h"
  12. #include "ash/system/model/system_tray_model.h"
  13. #include "ash/system/network/network_icon.h"
  14. #include "ash/system/network/network_icon_animation.h"
  15. #include "ash/system/network/tray_network_state_model.h"
  16. #include "ash/system/power/power_status.h"
  17. #include "ash/system/tray/hover_highlight_view.h"
  18. #include "ash/system/tray/tray_info_label.h"
  19. #include "ash/system/tray/tray_popup_utils.h"
  20. #include "ash/system/tray/tray_utils.h"
  21. #include "base/i18n/number_formatting.h"
  22. #include "chromeos/services/network_config/public/cpp/cros_network_config_util.h"
  23. #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h"
  24. #include "network_list_network_item_view.h"
  25. #include "ui/base/l10n/l10n_util.h"
  26. #include "ui/base/metadata/metadata_impl_macros.h"
  27. #include "ui/gfx/image/image_skia.h"
  28. #include "ui/gfx/image/image_skia_operations.h"
  29. #include "ui/gfx/paint_vector_icon.h"
  30. #include "ui/views/accessibility/view_accessibility.h"
  31. #include "ui/views/controls/image_view.h"
  32. namespace ash {
  33. namespace {
  34. using chromeos::network_config::IsInhibited;
  35. using chromeos::network_config::NetworkTypeMatchesType;
  36. using chromeos::network_config::StateIsConnected;
  37. using chromeos::network_config::mojom::ActivationStateType;
  38. using chromeos::network_config::mojom::CellularStateProperties;
  39. using chromeos::network_config::mojom::ConnectionStateType;
  40. using chromeos::network_config::mojom::NetworkStateProperties;
  41. using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
  42. using chromeos::network_config::mojom::NetworkType;
  43. using chromeos::network_config::mojom::OncSource;
  44. using chromeos::network_config::mojom::ProxyMode;
  45. using chromeos::network_config::mojom::SecurityType;
  46. const int kMobileNetworkBatteryIconSize = 20;
  47. const int kPowerStatusPaddingRight = 10;
  48. const double kAlphaValueForInhibitedIconOpacity = 0.3;
  49. bool IsSecondaryUser() {
  50. SessionControllerImpl* session_controller =
  51. Shell::Get()->session_controller();
  52. return session_controller->IsActiveUserSessionStarted() &&
  53. !session_controller->IsUserPrimary();
  54. }
  55. bool NetworkTypeIsConfigurable(NetworkType type) {
  56. switch (type) {
  57. case NetworkType::kVPN:
  58. case NetworkType::kWiFi:
  59. return true;
  60. case NetworkType::kAll:
  61. case NetworkType::kCellular:
  62. case NetworkType::kEthernet:
  63. case NetworkType::kMobile:
  64. case NetworkType::kTether:
  65. case NetworkType::kWireless:
  66. return false;
  67. }
  68. NOTREACHED();
  69. return false;
  70. }
  71. ActivationStateType GetNetworkActivationState(
  72. const NetworkStatePropertiesPtr& network_properties) {
  73. if (NetworkTypeMatchesType(network_properties->type,
  74. NetworkType::kCellular)) {
  75. return network_properties->type_state->get_cellular()->activation_state;
  76. }
  77. return ActivationStateType::kUnknown;
  78. }
  79. bool IsNetworkInhibited(const NetworkStatePropertiesPtr& network_properties) {
  80. if (!NetworkTypeMatchesType(network_properties->type,
  81. NetworkType::kCellular)) {
  82. return false;
  83. }
  84. const chromeos::network_config::mojom::DeviceStateProperties*
  85. cellular_device =
  86. Shell::Get()->system_tray_model()->network_state_model()->GetDevice(
  87. NetworkType::kCellular);
  88. return cellular_device && IsInhibited(cellular_device);
  89. }
  90. bool IsCellularNetworkSimLocked(
  91. const NetworkStatePropertiesPtr& network_properties) {
  92. DCHECK(
  93. NetworkTypeMatchesType(network_properties->type, NetworkType::kCellular));
  94. return network_properties->type_state->get_cellular()->sim_locked;
  95. }
  96. bool IsNetworkConnectable(const NetworkStatePropertiesPtr& network_properties) {
  97. // The network must not already be connected to be able to be connected to.
  98. if (network_properties->connection_state !=
  99. ConnectionStateType::kNotConnected) {
  100. return false;
  101. }
  102. if (NetworkTypeMatchesType(network_properties->type,
  103. NetworkType::kCellular)) {
  104. // Cellular networks must be activated, uninhibited, and have an unlocked
  105. // SIM to be able to be connected to.
  106. const CellularStateProperties* cellular =
  107. network_properties->type_state->get_cellular().get();
  108. if (cellular->activation_state == ActivationStateType::kNotActivated &&
  109. !cellular->eid.empty()) {
  110. return false;
  111. }
  112. if (IsNetworkInhibited(network_properties) || cellular->sim_locked) {
  113. return false;
  114. }
  115. if (cellular->activation_state == ActivationStateType::kActivated) {
  116. return true;
  117. }
  118. }
  119. // The network can be connected to if the network is connectable.
  120. if (network_properties->connectable) {
  121. return true;
  122. }
  123. // Network can be connected to if the active user is the primary user and the
  124. // network is configurable.
  125. if (!IsSecondaryUser() &&
  126. NetworkTypeIsConfigurable(network_properties->type)) {
  127. return true;
  128. }
  129. return false;
  130. }
  131. bool IsNetworkDisabled(const NetworkStatePropertiesPtr& network_properties) {
  132. if (!NetworkTypeMatchesType(network_properties->type,
  133. NetworkType::kCellular)) {
  134. return false;
  135. }
  136. const CellularStateProperties* cellular =
  137. network_properties->type_state->get_cellular().get();
  138. if (!Shell::Get()->session_controller()->IsActiveUserSessionStarted() &&
  139. cellular->sim_locked) {
  140. return true;
  141. }
  142. if (cellular->activation_state == ActivationStateType::kActivating) {
  143. return true;
  144. }
  145. if (IsNetworkInhibited(network_properties)) {
  146. return true;
  147. }
  148. return network_properties->prohibited_by_policy;
  149. }
  150. bool IsWifiNetworkSecured(const NetworkStatePropertiesPtr& network_properties) {
  151. DCHECK(NetworkTypeMatchesType(network_properties->type, NetworkType::kWiFi));
  152. return network_properties->type_state->get_wifi()->security !=
  153. SecurityType::kNone;
  154. }
  155. bool IsNetworkManagedByPolicy(
  156. const NetworkStatePropertiesPtr& network_properties) {
  157. return network_properties->source == OncSource::kDevicePolicy ||
  158. network_properties->source == OncSource::kUserPolicy;
  159. }
  160. bool ShouldShowActivateCellularNetwork(
  161. const NetworkStatePropertiesPtr& network_properties) {
  162. return GetNetworkActivationState(network_properties) ==
  163. ActivationStateType::kNotActivated &&
  164. network_properties->type_state->get_cellular()->eid.empty();
  165. }
  166. bool ShouldShowContactCarrier(
  167. const NetworkStatePropertiesPtr& network_properties) {
  168. return GetNetworkActivationState(network_properties) ==
  169. ActivationStateType::kNotActivated &&
  170. !network_properties->type_state->get_cellular()->eid.empty();
  171. }
  172. gfx::ImageSkia GetNetworkImageForNetwork(
  173. const NetworkStatePropertiesPtr& network_properties) {
  174. gfx::ImageSkia network_image;
  175. const gfx::ImageSkia image = network_icon::GetImageForNonVirtualNetwork(
  176. network_properties.get(), network_icon::ICON_TYPE_LIST,
  177. /*badge_vpn=*/false);
  178. if (NetworkTypeMatchesType(network_properties->type, NetworkType::kMobile) &&
  179. network_properties->connection_state ==
  180. ConnectionStateType::kNotConnected) {
  181. // Mobile icons which are not connecting or connected should display a small
  182. // "X" icon superimposed so that it is clear that they are disconnected.
  183. const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
  184. AshColorProvider::ContentLayerType::kIconColorPrimary);
  185. network_image = gfx::ImageSkiaOperations::CreateSuperimposedImage(
  186. image, gfx::CreateVectorIcon(kNetworkMobileNotConnectedXIcon,
  187. image.height(), icon_color));
  188. } else {
  189. network_image = image;
  190. }
  191. // When the network is disabled, its appearance should be grayed out to
  192. // indicate users that these networks are unavailable. We must change the
  193. // image before we add it to the view, and then alter the label and sub-label
  194. // if they exist after it is added to the view.
  195. if (IsNetworkDisabled(network_properties)) {
  196. network_image = gfx::ImageSkiaOperations::CreateTransparentImage(
  197. network_image, kAlphaValueForInhibitedIconOpacity);
  198. }
  199. return network_image;
  200. }
  201. int GetCellularNetworkSubText(
  202. const NetworkStatePropertiesPtr& network_properties) {
  203. if (ShouldShowActivateCellularNetwork(network_properties))
  204. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_ACTIVATE;
  205. if (ShouldShowContactCarrier(network_properties))
  206. return IDS_ASH_STATUS_TRAY_NETWORK_UNAVAILABLE_SIM_NETWORK;
  207. if (!IsCellularNetworkSimLocked(network_properties))
  208. return 0;
  209. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
  210. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_UNLOCK;
  211. }
  212. return IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SIGN_IN_TO_UNLOCK;
  213. }
  214. } // namespace
  215. NetworkListNetworkItemView::NetworkListNetworkItemView(
  216. ViewClickListener* listener)
  217. : NetworkListItemView(listener) {}
  218. NetworkListNetworkItemView::~NetworkListNetworkItemView() = default;
  219. void NetworkListNetworkItemView::UpdateViewForNetwork(
  220. const NetworkStatePropertiesPtr& network_properties) {
  221. network_properties_ = mojo::Clone(network_properties);
  222. Reset();
  223. const std::u16string label = GetLabel();
  224. AddIconAndLabel(GetNetworkImageForNetwork(network_properties_), label);
  225. if (network_properties_.get()->type == NetworkType::kCellular) {
  226. SetupCellularSubtext();
  227. } else {
  228. SetupNetworkSubtext();
  229. }
  230. if (IsNetworkDisabled(network_properties)) {
  231. UpdateDisabledTextColor();
  232. }
  233. if (network_properties_->prohibited_by_policy) {
  234. SetTooltipText(
  235. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED));
  236. }
  237. // Add an additional icon to the right of the label for networks
  238. // that require it (e.g. Tether, controlled by extension).
  239. if (network_properties_->type == NetworkType::kTether) {
  240. AddPowerStatusView();
  241. } else if (IsNetworkManagedByPolicy(network_properties)) {
  242. AddPolicyView();
  243. }
  244. SetAccessibleName(GenerateAccessibilityLabel(label));
  245. GetViewAccessibility().OverrideDescription(
  246. GenerateAccessibilityDescription());
  247. }
  248. void NetworkListNetworkItemView::SetupCellularSubtext() {
  249. int cellular_subtext_message_id =
  250. GetCellularNetworkSubText(network_properties_);
  251. if (!cellular_subtext_message_id) {
  252. SetupNetworkSubtext();
  253. return;
  254. }
  255. if (text_label()) {
  256. const SkColor primary_text_color =
  257. AshColorProvider::Get()->GetContentLayerColor(
  258. AshColorProvider::ContentLayerType::kTextColorPrimary);
  259. text_label()->SetEnabledColor(primary_text_color);
  260. }
  261. SetSubText(l10n_util::GetStringUTF16(cellular_subtext_message_id));
  262. const SkColor sub_text_color = AshColorProvider::Get()->GetContentLayerColor(
  263. AshColorProvider::ContentLayerType::kTextColorWarning);
  264. sub_text_label()->SetEnabledColor(sub_text_color);
  265. }
  266. void NetworkListNetworkItemView::SetupNetworkSubtext() {
  267. if (StateIsConnected(network_properties()->connection_state)) {
  268. SetupConnectedScrollListItem(this);
  269. } else if (network_properties_.get()->connection_state ==
  270. ConnectionStateType::kConnecting) {
  271. SetupConnectingScrollListItem(this);
  272. }
  273. }
  274. void NetworkListNetworkItemView::UpdateDisabledTextColor() {
  275. if (text_label()) {
  276. SkColor primary_text_color = text_label()->GetEnabledColor();
  277. text_label()->SetEnabledColor(
  278. AshColorProvider::GetDisabledColor(primary_text_color));
  279. }
  280. if (sub_text_label()) {
  281. SkColor sub_text_color = sub_text_label()->GetEnabledColor();
  282. sub_text_label()->SetEnabledColor(
  283. AshColorProvider::GetDisabledColor(sub_text_color));
  284. }
  285. }
  286. void NetworkListNetworkItemView::AddPowerStatusView() {
  287. auto image_icon = std::make_unique<views::ImageView>();
  288. const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
  289. AshColorProvider::ContentLayerType::kIconColorPrimary);
  290. image_icon->SetPreferredSize(gfx::Size(kMenuIconSize, kMenuIconSize));
  291. image_icon->SetFlipCanvasOnPaintForRTLUI(true);
  292. PowerStatus::BatteryImageInfo icon_info;
  293. int battery_percentage =
  294. network_properties()->type_state->get_tether()->battery_percentage;
  295. icon_info.charge_percent = battery_percentage;
  296. image_icon->SetImage(PowerStatus::GetBatteryImage(
  297. icon_info, kMobileNetworkBatteryIconSize,
  298. AshColorProvider::GetSecondToneColor(icon_color), icon_color));
  299. // Show the numeric battery percentage on hover.
  300. image_icon->SetTooltipText(base::FormatPercent(battery_percentage));
  301. AddRightView(image_icon.release(), views::CreateEmptyBorder(gfx::Insets::TLBR(
  302. 0, 0, 0, kPowerStatusPaddingRight)));
  303. }
  304. void NetworkListNetworkItemView::AddPolicyView() {
  305. std::unique_ptr<views::ImageView> controlled_icon(
  306. TrayPopupUtils::CreateMainImageView());
  307. const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
  308. AshColorProvider::ContentLayerType::kIconColorPrimary);
  309. controlled_icon->SetImage(
  310. gfx::CreateVectorIcon(kSystemMenuBusinessIcon, icon_color));
  311. AddRightView(controlled_icon.release());
  312. }
  313. std::u16string NetworkListNetworkItemView::GenerateAccessibilityLabel(
  314. const std::u16string& label) {
  315. if (IsNetworkConnectable(network_properties())) {
  316. return l10n_util::GetStringFUTF16(
  317. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_CONNECT, label);
  318. }
  319. if (ShouldShowActivateCellularNetwork(network_properties())) {
  320. return l10n_util::GetStringFUTF16(
  321. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_ACTIVATE, label);
  322. }
  323. if (ShouldShowContactCarrier(network_properties())) {
  324. return l10n_util::GetStringFUTF16(
  325. IDS_ASH_STATUS_TRAY_NETWORK_A11Y_UNAVAILABLE_SIM_NETWORK, label);
  326. }
  327. return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_A11Y_LABEL_OPEN,
  328. label);
  329. }
  330. std::u16string NetworkListNetworkItemView::GenerateAccessibilityDescription() {
  331. std::u16string connection_status;
  332. if (StateIsConnected(network_properties()->connection_state)) {
  333. connection_status =
  334. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED);
  335. } else if (network_properties()->connection_state ==
  336. ConnectionStateType::kConnecting) {
  337. connection_status = l10n_util::GetStringUTF16(
  338. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTING);
  339. }
  340. int signal_strength = chromeos::network_config::GetWirelessSignalStrength(
  341. network_properties().get());
  342. switch (network_properties()->type) {
  343. case NetworkType::kEthernet:
  344. return GenerateAccessibilityDescriptionForEthernet(connection_status);
  345. case NetworkType::kWiFi:
  346. return GenerateAccessibilityDescriptionForWifi(connection_status,
  347. signal_strength);
  348. case NetworkType::kCellular:
  349. return GenerateAccessibilityDescriptionForCellular(connection_status,
  350. signal_strength);
  351. case NetworkType::kTether:
  352. return GenerateAccessibilityDescriptionForTether(connection_status,
  353. signal_strength);
  354. default:
  355. return u"";
  356. }
  357. }
  358. std::u16string
  359. NetworkListNetworkItemView::GenerateAccessibilityDescriptionForEthernet(
  360. const std::u16string& connection_status) {
  361. if (!connection_status.empty()) {
  362. if (IsNetworkManagedByPolicy(network_properties())) {
  363. return l10n_util::GetStringFUTF16(
  364. IDS_ASH_STATUS_TRAY_ETHERNET_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  365. connection_status);
  366. }
  367. return connection_status;
  368. }
  369. if (IsNetworkManagedByPolicy(network_properties())) {
  370. return l10n_util::GetStringUTF16(
  371. IDS_ASH_STATUS_TRAY_ETHERNET_A11Y_DESC_MANAGED);
  372. }
  373. return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ETHERNET);
  374. }
  375. std::u16string
  376. NetworkListNetworkItemView::GenerateAccessibilityDescriptionForWifi(
  377. const std::u16string& connection_status,
  378. int signal_strength) {
  379. const std::u16string security_label = l10n_util::GetStringUTF16(
  380. IsWifiNetworkSecured(network_properties())
  381. ? IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SECURED
  382. : IDS_ASH_STATUS_TRAY_NETWORK_STATUS_UNSECURED);
  383. if (!connection_status.empty()) {
  384. if (IsNetworkManagedByPolicy(network_properties())) {
  385. return l10n_util::GetStringFUTF16(
  386. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  387. security_label, connection_status,
  388. base::FormatPercent(signal_strength));
  389. }
  390. return l10n_util::GetStringFUTF16(
  391. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  392. security_label, connection_status,
  393. base::FormatPercent(signal_strength));
  394. }
  395. if (IsNetworkManagedByPolicy(network_properties())) {
  396. return l10n_util::GetStringFUTF16(
  397. IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC_MANAGED, security_label,
  398. base::FormatPercent(signal_strength));
  399. }
  400. return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_WIFI_NETWORK_A11Y_DESC,
  401. security_label,
  402. base::FormatPercent(signal_strength));
  403. }
  404. std::u16string
  405. NetworkListNetworkItemView::GenerateAccessibilityDescriptionForCellular(
  406. const std::u16string& connection_status,
  407. int signal_strength) {
  408. if (ShouldShowActivateCellularNetwork(network_properties())) {
  409. return l10n_util::GetStringUTF16(
  410. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_ACTIVATE);
  411. }
  412. if (ShouldShowContactCarrier(network_properties())) {
  413. return l10n_util::GetStringUTF16(
  414. IDS_ASH_STATUS_TRAY_NETWORK_UNAVAILABLE_SIM_NETWORK);
  415. }
  416. if (IsCellularNetworkSimLocked(network_properties())) {
  417. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted()) {
  418. return l10n_util::GetStringUTF16(
  419. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CLICK_TO_UNLOCK);
  420. }
  421. return l10n_util::GetStringUTF16(
  422. IDS_ASH_STATUS_TRAY_NETWORK_STATUS_SIGN_IN_TO_UNLOCK);
  423. }
  424. if (!connection_status.empty()) {
  425. if (IsNetworkManagedByPolicy(network_properties())) {
  426. return l10n_util::GetStringFUTF16(
  427. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_MANAGED_WITH_CONNECTION_STATUS,
  428. connection_status, base::FormatPercent(signal_strength));
  429. }
  430. return l10n_util::GetStringFUTF16(
  431. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  432. connection_status, base::FormatPercent(signal_strength));
  433. }
  434. if (IsNetworkManagedByPolicy(network_properties())) {
  435. return l10n_util::GetStringFUTF16(
  436. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC_MANAGED,
  437. base::FormatPercent(signal_strength));
  438. }
  439. return l10n_util::GetStringFUTF16(
  440. IDS_ASH_STATUS_TRAY_CELLULAR_NETWORK_A11Y_DESC,
  441. base::FormatPercent(signal_strength));
  442. }
  443. std::u16string
  444. NetworkListNetworkItemView::GenerateAccessibilityDescriptionForTether(
  445. const std::u16string& connection_status,
  446. int signal_strength) {
  447. int battery_percentage =
  448. network_properties()->type_state->get_tether()->battery_percentage;
  449. if (!connection_status.empty()) {
  450. return l10n_util::GetStringFUTF16(
  451. IDS_ASH_STATUS_TRAY_TETHER_NETWORK_A11Y_DESC_WITH_CONNECTION_STATUS,
  452. connection_status, base::FormatPercent(signal_strength),
  453. base::FormatPercent(battery_percentage));
  454. }
  455. return l10n_util::GetStringFUTF16(
  456. IDS_ASH_STATUS_TRAY_TETHER_NETWORK_A11Y_DESC,
  457. base::FormatPercent(signal_strength),
  458. base::FormatPercent(battery_percentage));
  459. }
  460. BEGIN_METADATA(NetworkListNetworkItemView, NetworkListItemView)
  461. END_METADATA
  462. } // namespace ash