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