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