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