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/metrics/user_metrics_recorder.h" 9 #include "ash/shell.h" 10 #include "ash/system/chromeos/network/network_icon_animation.h" 11 #include "ash/system/chromeos/network/network_state_list_detailed_view.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_utils.h" 20 #include "base/command_line.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "chromeos/network/network_state.h" 23 #include "chromeos/network/network_state_handler.h" 24 #include "grit/ash_resources.h" 25 #include "grit/ash_strings.h" 26 #include "third_party/cros_system_api/dbus/service_constants.h" 27 #include "ui/accessibility/ax_view_state.h" 28 #include "ui/base/l10n/l10n_util.h" 29 #include "ui/base/resource/resource_bundle.h" 30 #include "ui/views/controls/image_view.h" 31 #include "ui/views/controls/link.h" 32 #include "ui/views/controls/link_listener.h" 33 #include "ui/views/layout/box_layout.h" 34 #include "ui/views/widget/widget.h" 35 36 using chromeos::NetworkHandler; 37 using chromeos::NetworkState; 38 using chromeos::NetworkStateHandler; 39 using chromeos::NetworkTypePattern; 40 41 namespace ash { 42 namespace tray { 43 44 class NetworkTrayView : public TrayItemView, 45 public network_icon::AnimationObserver { 46 public: 47 explicit NetworkTrayView(TrayNetwork* network_tray) 48 : TrayItemView(network_tray), 49 network_tray_(network_tray) { 50 SetLayoutManager( 51 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0)); 52 53 image_view_ = new views::ImageView; 54 AddChildView(image_view_); 55 56 UpdateNetworkStateHandlerIcon(); 57 } 58 59 virtual ~NetworkTrayView() { 60 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 61 } 62 63 virtual const char* GetClassName() const OVERRIDE { 64 return "NetworkTrayView"; 65 } 66 67 void UpdateNetworkStateHandlerIcon() { 68 NetworkStateHandler* handler = 69 NetworkHandler::Get()->network_state_handler(); 70 gfx::ImageSkia image; 71 base::string16 name; 72 bool animating = false; 73 network_icon::GetDefaultNetworkImageAndLabel( 74 network_icon::ICON_TYPE_TRAY, &image, &name, &animating); 75 bool show_in_tray = !image.isNull(); 76 UpdateIcon(show_in_tray, image); 77 if (animating) 78 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 79 else 80 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 81 // Update accessibility. 82 const NetworkState* connected_network = 83 handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual()); 84 if (connected_network) { 85 UpdateConnectionStatus( 86 base::UTF8ToUTF16(connected_network->name()), true); 87 } else { 88 UpdateConnectionStatus(base::string16(), false); 89 } 90 } 91 92 void UpdateAlignment(ShelfAlignment alignment) { 93 SetLayoutManager(new views::BoxLayout( 94 alignment == SHELF_ALIGNMENT_BOTTOM ? 95 views::BoxLayout::kHorizontal : views::BoxLayout::kVertical, 96 0, 0, 0)); 97 Layout(); 98 } 99 100 // views::View override. 101 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE { 102 state->name = connection_status_string_; 103 state->role = ui::AX_ROLE_BUTTON; 104 } 105 106 // network_icon::AnimationObserver 107 virtual void NetworkIconChanged() OVERRIDE { 108 UpdateNetworkStateHandlerIcon(); 109 } 110 111 private: 112 // Updates connection status and notifies accessibility event when necessary. 113 void UpdateConnectionStatus(const base::string16& network_name, 114 bool connected) { 115 base::string16 new_connection_status_string; 116 if (connected) { 117 new_connection_status_string = l10n_util::GetStringFUTF16( 118 IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, network_name); 119 } 120 if (new_connection_status_string != connection_status_string_) { 121 connection_status_string_ = new_connection_status_string; 122 if(!connection_status_string_.empty()) 123 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true); 124 } 125 } 126 127 void UpdateIcon(bool tray_icon_visible, const gfx::ImageSkia& image) { 128 image_view_->SetImage(image); 129 SetVisible(tray_icon_visible); 130 SchedulePaint(); 131 } 132 133 TrayNetwork* network_tray_; 134 views::ImageView* image_view_; 135 base::string16 connection_status_string_; 136 137 DISALLOW_COPY_AND_ASSIGN(NetworkTrayView); 138 }; 139 140 class NetworkDefaultView : public TrayItemMore, 141 public network_icon::AnimationObserver { 142 public: 143 NetworkDefaultView(TrayNetwork* network_tray, bool show_more) 144 : TrayItemMore(network_tray, show_more), 145 network_tray_(network_tray) { 146 Update(); 147 } 148 149 virtual ~NetworkDefaultView() { 150 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 151 } 152 153 void Update() { 154 gfx::ImageSkia image; 155 base::string16 label; 156 bool animating = false; 157 network_icon::GetDefaultNetworkImageAndLabel( 158 network_icon::ICON_TYPE_DEFAULT_VIEW, &image, &label, &animating); 159 if (animating) 160 network_icon::NetworkIconAnimation::GetInstance()->AddObserver(this); 161 else 162 network_icon::NetworkIconAnimation::GetInstance()->RemoveObserver(this); 163 SetImage(&image); 164 SetLabel(label); 165 SetAccessibleName(label); 166 } 167 168 // network_icon::AnimationObserver 169 virtual void NetworkIconChanged() OVERRIDE { 170 Update(); 171 } 172 173 private: 174 TrayNetwork* network_tray_; 175 176 DISALLOW_COPY_AND_ASSIGN(NetworkDefaultView); 177 }; 178 179 class NetworkWifiDetailedView : public NetworkDetailedView { 180 public: 181 explicit NetworkWifiDetailedView(SystemTrayItem* owner) 182 : NetworkDetailedView(owner) { 183 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kHorizontal, 184 kTrayPopupPaddingHorizontal, 185 10, 186 kTrayPopupPaddingBetweenItems)); 187 image_view_ = new views::ImageView; 188 AddChildView(image_view_); 189 190 label_view_ = new views::Label(); 191 label_view_->SetMultiLine(true); 192 label_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 193 AddChildView(label_view_); 194 195 Update(); 196 } 197 198 virtual ~NetworkWifiDetailedView() { 199 } 200 201 // Overridden from NetworkDetailedView: 202 203 virtual void Init() OVERRIDE { 204 } 205 206 virtual NetworkDetailedView::DetailedViewType GetViewType() const OVERRIDE { 207 return NetworkDetailedView::WIFI_VIEW; 208 } 209 210 virtual void ManagerChanged() OVERRIDE { 211 Update(); 212 } 213 214 virtual void NetworkListChanged() OVERRIDE { 215 Update(); 216 } 217 218 virtual void NetworkServiceChanged( 219 const chromeos::NetworkState* network) OVERRIDE { 220 } 221 222 private: 223 virtual void Layout() OVERRIDE { 224 // Center both views vertically. 225 views::View::Layout(); 226 image_view_->SetY( 227 (height() - image_view_->GetPreferredSize().height()) / 2); 228 label_view_->SetY( 229 (height() - label_view_->GetPreferredSize().height()) / 2); 230 } 231 232 void Update() { 233 bool wifi_enabled = 234 NetworkHandler::Get()->network_state_handler()->IsTechnologyEnabled( 235 NetworkTypePattern::WiFi()); 236 const int image_id = wifi_enabled ? 237 IDR_AURA_UBER_TRAY_WIFI_ENABLED : IDR_AURA_UBER_TRAY_WIFI_DISABLED; 238 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 239 image_view_->SetImage(bundle.GetImageNamed(image_id).ToImageSkia()); 240 241 const int string_id = wifi_enabled ? 242 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_ENABLED : 243 IDS_ASH_STATUS_TRAY_NETWORK_WIFI_DISABLED; 244 label_view_->SetText(bundle.GetLocalizedString(string_id)); 245 label_view_->SizeToFit(kTrayPopupMinWidth - 246 kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems - 247 kTrayPopupDetailsIconWidth); 248 } 249 250 views::ImageView* image_view_; 251 views::Label* label_view_; 252 253 DISALLOW_COPY_AND_ASSIGN(NetworkWifiDetailedView); 254 }; 255 256 } // namespace tray 257 258 TrayNetwork::TrayNetwork(SystemTray* system_tray) 259 : SystemTrayItem(system_tray), 260 tray_(NULL), 261 default_(NULL), 262 detailed_(NULL), 263 request_wifi_view_(false) { 264 network_state_observer_.reset(new TrayNetworkStateObserver(this)); 265 SystemTrayNotifier* notifier = Shell::GetInstance()->system_tray_notifier(); 266 notifier->AddNetworkObserver(this); 267 notifier->AddNetworkPortalDetectorObserver(this); 268 } 269 270 TrayNetwork::~TrayNetwork() { 271 SystemTrayNotifier* notifier = Shell::GetInstance()->system_tray_notifier(); 272 notifier->RemoveNetworkObserver(this); 273 notifier->RemoveNetworkPortalDetectorObserver(this); 274 } 275 276 views::View* TrayNetwork::CreateTrayView(user::LoginStatus status) { 277 CHECK(tray_ == NULL); 278 if (!chromeos::NetworkHandler::IsInitialized()) 279 return NULL; 280 tray_ = new tray::NetworkTrayView(this); 281 return tray_; 282 } 283 284 views::View* TrayNetwork::CreateDefaultView(user::LoginStatus status) { 285 CHECK(default_ == NULL); 286 if (!chromeos::NetworkHandler::IsInitialized()) 287 return NULL; 288 CHECK(tray_ != NULL); 289 default_ = new tray::NetworkDefaultView( 290 this, status != user::LOGGED_IN_LOCKED); 291 return default_; 292 } 293 294 views::View* TrayNetwork::CreateDetailedView(user::LoginStatus status) { 295 CHECK(detailed_ == NULL); 296 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 297 ash::UMA_STATUS_AREA_DETAILED_NETWORK_VIEW); 298 if (!chromeos::NetworkHandler::IsInitialized()) 299 return NULL; 300 if (request_wifi_view_) { 301 detailed_ = new tray::NetworkWifiDetailedView(this); 302 request_wifi_view_ = false; 303 } else { 304 detailed_ = new tray::NetworkStateListDetailedView( 305 this, tray::NetworkStateListDetailedView::LIST_TYPE_NETWORK, status); 306 detailed_->Init(); 307 } 308 return detailed_; 309 } 310 311 void TrayNetwork::DestroyTrayView() { 312 tray_ = NULL; 313 } 314 315 void TrayNetwork::DestroyDefaultView() { 316 default_ = NULL; 317 } 318 319 void TrayNetwork::DestroyDetailedView() { 320 detailed_ = NULL; 321 } 322 323 void TrayNetwork::UpdateAfterLoginStatusChange(user::LoginStatus status) { 324 } 325 326 void TrayNetwork::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) { 327 if (tray_) { 328 SetTrayImageItemBorder(tray_, alignment); 329 tray_->UpdateAlignment(alignment); 330 } 331 } 332 333 void TrayNetwork::RequestToggleWifi() { 334 // This will always be triggered by a user action (e.g. keyboard shortcut) 335 if (!detailed_ || 336 detailed_->GetViewType() == tray::NetworkDetailedView::WIFI_VIEW) { 337 request_wifi_view_ = true; 338 PopupDetailedView(kTrayPopupAutoCloseDelayForTextInSeconds, false); 339 } 340 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 341 bool enabled = handler->IsTechnologyEnabled(NetworkTypePattern::WiFi()); 342 Shell::GetInstance()->metrics()->RecordUserMetricsAction( 343 enabled ? 344 ash::UMA_STATUS_AREA_DISABLE_WIFI : 345 ash::UMA_STATUS_AREA_ENABLE_WIFI); 346 handler->SetTechnologyEnabled(NetworkTypePattern::WiFi(), 347 !enabled, 348 chromeos::network_handler::ErrorCallback()); 349 } 350 351 void TrayNetwork::OnCaptivePortalDetected( 352 const std::string& /* service_path */) { 353 NetworkStateChanged(false); 354 } 355 356 void TrayNetwork::NetworkStateChanged(bool list_changed) { 357 if (tray_) 358 tray_->UpdateNetworkStateHandlerIcon(); 359 if (default_) 360 default_->Update(); 361 if (detailed_) { 362 if (list_changed) 363 detailed_->NetworkListChanged(); 364 else 365 detailed_->ManagerChanged(); 366 } 367 } 368 369 void TrayNetwork::NetworkServiceChanged(const chromeos::NetworkState* network) { 370 if (detailed_) 371 detailed_->NetworkServiceChanged(network); 372 } 373 374 } // namespace ash 375