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