tray_network.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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_state_list_detailed_view.h" 11#include "ash/system/chromeos/network/network_tray_delegate.h" 12#include "ash/system/chromeos/network/tray_network_state_observer.h" 13#include "ash/system/tray/system_tray.h" 14#include "ash/system/tray/system_tray_delegate.h" 15#include "ash/system/tray/system_tray_notifier.h" 16#include "ash/system/tray/tray_constants.h" 17#include "ash/system/tray/tray_item_more.h" 18#include "ash/system/tray/tray_item_view.h" 19#include "ash/system/tray/tray_notification_view.h" 20#include "ash/system/tray/tray_utils.h" 21#include "base/command_line.h" 22#include "base/strings/utf_string_conversions.h" 23#include "chromeos/network/network_state.h" 24#include "chromeos/network/network_state_handler.h" 25#include "grit/ash_resources.h" 26#include "grit/ash_strings.h" 27#include "third_party/cros_system_api/dbus/service_constants.h" 28#include "ui/base/accessibility/accessible_view_state.h" 29#include "ui/base/l10n/l10n_util.h" 30#include "ui/base/resource/resource_bundle.h" 31#include "ui/views/controls/image_view.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 chromeos::NetworkHandler; 38using chromeos::NetworkState; 39using chromeos::NetworkStateHandler; 40 41namespace ash { 42namespace internal { 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 65} // namespace 66 67namespace tray { 68 69class NetworkMessages { 70 public: 71 struct Message { 72 Message() : delegate(NULL) {} 73 Message(NetworkTrayDelegate* in_delegate, 74 NetworkObserver::NetworkType network_type, 75 const base::string16& in_title, 76 const base::string16& in_message, 77 const std::vector<base::string16>& in_links) : 78 delegate(in_delegate), 79 network_type_(network_type), 80 title(in_title), 81 message(in_message), 82 links(in_links) {} 83 NetworkTrayDelegate* delegate; 84 NetworkObserver::NetworkType network_type_; 85 base::string16 title; 86 base::string16 message; 87 std::vector<base::string16> links; 88 }; 89 typedef std::map<NetworkObserver::MessageType, Message> MessageMap; 90 91 MessageMap& messages() { return messages_; } 92 const MessageMap& messages() const { return messages_; } 93 94 private: 95 MessageMap messages_; 96}; 97 98class NetworkTrayView : public TrayItemView, 99 public network_icon::AnimationObserver { 100 public: 101 explicit NetworkTrayView(TrayNetwork* network_tray) 102 : TrayItemView(network_tray), 103 network_tray_(network_tray) { 104 SetLayoutManager( 105 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); 106 107 image_view_ = new views::ImageView; 108 AddChildView(image_view_); 109 110 UpdateNetworkStateHandlerIcon(); 111 } 112 113 virtual ~NetworkTrayView() { 114 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 115 } 116 117 virtual const char* GetClassName() const OVERRIDE { 118 return "NetworkTrayView"; 119 } 120 121 void UpdateNetworkStateHandlerIcon() { 122 NetworkStateHandler* handler = 123 NetworkHandler::Get()->network_state_handler(); 124 gfx::ImageSkia image; 125 base::string16 name; 126 bool animating = false; 127 network_icon::GetDefaultNetworkImageAndLabel( 128 network_icon::ICON_TYPE_TRAY, &image, &name, &animating); 129 bool show_in_tray = !image.isNull(); 130 UpdateIcon(show_in_tray, image); 131 if (animating) 132 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 133 else 134 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 135 // Update accessibility. 136 const NetworkState* connected_network = handler->ConnectedNetworkByType( 137 NetworkStateHandler::kMatchTypeNonVirtual); 138 if (connected_network) 139 UpdateConnectionStatus(UTF8ToUTF16(connected_network->name()), true); 140 else 141 UpdateConnectionStatus(base::string16(), false); 142 } 143 144 // views::View override. 145 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE { 146 state->name = connection_status_string_; 147 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; 148 } 149 150 // network_icon::AnimationObserver 151 virtual void NetworkIconChanged() OVERRIDE { 152 UpdateNetworkStateHandlerIcon(); 153 } 154 155 private: 156 // Updates connection status and notifies accessibility event when necessary. 157 void UpdateConnectionStatus(const base::string16& network_name, 158 bool connected) { 159 base::string16 new_connection_status_string; 160 if (connected) { 161 new_connection_status_string = l10n_util::GetStringFUTF16( 162 IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, network_name); 163 } 164 if (new_connection_status_string != connection_status_string_) { 165 connection_status_string_ = new_connection_status_string; 166 if(!connection_status_string_.empty()) 167 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true); 168 } 169 } 170 171 void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image) { 172 image_view_->SetImage(image); 173 SetVisible(tray_icon_visible); 174 SchedulePaint(); 175 } 176 177 TrayNetwork* network_tray_; 178 views::ImageView* image_view_; 179 base::string16 connection_status_string_; 180 181 DISALLOW_COPY_AND_ASSIGN(NetworkTrayView); 182}; 183 184class NetworkDefaultView : public TrayItemMore, 185 public network_icon::AnimationObserver { 186 public: 187 NetworkDefaultView(TrayNetwork* network_tray, bool show_more) 188 : TrayItemMore(network_tray, show_more), 189 network_tray_(network_tray) { 190 Update(); 191 } 192 193 virtual ~NetworkDefaultView() { 194 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 195 } 196 197 void Update() { 198 gfx::ImageSkia image; 199 base::string16 label; 200 bool animating = false; 201 network_icon::GetDefaultNetworkImageAndLabel( 202 network_icon::ICON_TYPE_DEFAULT_VIEW, &image, &label, &animating); 203 if (animating) 204 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 205 else 206 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 207 SetImage(&image); 208 SetLabel(label); 209 SetAccessibleName(label); 210 } 211 212 // network_icon::AnimationObserver 213 virtual void NetworkIconChanged() OVERRIDE { 214 Update(); 215 } 216 217 private: 218 TrayNetwork* network_tray_; 219 220 DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView); 221}; 222 223class NetworkWifiDetailedView : public NetworkDetailedView { 224 public: 225 explicit NetworkWifiDetailedView(SystemTrayItem* owner) 226 : NetworkDetailedView(owner) { 227 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 228 kTrayPopupPaddingHorizontal, 229 10, 230 kTrayPopupPaddingBetweenItems)); 231 image_view_ = new views::ImageView; 232 AddChildView(image_view_); 233 234 label_view_ = new views::Label(); 235 label_view_->SetMultiLine(true); 236 label_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 237 AddChildView(label_view_); 238 239 Update(); 240 } 241 242 virtual ~NetworkWifiDetailedView() { 243 } 244 245 // Overridden from NetworkDetailedView: 246 247 virtual void Init() OVERRIDE { 248 } 249 250 virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE { 251 return NetworkDetailedView::WIFI_VIEW; 252 } 253 254 virtual void ManagerChanged() OVERRIDE { 255 Update(); 256 } 257 258 virtual void NetworkListChanged() OVERRIDE { 259 Update(); 260 } 261 262 virtual void NetworkServiceChanged( 263 const chromeos::NetworkState* network) OVERRIDE { 264 } 265 266 private: 267 void Update() { 268 bool wifi_enabled = NetworkHandler::Get()->network_state_handler()-> 269 IsTechnologyEnabled(flimflam::kTypeWifi); 270 const int image_id = wifi_enabled ? 271 IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED; 272 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 273 image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia()); 274 275 const int string_id = wifi_enabled ? 276 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED : 277 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED; 278 label_view_->SetText(bundle.GetLocalizedString(string_id)); 279 } 280 281 views::ImageView* image_view_; 282 views::Label* label_view_; 283 284 DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView); 285}; 286 287class NetworkMessageView : public views::View, 288 public views::LinkListener { 289 public: 290 NetworkMessageView(TrayNetwork* tray_network, 291 NetworkObserver::MessageType message_type, 292 const NetworkMessages::Message& network_msg) 293 : tray_network_(tray_network), 294 message_type_(message_type), 295 network_type_(network_msg.network_type_) { 296 SetLayoutManager( 297 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); 298 299 if (!network_msg.title.empty()) { 300 views::Label* title = new views::Label(network_msg.title); 301 title->SetHorizontalAlignment(gfx::ALIGN_LEFT); 302 title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD)); 303 AddChildView(title); 304 } 305 306 if (!network_msg.message.empty()) { 307 views::Label* message = new views::Label(network_msg.message); 308 message->SetHorizontalAlignment(gfx::ALIGN_LEFT); 309 message->SetMultiLine(true); 310 message->SizeToFit(kTrayNotificationContentsWidth); 311 AddChildView(message); 312 } 313 314 if (!network_msg.links.empty()) { 315 for (size_t i = 0; i < network_msg.links.size(); ++i) { 316 views::Link* link = new views::Link(network_msg.links[i]); 317 link->set_id(i); 318 link->set_listener(this); 319 link->SetHorizontalAlignment(gfx::ALIGN_LEFT); 320 link->SetMultiLine(true); 321 link->SizeToFit(kTrayNotificationContentsWidth); 322 AddChildView(link); 323 } 324 } 325 } 326 327 virtual ~NetworkMessageView() { 328 } 329 330 // Overridden from views::LinkListener. 331 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE { 332 tray_network_->LinkClicked(message_type_, source->id()); 333 } 334 335 NetworkObserver::MessageType message_type() const { return message_type_; } 336 NetworkObserver::NetworkType network_type() const { return network_type_; } 337 338 private: 339 TrayNetwork* tray_network_; 340 NetworkObserver::MessageType message_type_; 341 NetworkObserver::NetworkType network_type_; 342 343 DISALLOW_COPY_AND_ASSIGN(NetworkMessageView); 344}; 345 346class NetworkNotificationView : public TrayNotificationView { 347 public: 348 explicit NetworkNotificationView(TrayNetwork* tray_network) 349 : TrayNotificationView(tray_network, 0), 350 tray_network_(tray_network) { 351 CreateMessageView(); 352 InitView(network_message_view_); 353 SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 354 GetMessageIcon(network_message_view_->message_type(), 355 network_message_view_->network_type()))); 356 } 357 358 // Overridden from TrayNotificationView. 359 virtual void OnClose() OVERRIDE { 360 tray_network_->ClearNetworkMessage(network_message_view_->message_type()); 361 } 362 363 virtual void OnClickAction() OVERRIDE { 364 if (network_message_view_->message_type() != 365 TrayNetwork::MESSAGE_DATA_PROMO) 366 tray_network_->PopupDetailedView(0, true); 367 } 368 369 void Update() { 370 CreateMessageView(); 371 UpdateViewAndImage(network_message_view_, 372 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 373 GetMessageIcon(network_message_view_->message_type(), 374 network_message_view_->network_type()))); 375 } 376 377 private: 378 void CreateMessageView() { 379 // Display the first (highest priority) message. 380 CHECK(!tray_network_->messages()->messages().empty()); 381 NetworkMessages::MessageMap::const_iterator iter = 382 tray_network_->messages()->messages().begin(); 383 network_message_view_ = 384 new NetworkMessageView(tray_network_, iter->first, iter->second); 385 } 386 387 TrayNetwork* tray_network_; 388 tray::NetworkMessageView* network_message_view_; 389 390 DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView); 391}; 392 393} // namespace tray 394 395TrayNetwork::TrayNetwork(SystemTray* system_tray) 396 : SystemTrayItem(system_tray), 397 tray_(NULL), 398 default_(NULL), 399 detailed_(NULL), 400 notification_(NULL), 401 messages_(new tray::NetworkMessages()), 402 request_wifi_view_(false) { 403 network_state_observer_.reset(new TrayNetworkStateObserver(this)); 404 Shell::GetInstance()->system_tray_notifier()->AddNetworkObserver(this); 405} 406 407TrayNetwork::~TrayNetwork() { 408 Shell::GetInstance()->system_tray_notifier()->RemoveNetworkObserver(this); 409} 410 411views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) { 412 CHECK(tray_ == NULL); 413 if (!chromeos::NetworkHandler::IsInitialized()) 414 return NULL; 415 tray_ = new tray::NetworkTrayView(this); 416 return tray_; 417} 418 419views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) { 420 CHECK(default_ == NULL); 421 if (!chromeos::NetworkHandler::IsInitialized()) 422 return NULL; 423 CHECK(tray_ != NULL); 424 default_ = new tray::NetworkDefaultView( 425 this, status != user::LOGGED_IN_LOCKED); 426 return default_; 427} 428 429views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) { 430 CHECK(detailed_ == NULL); 431 if (!chromeos::NetworkHandler::IsInitialized()) 432 return NULL; 433 // Clear any notifications when showing the detailed view. 434 messages_->messages().clear(); 435 HideNotificationView(); 436 if (request_wifi_view_) { 437 detailed_ = new tray::NetworkWifiDetailedView(this); 438 request_wifi_view_ = false; 439 } else { 440 detailed_ = new tray::NetworkStateListDetailedView( 441 this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status); 442 detailed_->Init(); 443 } 444 return detailed_; 445} 446 447views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) { 448 CHECK(notification_ == NULL); 449 if (messages_->messages().empty()) 450 return NULL; // Message has already been cleared. 451 notification_ = new tray::NetworkNotificationView(this); 452 return notification_; 453} 454 455void TrayNetwork::DestroyTrayView() { 456 tray_ = NULL; 457} 458 459void TrayNetwork::DestroyDefaultView() { 460 default_ = NULL; 461} 462 463void TrayNetwork::DestroyDetailedView() { 464 detailed_ = NULL; 465} 466 467void TrayNetwork::DestroyNotificationView() { 468 notification_ = NULL; 469} 470 471void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) { 472} 473 474void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 475 if (tray_) 476 SetTrayImageItemBorder(tray_, alignment); 477} 478 479void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate, 480 MessageType message_type, 481 NetworkType network_type, 482 const base::string16& title, 483 const base::string16& message, 484 const std::vector<base::string16>& links) { 485 messages_->messages()[message_type] = tray::NetworkMessages::Message( 486 delegate, network_type, title, message, links); 487 if (!Shell::GetInstance()->system_tray_delegate()->IsOobeCompleted()) 488 return; 489 if (notification_) 490 notification_->Update(); 491 else 492 ShowNotificationView(); 493} 494 495void TrayNetwork::ClearNetworkMessage(MessageType message_type) { 496 messages_->messages().erase(message_type); 497 if (messages_->messages().empty()) { 498 HideNotificationView(); 499 return; 500 } 501 if (notification_) 502 notification_->Update(); 503 else 504 ShowNotificationView(); 505} 506 507void TrayNetwork::RequestToggleWifi() { 508 // This will always be triggered by a user action (e.g. keyboard shortcut) 509 if (!detailed_ || 510 detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) { 511 request_wifi_view_ = true; 512 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); 513 } 514 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 515 bool enabled = handler->IsTechnologyEnabled(flimflam::kTypeWifi); 516 handler->SetTechnologyEnabled( 517 flimflam::kTypeWifi, !enabled, 518 chromeos::network_handler::ErrorCallback()); 519} 520 521void TrayNetwork::NetworkStateChanged(bool list_changed) { 522 if (tray_) 523 tray_->UpdateNetworkStateHandlerIcon(); 524 if (default_) 525 default_->Update(); 526 if (detailed_) { 527 if (list_changed) 528 detailed_->NetworkListChanged(); 529 else 530 detailed_->ManagerChanged(); 531 } 532} 533 534void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) { 535 if (detailed_) 536 detailed_->NetworkServiceChanged(network); 537} 538 539void TrayNetwork::LinkClicked(MessageType message_type, int link_id) { 540 tray::NetworkMessages::MessageMap::const_iterator iter = 541 messages()->messages().find(message_type); 542 if (iter != messages()->messages().end() && iter->second.delegate) 543 iter->second.delegate->NotificationLinkClicked(message_type, link_id); 544} 545 546} // namespace internal 547} // namespace ash 548