OLD | NEW |
| (Empty) |
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 | |
5 #include "ui/chromeos/network/network_list_md.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include "base/memory/ptr_util.h" | |
10 #include "chromeos/dbus/dbus_thread_manager.h" | |
11 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" | |
12 #include "chromeos/dbus/power_manager_client.h" | |
13 #include "chromeos/login/login_state.h" | |
14 #include "chromeos/network/managed_network_configuration_handler.h" | |
15 #include "chromeos/network/network_state.h" | |
16 #include "chromeos/network/network_state_handler.h" | |
17 #include "chromeos/network/network_state_handler_observer.h" | |
18 #include "components/device_event_log/device_event_log.h" | |
19 #include "grit/ui_chromeos_strings.h" | |
20 #include "ui/base/l10n/l10n_util.h" | |
21 #include "ui/base/resource/resource_bundle.h" | |
22 #include "ui/chromeos/network/network_icon.h" | |
23 #include "ui/chromeos/network/network_icon_animation.h" | |
24 #include "ui/chromeos/network/network_info.h" | |
25 #include "ui/chromeos/network/network_list_delegate.h" | |
26 #include "ui/gfx/color_palette.h" | |
27 #include "ui/gfx/font.h" | |
28 #include "ui/gfx/paint_vector_icon.h" | |
29 #include "ui/gfx/vector_icons_public.h" | |
30 #include "ui/views/border.h" | |
31 #include "ui/views/controls/button/image_button.h" | |
32 #include "ui/views/controls/button/toggle_button.h" | |
33 #include "ui/views/controls/label.h" | |
34 #include "ui/views/layout/box_layout.h" | |
35 #include "ui/views/layout/fill_layout.h" | |
36 #include "ui/views/painter.h" | |
37 #include "ui/views/view.h" | |
38 | |
39 using chromeos::LoginState; | |
40 using chromeos::NetworkHandler; | |
41 using chromeos::NetworkStateHandler; | |
42 using chromeos::ManagedNetworkConfigurationHandler; | |
43 using chromeos::NetworkTypePattern; | |
44 | |
45 namespace ui { | |
46 | |
47 namespace { | |
48 | |
49 const int kWiFiButtonSize = 48; | |
50 const int kWifiRowVerticalInset = 4; | |
51 const int kWifiRowLeftInset = 18; | |
52 const int kWifiRowRightInset = 14; | |
53 const int kWifiRowSeparatorThickness = 1; | |
54 const int kWifiRowChildSpacing = 14; | |
55 const int kFocusBorderInset = 1; | |
56 | |
57 const SkColor kWifiRowSeparatorColor = SkColorSetA(SK_ColorBLACK, 0x1F); | |
58 | |
59 bool IsProhibitedByPolicy(const chromeos::NetworkState* network) { | |
60 if (!NetworkTypePattern::WiFi().MatchesType(network->type())) | |
61 return false; | |
62 if (!LoginState::IsInitialized() || !LoginState::Get()->IsUserLoggedIn()) | |
63 return false; | |
64 ManagedNetworkConfigurationHandler* managed_configuration_handler = | |
65 NetworkHandler::Get()->managed_network_configuration_handler(); | |
66 const base::DictionaryValue* global_network_config = | |
67 managed_configuration_handler->GetGlobalConfigFromPolicy( | |
68 std::string() /* no username hash, device policy */); | |
69 bool policy_prohibites_unmanaged = false; | |
70 if (global_network_config) { | |
71 global_network_config->GetBooleanWithoutPathExpansion( | |
72 ::onc::global_network_config::kAllowOnlyPolicyNetworksToConnect, | |
73 &policy_prohibites_unmanaged); | |
74 } | |
75 if (!policy_prohibites_unmanaged) | |
76 return false; | |
77 return !managed_configuration_handler->FindPolicyByGuidAndProfile( | |
78 network->guid(), network->profile_path()); | |
79 } | |
80 | |
81 } // namespace | |
82 | |
83 class NetworkListViewMd::WifiHeaderRowView : public views::View { | |
84 public: | |
85 WifiHeaderRowView(views::ButtonListener* listener, bool enabled) | |
86 : views::View(), | |
87 listener_(listener), | |
88 label_(nullptr), | |
89 toggle_(nullptr), | |
90 join_(nullptr) { | |
91 Init(); | |
92 SetWifiEnabled(enabled); | |
93 } | |
94 | |
95 ~WifiHeaderRowView() override {} | |
96 | |
97 void SetWifiEnabled(bool enabled) { | |
98 join_->SetVisible(enabled); | |
99 toggle_->SetIsOn(enabled, true); | |
100 } | |
101 | |
102 const views::Button* toggle() const { return toggle_; } | |
103 const views::Button* join() const { return join_; } | |
104 bool is_toggled() const { return toggle_->is_on(); } | |
105 | |
106 protected: | |
107 // views::View: | |
108 gfx::Size GetPreferredSize() const override { | |
109 gfx::Size size = views::View::GetPreferredSize(); | |
110 size.set_height(kWiFiButtonSize + kWifiRowVerticalInset * 2); | |
111 return size; | |
112 } | |
113 | |
114 int GetHeightForWidth(int w) const override { | |
115 // Make row height fixed avoiding layout manager adjustments. | |
116 return GetPreferredSize().height(); | |
117 } | |
118 | |
119 private: | |
120 void Init() { | |
121 // TODO(tdanderson): Need to unify this with the generic menu row class. | |
122 SetBorder(views::Border::CreateSolidSidedBorder( | |
123 kWifiRowSeparatorThickness, 0, 0, 0, kWifiRowSeparatorColor)); | |
124 views::View* container = new views::View; | |
125 container->SetBorder(views::Border::CreateEmptyBorder( | |
126 0, kWifiRowLeftInset, 0, kWifiRowRightInset)); | |
127 views::FillLayout* layout = new views::FillLayout; | |
128 SetLayoutManager(layout); | |
129 AddChildView(container); | |
130 | |
131 views::BoxLayout* container_layout = new views::BoxLayout( | |
132 views::BoxLayout::kHorizontal, 0, 0, kWifiRowChildSpacing); | |
133 container_layout->set_cross_axis_alignment( | |
134 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); | |
135 container->SetLayoutManager(container_layout); | |
136 ui::NativeTheme* theme = GetNativeTheme(); | |
137 const SkColor prominent_color = | |
138 theme->GetSystemColor(ui::NativeTheme::kColorId_ProminentButtonColor); | |
139 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
140 label_ = new views::Label( | |
141 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_WIFI), | |
142 rb.GetFontList(ui::ResourceBundle::MediumFont)); | |
143 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
144 label_->SetEnabledColor(prominent_color); | |
145 container->AddChildView(label_); | |
146 container_layout->SetFlexForView(label_, 1); | |
147 | |
148 // TODO(varkha): Make this a SystemMenuButton. | |
149 join_ = new views::ImageButton(listener_); | |
150 join_image_ = network_icon::GetImageForNewWifiNetwork( | |
151 SkColorSetA(prominent_color, 0xFF / 2), prominent_color); | |
152 join_->SetImage(views::CustomButton::STATE_NORMAL, &join_image_); | |
153 | |
154 const SkColor focus_color = | |
155 theme->GetSystemColor(ui::NativeTheme::kColorId_FocusedBorderColor); | |
156 const int horizontal_padding = (kWiFiButtonSize - join_image_.width()) / 2; | |
157 const int vertical_padding = (kWiFiButtonSize - join_image_.height()) / 2; | |
158 join_->SetBorder(views::Border::CreateEmptyBorder( | |
159 gfx::Insets(vertical_padding, horizontal_padding))); | |
160 join_->SetFocusForPlatform(); | |
161 join_->SetFocusPainter(views::Painter::CreateSolidFocusPainter( | |
162 focus_color, gfx::Insets(kFocusBorderInset))); | |
163 container->AddChildView(join_); | |
164 | |
165 toggle_ = new views::ToggleButton(listener_); | |
166 // TODO(varkha): Implement focus painter. | |
167 toggle_->SetFocusForPlatform(); | |
168 container->AddChildView(toggle_); | |
169 } | |
170 | |
171 // ButtonListener to notify when |toggle_| or |join_| are clicked. | |
172 views::ButtonListener* listener_; | |
173 | |
174 // Text label for the row. | |
175 views::Label* label_; | |
176 | |
177 // ToggleButton to toggle Wi-Fi on or off. | |
178 views::ToggleButton* toggle_; | |
179 | |
180 // A button to invoke "Join Wi-Fi network" dialog. | |
181 views::ImageButton* join_; | |
182 | |
183 // Image used for the |join_| button. | |
184 gfx::ImageSkia join_image_; | |
185 | |
186 DISALLOW_COPY_AND_ASSIGN(WifiHeaderRowView); | |
187 }; | |
188 | |
189 // NetworkListViewMd: | |
190 | |
191 NetworkListViewMd::NetworkListViewMd(NetworkListDelegate* delegate) | |
192 : needs_relayout_(false), | |
193 delegate_(delegate), | |
194 no_wifi_networks_view_(nullptr), | |
195 no_cellular_networks_view_(nullptr), | |
196 wifi_header_view_(nullptr) { | |
197 CHECK(delegate_); | |
198 } | |
199 | |
200 NetworkListViewMd::~NetworkListViewMd() { | |
201 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); | |
202 } | |
203 | |
204 void NetworkListViewMd::Update() { | |
205 CHECK(container()); | |
206 NetworkStateHandler::NetworkStateList network_list; | |
207 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); | |
208 handler->GetVisibleNetworkList(&network_list); | |
209 UpdateNetworks(network_list); | |
210 OrderNetworks(); | |
211 UpdateNetworkIcons(); | |
212 UpdateNetworkListInternal(); | |
213 } | |
214 | |
215 bool NetworkListViewMd::IsNetworkEntry(views::View* view, | |
216 std::string* service_path) const { | |
217 std::map<views::View*, std::string>::const_iterator found = | |
218 network_map_.find(view); | |
219 if (found == network_map_.end()) | |
220 return false; | |
221 *service_path = found->second; | |
222 return true; | |
223 } | |
224 | |
225 void NetworkListViewMd::UpdateNetworks( | |
226 const NetworkStateHandler::NetworkStateList& networks) { | |
227 SCOPED_NET_LOG_IF_SLOW(); | |
228 network_list_.clear(); | |
229 const NetworkTypePattern pattern = delegate_->GetNetworkTypePattern(); | |
230 for (const auto& network : networks) { | |
231 if (pattern.MatchesType(network->type())) | |
232 network_list_.push_back(base::MakeUnique<NetworkInfo>(network->path())); | |
233 } | |
234 } | |
235 | |
236 void NetworkListViewMd::OrderNetworks() { | |
237 struct CompareNetwork { | |
238 explicit CompareNetwork(NetworkStateHandler* handler) : handler_(handler) {} | |
239 | |
240 // Returns true if |network1| is less than (i.e. is ordered before) | |
241 // |network2|. | |
242 bool operator()(const std::unique_ptr<NetworkInfo>& network1, | |
243 const std::unique_ptr<NetworkInfo>& network2) { | |
244 const int order1 = | |
245 GetOrder(handler_->GetNetworkState(network1->service_path)); | |
246 const int order2 = | |
247 GetOrder(handler_->GetNetworkState(network2->service_path)); | |
248 if (order1 != order2) | |
249 return order1 < order2; | |
250 if (network1->highlight != network2->highlight) | |
251 return network1->highlight; | |
252 return network1->service_path.compare(network2->service_path) < 0; | |
253 } | |
254 | |
255 private: | |
256 static int GetOrder(const chromeos::NetworkState* network) { | |
257 if (!network) | |
258 return 999; | |
259 if (network->Matches(NetworkTypePattern::Ethernet())) | |
260 return 0; | |
261 if (network->Matches(NetworkTypePattern::Cellular())) | |
262 return 1; | |
263 if (network->Matches(NetworkTypePattern::Mobile())) | |
264 return 2; | |
265 if (network->Matches(NetworkTypePattern::WiFi())) | |
266 return 3; | |
267 return 4; | |
268 } | |
269 | |
270 NetworkStateHandler* handler_; | |
271 }; | |
272 std::sort(network_list_.begin(), network_list_.end(), | |
273 CompareNetwork(NetworkHandler::Get()->network_state_handler())); | |
274 } | |
275 | |
276 void NetworkListViewMd::UpdateNetworkIcons() { | |
277 SCOPED_NET_LOG_IF_SLOW(); | |
278 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); | |
279 | |
280 // First, update state for all networks. | |
281 bool animating = false; | |
282 | |
283 for (auto& info : network_list_) { | |
284 const chromeos::NetworkState* network = | |
285 handler->GetNetworkState(info->service_path); | |
286 if (!network) | |
287 continue; | |
288 bool prohibited_by_policy = IsProhibitedByPolicy(network); | |
289 info->label = | |
290 network_icon::GetLabelForNetwork(network, network_icon::ICON_TYPE_LIST); | |
291 info->image = | |
292 network_icon::GetImageForNetwork(network, network_icon::ICON_TYPE_LIST); | |
293 info->disable = | |
294 (network->activation_state() == shill::kActivationStateActivating) || | |
295 prohibited_by_policy; | |
296 info->highlight = | |
297 network->IsConnectedState() || network->IsConnectingState(); | |
298 info->is_wifi = network->Matches(NetworkTypePattern::WiFi()); | |
299 if (prohibited_by_policy) { | |
300 info->tooltip = | |
301 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_PROHIBITED); | |
302 } | |
303 if (!animating && network->IsConnectingState()) | |
304 animating = true; | |
305 } | |
306 if (animating) | |
307 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); | |
308 else | |
309 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); | |
310 } | |
311 | |
312 void NetworkListViewMd::UpdateNetworkListInternal() { | |
313 SCOPED_NET_LOG_IF_SLOW(); | |
314 // Get the updated list entries. | |
315 needs_relayout_ = false; | |
316 network_map_.clear(); | |
317 std::unique_ptr<std::set<std::string>> new_service_paths = | |
318 UpdateNetworkListEntries(); | |
319 | |
320 // Remove old children. | |
321 std::set<std::string> remove_service_paths; | |
322 for (const auto& iter : service_path_map_) { | |
323 if (new_service_paths->find(iter.first) == new_service_paths->end()) { | |
324 remove_service_paths.insert(iter.first); | |
325 network_map_.erase(iter.second); | |
326 delete iter.second; | |
327 needs_relayout_ = true; | |
328 } | |
329 } | |
330 | |
331 for (const auto& remove_iter : remove_service_paths) | |
332 service_path_map_.erase(remove_iter); | |
333 | |
334 if (!needs_relayout_) | |
335 return; | |
336 | |
337 views::View* selected_view = nullptr; | |
338 for (const auto& iter : service_path_map_) { | |
339 if (delegate_->IsViewHovered(iter.second)) { | |
340 selected_view = iter.second; | |
341 break; | |
342 } | |
343 } | |
344 container()->SizeToPreferredSize(); | |
345 delegate_->RelayoutScrollList(); | |
346 if (selected_view) | |
347 container()->ScrollRectToVisible(selected_view->bounds()); | |
348 } | |
349 | |
350 std::unique_ptr<std::set<std::string>> | |
351 NetworkListViewMd::UpdateNetworkListEntries() { | |
352 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); | |
353 | |
354 // First add high-priority networks (not Wi-Fi). | |
355 std::unique_ptr<std::set<std::string>> new_service_paths = | |
356 UpdateNetworkChildren(false /* not Wi-Fi */, 0); | |
357 | |
358 // Keep an index of the last inserted child. | |
359 int index = new_service_paths->size(); | |
360 | |
361 const NetworkTypePattern pattern = delegate_->GetNetworkTypePattern(); | |
362 if (pattern.MatchesPattern(NetworkTypePattern::Cellular())) { | |
363 // Cellular initializing. | |
364 int message_id = network_icon::GetCellularUninitializedMsg(); | |
365 if (!message_id && | |
366 handler->IsTechnologyEnabled(NetworkTypePattern::Mobile()) && | |
367 !handler->FirstNetworkByType(NetworkTypePattern::Mobile())) { | |
368 message_id = IDS_ASH_STATUS_TRAY_NO_CELLULAR_NETWORKS; | |
369 } | |
370 UpdateInfoLabel(message_id, index, &no_cellular_networks_view_); | |
371 if (message_id) | |
372 ++index; | |
373 } | |
374 | |
375 if (pattern.MatchesPattern(NetworkTypePattern::WiFi())) { | |
376 UpdateWifiHeaderRow( | |
377 handler->IsTechnologyEnabled(NetworkTypePattern::WiFi()), index, | |
378 &wifi_header_view_); | |
379 ++index; | |
380 | |
381 // "Wifi Enabled / Disabled". | |
382 int message_id = 0; | |
383 if (network_list_.empty()) { | |
384 message_id = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi()) | |
385 ? IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED | |
386 : IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED; | |
387 } | |
388 UpdateInfoLabel(message_id, index, &no_wifi_networks_view_); | |
389 if (message_id) | |
390 ++index; | |
391 | |
392 // Add Wi-Fi networks. | |
393 std::unique_ptr<std::set<std::string>> new_wifi_service_paths = | |
394 UpdateNetworkChildren(true /* Wi-Fi */, index); | |
395 index += new_wifi_service_paths->size(); | |
396 new_service_paths->insert(new_wifi_service_paths->begin(), | |
397 new_wifi_service_paths->end()); | |
398 } | |
399 | |
400 // No networks or other messages (fallback). | |
401 if (index == 0) { | |
402 UpdateInfoLabel(IDS_ASH_STATUS_TRAY_NO_NETWORKS, index, | |
403 &no_wifi_networks_view_); | |
404 } | |
405 | |
406 return new_service_paths; | |
407 } | |
408 | |
409 std::unique_ptr<std::set<std::string>> NetworkListViewMd::UpdateNetworkChildren( | |
410 bool is_wifi, | |
411 int index) { | |
412 std::unique_ptr<std::set<std::string>> new_service_paths( | |
413 new std::set<std::string>); | |
414 for (const auto& info : network_list_) { | |
415 if (info->is_wifi != is_wifi) | |
416 continue; | |
417 UpdateNetworkChild(index++, info.get()); | |
418 new_service_paths->insert(info->service_path); | |
419 } | |
420 return new_service_paths; | |
421 } | |
422 | |
423 void NetworkListViewMd::UpdateNetworkChild(int index, const NetworkInfo* info) { | |
424 views::View* network_view = nullptr; | |
425 ServicePathMap::const_iterator found = | |
426 service_path_map_.find(info->service_path); | |
427 if (found == service_path_map_.end()) { | |
428 network_view = delegate_->CreateViewForNetwork(*info); | |
429 } else { | |
430 network_view = found->second; | |
431 network_view->RemoveAllChildViews(true); | |
432 delegate_->UpdateViewForNetwork(network_view, *info); | |
433 network_view->Layout(); | |
434 network_view->SchedulePaint(); | |
435 } | |
436 PlaceViewAtIndex(network_view, index); | |
437 if (info->disable) | |
438 network_view->SetEnabled(false); | |
439 network_map_[network_view] = info->service_path; | |
440 service_path_map_[info->service_path] = network_view; | |
441 } | |
442 | |
443 void NetworkListViewMd::PlaceViewAtIndex(views::View* view, int index) { | |
444 if (view->parent() != container()) { | |
445 container()->AddChildViewAt(view, index); | |
446 } else { | |
447 if (container()->child_at(index) == view) | |
448 return; | |
449 container()->ReorderChildView(view, index); | |
450 } | |
451 needs_relayout_ = true; | |
452 } | |
453 | |
454 void NetworkListViewMd::UpdateInfoLabel(int message_id, | |
455 int insertion_index, | |
456 views::Label** label_ptr) { | |
457 views::Label* label = *label_ptr; | |
458 if (!message_id) { | |
459 if (label) { | |
460 needs_relayout_ = true; | |
461 delete label; | |
462 *label_ptr = nullptr; | |
463 } | |
464 return; | |
465 } | |
466 base::string16 text = | |
467 ui::ResourceBundle::GetSharedInstance().GetLocalizedString(message_id); | |
468 if (!label) | |
469 label = delegate_->CreateInfoLabel(); | |
470 label->SetText(text); | |
471 PlaceViewAtIndex(label, insertion_index); | |
472 *label_ptr = label; | |
473 } | |
474 | |
475 void NetworkListViewMd::UpdateWifiHeaderRow(bool enabled, | |
476 int child_index, | |
477 WifiHeaderRowView** view) { | |
478 if (!*view) | |
479 *view = new WifiHeaderRowView(this, enabled); | |
480 (*view)->SetWifiEnabled(enabled); | |
481 PlaceViewAtIndex(*view, child_index); | |
482 } | |
483 | |
484 void NetworkListViewMd::NetworkIconChanged() { | |
485 Update(); | |
486 } | |
487 | |
488 void NetworkListViewMd::ButtonPressed(views::Button* sender, | |
489 const ui::Event& event) { | |
490 if (sender == wifi_header_view_->toggle()) { | |
491 NetworkStateHandler* handler = | |
492 NetworkHandler::Get()->network_state_handler(); | |
493 handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(), | |
494 wifi_header_view_->is_toggled(), | |
495 chromeos::network_handler::ErrorCallback()); | |
496 } else if (sender == wifi_header_view_->join()) { | |
497 delegate_->OnOtherWifiClicked(); | |
498 } else { | |
499 NOTREACHED(); | |
500 } | |
501 } | |
502 | |
503 } // namespace ui | |
OLD | NEW |