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