• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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