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/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/network_configuration_handler.h"
30#include "chromeos/network/network_state.h"
31#include "chromeos/network/network_state_handler.h"
32#include "grit/ash_resources.h"
33#include "grit/ash_strings.h"
34#include "grit/ui_chromeos_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/chromeos/network/network_icon.h"
40#include "ui/chromeos/network/network_icon_animation.h"
41#include "ui/chromeos/network/network_info.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
48using chromeos::DeviceState;
49using chromeos::NetworkHandler;
50using chromeos::NetworkState;
51using chromeos::NetworkStateHandler;
52using chromeos::NetworkTypePattern;
53using ui::NetworkInfo;
54
55namespace ash {
56namespace tray {
57namespace {
58
59// Delay between scan requests.
60const int kRequestScanDelaySeconds = 10;
61
62// Create a label with the font size and color used in the network info bubble.
63views::Label* CreateInfoBubbleLabel(const base::string16& text) {
64  views::Label* label = new views::Label(text);
65  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
66  label->SetFontList(rb.GetFontList(ui::ResourceBundle::SmallFont));
67  label->SetEnabledColor(SkColorSetARGB(127, 0, 0, 0));
68  return label;
69}
70
71// Create a row of labels for the network info bubble.
72views::View* CreateInfoBubbleLine(const base::string16& text_label,
73                                  const std::string& text_string) {
74  views::View* view = new views::View;
75  view->SetLayoutManager(
76      new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 1));
77  view->AddChildView(CreateInfoBubbleLabel(text_label));
78  view->AddChildView(CreateInfoBubbleLabel(base::UTF8ToUTF16(": ")));
79  view->AddChildView(CreateInfoBubbleLabel(base::UTF8ToUTF16(text_string)));
80  return view;
81}
82
83}  // namespace
84
85
86//------------------------------------------------------------------------------
87
88// A bubble which displays network info.
89class NetworkStateListDetailedView::InfoBubble
90    : public views::BubbleDelegateView {
91 public:
92  InfoBubble(views::View* anchor,
93             views::View* content,
94             NetworkStateListDetailedView* detailed_view)
95      : views::BubbleDelegateView(anchor, views::BubbleBorder::TOP_RIGHT),
96        detailed_view_(detailed_view) {
97    set_can_activate(false);
98    set_parent_window(ash::Shell::GetContainer(
99        anchor->GetWidget()->GetNativeWindow()->GetRootWindow(),
100        ash::kShellWindowId_SettingBubbleContainer));
101    SetLayoutManager(new views::FillLayout());
102    AddChildView(content);
103  }
104
105  virtual ~InfoBubble() {
106    detailed_view_->OnInfoBubbleDestroyed();
107  }
108
109 private:
110  // Not owned.
111  NetworkStateListDetailedView* detailed_view_;
112
113  DISALLOW_COPY_AND_ASSIGN(InfoBubble);
114};
115
116//------------------------------------------------------------------------------
117// NetworkStateListDetailedView
118
119NetworkStateListDetailedView::NetworkStateListDetailedView(
120    SystemTrayItem* owner,
121    ListType list_type,
122    user::LoginStatus login)
123    : NetworkDetailedView(owner),
124      list_type_(list_type),
125      login_(login),
126      info_icon_(NULL),
127      button_wifi_(NULL),
128      button_mobile_(NULL),
129      other_wifi_(NULL),
130      turn_on_wifi_(NULL),
131      other_mobile_(NULL),
132      other_vpn_(NULL),
133      settings_(NULL),
134      proxy_settings_(NULL),
135      info_bubble_(NULL),
136      network_list_view_(this) {
137}
138
139NetworkStateListDetailedView::~NetworkStateListDetailedView() {
140  if (info_bubble_)
141    info_bubble_->GetWidget()->CloseNow();
142}
143
144void NetworkStateListDetailedView::ManagerChanged() {
145  UpdateNetworkList();
146  UpdateHeaderButtons();
147  UpdateNetworkExtra();
148  Layout();
149}
150
151void NetworkStateListDetailedView::NetworkListChanged() {
152  UpdateNetworkList();
153  UpdateHeaderButtons();
154  UpdateNetworkExtra();
155  Layout();
156}
157
158void NetworkStateListDetailedView::NetworkServiceChanged(
159    const NetworkState* network) {
160  UpdateNetworkList();
161  Layout();
162}
163
164// Overridden from NetworkDetailedView:
165
166void NetworkStateListDetailedView::Init() {
167  Reset();
168  info_icon_ = NULL;
169  button_wifi_ = NULL;
170  button_mobile_ = NULL;
171  other_wifi_ = NULL;
172  turn_on_wifi_ = NULL;
173  other_mobile_ = NULL;
174  other_vpn_ = NULL;
175  settings_ = NULL;
176  proxy_settings_ = NULL;
177
178  CreateScrollableList();
179  CreateNetworkExtra();
180  CreateHeaderEntry();
181  CreateHeaderButtons();
182
183  network_list_view_.set_content_view(scroll_content());
184  NetworkListChanged();
185
186  CallRequestScan();
187}
188
189NetworkDetailedView::DetailedViewType
190NetworkStateListDetailedView::GetViewType() const {
191  return STATE_LIST_VIEW;
192}
193
194// Views overrides
195
196void NetworkStateListDetailedView::ButtonPressed(views::Button* sender,
197                                                 const ui::Event& event) {
198  if (sender == info_icon_) {
199    ToggleInfoBubble();
200    return;
201  }
202
203  // If the info bubble was visible, close it when some other item is clicked.
204  ResetInfoBubble();
205
206  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
207  ash::SystemTrayDelegate* delegate =
208      ash::Shell::GetInstance()->system_tray_delegate();
209  if (sender == button_wifi_) {
210    bool enabled = handler->IsTechnologyEnabled(
211        NetworkTypePattern::WiFi());
212    handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(),
213                                  !enabled,
214                                  chromeos::network_handler::ErrorCallback());
215  } else if (sender == turn_on_wifi_) {
216    handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(),
217                                  true,
218                                  chromeos::network_handler::ErrorCallback());
219  } else if (sender == button_mobile_) {
220    ToggleMobile();
221  } else if (sender == settings_) {
222    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
223        list_type_ == LIST_TYPE_VPN ?
224        ash::UMA_STATUS_AREA_VPN_SETTINGS_CLICKED :
225        ash::UMA_STATUS_AREA_NETWORK_SETTINGS_CLICKED);
226    delegate->ShowNetworkSettings("");
227  } else if (sender == proxy_settings_) {
228    delegate->ChangeProxySettings();
229  } else if (sender == other_mobile_) {
230    delegate->ShowOtherNetworkDialog(shill::kTypeCellular);
231  } else if (sender == other_wifi_) {
232    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
233        ash::UMA_STATUS_AREA_NETWORK_JOIN_OTHER_CLICKED);
234    delegate->ShowOtherNetworkDialog(shill::kTypeWifi);
235  } else if (sender == other_vpn_) {
236    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
237        ash::UMA_STATUS_AREA_VPN_JOIN_OTHER_CLICKED);
238    delegate->ShowOtherNetworkDialog(shill::kTypeVPN);
239  } else {
240    NOTREACHED();
241  }
242}
243
244void NetworkStateListDetailedView::OnViewClicked(views::View* sender) {
245  // If the info bubble was visible, close it when some other item is clicked.
246  ResetInfoBubble();
247
248  if (sender == footer()->content()) {
249    TransitionToDefaultView();
250    return;
251  }
252
253  if (login_ == user::LOGGED_IN_LOCKED)
254    return;
255
256  std::string service_path;
257  if (!network_list_view_.IsViewInList(sender, &service_path))
258    return;
259
260  const NetworkState* network = NetworkHandler::Get()->network_state_handler()->
261      GetNetworkState(service_path);
262  if (!network || network->IsConnectedState() || network->IsConnectingState()) {
263    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
264        list_type_ == LIST_TYPE_VPN ?
265        ash::UMA_STATUS_AREA_SHOW_NETWORK_CONNECTION_DETAILS :
266        ash::UMA_STATUS_AREA_SHOW_VPN_CONNECTION_DETAILS);
267    Shell::GetInstance()->system_tray_delegate()->ShowNetworkSettings(
268        service_path);
269  } else {
270    Shell::GetInstance()->metrics()->RecordUserMetricsAction(
271        list_type_ == LIST_TYPE_VPN ?
272        ash::UMA_STATUS_AREA_CONNECT_TO_VPN :
273        ash::UMA_STATUS_AREA_CONNECT_TO_CONFIGURED_NETWORK);
274    ash::network_connect::ConnectToNetwork(service_path);
275  }
276}
277
278// Create UI components.
279
280void NetworkStateListDetailedView::CreateHeaderEntry() {
281  CreateSpecialRow(IDS_ASH_STATUS_TRAY_NETWORK, this);
282}
283
284void NetworkStateListDetailedView::CreateHeaderButtons() {
285  if (list_type_ != LIST_TYPE_VPN) {
286    button_wifi_ = new TrayPopupHeaderButton(
287        this,
288        IDR_AURA_UBER_TRAY_WIFI_ENABLED,
289        IDR_AURA_UBER_TRAY_WIFI_DISABLED,
290        IDR_AURA_UBER_TRAY_WIFI_ENABLED_HOVER,
291        IDR_AURA_UBER_TRAY_WIFI_DISABLED_HOVER,
292        IDS_ASH_STATUS_TRAY_WIFI);
293    button_wifi_->SetTooltipText(
294        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_WIFI));
295    button_wifi_->SetToggledTooltipText(
296        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_WIFI));
297    footer()->AddButton(button_wifi_);
298
299    button_mobile_ = new TrayPopupHeaderButton(
300        this,
301        IDR_AURA_UBER_TRAY_CELLULAR_ENABLED,
302        IDR_AURA_UBER_TRAY_CELLULAR_DISABLED,
303        IDR_AURA_UBER_TRAY_CELLULAR_ENABLED_HOVER,
304        IDR_AURA_UBER_TRAY_CELLULAR_DISABLED_HOVER,
305        IDS_ASH_STATUS_TRAY_CELLULAR);
306    button_mobile_->SetTooltipText(
307        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_DISABLE_MOBILE));
308    button_mobile_->SetToggledTooltipText(
309        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ENABLE_MOBILE));
310    footer()->AddButton(button_mobile_);
311  }
312
313  info_icon_ = new TrayPopupHeaderButton(
314      this,
315      IDR_AURA_UBER_TRAY_NETWORK_INFO,
316      IDR_AURA_UBER_TRAY_NETWORK_INFO,
317      IDR_AURA_UBER_TRAY_NETWORK_INFO_HOVER,
318      IDR_AURA_UBER_TRAY_NETWORK_INFO_HOVER,
319      IDS_ASH_STATUS_TRAY_NETWORK_INFO);
320  info_icon_->SetTooltipText(
321      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_INFO));
322  footer()->AddButton(info_icon_);
323}
324
325void NetworkStateListDetailedView::CreateNetworkExtra() {
326  if (login_ == user::LOGGED_IN_LOCKED)
327    return;
328
329  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
330
331  views::View* bottom_row = new views::View();
332  views::BoxLayout* layout = new views::BoxLayout(
333      views::BoxLayout::kHorizontal,
334      kTrayMenuBottomRowPadding,
335      kTrayMenuBottomRowPadding,
336      kTrayMenuBottomRowPaddingBetweenItems);
337  layout->SetDefaultFlex(1);
338  bottom_row->SetLayoutManager(layout);
339
340  if (list_type_ != LIST_TYPE_VPN) {
341    other_wifi_ = new TrayPopupLabelButton(
342        this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_WIFI));
343    bottom_row->AddChildView(other_wifi_);
344
345    turn_on_wifi_ = new TrayPopupLabelButton(
346        this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_TURN_ON_WIFI));
347    bottom_row->AddChildView(turn_on_wifi_);
348
349    other_mobile_ = new TrayPopupLabelButton(
350        this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_OTHER_MOBILE));
351    bottom_row->AddChildView(other_mobile_);
352  } else {
353    other_vpn_ = new TrayPopupLabelButton(
354        this,
355        ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
356            IDS_ASH_STATUS_TRAY_OTHER_VPN));
357    bottom_row->AddChildView(other_vpn_);
358  }
359
360  CreateSettingsEntry();
361
362  // Both settings_ and proxy_settings_ can be NULL. This happens when
363  // we're logged in but showing settings page is not enabled.
364  // Example: supervised user creation flow where user session is active
365  // but all action happens on the login window.
366  // Allowing opening proxy settigns dialog will break assumption in
367  //  SystemTrayDelegateChromeOS::ChangeProxySettings(), see CHECK.
368  if (settings_ || proxy_settings_)
369    bottom_row->AddChildView(settings_ ? settings_ : proxy_settings_);
370
371  AddChildView(bottom_row);
372}
373
374// Update UI components.
375
376void NetworkStateListDetailedView::UpdateHeaderButtons() {
377  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
378  if (button_wifi_)
379    UpdateTechnologyButton(button_wifi_, NetworkTypePattern::WiFi());
380  if (button_mobile_) {
381    UpdateTechnologyButton(button_mobile_, NetworkTypePattern::Mobile());
382  }
383  if (proxy_settings_)
384    proxy_settings_->SetEnabled(handler->DefaultNetwork() != NULL);
385
386  static_cast<views::View*>(footer())->Layout();
387}
388
389void NetworkStateListDetailedView::UpdateTechnologyButton(
390    TrayPopupHeaderButton* button,
391    const NetworkTypePattern& technology) {
392  NetworkStateHandler::TechnologyState state =
393      NetworkHandler::Get()->network_state_handler()->GetTechnologyState(
394          technology);
395  if (state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
396    button->SetVisible(false);
397    return;
398  }
399  button->SetVisible(true);
400  if (state == NetworkStateHandler::TECHNOLOGY_AVAILABLE) {
401    button->SetEnabled(true);
402    button->SetToggled(true);
403  } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLED) {
404    button->SetEnabled(true);
405    button->SetToggled(false);
406  } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLING) {
407    button->SetEnabled(false);
408    button->SetToggled(false);
409  } else {  // Initializing
410    button->SetEnabled(false);
411    button->SetToggled(true);
412  }
413}
414
415void NetworkStateListDetailedView::UpdateNetworkList() {
416  network_list_view_.UpdateNetworkList();
417}
418
419bool NetworkStateListDetailedView::OrderChild(views::View* view, int index) {
420  if (scroll_content()->child_at(index) != view) {
421    scroll_content()->ReorderChildView(view, index);
422    return true;
423  }
424  return false;
425}
426
427void NetworkStateListDetailedView::UpdateNetworkExtra() {
428  if (login_ == user::LOGGED_IN_LOCKED)
429    return;
430
431  View* layout_parent = NULL;  // All these buttons have the same parent.
432  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
433  if (other_wifi_) {
434    DCHECK(turn_on_wifi_);
435    NetworkStateHandler::TechnologyState state =
436        handler->GetTechnologyState(NetworkTypePattern::WiFi());
437    if (state == NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
438      turn_on_wifi_->SetVisible(false);
439      other_wifi_->SetVisible(false);
440    } else {
441      if (state == NetworkStateHandler::TECHNOLOGY_AVAILABLE) {
442        turn_on_wifi_->SetVisible(true);
443        turn_on_wifi_->SetEnabled(true);
444        other_wifi_->SetVisible(false);
445      } else if (state == NetworkStateHandler::TECHNOLOGY_ENABLED) {
446        turn_on_wifi_->SetVisible(false);
447        other_wifi_->SetVisible(true);
448      } else {
449        // Initializing or Enabling
450        turn_on_wifi_->SetVisible(true);
451        turn_on_wifi_->SetEnabled(false);
452        other_wifi_->SetVisible(false);
453      }
454    }
455    layout_parent = other_wifi_->parent();
456  }
457
458  if (other_mobile_) {
459    bool show_other_mobile = false;
460    NetworkStateHandler::TechnologyState state =
461        handler->GetTechnologyState(NetworkTypePattern::Mobile());
462    if (state != NetworkStateHandler::TECHNOLOGY_UNAVAILABLE) {
463      const chromeos::DeviceState* device =
464          handler->GetDeviceStateByType(NetworkTypePattern::Mobile());
465      show_other_mobile = (device && device->support_network_scan());
466    }
467    if (show_other_mobile) {
468      other_mobile_->SetVisible(true);
469      other_mobile_->SetEnabled(
470          state == NetworkStateHandler::TECHNOLOGY_ENABLED);
471    } else {
472      other_mobile_->SetVisible(false);
473    }
474    if (!layout_parent)
475      layout_parent = other_wifi_->parent();
476  }
477
478  if (layout_parent)
479    layout_parent->Layout();
480}
481
482void NetworkStateListDetailedView::CreateSettingsEntry() {
483  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
484  bool show_settings = ash::Shell::GetInstance()->
485      system_tray_delegate()->ShouldShowSettings();
486  if (login_ != user::LOGGED_IN_NONE) {
487    // Allow user access settings only if user is logged in
488    // and showing settings is allowed. There're situations (supervised user
489    // creation flow) when session is started but UI flow continues within
490    // login UI i.e. no browser window is yet avaialable.
491    if (show_settings) {
492      settings_ = new TrayPopupLabelButton(
493          this, rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_SETTINGS));
494    }
495  } else  {
496    // Allow users to change proxy settings only when not logged in.
497    proxy_settings_ = new TrayPopupLabelButton(
498        this,
499        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_NETWORK_PROXY_SETTINGS));
500  }
501}
502
503void NetworkStateListDetailedView::ToggleInfoBubble() {
504  if (ResetInfoBubble())
505    return;
506
507  info_bubble_ = new InfoBubble(
508      info_icon_, CreateNetworkInfoView(), this);
509  views::BubbleDelegateView::CreateBubble(info_bubble_)->Show();
510}
511
512bool NetworkStateListDetailedView::ResetInfoBubble() {
513  if (!info_bubble_)
514    return false;
515  info_bubble_->GetWidget()->Close();
516  info_bubble_ = NULL;
517  return true;
518}
519
520void NetworkStateListDetailedView::OnInfoBubbleDestroyed() {
521  info_bubble_ = NULL;
522}
523
524views::View* NetworkStateListDetailedView::CreateNetworkInfoView() {
525  ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
526  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
527
528  std::string ip_address("0.0.0.0");
529  const NetworkState* network = handler->DefaultNetwork();
530  if (network)
531    ip_address = network->ip_address();
532
533  views::View* container = new views::View;
534  container->SetLayoutManager(
535      new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1));
536  container->SetBorder(views::Border::CreateEmptyBorder(0, 5, 0, 5));
537
538  std::string ethernet_address, wifi_address, vpn_address;
539  if (list_type_ != LIST_TYPE_VPN) {
540    ethernet_address = handler->FormattedHardwareAddressForType(
541        NetworkTypePattern::Ethernet());
542    wifi_address =
543        handler->FormattedHardwareAddressForType(NetworkTypePattern::WiFi());
544  } else {
545    vpn_address =
546        handler->FormattedHardwareAddressForType(NetworkTypePattern::VPN());
547  }
548
549  if (!ip_address.empty()) {
550    container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
551        IDS_ASH_STATUS_TRAY_IP), ip_address));
552  }
553  if (!ethernet_address.empty()) {
554    container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
555        IDS_ASH_STATUS_TRAY_ETHERNET), ethernet_address));
556  }
557  if (!wifi_address.empty()) {
558    container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
559        IDS_ASH_STATUS_TRAY_WIFI), wifi_address));
560  }
561  if (!vpn_address.empty()) {
562    container->AddChildView(CreateInfoBubbleLine(bundle.GetLocalizedString(
563        IDS_ASH_STATUS_TRAY_VPN), vpn_address));
564  }
565
566  // Avoid an empty bubble in the unlikely event that there is no network
567  // information at all.
568  if (!container->has_children()) {
569    container->AddChildView(CreateInfoBubbleLabel(bundle.GetLocalizedString(
570        IDS_ASH_STATUS_TRAY_NO_NETWORKS)));
571  }
572
573  return container;
574}
575
576void NetworkStateListDetailedView::CallRequestScan() {
577  VLOG(1) << "Requesting Network Scan.";
578  NetworkHandler::Get()->network_state_handler()->RequestScan();
579  // Periodically request a scan while this UI is open.
580  base::MessageLoopForUI::current()->PostDelayedTask(
581      FROM_HERE,
582      base::Bind(&NetworkStateListDetailedView::CallRequestScan, AsWeakPtr()),
583      base::TimeDelta::FromSeconds(kRequestScanDelaySeconds));
584}
585
586void NetworkStateListDetailedView::ToggleMobile() {
587  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
588  bool enabled =
589      handler->IsTechnologyEnabled(NetworkTypePattern::Mobile());
590  ash::network_connect::SetTechnologyEnabled(NetworkTypePattern::Mobile(),
591                                             !enabled);
592}
593
594views::View* NetworkStateListDetailedView::CreateViewForNetwork(
595    const ui::NetworkInfo& info) {
596  gfx::Font::FontStyle font =
597      info.highlight ? gfx::Font::BOLD : gfx::Font::NORMAL;
598  HoverHighlightView* view = new HoverHighlightView(this);
599  view->AddIconAndLabel(info.image, info.label, font);
600  view->SetBorder(
601      views::Border::CreateEmptyBorder(0, kTrayPopupPaddingHorizontal, 0, 0));
602  return view;
603}
604
605bool NetworkStateListDetailedView::IsViewHovered(views::View* view) {
606  return static_cast<HoverHighlightView*>(view)->hover();
607}
608
609NetworkTypePattern NetworkStateListDetailedView::GetNetworkTypePattern() const {
610  return list_type_ == LIST_TYPE_VPN ? NetworkTypePattern::VPN()
611                                     : NetworkTypePattern::NonVirtual();
612}
613
614void NetworkStateListDetailedView::UpdateViewForNetwork(
615    views::View* view,
616    const NetworkInfo& info) {
617  gfx::Font::FontStyle font =
618      info.highlight ? gfx::Font::BOLD : gfx::Font::NORMAL;
619  HoverHighlightView* highlight = static_cast<HoverHighlightView*>(view);
620  highlight->AddIconAndLabel(info.image, info.label, font);
621}
622
623views::Label* NetworkStateListDetailedView::CreateInfoLabel() {
624  views::Label* label = new views::Label();
625  label->SetBorder(
626      views::Border::CreateEmptyBorder(ash::kTrayPopupPaddingBetweenItems,
627                                       ash::kTrayPopupPaddingHorizontal,
628                                       ash::kTrayPopupPaddingBetweenItems,
629                                       0));
630  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
631  label->SetEnabledColor(SkColorSetARGB(192, 0, 0, 0));
632  return label;
633}
634
635void NetworkStateListDetailedView::RelayoutScrollList() {
636  scroller()->Layout();
637}
638
639}  // namespace tray
640}  // namespace ash
641