tray_network.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/tray_network.h" 6 7#include "ash/ash_switches.h" 8#include "ash/shell.h" 9#include "ash/system/chromeos/network/network_icon_animation.h" 10#include "ash/system/chromeos/network/network_list_detailed_view.h" 11#include "ash/system/chromeos/network/network_list_detailed_view_base.h" 12#include "ash/system/chromeos/network/network_state_list_detailed_view.h" 13#include "ash/system/chromeos/network/network_state_notifier.h" 14#include "ash/system/chromeos/network/network_tray_delegate.h" 15#include "ash/system/tray/system_tray.h" 16#include "ash/system/tray/system_tray_delegate.h" 17#include "ash/system/tray/system_tray_notifier.h" 18#include "ash/system/tray/tray_constants.h" 19#include "ash/system/tray/tray_item_more.h" 20#include "ash/system/tray/tray_item_view.h" 21#include "ash/system/tray/tray_notification_view.h" 22#include "ash/system/tray/tray_utils.h" 23#include "base/command_line.h" 24#include "base/utf_string_conversions.h" 25#include "chromeos/network/network_state.h" 26#include "chromeos/network/network_state_handler.h" 27#include "grit/ash_resources.h" 28#include "grit/ash_strings.h" 29#include "third_party/cros_system_api/dbus/service_constants.h" 30#include "ui/base/accessibility/accessible_view_state.h" 31#include "ui/base/l10n/l10n_util.h" 32#include "ui/base/resource/resource_bundle.h" 33#include "ui/views/controls/image_view.h" 34#include "ui/views/controls/link.h" 35#include "ui/views/controls/link_listener.h" 36#include "ui/views/layout/box_layout.h" 37#include "ui/views/widget/widget.h" 38 39using ash::internal::TrayNetwork; 40using ash::NetworkObserver; 41using chromeos::NetworkState; 42using chromeos::NetworkStateHandler; 43 44namespace { 45 46int GetMessageIcon(NetworkObserver::MessageType message_type, 47 NetworkObserver::NetworkType network_type) { 48 switch(message_type) { 49 case NetworkObserver::ERROR_CONNECT_FAILED: 50 if (NetworkObserver::NETWORK_CELLULAR == network_type) 51 return IDR_AURA_UBER_TRAY_CELLULAR_NETWORK_FAILED; 52 else 53 return IDR_AURA_UBER_TRAY_NETWORK_FAILED; 54 case NetworkObserver::ERROR_OUT_OF_CREDITS: 55 case NetworkObserver::MESSAGE_DATA_PROMO: 56 if (network_type == TrayNetwork::NETWORK_CELLULAR_LTE) 57 return IDR_AURA_UBER_TRAY_NOTIFICATION_LTE; 58 else 59 return IDR_AURA_UBER_TRAY_NOTIFICATION_3G; 60 } 61 NOTREACHED(); 62 return 0; 63} 64 65bool UseNewNetworkHandlers() { 66 return !CommandLine::ForCurrentProcess()->HasSwitch( 67 ash::switches::kAshDisableNewNetworkStatusArea) && 68 NetworkStateHandler::IsInitialized(); 69} 70 71} // namespace 72 73namespace ash { 74namespace internal { 75 76namespace tray { 77 78class NetworkMessages { 79 public: 80 struct Message { 81 Message() : delegate(NULL) {} 82 Message(NetworkTrayDelegate* in_delegate, 83 NetworkObserver::NetworkType network_type, 84 const base::string16& in_title, 85 const base::string16& in_message, 86 const std::vector<base::string16>& in_links) : 87 delegate(in_delegate), 88 network_type_(network_type), 89 title(in_title), 90 message(in_message), 91 links(in_links) {} 92 NetworkTrayDelegate* delegate; 93 NetworkObserver::NetworkType network_type_; 94 base::string16 title; 95 base::string16 message; 96 std::vector<base::string16> links; 97 }; 98 typedef std::map<NetworkObserver::MessageType, Message> MessageMap; 99 100 MessageMap& messages() { return messages_; } 101 const MessageMap& messages() const { return messages_; } 102 103 private: 104 MessageMap messages_; 105}; 106 107class NetworkTrayView : public TrayItemView, 108 public network_icon::AnimationObserver { 109 public: 110 explicit NetworkTrayView(TrayNetwork* network_tray) 111 : TrayItemView(network_tray), 112 network_tray_(network_tray) { 113 SetLayoutManager( 114 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); 115 116 image_view_ = new views::ImageView; 117 AddChildView(image_view_); 118 119 NetworkIconInfo info; 120 if (UseNewNetworkHandlers()) { 121 UpdateNetworkStateHandlerIcon(); 122 } else { 123 Shell::GetInstance()->system_tray_delegate()-> 124 GetMostRelevantNetworkIcon(&info, false); 125 UpdateIcon(info.tray_icon_visible, info.image); 126 } 127 } 128 129 virtual ~NetworkTrayView() { 130 if (UseNewNetworkHandlers()) 131 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 132 } 133 134 virtual const char* GetClassName() const OVERRIDE { 135 return "NetworkTrayView"; 136 } 137 138 void Update(const NetworkIconInfo& info) { 139 if (UseNewNetworkHandlers()) 140 return; 141 UpdateIcon(info.tray_icon_visible, info.image); 142 UpdateConnectionStatus(info.name, info.connected); 143 } 144 145 void UpdateNetworkStateHandlerIcon() { 146 DCHECK(UseNewNetworkHandlers()); 147 NetworkStateHandler* handler = NetworkStateHandler::Get(); 148 gfx::ImageSkia image; 149 base::string16 name; 150 bool animating = false; 151 network_tray_->GetNetworkStateHandlerImageAndLabel( 152 network_icon::ICON_TYPE_TRAY, &image, &name, &animating); 153 bool show_in_tray = !image.isNull(); 154 UpdateIcon(show_in_tray, image); 155 if (animating) 156 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 157 else 158 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 159 // Update accessibility. 160 const NetworkState* connected_network = handler->ConnectedNetworkByType( 161 NetworkStateHandler::kMatchTypeNonVirtual); 162 if (connected_network) 163 UpdateConnectionStatus(UTF8ToUTF16(connected_network->name()), true); 164 else 165 UpdateConnectionStatus(base::string16(), false); 166 } 167 168 // views::View override. 169 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE { 170 state->name = connection_status_string_; 171 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; 172 } 173 174 // network_icon::AnimationObserver 175 virtual void NetworkIconChanged() OVERRIDE { 176 if (UseNewNetworkHandlers()) 177 UpdateNetworkStateHandlerIcon(); 178 } 179 180 private: 181 // Updates connection status and notifies accessibility event when necessary. 182 void UpdateConnectionStatus(const base::string16& network_name, 183 bool connected) { 184 base::string16 new_connection_status_string; 185 if (connected) { 186 new_connection_status_string = l10n_util::GetStringFUTF16( 187 IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, network_name); 188 } 189 if (new_connection_status_string != connection_status_string_) { 190 connection_status_string_ = new_connection_status_string; 191 if(!connection_status_string_.empty()) 192 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true); 193 } 194 } 195 196 void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image) { 197 image_view_->SetImage(image); 198 SetVisible(tray_icon_visible); 199 SchedulePaint(); 200 } 201 202 TrayNetwork* network_tray_; 203 views::ImageView* image_view_; 204 base::string16 connection_status_string_; 205 206 DISALLOW_COPY_AND_ASSIGN(NetworkTrayView); 207}; 208 209class NetworkDefaultView : public TrayItemMore, 210 public network_icon::AnimationObserver { 211 public: 212 NetworkDefaultView(TrayNetwork* network_tray, bool show_more) 213 : TrayItemMore(network_tray, show_more), 214 network_tray_(network_tray) { 215 Update(); 216 } 217 218 virtual ~NetworkDefaultView() { 219 if (UseNewNetworkHandlers()) 220 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 221 } 222 223 void Update() { 224 if (UseNewNetworkHandlers()) { 225 gfx::ImageSkia image; 226 base::string16 label; 227 bool animating = false; 228 network_tray_->GetNetworkStateHandlerImageAndLabel( 229 network_icon::ICON_TYPE_DEFAULT_VIEW, &image, &label, &animating); 230 if (animating) 231 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 232 else 233 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 234 SetImage(&image); 235 SetLabel(label); 236 SetAccessibleName(label); 237 } else { 238 NetworkIconInfo info; 239 Shell::GetInstance()->system_tray_delegate()-> 240 GetMostRelevantNetworkIcon(&info, true); 241 SetImage(&info.image); 242 SetLabel(info.description); 243 SetAccessibleName(info.description); 244 } 245 } 246 247 // network_icon::AnimationObserver 248 virtual void NetworkIconChanged() OVERRIDE { 249 Update(); 250 } 251 252 private: 253 TrayNetwork* network_tray_; 254 255 DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView); 256}; 257 258class NetworkWifiDetailedView : public NetworkDetailedView { 259 public: 260 NetworkWifiDetailedView(SystemTrayItem* owner, bool wifi_enabled) 261 : NetworkDetailedView(owner) { 262 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 263 kTrayPopupPaddingHorizontal, 264 10, 265 kTrayPopupPaddingBetweenItems)); 266 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 267 views::ImageView* image = new views::ImageView; 268 const int image_id = wifi_enabled ? 269 IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED; 270 image->SetImage(bundle.GetImageNamed(image_id).ToImageSkia()); 271 AddChildView(image); 272 273 const int string_id = wifi_enabled ? 274 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED: 275 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED; 276 views::Label* label = 277 new views::Label(bundle.GetLocalizedString(string_id)); 278 label->SetMultiLine(true); 279 label->SetHorizontalAlignment(gfx::ALIGN_LEFT); 280 AddChildView(label); 281 } 282 283 virtual ~NetworkWifiDetailedView() {} 284 285 // Overridden from NetworkDetailedView: 286 287 virtual void Init() OVERRIDE { 288 } 289 290 virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE { 291 return NetworkDetailedView::WIFI_VIEW; 292 } 293 294 virtual void ManagerChanged() OVERRIDE { 295 } 296 297 virtual void NetworkListChanged() OVERRIDE { 298 } 299 300 virtual void NetworkServiceChanged( 301 const chromeos::NetworkState* network) OVERRIDE { 302 } 303 304 private: 305 DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView); 306}; 307 308class NetworkMessageView : public views::View, 309 public views::LinkListener { 310 public: 311 NetworkMessageView(TrayNetwork* tray_network, 312 NetworkObserver::MessageType message_type, 313 const NetworkMessages::Message& network_msg) 314 : tray_network_(tray_network), 315 message_type_(message_type), 316 network_type_(network_msg.network_type_) { 317 SetLayoutManager( 318 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); 319 320 if (!network_msg.title.empty()) { 321 views::Label* title = new views::Label(network_msg.title); 322 title->SetHorizontalAlignment(gfx::ALIGN_LEFT); 323 title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD)); 324 AddChildView(title); 325 } 326 327 if (!network_msg.message.empty()) { 328 views::Label* message = new views::Label(network_msg.message); 329 message->SetHorizontalAlignment(gfx::ALIGN_LEFT); 330 message->SetMultiLine(true); 331 message->SizeToFit(kTrayNotificationContentsWidth); 332 AddChildView(message); 333 } 334 335 if (!network_msg.links.empty()) { 336 for (size_t i = 0; i < network_msg.links.size(); ++i) { 337 views::Link* link = new views::Link(network_msg.links[i]); 338 link->set_id(i); 339 link->set_listener(this); 340 link->SetHorizontalAlignment(gfx::ALIGN_LEFT); 341 link->SetMultiLine(true); 342 link->SizeToFit(kTrayNotificationContentsWidth); 343 AddChildView(link); 344 } 345 } 346 } 347 348 virtual ~NetworkMessageView() { 349 } 350 351 // Overridden from views::LinkListener. 352 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE { 353 tray_network_->LinkClicked(message_type_, source->id()); 354 } 355 356 NetworkObserver::MessageType message_type() const { return message_type_; } 357 NetworkObserver::NetworkType network_type() const { return network_type_; } 358 359 private: 360 TrayNetwork* tray_network_; 361 NetworkObserver::MessageType message_type_; 362 NetworkObserver::NetworkType network_type_; 363 364 DISALLOW_COPY_AND_ASSIGN(NetworkMessageView); 365}; 366 367class NetworkNotificationView : public TrayNotificationView { 368 public: 369 explicit NetworkNotificationView(TrayNetwork* tray_network) 370 : TrayNotificationView(tray_network, 0), 371 tray_network_(tray_network) { 372 CreateMessageView(); 373 InitView(network_message_view_); 374 SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 375 GetMessageIcon(network_message_view_->message_type(), 376 network_message_view_->network_type()))); 377 } 378 379 // Overridden from TrayNotificationView. 380 virtual void OnClose() OVERRIDE { 381 tray_network_->ClearNetworkMessage(network_message_view_->message_type()); 382 } 383 384 virtual void OnClickAction() OVERRIDE { 385 if (network_message_view_->message_type() != 386 TrayNetwork::MESSAGE_DATA_PROMO) 387 tray_network_->PopupDetailedView(0, true); 388 } 389 390 void Update() { 391 CreateMessageView(); 392 UpdateViewAndImage(network_message_view_, 393 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 394 GetMessageIcon(network_message_view_->message_type(), 395 network_message_view_->network_type()))); 396 } 397 398 private: 399 void CreateMessageView() { 400 // Display the first (highest priority) message. 401 CHECK(!tray_network_->messages()->messages().empty()); 402 NetworkMessages::MessageMap::const_iterator iter = 403 tray_network_->messages()->messages().begin(); 404 network_message_view_ = 405 new NetworkMessageView(tray_network_, iter->first, iter->second); 406 } 407 408 TrayNetwork* tray_network_; 409 tray::NetworkMessageView* network_message_view_; 410 411 DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView); 412}; 413 414} // namespace tray 415 416TrayNetwork::TrayNetwork(SystemTray* system_tray) 417 : SystemTrayItem(system_tray), 418 tray_(NULL), 419 default_(NULL), 420 detailed_(NULL), 421 notification_(NULL), 422 messages_(new tray::NetworkMessages()), 423 request_wifi_view_(false) { 424 if (UseNewNetworkHandlers()) 425 network_state_observer_.reset(new TrayNetworkStateObserver(this)); 426 if (NetworkStateHandler::IsInitialized()) 427 network_state_notifier_.reset(new NetworkStateNotifier()); 428 Shell::GetInstance()->system_tray_notifier()->AddNetworkObserver(this); 429} 430 431TrayNetwork::~TrayNetwork() { 432 network_state_notifier_.reset(); 433 Shell::GetInstance()->system_tray_notifier()->RemoveNetworkObserver(this); 434} 435 436views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) { 437 CHECK(tray_ == NULL); 438 tray_ = new tray::NetworkTrayView(this); 439 return tray_; 440} 441 442views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) { 443 CHECK(default_ == NULL); 444 CHECK(tray_ != NULL); 445 default_ = new tray::NetworkDefaultView( 446 this, status != user::LOGGED_IN_LOCKED); 447 return default_; 448} 449 450views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) { 451 CHECK(detailed_ == NULL); 452 // Clear any notifications when showing the detailed view. 453 messages_->messages().clear(); 454 HideNotificationView(); 455 if (request_wifi_view_) { 456 SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate(); 457 // The Wi-Fi state is not toggled yet at this point. 458 detailed_ = new tray::NetworkWifiDetailedView(this, 459 !delegate->GetWifiEnabled()); 460 request_wifi_view_ = false; 461 } else { 462 if (UseNewNetworkHandlers()) { 463 detailed_ = new tray::NetworkStateListDetailedView( 464 this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status); 465 } else { 466 detailed_ = new tray::NetworkListDetailedView( 467 this, status, IDS_ASH_STATUS_TRAY_NETWORK); 468 } 469 detailed_->Init(); 470 } 471 return detailed_; 472} 473 474views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) { 475 CHECK(notification_ == NULL); 476 if (messages_->messages().empty()) 477 return NULL; // Message has already been cleared. 478 notification_ = new tray::NetworkNotificationView(this); 479 return notification_; 480} 481 482void TrayNetwork::DestroyTrayView() { 483 tray_ = NULL; 484} 485 486void TrayNetwork::DestroyDefaultView() { 487 default_ = NULL; 488} 489 490void TrayNetwork::DestroyDetailedView() { 491 detailed_ = NULL; 492} 493 494void TrayNetwork::DestroyNotificationView() { 495 notification_ = NULL; 496} 497 498void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) { 499} 500 501void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 502 SetTrayImageItemBorder(tray_, alignment); 503} 504 505void TrayNetwork::OnNetworkRefresh(const NetworkIconInfo& info) { 506 if (tray_) 507 tray_->Update(info); 508 if (default_) 509 default_->Update(); 510 if (detailed_) 511 detailed_->ManagerChanged(); 512} 513 514void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate, 515 MessageType message_type, 516 NetworkType network_type, 517 const base::string16& title, 518 const base::string16& message, 519 const std::vector<base::string16>& links) { 520 messages_->messages()[message_type] = tray::NetworkMessages::Message( 521 delegate, network_type, title, message, links); 522 if (!Shell::GetInstance()->system_tray_delegate()->IsOobeCompleted()) 523 return; 524 if (notification_) 525 notification_->Update(); 526 else 527 ShowNotificationView(); 528} 529 530void TrayNetwork::ClearNetworkMessage(MessageType message_type) { 531 messages_->messages().erase(message_type); 532 if (messages_->messages().empty()) { 533 HideNotificationView(); 534 return; 535 } 536 if (notification_) 537 notification_->Update(); 538 else 539 ShowNotificationView(); 540} 541 542void TrayNetwork::OnWillToggleWifi() { 543 // Triggered by a user action (e.g. keyboard shortcut) 544 if (!detailed_ || 545 detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) { 546 request_wifi_view_ = true; 547 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); 548 } 549} 550 551void TrayNetwork::NetworkStateChanged(bool list_changed) { 552 if (tray_ && UseNewNetworkHandlers()) 553 tray_->UpdateNetworkStateHandlerIcon(); 554 if (default_) 555 default_->Update(); 556 if (detailed_) { 557 if (list_changed) 558 detailed_->NetworkListChanged(); 559 else 560 detailed_->ManagerChanged(); 561 } 562} 563 564void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) { 565 if (detailed_) 566 detailed_->NetworkServiceChanged(network); 567} 568 569void TrayNetwork::GetNetworkStateHandlerImageAndLabel( 570 network_icon::IconType icon_type, 571 gfx::ImageSkia* image, 572 base::string16* label, 573 bool* animating) { 574 NetworkStateHandler* handler = NetworkStateHandler::Get(); 575 const NetworkState* connected_network = handler->ConnectedNetworkByType( 576 NetworkStateHandler::kMatchTypeNonVirtual); 577 const NetworkState* connecting_network = handler->ConnectingNetworkByType( 578 NetworkStateHandler::kMatchTypeWireless); 579 if (!connecting_network && icon_type == network_icon::ICON_TYPE_TRAY) 580 connecting_network = handler->ConnectingNetworkByType(flimflam::kTypeVPN); 581 582 const NetworkState* network; 583 // If we are connecting to a network, and there is either no connected 584 // network, or the connection was user requested, use the connecting 585 // network. 586 if (connecting_network && 587 (!connected_network || 588 handler->connecting_network() == connecting_network->path())) { 589 network = connecting_network; 590 } else { 591 network = connected_network; 592 } 593 594 // Don't show ethernet in the tray 595 if (icon_type == network_icon::ICON_TYPE_TRAY && 596 network && network->type() == flimflam::kTypeEthernet) { 597 *image = gfx::ImageSkia(); 598 *animating = false; 599 return; 600 } 601 602 if (!network) { 603 // If no connecting network, check if we are activating a network. 604 const NetworkState* mobile_network = handler->FirstNetworkByType( 605 NetworkStateHandler::kMatchTypeMobile); 606 if (mobile_network && (mobile_network->activation_state() == 607 flimflam::kActivationStateActivating)) { 608 network = mobile_network; 609 } 610 } 611 if (!network) { 612 // If no connecting network, check for cellular initializing. 613 int uninitialized_msg = network_icon::GetCellularUninitializedMsg(); 614 if (uninitialized_msg != 0) { 615 *image = network_icon::GetImageForConnectingNetwork( 616 icon_type, flimflam::kTypeCellular); 617 if (label) 618 *label = l10n_util::GetStringUTF16(uninitialized_msg); 619 *animating = true; 620 } else { 621 // Otherwise show the disconnected wifi icon. 622 *image = network_icon::GetImageForDisconnectedNetwork( 623 icon_type, flimflam::kTypeWifi); 624 if (label) { 625 *label = l10n_util::GetStringUTF16( 626 IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED); 627 } 628 *animating = false; 629 } 630 return; 631 } 632 *animating = network->IsConnectingState(); 633 // Get icon and label for connected or connecting network. 634 *image = network_icon::GetImageForNetwork(network, icon_type); 635 if (label) 636 *label = network_icon::GetLabelForNetwork(network, icon_type); 637} 638 639void TrayNetwork::LinkClicked(MessageType message_type, int link_id) { 640 tray::NetworkMessages::MessageMap::const_iterator iter = 641 messages()->messages().find(message_type); 642 if (iter != messages()->messages().end() && iter->second.delegate) 643 iter->second.delegate->NotificationLinkClicked(message_type, link_id); 644} 645 646} // namespace internal 647} // namespace ash 648