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