Home | History | Annotate | Download | only in network
      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.
     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
     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.
     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 {
     99   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:
    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 
    131   virtual ~InfoBubble() {
    132     detailed_view_->OnInfoBubbleDestroyed();
    133   }
    134 
    135   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 
    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 
    169 NetworkStateListDetailedView::~NetworkStateListDetailedView() {
    170   if (info_bubble_)
    171     info_bubble_->GetWidget()->CloseNow();
    172   network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this);
    173 }
    174 
    175 void NetworkStateListDetailedView::ManagerChanged() {
    176   UpdateNetworkList();
    177   UpdateHeaderButtons();
    178   UpdateNetworkExtra();
    179   Layout();
    180 }
    181 
    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 
    193 void NetworkStateListDetailedView::NetworkServiceChanged(
    194     const NetworkState* network) {
    195   UpdateNetworkList();
    196   Layout();
    197 }
    198 
    199 void NetworkStateListDetailedView::NetworkIconChanged() {
    200   UpdateNetworkList();
    201   Layout();
    202 }
    203 
    204 // Overridden from NetworkDetailedView:
    205 
    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
    234 NetworkStateListDetailedView::GetViewType() const {
    235   return STATE_LIST_VIEW;
    236 }
    237 
    238 // Views overrides
    239 
    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 
    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 
    326 void NetworkStateListDetailedView::CreateHeaderEntry() {
    327   CreateSpecialRow(IDS_ASH_STATUS_TRAY_NETWORK, this);
    328 }
    329 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    774 void NetworkStateListDetailedView::OnInfoBubbleDestroyed() {
    775   info_bubble_ = NULL;
    776 }
    777 
    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 
    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 
    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