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/network/network_state_list_detailed_view.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/metrics/user_metrics_recorder.h"
9 #include "ash/root_window_controller.h"
10 #include "ash/shell.h"
11 #include "ash/shell_delegate.h"
12 #include "ash/shell_window_ids.h"
13 #include "ash/system/chromeos/network/network_connect.h"
14 #include "ash/system/chromeos/network/network_icon.h"
15 #include "ash/system/chromeos/network/network_icon_animation.h"
16 #include "ash/system/chromeos/network/tray_network_state_observer.h"
17 #include "ash/system/tray/fixed_sized_scroll_view.h"
18 #include "ash/system/tray/hover_highlight_view.h"
19 #include "ash/system/tray/system_tray.h"
20 #include "ash/system/tray/system_tray_delegate.h"
21 #include "ash/system/tray/tray_constants.h"
22 #include "ash/system/tray/tray_details_view.h"
23 #include "ash/system/tray/tray_popup_header_button.h"
24 #include "ash/system/tray/tray_popup_label_button.h"
25 #include "base/command_line.h"
26 #include "base/message_loop/message_loop.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/time/time.h"
29 #include "chromeos/chromeos_switches.h"
30 #include "chromeos/network/device_state.h"
31 #include "chromeos/network/network_configuration_handler.h"
32 #include "chromeos/network/network_state.h"
33 #include "chromeos/network/network_state_handler.h"
34 #include "grit/ash_resources.h"
35 #include "grit/ash_strings.h"
36 #include "third_party/cros_system_api/dbus/service_constants.h"
37 #include "ui/aura/window.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/views/bubble/bubble_delegate.h"
41 #include "ui/views/controls/label.h"
42 #include "ui/views/layout/box_layout.h"
43 #include "ui/views/layout/fill_layout.h"
44 #include "ui/views/widget/widget.h"
45
46 using chromeos::DeviceState;
47 using chromeos::NetworkHandler;
48 using chromeos::NetworkState;
49 using chromeos::NetworkStateHandler;
50 using chromeos::NetworkTypePattern;
51
52 namespace ash {
53 namespace tray {
54 namespace {
55
56 // Delay between scan requests.
57 const int kRequestScanDelaySeconds = 10;
58
59 // Create a label with the font size and color used in the network info bubble.
CreateInfoBubbleLabel(const base::string16 & text)60 views::Label* CreateInfoBubbleLabel(const base::string16& text) {
61 views::Label* label = new views::Label(text);
62 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
63 label->SetFontList(rb.GetFontList(ui::ResourceBundle::SmallFont));
64 label->SetEnabledColor(SkColorSetARGB(127, 0, 0, 0));
65 return label;
66 }
67
68 // Create a label formatted for info items in the menu
CreateMenuInfoLabel(const base::string16 & text)69 views::Label* CreateMenuInfoLabel(const base::string16& text) {
70 views::Label* label = new views::Label(text);
71 label->SetBorder(
72 views::Border::CreateEmptyBorder(ash::kTrayPopupPaddingBetweenItems,
73 ash::kTrayPopupPaddingHorizontal,
74 ash::kTrayPopupPaddingBetweenItems,
75 0));
76 label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
77 label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0));
78 return label;
79 }
80
81 // Create a row of labels for the network info bubble.
CreateInfoBubbleLine(const base::string16 & text_label,const std::string & text_string)82 views::View* CreateInfoBubbleLine(const base::string16& text_label,
83 const std::string& text_string) {
84 views::View* view = new views::View;
85 view->SetLayoutManager(
86 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 1));
87 view->AddChildView(CreateInfoBubbleLabel(text_label));
88 view->AddChildView(CreateInfoBubbleLabel(base::UTF8ToUTF16(": ")));
89 view->AddChildView(CreateInfoBubbleLabel(base::UTF8ToUTF16(text_string)));
90 return view;
91 }
92
93 } // namespace
94
95
96 //------------------------------------------------------------------------------
97
98 struct NetworkInfo {
NetworkInfoash::tray::NetworkInfo99 NetworkInfo(const std::string& path)
100 : service_path(path),
101 disable(false),
102 highlight(false) {
103 }
104
105 std::string service_path;
106 base::string16 label;
107 gfx::ImageSkia image;
108 bool disable;
109 bool highlight;
110 };
111
112 //------------------------------------------------------------------------------
113
114 // A bubble which displays network info.
115 class NetworkStateListDetailedView::InfoBubble
116 : public views::BubbleDelegateView {
117 public:
InfoBubble(views::View * anchor,views::View * content,NetworkStateListDetailedView * detailed_view)118 InfoBubble(views::View* anchor,
119 views::View* content,
120 NetworkStateListDetailedView* detailed_view)
121 : views::BubbleDelegateView(anchor, views::BubbleBorder::TOP_RIGHT),
122 detailed_view_(detailed_view) {
123 set_use_focusless(true);
124 set_parent_window(ash::Shell::GetContainer(
125 anchor->GetWidget()->GetNativeWindow()->GetRootWindow(),
126 ash::kShellWindowId_SettingBubbleContainer));
127 SetLayoutManager(new views::FillLayout());
128 AddChildView(content);
129 }
130
~InfoBubble()131 virtual ~InfoBubble() {
132 detailed_view_->OnInfoBubbleDestroyed();
133 }
134
CanActivate() const135 virtual bool CanActivate() const OVERRIDE { return false; }
136
137 private:
138 // Not owned.
139 NetworkStateListDetailedView* detailed_view_;
140
141 DISALLOW_COPY_AND_ASSIGN(InfoBubble);
142 };
143
144 //------------------------------------------------------------------------------
145 // NetworkStateListDetailedView
146
NetworkStateListDetailedView(SystemTrayItem * owner,ListType list_type,user::LoginStatus login)147 NetworkStateListDetailedView::NetworkStateListDetailedView(
148 SystemTrayItem* owner,
149 ListType list_type,
150 user::LoginStatus login)
151 : NetworkDetailedView(owner),
152 list_type_(list_type),
153 login_(login),
154 info_icon_(NULL),
155 button_wifi_(NULL),
156 button_mobile_(NULL),
157 other_wifi_(NULL),
158 turn_on_wifi_(NULL),
159 other_mobile_(NULL),
160 other_vpn_(NULL),
161 settings_(NULL),
162 proxy_settings_(NULL),
163 scanning_view_(NULL),
164 no_wifi_networks_view_(NULL),
165 no_cellular_networks_view_(NULL),
166 info_bubble_(NULL) {
167 }
168
~NetworkStateListDetailedView()169 NetworkStateListDetailedView::~NetworkStateListDetailedView() {
170 if (info_bubble_)
171 info_bubble_->GetWidget()->CloseNow();
172 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
173 }
174
ManagerChanged()175 void NetworkStateListDetailedView::ManagerChanged() {
176 UpdateNetworkList();
177 UpdateHeaderButtons();
178 UpdateNetworkExtra();
179 Layout();
180 }
181
NetworkListChanged()182 void NetworkStateListDetailedView::NetworkListChanged() {
183 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
184 NetworkStateHandler::NetworkStateList network_list;
185 handler->GetVisibleNetworkList(&network_list);
186 UpdateNetworks(network_list);
187 UpdateNetworkList();
188 UpdateHeaderButtons();
189 UpdateNetworkExtra();
190 Layout();
191 }
192
NetworkServiceChanged(const NetworkState * network)193 void NetworkStateListDetailedView::NetworkServiceChanged(
194 const NetworkState* network) {
195 UpdateNetworkList();
196 Layout();
197 }
198
NetworkIconChanged()199 void NetworkStateListDetailedView::NetworkIconChanged() {
200 UpdateNetworkList();
201 Layout();
202 }
203
204 // Overridden from NetworkDetailedView:
205
Init()206 void NetworkStateListDetailedView::Init() {
207 Reset();
208 network_map_.clear();
209 service_path_map_.clear();
210 info_icon_ = NULL;
211 button_wifi_ = NULL;
212 button_mobile_ = NULL;
213 other_wifi_ = NULL;
214 turn_on_wifi_ = NULL;
215 other_mobile_ = NULL;
216 other_vpn_ = NULL;
217 settings_ = NULL;
218 proxy_settings_ = NULL;
219 scanning_view_ = NULL;
220 no_wifi_networks_view_ = NULL;
221 no_cellular_networks_view_ = NULL;
222
223 CreateScrollableList();
224 CreateNetworkExtra();
225 CreateHeaderEntry();
226 CreateHeaderButtons();
227
228 NetworkListChanged();
229
230 CallRequestScan();
231 }
232
233 NetworkDetailedView::DetailedViewType
GetViewType() const234 NetworkStateListDetailedView::GetViewType() const {
235 return STATE_LIST_VIEW;
236 }
237
238 // Views overrides
239
ButtonPressed(views::Button * sender,const ui::Event & event)240 void NetworkStateListDetailedView::ButtonPressed(views::Button* sender,
241 const ui::Event& event) {
242 if (sender == info_icon_) {
243 ToggleInfoBubble();
244 return;
245 }
246
247 // If the info bubble was visible, close it when some other item is clicked.
248 ResetInfoBubble();
249
250 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
251 ash::SystemTrayDelegate* delegate =
252 ash::Shell::GetInstance()->system_tray_delegate();
253 if (sender == button_wifi_) {
254 bool enabled = handler->IsTechnologyEnabled(
255 NetworkTypePattern::WiFi());
256 handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(),
257 !enabled,
258 chromeos::network_handler::ErrorCallback());
259 } else if (sender == turn_on_wifi_) {
260 handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(),
261 true,
262 chromeos::network_handler::ErrorCallback());
263 } else if (sender == button_mobile_) {
264 ToggleMobile();
265 } else if (sender == settings_) {
266 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
267 list_type_ == LIST_TYPE_VPN ?
268 ash::UMA_STATUS_AREA_VPN_SETTINGS_CLICKED :
269 ash::UMA_STATUS_AREA_NETWORK_SETTINGS_CLICKED);
270 delegate->ShowNetworkSettings("");
271 } else if (sender == proxy_settings_) {
272 delegate->ChangeProxySettings();
273 } else if (sender == other_mobile_) {
274 delegate->ShowOtherNetworkDialog(shill::kTypeCellular);
275 } else if (sender == other_wifi_) {
276 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
277 ash::UMA_STATUS_AREA_NETWORK_JOIN_OTHER_CLICKED);
278 delegate->ShowOtherNetworkDialog(shill::kTypeWifi);
279 } else if (sender == other_vpn_) {
280 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
281 ash::UMA_STATUS_AREA_VPN_JOIN_OTHER_CLICKED);
282 delegate->ShowOtherNetworkDialog(shill::kTypeVPN);
283 } else {
284 NOTREACHED();
285 }
286 }
287
OnViewClicked(views::View * sender)288 void NetworkStateListDetailedView::OnViewClicked(views::View* sender) {
289 // If the info bubble was visible, close it when some other item is clicked.
290 ResetInfoBubble();
291
292 if (sender == footer()->content()) {
293 TransitionToDefaultView();
294 return;
295 }
296
297 if (login_ == user::LOGGED_IN_LOCKED)
298 return;
299
300 std::map<views::View*, std::string>::iterator found =
301 network_map_.find(sender);
302 if (found == network_map_.end())
303 return;
304
305 const std::string& service_path = found->second;
306 const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
307 GetNetworkState(service_path);
308 if (!network || network->IsConnectedState() || network->IsConnectingState()) {
309 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
310 list_type_ == LIST_TYPE_VPN ?
311 ash::UMA_STATUS_AREA_SHOW_NETWORK_CONNECTION_DETAILS :
312 ash::UMA_STATUS_AREA_SHOW_VPN_CONNECTION_DETAILS);
313 Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings(
314 service_path);
315 } else {
316 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
317 list_type_ == LIST_TYPE_VPN ?
318 ash::UMA_STATUS_AREA_CONNECT_TO_VPN :
319 ash::UMA_STATUS_AREA_CONNECT_TO_CONFIGURED_NETWORK);
320 ash::network_connect::ConnectToNetwork(service_path, NULL);
321 }
322 }
323
324 // Create UI components.
325
CreateHeaderEntry()326 void NetworkStateListDetailedView::CreateHeaderEntry() {
327 CreateSpecialRow(IDS_ASH_STATUS_TRAY_NETWORK, this);
328 }
329
CreateHeaderButtons()330 void NetworkStateListDetailedView::CreateHeaderButtons() {
331 if (list_type_ != LIST_TYPE_VPN) {
332 button_wifi_ = new TrayPopupHeaderButton(
333 this,
334 IDR_AURA_UBER_TRAY_WIFI_ENABLED,
335 IDR_AURA_UBER_TRAY_WIFI_DISABLED,
336 IDR_AURA_UBER_TRAY_WIFI_ENABLED_HOVER,
337 IDR_AURA_UBER_TRAY_WIFI_DISABLED_HOVER,
338 IDS_ASH_STATUS_TRAY_WIFI);
339 button_wifi_->SetTooltipText(
340 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_WIFI));
341 button_wifi_->SetToggledTooltipText(
342 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_WIFI));
343 footer()->AddButton(button_wifi_);
344
345 button_mobile_ = new TrayPopupHeaderButton(
346 this,
347 IDR_AURA_UBER_TRAY_CELLULAR_ENABLED,
348 IDR_AURA_UBER_TRAY_CELLULAR_DISABLED,
349 IDR_AURA_UBER_TRAY_CELLULAR_ENABLED_HOVER,
350 IDR_AURA_UBER_TRAY_CELLULAR_DISABLED_HOVER,
351 IDS_ASH_STATUS_TRAY_CELLULAR);
352 button_mobile_->SetTooltipText(
353 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_MOBILE));
354 button_mobile_->SetToggledTooltipText(
355 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_MOBILE));
356 footer()->AddButton(button_mobile_);
357 }
358
359 info_icon_ = new TrayPopupHeaderButton(
360 this,
361 IDR_AURA_UBER_TRAY_NETWORK_INFO,
362 IDR_AURA_UBER_TRAY_NETWORK_INFO,
363 IDR_AURA_UBER_TRAY_NETWORK_INFO_HOVER,
364 IDR_AURA_UBER_TRAY_NETWORK_INFO_HOVER,
365 IDS_ASH_STATUS_TRAY_NETWORK_INFO);
366 info_icon_->SetTooltipText(
367 l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_INFO));
368 footer()->AddButton(info_icon_);
369 }
370
CreateNetworkExtra()371 void NetworkStateListDetailedView::CreateNetworkExtra() {
372 if (login_ == user::LOGGED_IN_LOCKED)
373 return;
374
375 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
376
377 views::View* bottom_row = new views::View();
378 views::BoxLayout* layout = new views::BoxLayout(
379 views::BoxLayout::kHorizontal,
380 kTrayMenuBottomRowPadding,
381 kTrayMenuBottomRowPadding,
382 kTrayMenuBottomRowPaddingBetweenItems);
383 layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_FILL);
384 bottom_row->SetLayoutManager(layout);
385
386 if (list_type_ != LIST_TYPE_VPN) {
387 other_wifi_ = new TrayPopupLabelButton(
388 this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_WIFI));
389 bottom_row->AddChildView(other_wifi_);
390
391 turn_on_wifi_ = new TrayPopupLabelButton(
392 this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_TURN_ON_WIFI));
393 bottom_row->AddChildView(turn_on_wifi_);
394
395 other_mobile_ = new TrayPopupLabelButton(
396 this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_MOBILE));
397 bottom_row->AddChildView(other_mobile_);
398 } else {
399 other_vpn_ = new TrayPopupLabelButton(
400 this,
401 ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
402 IDS_ASH_STATUS_TRAY_OTHER_VPN));
403 bottom_row->AddChildView(other_vpn_);
404 }
405
406 CreateSettingsEntry();
407
408 // Both settings_ and proxy_settings_ can be NULL. This happens when
409 // we're logged in but showing settings page is not enabled.
410 // Example: supervised user creation flow where user session is active
411 // but all action happens on the login window.
412 // Allowing opening proxy settigns dialog will break assumption in
413 // SystemTrayDelegateChromeOS::ChangeProxySettings(), see CHECK.
414 if (settings_ || proxy_settings_)
415 bottom_row->AddChildView(settings_ ? settings_ : proxy_settings_);
416
417 AddChildView(bottom_row);
418 }
419
420 // Update UI components.
421
UpdateHeaderButtons()422 void NetworkStateListDetailedView::UpdateHeaderButtons() {
423 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
424 if (button_wifi_)
425 UpdateTechnologyButton(button_wifi_, NetworkTypePattern::WiFi());
426 if (button_mobile_) {
427 UpdateTechnologyButton(button_mobile_, NetworkTypePattern::Mobile());
428 }
429 if (proxy_settings_)
430 proxy_settings_->SetEnabled(handler->DefaultNetwork() != NULL);
431
432 static_cast<views::View*>(footer())->Layout();
433 }
434
UpdateTechnologyButton(TrayPopupHeaderButton * button,const NetworkTypePattern & technology)435 void NetworkStateListDetailedView::UpdateTechnologyButton(
436 TrayPopupHeaderButton* button,
437 const NetworkTypePattern& technology) {
438 NetworkStateHandler::TechnologyState state =
439 NetworkHandler::Get()->network_state_handler()->GetTechnologyState(
440 technology);
441 if (state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
442 button->SetVisible(false);
443 return;
444 }
445 button->SetVisible(true);
446 if (state == NetworkStateHandler::TECHNOLOGY_AVAILABLE) {
447 button->SetEnabled(true);
448 button->SetToggled(true);
449 } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLED) {
450 button->SetEnabled(true);
451 button->SetToggled(false);
452 } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLING) {
453 button->SetEnabled(false);
454 button->SetToggled(false);
455 } else { // Initializing
456 button->SetEnabled(false);
457 button->SetToggled(true);
458 }
459 }
460
UpdateNetworks(const NetworkStateHandler::NetworkStateList & networks)461 void NetworkStateListDetailedView::UpdateNetworks(
462 const NetworkStateHandler::NetworkStateList& networks) {
463 network_list_.clear();
464 for (NetworkStateHandler::NetworkStateList::const_iterator iter =
465 networks.begin(); iter != networks.end(); ++iter) {
466 const NetworkState* network = *iter;
467 if ((list_type_ == LIST_TYPE_NETWORK &&
468 network->type() != shill::kTypeVPN) ||
469 (list_type_ == LIST_TYPE_VPN &&
470 network->type() == shill::kTypeVPN)) {
471 NetworkInfo* info = new NetworkInfo(network->path());
472 network_list_.push_back(info);
473 }
474 }
475 }
476
UpdateNetworkList()477 void NetworkStateListDetailedView::UpdateNetworkList() {
478 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
479
480 // First, update state for all networks
481 bool animating = false;
482 for (size_t i = 0; i < network_list_.size(); ++i) {
483 NetworkInfo* info = network_list_[i];
484 const NetworkState* network =
485 handler->GetNetworkState(info->service_path);
486 if (!network)
487 continue;
488 info->image = network_icon::GetImageForNetwork(
489 network, network_icon::ICON_TYPE_LIST);
490 info->label = network_icon::GetLabelForNetwork(
491 network, network_icon::ICON_TYPE_LIST);
492 info->highlight =
493 network->IsConnectedState() || network->IsConnectingState();
494 info->disable =
495 network->activation_state() == shill::kActivationStateActivating;
496 if (!animating && network->IsConnectingState())
497 animating = true;
498 }
499 if (animating)
500 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this);
501 else
502 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
503
504 // Get the updated list entries
505 network_map_.clear();
506 std::set<std::string> new_service_paths;
507 bool needs_relayout = UpdateNetworkListEntries(&new_service_paths);
508
509 // Remove old children
510 std::set<std::string> remove_service_paths;
511 for (ServicePathMap::const_iterator it = service_path_map_.begin();
512 it != service_path_map_.end(); ++it) {
513 if (new_service_paths.find(it->first) == new_service_paths.end()) {
514 remove_service_paths.insert(it->first);
515 network_map_.erase(it->second);
516 scroll_content()->RemoveChildView(it->second);
517 needs_relayout = true;
518 }
519 }
520
521 for (std::set<std::string>::const_iterator remove_it =
522 remove_service_paths.begin();
523 remove_it != remove_service_paths.end(); ++remove_it) {
524 service_path_map_.erase(*remove_it);
525 }
526
527 if (needs_relayout) {
528 views::View* selected_view = NULL;
529 for (ServicePathMap::const_iterator iter = service_path_map_.begin();
530 iter != service_path_map_.end(); ++iter) {
531 if (iter->second->hover()) {
532 selected_view = iter->second;
533 break;
534 }
535 }
536 scroll_content()->SizeToPreferredSize();
537 static_cast<views::View*>(scroller())->Layout();
538 if (selected_view)
539 scroll_content()->ScrollRectToVisible(selected_view->bounds());
540 }
541 }
542
CreateOrUpdateInfoLabel(int index,const base::string16 & text,views::Label ** label)543 bool NetworkStateListDetailedView::CreateOrUpdateInfoLabel(
544 int index, const base::string16& text, views::Label** label) {
545 if (*label == NULL) {
546 *label = CreateMenuInfoLabel(text);
547 scroll_content()->AddChildViewAt(*label, index);
548 return true;
549 } else {
550 (*label)->SetText(text);
551 return OrderChild(*label, index);
552 }
553 }
554
UpdateNetworkChild(int index,const NetworkInfo * info)555 bool NetworkStateListDetailedView::UpdateNetworkChild(int index,
556 const NetworkInfo* info) {
557 bool needs_relayout = false;
558 HoverHighlightView* container = NULL;
559 ServicePathMap::const_iterator found =
560 service_path_map_.find(info->service_path);
561 gfx::Font::FontStyle font =
562 info->highlight ? gfx::Font::BOLD : gfx::Font::NORMAL;
563 if (found == service_path_map_.end()) {
564 container = new HoverHighlightView(this);
565 container->AddIconAndLabel(info->image, info->label, font);
566 scroll_content()->AddChildViewAt(container, index);
567 container->SetBorder(
568 views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, 0, 0));
569 needs_relayout = true;
570 } else {
571 container = found->second;
572 container->RemoveAllChildViews(true);
573 container->AddIconAndLabel(info->image, info->label, font);
574 container->Layout();
575 container->SchedulePaint();
576 needs_relayout = OrderChild(container, index);
577 }
578 if (info->disable)
579 container->SetEnabled(false);
580 network_map_[container] = info->service_path;
581 service_path_map_[info->service_path] = container;
582 return needs_relayout;
583 }
584
OrderChild(views::View * view,int index)585 bool NetworkStateListDetailedView::OrderChild(views::View* view, int index) {
586 if (scroll_content()->child_at(index) != view) {
587 scroll_content()->ReorderChildView(view, index);
588 return true;
589 }
590 return false;
591 }
592
UpdateNetworkListEntries(std::set<std::string> * new_service_paths)593 bool NetworkStateListDetailedView::UpdateNetworkListEntries(
594 std::set<std::string>* new_service_paths) {
595 bool needs_relayout = false;
596 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
597 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
598
599 // Insert child views
600 int index = 0;
601
602 // Highlighted networks
603 for (size_t i = 0; i < network_list_.size(); ++i) {
604 const NetworkInfo* info = network_list_[i];
605 if (info->highlight) {
606 if (UpdateNetworkChild(index++, info))
607 needs_relayout = true;
608 new_service_paths->insert(info->service_path);
609 }
610 }
611
612 if (list_type_ == LIST_TYPE_NETWORK) {
613 // Cellular initializing
614 int status_message_id = network_icon::GetCellularUninitializedMsg();
615 if (!status_message_id &&
616 handler->IsTechnologyEnabled(NetworkTypePattern::Mobile()) &&
617 !handler->FirstNetworkByType(NetworkTypePattern::Mobile())) {
618 status_message_id = IDS_ASH_STATUS_TRAY_NO_CELLULAR_NETWORKS;
619 }
620 if (status_message_id) {
621 base::string16 text = rb.GetLocalizedString(status_message_id);
622 if (CreateOrUpdateInfoLabel(index++, text, &no_cellular_networks_view_))
623 needs_relayout = true;
624 } else if (no_cellular_networks_view_) {
625 scroll_content()->RemoveChildView(no_cellular_networks_view_);
626 no_cellular_networks_view_ = NULL;
627 needs_relayout = true;
628 }
629
630 // "Wifi Enabled / Disabled"
631 if (network_list_.empty()) {
632 int message_id = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi())
633 ? IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED
634 : IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED;
635 base::string16 text = rb.GetLocalizedString(message_id);
636 if (CreateOrUpdateInfoLabel(index++, text, &no_wifi_networks_view_))
637 needs_relayout = true;
638 } else if (no_wifi_networks_view_) {
639 scroll_content()->RemoveChildView(no_wifi_networks_view_);
640 no_wifi_networks_view_ = NULL;
641 needs_relayout = true;
642 }
643
644 // "Wifi Scanning"
645 if (handler->GetScanningByType(NetworkTypePattern::WiFi())) {
646 base::string16 text =
647 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_WIFI_SCANNING_MESSAGE);
648 if (CreateOrUpdateInfoLabel(index++, text, &scanning_view_))
649 needs_relayout = true;
650 } else if (scanning_view_ != NULL) {
651 scroll_content()->RemoveChildView(scanning_view_);
652 scanning_view_ = NULL;
653 needs_relayout = true;
654 }
655 }
656
657 // Un-highlighted networks
658 for (size_t i = 0; i < network_list_.size(); ++i) {
659 const NetworkInfo* info = network_list_[i];
660 if (!info->highlight) {
661 if (UpdateNetworkChild(index++, info))
662 needs_relayout = true;
663 new_service_paths->insert(info->service_path);
664 }
665 }
666
667 // No networks or other messages (fallback)
668 if (index == 0) {
669 base::string16 text;
670 if (list_type_ == LIST_TYPE_VPN)
671 text = rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_NO_VPN);
672 else
673 text = rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NO_NETWORKS);
674 if (CreateOrUpdateInfoLabel(index++, text, &scanning_view_))
675 needs_relayout = true;
676 }
677
678 return needs_relayout;
679 }
680
UpdateNetworkExtra()681 void NetworkStateListDetailedView::UpdateNetworkExtra() {
682 if (login_ == user::LOGGED_IN_LOCKED)
683 return;
684
685 View* layout_parent = NULL; // All these buttons have the same parent.
686 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
687 if (other_wifi_) {
688 DCHECK(turn_on_wifi_);
689 NetworkStateHandler::TechnologyState state =
690 handler->GetTechnologyState(NetworkTypePattern::WiFi());
691 if (state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
692 turn_on_wifi_->SetVisible(false);
693 other_wifi_->SetVisible(false);
694 } else {
695 if (state == NetworkStateHandler::TECHNOLOGY_AVAILABLE) {
696 turn_on_wifi_->SetVisible(true);
697 turn_on_wifi_->SetEnabled(true);
698 other_wifi_->SetVisible(false);
699 } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLED) {
700 turn_on_wifi_->SetVisible(false);
701 other_wifi_->SetVisible(true);
702 } else {
703 // Initializing or Enabling
704 turn_on_wifi_->SetVisible(true);
705 turn_on_wifi_->SetEnabled(false);
706 other_wifi_->SetVisible(false);
707 }
708 }
709 layout_parent = other_wifi_->parent();
710 }
711
712 if (other_mobile_) {
713 bool show_other_mobile = false;
714 NetworkStateHandler::TechnologyState state =
715 handler->GetTechnologyState(NetworkTypePattern::Mobile());
716 if (state != NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
717 const chromeos::DeviceState* device =
718 handler->GetDeviceStateByType(NetworkTypePattern::Mobile());
719 show_other_mobile = (device && device->support_network_scan());
720 }
721 if (show_other_mobile) {
722 other_mobile_->SetVisible(true);
723 other_mobile_->SetEnabled(
724 state == NetworkStateHandler::TECHNOLOGY_ENABLED);
725 } else {
726 other_mobile_->SetVisible(false);
727 }
728 if (!layout_parent)
729 layout_parent = other_wifi_->parent();
730 }
731
732 if (layout_parent)
733 layout_parent->Layout();
734 }
735
CreateSettingsEntry()736 void NetworkStateListDetailedView::CreateSettingsEntry() {
737 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
738 bool show_settings = ash::Shell::GetInstance()->
739 system_tray_delegate()->ShouldShowSettings();
740 if (login_ != user::LOGGED_IN_NONE) {
741 // Allow user access settings only if user is logged in
742 // and showing settings is allowed. There're situations (supervised user
743 // creation flow) when session is started but UI flow continues within
744 // login UI i.e. no browser window is yet avaialable.
745 if (show_settings) {
746 settings_ = new TrayPopupLabelButton(
747 this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS));
748 }
749 } else {
750 // Allow users to change proxy settings only when not logged in.
751 proxy_settings_ = new TrayPopupLabelButton(
752 this,
753 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_PROXY_SETTINGS));
754 }
755 }
756
ToggleInfoBubble()757 void NetworkStateListDetailedView::ToggleInfoBubble() {
758 if (ResetInfoBubble())
759 return;
760
761 info_bubble_ = new InfoBubble(
762 info_icon_, CreateNetworkInfoView(), this);
763 views::BubbleDelegateView::CreateBubble(info_bubble_)->Show();
764 }
765
ResetInfoBubble()766 bool NetworkStateListDetailedView::ResetInfoBubble() {
767 if (!info_bubble_)
768 return false;
769 info_bubble_->GetWidget()->Close();
770 info_bubble_ = NULL;
771 return true;
772 }
773
OnInfoBubbleDestroyed()774 void NetworkStateListDetailedView::OnInfoBubbleDestroyed() {
775 info_bubble_ = NULL;
776 }
777
CreateNetworkInfoView()778 views::View* NetworkStateListDetailedView::CreateNetworkInfoView() {
779 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
780 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
781
782 std::string ip_address("0.0.0.0");
783 const NetworkState* network = handler->DefaultNetwork();
784 if (network)
785 ip_address = network->ip_address();
786
787 views::View* container = new views::View;
788 container->SetLayoutManager(
789 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
790 container->SetBorder(views::Border::CreateEmptyBorder(0, 5, 0, 5));
791
792 std::string ethernet_address, wifi_address, vpn_address;
793 if (list_type_ != LIST_TYPE_VPN) {
794 ethernet_address = handler->FormattedHardwareAddressForType(
795 NetworkTypePattern::Ethernet());
796 wifi_address =
797 handler->FormattedHardwareAddressForType(NetworkTypePattern::WiFi());
798 } else {
799 vpn_address =
800 handler->FormattedHardwareAddressForType(NetworkTypePattern::VPN());
801 }
802
803 if (!ip_address.empty()) {
804 container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
805 IDS_ASH_STATUS_TRAY_IP), ip_address));
806 }
807 if (!ethernet_address.empty()) {
808 container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
809 IDS_ASH_STATUS_TRAY_ETHERNET), ethernet_address));
810 }
811 if (!wifi_address.empty()) {
812 container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
813 IDS_ASH_STATUS_TRAY_WIFI), wifi_address));
814 }
815 if (!vpn_address.empty()) {
816 container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
817 IDS_ASH_STATUS_TRAY_VPN), vpn_address));
818 }
819
820 // Avoid an empty bubble in the unlikely event that there is no network
821 // information at all.
822 if (!container->has_children()) {
823 container->AddChildView(CreateInfoBubbleLabel(bundle.GetLocalizedString(
824 IDS_ASH_STATUS_TRAY_NO_NETWORKS)));
825 }
826
827 return container;
828 }
829
CallRequestScan()830 void NetworkStateListDetailedView::CallRequestScan() {
831 VLOG(1) << "Requesting Network Scan.";
832 NetworkHandler::Get()->network_state_handler()->RequestScan();
833 // Periodically request a scan while this UI is open.
834 base::MessageLoopForUI::current()->PostDelayedTask(
835 FROM_HERE,
836 base::Bind(&NetworkStateListDetailedView::CallRequestScan, AsWeakPtr()),
837 base::TimeDelta::FromSeconds(kRequestScanDelaySeconds));
838 }
839
ToggleMobile()840 void NetworkStateListDetailedView::ToggleMobile() {
841 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
842 bool enabled =
843 handler->IsTechnologyEnabled(NetworkTypePattern::Mobile());
844 ash::network_connect::SetTechnologyEnabled(NetworkTypePattern::Mobile(),
845 !enabled);
846 }
847
848 } // namespace tray
849 } // namespace ash
850