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 37 using chromeos::NetworkHandler; 38 using chromeos::NetworkState; 39 using chromeos::NetworkStateHandler; 40 41 namespace ash { 42 namespace internal { 43 44 namespace { 45 46 int 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 67 namespace tray { 68 69 class 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 98 class 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 184 class 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 223 class 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 virtual void Layout() OVERRIDE { 268 // Center both views vertically. 269 views::View::Layout(); 270 image_view_->SetY( 271 (height() - image_view_->GetPreferredSize().height()) / 2); 272 label_view_->SetY( 273 (height() - label_view_->GetPreferredSize().height()) / 2); 274 } 275 276 void Update() { 277 bool wifi_enabled = NetworkHandler::Get()->network_state_handler()-> 278 IsTechnologyEnabled(flimflam::kTypeWifi); 279 const int image_id = wifi_enabled ? 280 IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED; 281 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 282 image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia()); 283 284 const int string_id = wifi_enabled ? 285 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED : 286 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED; 287 label_view_->SetText(bundle.GetLocalizedString(string_id)); 288 label_view_->SizeToFit(kTrayPopupMinWidth - 289 kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems - 290 kTrayPopupDetailsIconWidth); 291 } 292 293 views::ImageView* image_view_; 294 views::Label* label_view_; 295 296 DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView); 297 }; 298 299 class NetworkMessageView : public views::View, 300 public views::LinkListener { 301 public: 302 NetworkMessageView(TrayNetwork* tray_network, 303 NetworkObserver::MessageType message_type, 304 const NetworkMessages::Message& network_msg) 305 : tray_network_(tray_network), 306 message_type_(message_type), 307 network_type_(network_msg.network_type_) { 308 SetLayoutManager( 309 new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 1)); 310 311 if (!network_msg.title.empty()) { 312 views::Label* title = new views::Label(network_msg.title); 313 title->SetHorizontalAlignment(gfx::ALIGN_LEFT); 314 title->SetFont(title->font().DeriveFont(0, gfx::Font::BOLD)); 315 AddChildView(title); 316 } 317 318 if (!network_msg.message.empty()) { 319 views::Label* message = new views::Label(network_msg.message); 320 message->SetHorizontalAlignment(gfx::ALIGN_LEFT); 321 message->SetMultiLine(true); 322 message->SizeToFit(kTrayNotificationContentsWidth); 323 AddChildView(message); 324 } 325 326 if (!network_msg.links.empty()) { 327 for (size_t i = 0; i < network_msg.links.size(); ++i) { 328 views::Link* link = new views::Link(network_msg.links[i]); 329 link->set_id(i); 330 link->set_listener(this); 331 link->SetHorizontalAlignment(gfx::ALIGN_LEFT); 332 link->SetMultiLine(true); 333 link->SizeToFit(kTrayNotificationContentsWidth); 334 AddChildView(link); 335 } 336 } 337 } 338 339 virtual ~NetworkMessageView() { 340 } 341 342 // Overridden from views::LinkListener. 343 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE { 344 tray_network_->LinkClicked(message_type_, source->id()); 345 } 346 347 NetworkObserver::MessageType message_type() const { return message_type_; } 348 NetworkObserver::NetworkType network_type() const { return network_type_; } 349 350 private: 351 TrayNetwork* tray_network_; 352 NetworkObserver::MessageType message_type_; 353 NetworkObserver::NetworkType network_type_; 354 355 DISALLOW_COPY_AND_ASSIGN(NetworkMessageView); 356 }; 357 358 class NetworkNotificationView : public TrayNotificationView { 359 public: 360 explicit NetworkNotificationView(TrayNetwork* tray_network) 361 : TrayNotificationView(tray_network, 0), 362 tray_network_(tray_network) { 363 CreateMessageView(); 364 InitView(network_message_view_); 365 SetIconImage(*ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 366 GetMessageIcon(network_message_view_->message_type(), 367 network_message_view_->network_type()))); 368 } 369 370 // Overridden from TrayNotificationView. 371 virtual void OnClose() OVERRIDE { 372 tray_network_->ClearNetworkMessage(network_message_view_->message_type()); 373 } 374 375 virtual void OnClickAction() OVERRIDE { 376 if (network_message_view_->message_type() != 377 TrayNetwork::MESSAGE_DATA_PROMO) 378 tray_network_->PopupDetailedView(0, true); 379 } 380 381 void Update() { 382 CreateMessageView(); 383 UpdateViewAndImage(network_message_view_, 384 *ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 385 GetMessageIcon(network_message_view_->message_type(), 386 network_message_view_->network_type()))); 387 } 388 389 private: 390 void CreateMessageView() { 391 // Display the first (highest priority) message. 392 CHECK(!tray_network_->messages()->messages().empty()); 393 NetworkMessages::MessageMap::const_iterator iter = 394 tray_network_->messages()->messages().begin(); 395 network_message_view_ = 396 new NetworkMessageView(tray_network_, iter->first, iter->second); 397 } 398 399 TrayNetwork* tray_network_; 400 tray::NetworkMessageView* network_message_view_; 401 402 DISALLOW_COPY_AND_ASSIGN(NetworkNotificationView); 403 }; 404 405 } // namespace tray 406 407 TrayNetwork::TrayNetwork(SystemTray* system_tray) 408 : SystemTrayItem(system_tray), 409 tray_(NULL), 410 default_(NULL), 411 detailed_(NULL), 412 notification_(NULL), 413 messages_(new tray::NetworkMessages()), 414 request_wifi_view_(false) { 415 network_state_observer_.reset(new TrayNetworkStateObserver(this)); 416 Shell::GetInstance()->system_tray_notifier()->AddNetworkObserver(this); 417 } 418 419 TrayNetwork::~TrayNetwork() { 420 Shell::GetInstance()->system_tray_notifier()->RemoveNetworkObserver(this); 421 } 422 423 views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) { 424 CHECK(tray_ == NULL); 425 if (!chromeos::NetworkHandler::IsInitialized()) 426 return NULL; 427 tray_ = new tray::NetworkTrayView(this); 428 return tray_; 429 } 430 431 views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) { 432 CHECK(default_ == NULL); 433 if (!chromeos::NetworkHandler::IsInitialized()) 434 return NULL; 435 CHECK(tray_ != NULL); 436 default_ = new tray::NetworkDefaultView( 437 this, status != user::LOGGED_IN_LOCKED); 438 return default_; 439 } 440 441 views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) { 442 CHECK(detailed_ == NULL); 443 if (!chromeos::NetworkHandler::IsInitialized()) 444 return NULL; 445 // Clear any notifications when showing the detailed view. 446 messages_->messages().clear(); 447 HideNotificationView(); 448 if (request_wifi_view_) { 449 detailed_ = new tray::NetworkWifiDetailedView(this); 450 request_wifi_view_ = false; 451 } else { 452 detailed_ = new tray::NetworkStateListDetailedView( 453 this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status); 454 detailed_->Init(); 455 } 456 return detailed_; 457 } 458 459 views::View* TrayNetwork::CreateNotificationView(user::LoginStatus status) { 460 CHECK(notification_ == NULL); 461 if (messages_->messages().empty()) 462 return NULL; // Message has already been cleared. 463 notification_ = new tray::NetworkNotificationView(this); 464 return notification_; 465 } 466 467 void TrayNetwork::DestroyTrayView() { 468 tray_ = NULL; 469 } 470 471 void TrayNetwork::DestroyDefaultView() { 472 default_ = NULL; 473 } 474 475 void TrayNetwork::DestroyDetailedView() { 476 detailed_ = NULL; 477 } 478 479 void TrayNetwork::DestroyNotificationView() { 480 notification_ = NULL; 481 } 482 483 void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) { 484 } 485 486 void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 487 if (tray_) 488 SetTrayImageItemBorder(tray_, alignment); 489 } 490 491 void TrayNetwork::SetNetworkMessage(NetworkTrayDelegate* delegate, 492 MessageType message_type, 493 NetworkType network_type, 494 const base::string16& title, 495 const base::string16& message, 496 const std::vector<base::string16>& links) { 497 messages_->messages()[message_type] = tray::NetworkMessages::Message( 498 delegate, network_type, title, message, links); 499 if (!Shell::GetInstance()->system_tray_delegate()->IsOobeCompleted()) 500 return; 501 if (notification_) 502 notification_->Update(); 503 else 504 ShowNotificationView(); 505 } 506 507 void TrayNetwork::ClearNetworkMessage(MessageType message_type) { 508 messages_->messages().erase(message_type); 509 if (messages_->messages().empty()) { 510 HideNotificationView(); 511 return; 512 } 513 if (notification_) 514 notification_->Update(); 515 else 516 ShowNotificationView(); 517 } 518 519 void TrayNetwork::RequestToggleWifi() { 520 // This will always be triggered by a user action (e.g. keyboard shortcut) 521 if (!detailed_ || 522 detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) { 523 request_wifi_view_ = true; 524 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); 525 } 526 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 527 bool enabled = handler->IsTechnologyEnabled(flimflam::kTypeWifi); 528 handler->SetTechnologyEnabled( 529 flimflam::kTypeWifi, !enabled, 530 chromeos::network_handler::ErrorCallback()); 531 } 532 533 void TrayNetwork::NetworkStateChanged(bool list_changed) { 534 if (tray_) 535 tray_->UpdateNetworkStateHandlerIcon(); 536 if (default_) 537 default_->Update(); 538 if (detailed_) { 539 if (list_changed) 540 detailed_->NetworkListChanged(); 541 else 542 detailed_->ManagerChanged(); 543 } 544 } 545 546 void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) { 547 if (detailed_) 548 detailed_->NetworkServiceChanged(network); 549 } 550 551 void TrayNetwork::LinkClicked(MessageType message_type, int link_id) { 552 tray::NetworkMessages::MessageMap::const_iterator iter = 553 messages()->messages().find(message_type); 554 if (iter != messages()->messages().end() && iter->second.delegate) 555 iter->second.delegate->NotificationLinkClicked(message_type, link_id); 556 } 557 558 } // namespace internal 559 } // namespace ash 560