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 "ui/chromeos/network/network_icon.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chromeos/network/device_state.h" 9 #include "chromeos/network/network_connection_handler.h" 10 #include "chromeos/network/network_state.h" 11 #include "chromeos/network/network_state_handler.h" 12 #include "chromeos/network/portal_detector/network_portal_detector.h" 13 #include "grit/ui_chromeos_resources.h" 14 #include "grit/ui_chromeos_strings.h" 15 #include "third_party/cros_system_api/dbus/service_constants.h" 16 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/base/webui/web_ui_util.h" 19 #include "ui/chromeos/network/network_icon_animation.h" 20 #include "ui/chromeos/network/network_icon_animation_observer.h" 21 #include "ui/gfx/canvas.h" 22 #include "ui/gfx/image/image_skia_operations.h" 23 #include "ui/gfx/image/image_skia_source.h" 24 #include "ui/gfx/rect.h" 25 #include "ui/gfx/size_conversions.h" 26 27 using chromeos::DeviceState; 28 using chromeos::NetworkConnectionHandler; 29 using chromeos::NetworkHandler; 30 using chromeos::NetworkPortalDetector; 31 using chromeos::NetworkState; 32 using chromeos::NetworkStateHandler; 33 using chromeos::NetworkTypePattern; 34 35 namespace ui { 36 namespace network_icon { 37 38 namespace { 39 40 //------------------------------------------------------------------------------ 41 // Struct to pass icon badges to NetworkIconImageSource. 42 struct Badges { 43 Badges() 44 : top_left(NULL), 45 top_right(NULL), 46 bottom_left(NULL), 47 bottom_right(NULL) { 48 } 49 const gfx::ImageSkia* top_left; 50 const gfx::ImageSkia* top_right; 51 const gfx::ImageSkia* bottom_left; 52 const gfx::ImageSkia* bottom_right; 53 }; 54 55 //------------------------------------------------------------------------------ 56 // class used for maintaining a map of network state and images. 57 class NetworkIconImpl { 58 public: 59 NetworkIconImpl(const std::string& path, IconType icon_type); 60 61 // Determines whether or not the associated network might be dirty and if so 62 // updates and generates the icon. Does nothing if network no longer exists. 63 void Update(const chromeos::NetworkState* network); 64 65 // Returns the cached image url for |image_| based on |scale_factor|. 66 const std::string& GetImageUrl(float scale_factor); 67 68 const gfx::ImageSkia& image() const { return image_; } 69 70 private: 71 typedef std::map<float, std::string> ImageUrlMap; 72 73 // Updates |strength_index_| for wireless networks. Returns true if changed. 74 bool UpdateWirelessStrengthIndex(const chromeos::NetworkState* network); 75 76 // Updates the local state for cellular networks. Returns true if changed. 77 bool UpdateCellularState(const chromeos::NetworkState* network); 78 79 // Updates the portal state for wireless networks. Returns true if changed. 80 bool UpdatePortalState(const chromeos::NetworkState* network); 81 82 // Updates the VPN badge. Returns true if changed. 83 bool UpdateVPNBadge(); 84 85 // Gets |badges| based on |network| and the current state. 86 void GetBadges(const NetworkState* network, Badges* badges); 87 88 // Gets the appropriate icon and badges and composites the image. 89 void GenerateImage(const chromeos::NetworkState* network); 90 91 // Network path, used for debugging. 92 std::string network_path_; 93 94 // Defines color theme and VPN badging 95 const IconType icon_type_; 96 97 // Cached state of the network when the icon was last generated. 98 std::string state_; 99 100 // Cached strength index of the network when the icon was last generated. 101 int strength_index_; 102 103 // Cached technology badge for the network when the icon was last generated. 104 const gfx::ImageSkia* technology_badge_; 105 106 // Cached vpn badge for the network when the icon was last generated. 107 const gfx::ImageSkia* vpn_badge_; 108 109 // Cached roaming state of the network when the icon was last generated. 110 std::string roaming_state_; 111 112 // Cached portal state of the network when the icon was last generated. 113 bool behind_captive_portal_; 114 115 // Generated icon image. 116 gfx::ImageSkia image_; 117 118 // Map of cached image urls by scale factor. Cleared whenever image_ is 119 // generated. 120 ImageUrlMap image_urls_; 121 122 DISALLOW_COPY_AND_ASSIGN(NetworkIconImpl); 123 }; 124 125 //------------------------------------------------------------------------------ 126 // Maintain a static (global) icon map. Note: Icons are never destroyed; 127 // it is assumed that a finite and reasonable number of network icons will be 128 // created during a session. 129 130 typedef std::map<std::string, NetworkIconImpl*> NetworkIconMap; 131 132 NetworkIconMap* GetIconMapInstance(IconType icon_type, bool create) { 133 typedef std::map<IconType, NetworkIconMap*> IconTypeMap; 134 static IconTypeMap* s_icon_map = NULL; 135 if (s_icon_map == NULL) { 136 if (!create) 137 return NULL; 138 s_icon_map = new IconTypeMap; 139 } 140 if (s_icon_map->count(icon_type) == 0) { 141 if (!create) 142 return NULL; 143 (*s_icon_map)[icon_type] = new NetworkIconMap; 144 } 145 return (*s_icon_map)[icon_type]; 146 } 147 148 NetworkIconMap* GetIconMap(IconType icon_type) { 149 return GetIconMapInstance(icon_type, true); 150 } 151 152 void PurgeIconMap(IconType icon_type, 153 const std::set<std::string>& network_paths) { 154 NetworkIconMap* icon_map = GetIconMapInstance(icon_type, false); 155 if (!icon_map) 156 return; 157 for (NetworkIconMap::iterator loop_iter = icon_map->begin(); 158 loop_iter != icon_map->end(); ) { 159 NetworkIconMap::iterator cur_iter = loop_iter++; 160 if (network_paths.count(cur_iter->first) == 0) { 161 delete cur_iter->second; 162 icon_map->erase(cur_iter); 163 } 164 } 165 } 166 167 //------------------------------------------------------------------------------ 168 // Utilities for generating icon images. 169 170 // 'NONE' will default to ARCS behavior where appropriate (e.g. no network or 171 // if a new type gets added). 172 enum ImageType { 173 ARCS, 174 BARS, 175 NONE 176 }; 177 178 // Amount to fade icons while connecting. 179 const double kConnectingImageAlpha = 0.5; 180 181 // Images for strength bars for wired networks. 182 const int kNumBarsImages = 5; 183 184 // Imagaes for strength arcs for wireless networks. 185 const int kNumArcsImages = 5; 186 187 // Number of discrete images to use for alpha fade animation 188 const int kNumFadeImages = 10; 189 190 //------------------------------------------------------------------------------ 191 // Classes for generating scaled images. 192 193 const SkBitmap GetEmptyBitmap(const gfx::Size pixel_size) { 194 typedef std::pair<int, int> SizeKey; 195 typedef std::map<SizeKey, SkBitmap> SizeBitmapMap; 196 static SizeBitmapMap* s_empty_bitmaps = new SizeBitmapMap; 197 198 SizeKey key(pixel_size.width(), pixel_size.height()); 199 200 SizeBitmapMap::iterator iter = s_empty_bitmaps->find(key); 201 if (iter != s_empty_bitmaps->end()) 202 return iter->second; 203 204 SkBitmap empty; 205 empty.allocN32Pixels(key.first, key.second); 206 empty.eraseARGB(0, 0, 0, 0); 207 (*s_empty_bitmaps)[key] = empty; 208 return empty; 209 } 210 211 class EmptyImageSource: public gfx::ImageSkiaSource { 212 public: 213 explicit EmptyImageSource(const gfx::Size& size) 214 : size_(size) { 215 } 216 217 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 218 gfx::Size pixel_size = gfx::ToFlooredSize(gfx::ScaleSize(size_, scale)); 219 SkBitmap empty_bitmap = GetEmptyBitmap(pixel_size); 220 return gfx::ImageSkiaRep(empty_bitmap, scale); 221 } 222 223 private: 224 const gfx::Size size_; 225 226 DISALLOW_COPY_AND_ASSIGN(EmptyImageSource); 227 }; 228 229 // This defines how we assemble a network icon. 230 class NetworkIconImageSource : public gfx::ImageSkiaSource { 231 public: 232 NetworkIconImageSource(const gfx::ImageSkia& icon, const Badges& badges) 233 : icon_(icon), 234 badges_(badges) { 235 } 236 virtual ~NetworkIconImageSource() {} 237 238 // TODO(pkotwicz): Figure out what to do when a new image resolution becomes 239 // available. 240 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 241 gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale); 242 if (icon_rep.is_null()) 243 return gfx::ImageSkiaRep(); 244 gfx::Canvas canvas(icon_rep, false); 245 if (badges_.top_left) 246 canvas.DrawImageInt(*badges_.top_left, 0, 0); 247 if (badges_.top_right) 248 canvas.DrawImageInt(*badges_.top_right, 249 icon_.width() - badges_.top_right->width(), 0); 250 if (badges_.bottom_left) { 251 canvas.DrawImageInt(*badges_.bottom_left, 252 0, icon_.height() - badges_.bottom_left->height()); 253 } 254 if (badges_.bottom_right) { 255 canvas.DrawImageInt(*badges_.bottom_right, 256 icon_.width() - badges_.bottom_right->width(), 257 icon_.height() - badges_.bottom_right->height()); 258 } 259 return canvas.ExtractImageRep(); 260 } 261 262 private: 263 const gfx::ImageSkia icon_; 264 const Badges badges_; 265 266 DISALLOW_COPY_AND_ASSIGN(NetworkIconImageSource); 267 }; 268 269 //------------------------------------------------------------------------------ 270 // Utilities for extracting icon images. 271 272 // A struct used for caching image urls. 273 struct ImageIdForNetworkType { 274 ImageIdForNetworkType(IconType icon_type, 275 const std::string& network_type, 276 float scale_factor) : 277 icon_type(icon_type), 278 network_type(network_type), 279 scale_factor(scale_factor) {} 280 bool operator<(const ImageIdForNetworkType& other) const { 281 if (icon_type != other.icon_type) 282 return icon_type < other.icon_type; 283 if (network_type != other.network_type) 284 return network_type < other.network_type; 285 return scale_factor < other.scale_factor; 286 } 287 IconType icon_type; 288 std::string network_type; 289 float scale_factor; 290 }; 291 292 typedef std::map<ImageIdForNetworkType, std::string> ImageIdUrlMap; 293 294 bool IconTypeIsDark(IconType icon_type) { 295 return (icon_type != ICON_TYPE_TRAY); 296 } 297 298 bool IconTypeHasVPNBadge(IconType icon_type) { 299 return (icon_type != ICON_TYPE_LIST); 300 } 301 302 int NumImagesForType(ImageType type) { 303 return (type == BARS) ? kNumBarsImages : kNumArcsImages; 304 } 305 306 gfx::ImageSkia* BaseImageForType(ImageType image_type, IconType icon_type) { 307 gfx::ImageSkia* image; 308 if (image_type == BARS) { 309 image = ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 310 IconTypeIsDark(icon_type) ? 311 IDR_AURA_UBER_TRAY_NETWORK_BARS_DARK : 312 IDR_AURA_UBER_TRAY_NETWORK_BARS_LIGHT); 313 } else { 314 image = ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 315 IconTypeIsDark(icon_type) ? 316 IDR_AURA_UBER_TRAY_NETWORK_ARCS_DARK : 317 IDR_AURA_UBER_TRAY_NETWORK_ARCS_LIGHT); 318 } 319 return image; 320 } 321 322 ImageType ImageTypeForNetworkType(const std::string& type) { 323 if (type == shill::kTypeWifi) 324 return ARCS; 325 else if (type == shill::kTypeCellular || type == shill::kTypeWimax) 326 return BARS; 327 return NONE; 328 } 329 330 gfx::ImageSkia GetImageForIndex(ImageType image_type, 331 IconType icon_type, 332 int index) { 333 int num_images = NumImagesForType(image_type); 334 if (index < 0 || index >= num_images) 335 return gfx::ImageSkia(); 336 gfx::ImageSkia* images = BaseImageForType(image_type, icon_type); 337 int width = images->width(); 338 int height = images->height() / num_images; 339 return gfx::ImageSkiaOperations::ExtractSubset(*images, 340 gfx::Rect(0, index * height, width, height)); 341 } 342 343 const gfx::ImageSkia GetConnectedImage(IconType icon_type, 344 const std::string& network_type) { 345 if (network_type == shill::kTypeVPN) { 346 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 347 IDR_AURA_UBER_TRAY_NETWORK_VPN); 348 } 349 ImageType image_type = ImageTypeForNetworkType(network_type); 350 const int connected_index = NumImagesForType(image_type) - 1; 351 return GetImageForIndex(image_type, icon_type, connected_index); 352 } 353 354 const gfx::ImageSkia GetDisconnectedImage(IconType icon_type, 355 const std::string& network_type) { 356 if (network_type == shill::kTypeVPN) { 357 // Note: same as connected image, shouldn't normally be seen. 358 return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 359 IDR_AURA_UBER_TRAY_NETWORK_VPN); 360 } 361 ImageType image_type = ImageTypeForNetworkType(network_type); 362 const int disconnected_index = 0; 363 return GetImageForIndex(image_type, icon_type, disconnected_index); 364 } 365 366 gfx::ImageSkia* ConnectingWirelessImage(ImageType image_type, 367 IconType icon_type, 368 double animation) { 369 static gfx::ImageSkia* s_bars_images_dark[kNumBarsImages - 1]; 370 static gfx::ImageSkia* s_bars_images_light[kNumBarsImages - 1]; 371 static gfx::ImageSkia* s_arcs_images_dark[kNumArcsImages - 1]; 372 static gfx::ImageSkia* s_arcs_images_light[kNumArcsImages - 1]; 373 int image_count = NumImagesForType(image_type) - 1; 374 int index = animation * nextafter(static_cast<float>(image_count), 0); 375 index = std::max(std::min(index, image_count - 1), 0); 376 gfx::ImageSkia** images; 377 bool dark = IconTypeIsDark(icon_type); 378 if (image_type == BARS) 379 images = dark ? s_bars_images_dark : s_bars_images_light; 380 else 381 images = dark ? s_arcs_images_dark : s_arcs_images_light; 382 if (!images[index]) { 383 // Lazily cache images. 384 gfx::ImageSkia source = GetImageForIndex(image_type, icon_type, index + 1); 385 images[index] = new gfx::ImageSkia( 386 gfx::ImageSkiaOperations::CreateBlendedImage( 387 gfx::ImageSkia(new EmptyImageSource(source.size()), source.size()), 388 source, 389 kConnectingImageAlpha)); 390 } 391 return images[index]; 392 } 393 394 gfx::ImageSkia* ConnectingVpnImage(double animation) { 395 int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0); 396 static gfx::ImageSkia* s_vpn_images[kNumFadeImages]; 397 if (!s_vpn_images[index]) { 398 // Lazily cache images. 399 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 400 gfx::ImageSkia* icon = rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN); 401 s_vpn_images[index] = new gfx::ImageSkia( 402 gfx::ImageSkiaOperations::CreateBlendedImage( 403 gfx::ImageSkia(new EmptyImageSource(icon->size()), icon->size()), 404 *icon, 405 animation)); 406 } 407 return s_vpn_images[index]; 408 } 409 410 gfx::ImageSkia* ConnectingVpnBadge(double animation) { 411 int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0); 412 static gfx::ImageSkia* s_vpn_badges[kNumFadeImages]; 413 if (!s_vpn_badges[index]) { 414 // Lazily cache images. 415 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 416 gfx::ImageSkia* icon = 417 rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED); // For size 418 gfx::ImageSkia* badge = 419 rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE); 420 s_vpn_badges[index] = new gfx::ImageSkia( 421 gfx::ImageSkiaOperations::CreateBlendedImage( 422 gfx::ImageSkia(new EmptyImageSource(icon->size()), icon->size()), 423 *badge, 424 animation)); 425 } 426 return s_vpn_badges[index]; 427 } 428 429 int StrengthIndex(int strength, int count) { 430 // Return an index in the range [1, count-1]. 431 const float findex = (static_cast<float>(strength) / 100.0f) * 432 nextafter(static_cast<float>(count - 1), 0); 433 int index = 1 + static_cast<int>(findex); 434 index = std::max(std::min(index, count - 1), 1); 435 return index; 436 } 437 438 int GetStrengthIndex(const NetworkState* network) { 439 ImageType image_type = ImageTypeForNetworkType(network->type()); 440 if (image_type == ARCS) 441 return StrengthIndex(network->signal_strength(), kNumArcsImages); 442 else if (image_type == BARS) 443 return StrengthIndex(network->signal_strength(), kNumBarsImages); 444 return 0; 445 } 446 447 const gfx::ImageSkia* BadgeForNetworkTechnology(const NetworkState* network, 448 IconType icon_type) { 449 const int kUnknownBadgeType = -1; 450 int id = kUnknownBadgeType; 451 const std::string& technology = network->network_technology(); 452 if (technology == shill::kNetworkTechnologyEvdo) { 453 id = IconTypeIsDark(icon_type) ? 454 IDR_AURA_UBER_TRAY_NETWORK_EVDO_DARK : 455 IDR_AURA_UBER_TRAY_NETWORK_EVDO_LIGHT; 456 } else if (technology == shill::kNetworkTechnology1Xrtt) { 457 id = IDR_AURA_UBER_TRAY_NETWORK_1X; 458 } else if (technology == shill::kNetworkTechnologyGprs) { 459 id = IconTypeIsDark(icon_type) ? 460 IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK : 461 IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT; 462 } else if (technology == shill::kNetworkTechnologyEdge) { 463 id = IconTypeIsDark(icon_type) ? 464 IDR_AURA_UBER_TRAY_NETWORK_EDGE_DARK : 465 IDR_AURA_UBER_TRAY_NETWORK_EDGE_LIGHT; 466 } else if (technology == shill::kNetworkTechnologyUmts) { 467 id = IconTypeIsDark(icon_type) ? 468 IDR_AURA_UBER_TRAY_NETWORK_3G_DARK : 469 IDR_AURA_UBER_TRAY_NETWORK_3G_LIGHT; 470 } else if (technology == shill::kNetworkTechnologyHspa) { 471 id = IconTypeIsDark(icon_type) ? 472 IDR_AURA_UBER_TRAY_NETWORK_HSPA_DARK : 473 IDR_AURA_UBER_TRAY_NETWORK_HSPA_LIGHT; 474 } else if (technology == shill::kNetworkTechnologyHspaPlus) { 475 id = IconTypeIsDark(icon_type) ? 476 IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_DARK : 477 IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_LIGHT; 478 } else if (technology == shill::kNetworkTechnologyLte) { 479 id = IconTypeIsDark(icon_type) ? 480 IDR_AURA_UBER_TRAY_NETWORK_LTE_DARK : 481 IDR_AURA_UBER_TRAY_NETWORK_LTE_LIGHT; 482 } else if (technology == shill::kNetworkTechnologyLteAdvanced) { 483 id = IconTypeIsDark(icon_type) ? 484 IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_DARK : 485 IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_LIGHT; 486 } else if (technology == shill::kNetworkTechnologyGsm) { 487 id = IconTypeIsDark(icon_type) ? 488 IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK : 489 IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT; 490 } 491 if (id == kUnknownBadgeType) 492 return NULL; 493 else 494 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id); 495 } 496 497 const gfx::ImageSkia* BadgeForVPN(IconType icon_type) { 498 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 499 IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE); 500 } 501 502 gfx::ImageSkia GetIcon(const NetworkState* network, 503 IconType icon_type, 504 int strength_index) { 505 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 506 if (network->Matches(NetworkTypePattern::Ethernet())) { 507 return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED); 508 } else if (network->Matches(NetworkTypePattern::Wireless())) { 509 DCHECK(strength_index > 0); 510 return GetImageForIndex( 511 ImageTypeForNetworkType(network->type()), icon_type, strength_index); 512 } else if (network->Matches(NetworkTypePattern::VPN())) { 513 return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN); 514 } else { 515 LOG(WARNING) << "Request for icon for unsupported type: " 516 << network->type(); 517 return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED); 518 } 519 } 520 521 //------------------------------------------------------------------------------ 522 // Get connecting images 523 524 gfx::ImageSkia GetConnectingVpnImage(IconType icon_type) { 525 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 526 const NetworkState* connected_network = NULL; 527 if (icon_type == ICON_TYPE_TRAY) { 528 connected_network = 529 handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual()); 530 } 531 double animation = NetworkIconAnimation::GetInstance()->GetAnimation(); 532 533 if (connected_network) { 534 gfx::ImageSkia icon = GetImageForNetwork(connected_network, icon_type); 535 Badges badges; 536 badges.bottom_left = ConnectingVpnBadge(animation); 537 return gfx::ImageSkia( 538 new NetworkIconImageSource(icon, badges), icon.size()); 539 } else { 540 gfx::ImageSkia* icon = ConnectingVpnImage(animation); 541 return gfx::ImageSkia( 542 new NetworkIconImageSource(*icon, Badges()), icon->size()); 543 } 544 } 545 546 gfx::ImageSkia GetConnectingImage(IconType icon_type, 547 const std::string& network_type) { 548 if (network_type == shill::kTypeVPN) 549 return GetConnectingVpnImage(icon_type); 550 551 ImageType image_type = ImageTypeForNetworkType(network_type); 552 double animation = NetworkIconAnimation::GetInstance()->GetAnimation(); 553 554 gfx::ImageSkia* icon = ConnectingWirelessImage( 555 image_type, icon_type, animation); 556 return gfx::ImageSkia( 557 new NetworkIconImageSource(*icon, Badges()), icon->size()); 558 } 559 560 std::string GetConnectingImageUrl(IconType icon_type, 561 const std::string& network_type, 562 float scale_factor) { 563 // Caching the connecting image is complicated and we will never draw more 564 // than a few per frame, so just generate the image url each time. 565 gfx::ImageSkia image = GetConnectingImage(icon_type, network_type); 566 gfx::ImageSkiaRep image_rep = image.GetRepresentation(scale_factor); 567 return webui::GetBitmapDataUrl(image_rep.sk_bitmap()); 568 } 569 570 } // namespace 571 572 //------------------------------------------------------------------------------ 573 // NetworkIconImpl 574 575 NetworkIconImpl::NetworkIconImpl(const std::string& path, IconType icon_type) 576 : network_path_(path), 577 icon_type_(icon_type), 578 strength_index_(-1), 579 technology_badge_(NULL), 580 vpn_badge_(NULL), 581 behind_captive_portal_(false) { 582 // Default image 583 image_ = GetDisconnectedImage(icon_type, shill::kTypeWifi); 584 } 585 586 void NetworkIconImpl::Update(const NetworkState* network) { 587 DCHECK(network); 588 // Determine whether or not we need to update the icon. 589 bool dirty = image_.isNull(); 590 591 // If the network state has changed, the icon needs updating. 592 if (state_ != network->connection_state()) { 593 state_ = network->connection_state(); 594 dirty = true; 595 } 596 597 dirty |= UpdatePortalState(network); 598 599 if (network->Matches(NetworkTypePattern::Wireless())) { 600 dirty |= UpdateWirelessStrengthIndex(network); 601 } 602 603 if (network->Matches(NetworkTypePattern::Cellular())) 604 dirty |= UpdateCellularState(network); 605 606 if (IconTypeHasVPNBadge(icon_type_) && 607 network->Matches(NetworkTypePattern::NonVirtual())) { 608 dirty |= UpdateVPNBadge(); 609 } 610 611 if (dirty) { 612 // Set the icon and badges based on the network and generate the image. 613 GenerateImage(network); 614 } 615 } 616 617 bool NetworkIconImpl::UpdateWirelessStrengthIndex(const NetworkState* network) { 618 int index = GetStrengthIndex(network); 619 if (index != strength_index_) { 620 strength_index_ = index; 621 return true; 622 } 623 return false; 624 } 625 626 bool NetworkIconImpl::UpdateCellularState(const NetworkState* network) { 627 bool dirty = false; 628 const gfx::ImageSkia* technology_badge = 629 BadgeForNetworkTechnology(network, icon_type_); 630 if (technology_badge != technology_badge_) { 631 technology_badge_ = technology_badge; 632 dirty = true; 633 } 634 std::string roaming_state = network->roaming(); 635 if (roaming_state != roaming_state_) { 636 roaming_state_ = roaming_state; 637 dirty = true; 638 } 639 return dirty; 640 } 641 642 bool NetworkIconImpl::UpdatePortalState(const NetworkState* network) { 643 bool behind_captive_portal = false; 644 if (network && NetworkPortalDetector::IsInitialized()) { 645 NetworkPortalDetector::CaptivePortalState state = 646 NetworkPortalDetector::Get()->GetCaptivePortalState(network->guid()); 647 behind_captive_portal = 648 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL; 649 } 650 651 if (behind_captive_portal == behind_captive_portal_) 652 return false; 653 behind_captive_portal_ = behind_captive_portal; 654 return true; 655 } 656 657 bool NetworkIconImpl::UpdateVPNBadge() { 658 const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()-> 659 ConnectedNetworkByType(NetworkTypePattern::VPN()); 660 if (vpn && vpn_badge_ == NULL) { 661 vpn_badge_ = BadgeForVPN(icon_type_); 662 return true; 663 } else if (!vpn && vpn_badge_ != NULL) { 664 vpn_badge_ = NULL; 665 return true; 666 } 667 return false; 668 } 669 670 void NetworkIconImpl::GetBadges(const NetworkState* network, Badges* badges) { 671 DCHECK(network); 672 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 673 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 674 675 const std::string& type = network->type(); 676 if (type == shill::kTypeWifi) { 677 if (network->security() != shill::kSecurityNone && 678 IconTypeIsDark(icon_type_)) { 679 badges->bottom_right = rb.GetImageSkiaNamed( 680 IDR_AURA_UBER_TRAY_NETWORK_SECURE_DARK); 681 } 682 } else if (type == shill::kTypeWimax) { 683 technology_badge_ = rb.GetImageSkiaNamed( 684 IconTypeIsDark(icon_type_) ? 685 IDR_AURA_UBER_TRAY_NETWORK_4G_DARK : 686 IDR_AURA_UBER_TRAY_NETWORK_4G_LIGHT); 687 } else if (type == shill::kTypeCellular) { 688 if (network->roaming() == shill::kRoamingStateRoaming) { 689 // For networks that are always in roaming don't show roaming badge. 690 const DeviceState* device = 691 handler->GetDeviceState(network->device_path()); 692 LOG_IF(WARNING, !device) << "Could not find device state for " 693 << network->device_path(); 694 if (!device || !device->provider_requires_roaming()) { 695 badges->bottom_right = rb.GetImageSkiaNamed( 696 IconTypeIsDark(icon_type_) ? 697 IDR_AURA_UBER_TRAY_NETWORK_ROAMING_DARK : 698 IDR_AURA_UBER_TRAY_NETWORK_ROAMING_LIGHT); 699 } 700 } 701 } 702 if (!network->IsConnectingState()) { 703 badges->top_left = technology_badge_; 704 badges->bottom_left = vpn_badge_; 705 } 706 707 if (behind_captive_portal_) { 708 gfx::ImageSkia* badge = rb.GetImageSkiaNamed( 709 IconTypeIsDark(icon_type_) ? 710 IDR_AURA_UBER_TRAY_NETWORK_PORTAL_DARK : 711 IDR_AURA_UBER_TRAY_NETWORK_PORTAL_LIGHT); 712 badges->bottom_right = badge; 713 } 714 } 715 716 void NetworkIconImpl::GenerateImage(const NetworkState* network) { 717 DCHECK(network); 718 gfx::ImageSkia icon = GetIcon(network, icon_type_, strength_index_); 719 Badges badges; 720 GetBadges(network, &badges); 721 image_ = gfx::ImageSkia( 722 new NetworkIconImageSource(icon, badges), icon.size()); 723 image_urls_.clear(); 724 } 725 726 const std::string& NetworkIconImpl::GetImageUrl(float scale_factor) { 727 ImageUrlMap::iterator iter = image_urls_.find(scale_factor); 728 if (iter != image_urls_.end()) 729 return iter->second; 730 731 VLOG(2) << "Generating bitmap URL for: " << network_path_; 732 gfx::ImageSkiaRep image_rep = image_.GetRepresentation(scale_factor); 733 iter = image_urls_.insert(std::make_pair( 734 scale_factor, webui::GetBitmapDataUrl(image_rep.sk_bitmap()))).first; 735 return iter->second; 736 } 737 738 namespace { 739 740 NetworkIconImpl* FindAndUpdateImageImpl(const NetworkState* network, 741 IconType icon_type) { 742 // Find or add the icon. 743 NetworkIconMap* icon_map = GetIconMap(icon_type); 744 NetworkIconImpl* icon; 745 NetworkIconMap::iterator iter = icon_map->find(network->path()); 746 if (iter == icon_map->end()) { 747 icon = new NetworkIconImpl(network->path(), icon_type); 748 icon_map->insert(std::make_pair(network->path(), icon)); 749 } else { 750 icon = iter->second; 751 } 752 753 // Update and return the icon's image. 754 icon->Update(network); 755 return icon; 756 } 757 758 } // namespace 759 760 //------------------------------------------------------------------------------ 761 // Public interface 762 763 gfx::ImageSkia GetImageForNetwork(const NetworkState* network, 764 IconType icon_type) { 765 DCHECK(network); 766 if (!network->visible()) 767 return GetDisconnectedImage(icon_type, network->type()); 768 769 if (network->IsConnectingState()) 770 return GetConnectingImage(icon_type, network->type()); 771 772 NetworkIconImpl* icon = FindAndUpdateImageImpl(network, icon_type); 773 return icon->image(); 774 } 775 776 std::string GetImageUrlForNetwork(const NetworkState* network, 777 IconType icon_type, 778 float scale_factor) { 779 DCHECK(network); 780 // Handle connecting icons. 781 if (network->IsConnectingState()) 782 return GetConnectingImageUrl(icon_type, network->type(), scale_factor); 783 784 NetworkIconImpl* icon = FindAndUpdateImageImpl(network, icon_type); 785 return icon->GetImageUrl(scale_factor); 786 } 787 788 gfx::ImageSkia GetImageForConnectedNetwork(IconType icon_type, 789 const std::string& network_type) { 790 return GetConnectedImage(icon_type, network_type); 791 } 792 793 gfx::ImageSkia GetImageForConnectingNetwork(IconType icon_type, 794 const std::string& network_type) { 795 return GetConnectingImage(icon_type, network_type); 796 } 797 798 gfx::ImageSkia GetImageForDisconnectedNetwork(IconType icon_type, 799 const std::string& network_type) { 800 return GetDisconnectedImage(icon_type, network_type); 801 } 802 803 base::string16 GetLabelForNetwork(const chromeos::NetworkState* network, 804 IconType icon_type) { 805 DCHECK(network); 806 std::string activation_state = network->activation_state(); 807 if (icon_type == ICON_TYPE_LIST) { 808 // Show "<network>: [Connecting|Activating]..." 809 if (network->IsConnectingState()) { 810 return l10n_util::GetStringFUTF16( 811 IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING, 812 base::UTF8ToUTF16(network->name())); 813 } 814 if (activation_state == shill::kActivationStateActivating) { 815 return l10n_util::GetStringFUTF16( 816 IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING, 817 base::UTF8ToUTF16(network->name())); 818 } 819 // Show "Activate <network>" in list view only. 820 if (activation_state == shill::kActivationStateNotActivated || 821 activation_state == shill::kActivationStatePartiallyActivated) { 822 return l10n_util::GetStringFUTF16( 823 IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE, 824 base::UTF8ToUTF16(network->name())); 825 } 826 } else { 827 // Show "[Connected to|Connecting to|Activating] <network>" (non-list view). 828 if (network->IsConnectedState()) { 829 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, 830 base::UTF8ToUTF16(network->name())); 831 } 832 if (network->IsConnectingState()) { 833 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING, 834 base::UTF8ToUTF16(network->name())); 835 } 836 if (activation_state == shill::kActivationStateActivating) { 837 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING, 838 base::UTF8ToUTF16(network->name())); 839 } 840 } 841 842 // Otherwise just show the network name or 'Ethernet'. 843 if (network->Matches(NetworkTypePattern::Ethernet())) { 844 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ETHERNET); 845 } else { 846 return base::UTF8ToUTF16(network->name()); 847 } 848 } 849 850 int GetCellularUninitializedMsg() { 851 static base::Time s_uninitialized_state_time; 852 static int s_uninitialized_msg(0); 853 854 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); 855 if (handler->GetTechnologyState(NetworkTypePattern::Mobile()) 856 == NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) { 857 s_uninitialized_msg = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR; 858 s_uninitialized_state_time = base::Time::Now(); 859 return s_uninitialized_msg; 860 } else if (handler->GetScanningByType(NetworkTypePattern::Mobile())) { 861 s_uninitialized_msg = IDS_ASH_STATUS_TRAY_CELLULAR_SCANNING; 862 s_uninitialized_state_time = base::Time::Now(); 863 return s_uninitialized_msg; 864 } 865 // There can be a delay between leaving the Initializing state and when 866 // a Cellular device shows up, so keep showing the initializing 867 // animation for a bit to avoid flashing the disconnect icon. 868 const int kInitializingDelaySeconds = 1; 869 base::TimeDelta dtime = base::Time::Now() - s_uninitialized_state_time; 870 if (dtime.InSeconds() < kInitializingDelaySeconds) 871 return s_uninitialized_msg; 872 return 0; 873 } 874 875 void GetDefaultNetworkImageAndLabel(IconType icon_type, 876 gfx::ImageSkia* image, 877 base::string16* label, 878 bool* animating) { 879 NetworkStateHandler* state_handler = 880 NetworkHandler::Get()->network_state_handler(); 881 NetworkConnectionHandler* connect_handler = 882 NetworkHandler::Get()->network_connection_handler(); 883 const NetworkState* connected_network = 884 state_handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual()); 885 const NetworkState* connecting_network = 886 state_handler->ConnectingNetworkByType(NetworkTypePattern::Wireless()); 887 if (!connecting_network && icon_type == ICON_TYPE_TRAY) { 888 connecting_network = 889 state_handler->ConnectingNetworkByType(NetworkTypePattern::VPN()); 890 } 891 892 const NetworkState* network; 893 // If we are connecting to a network, and there is either no connected 894 // network, or the connection was user requested, use the connecting 895 // network. 896 if (connecting_network && 897 (!connected_network || 898 connect_handler->HasConnectingNetwork(connecting_network->path()))) { 899 network = connecting_network; 900 } else { 901 network = connected_network; 902 } 903 904 // Don't show ethernet in the tray 905 if (icon_type == ICON_TYPE_TRAY && network && 906 network->Matches(NetworkTypePattern::Ethernet())) { 907 *image = gfx::ImageSkia(); 908 *animating = false; 909 return; 910 } 911 912 if (!network) { 913 // If no connecting network, check if we are activating a network. 914 const NetworkState* mobile_network = 915 state_handler->FirstNetworkByType(NetworkTypePattern::Mobile()); 916 if (mobile_network && (mobile_network->activation_state() == 917 shill::kActivationStateActivating)) { 918 network = mobile_network; 919 } 920 } 921 if (!network) { 922 // If no connecting network, check for cellular initializing. 923 int uninitialized_msg = GetCellularUninitializedMsg(); 924 if (uninitialized_msg != 0) { 925 *image = GetImageForConnectingNetwork(icon_type, shill::kTypeCellular); 926 if (label) 927 *label = l10n_util::GetStringUTF16(uninitialized_msg); 928 *animating = true; 929 } else { 930 // Otherwise show the disconnected wifi icon. 931 *image = GetImageForDisconnectedNetwork(icon_type, shill::kTypeWifi); 932 if (label) { 933 *label = l10n_util::GetStringUTF16( 934 IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED); 935 } 936 *animating = false; 937 } 938 return; 939 } 940 *animating = network->IsConnectingState(); 941 // Get icon and label for connected or connecting network. 942 *image = GetImageForNetwork(network, icon_type); 943 if (label) 944 *label = GetLabelForNetwork(network, icon_type); 945 } 946 947 void PurgeNetworkIconCache() { 948 NetworkStateHandler::NetworkStateList networks; 949 NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkList( 950 &networks); 951 std::set<std::string> network_paths; 952 for (NetworkStateHandler::NetworkStateList::iterator iter = networks.begin(); 953 iter != networks.end(); ++iter) { 954 network_paths.insert((*iter)->path()); 955 } 956 PurgeIconMap(ICON_TYPE_TRAY, network_paths); 957 PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_paths); 958 PurgeIconMap(ICON_TYPE_LIST, network_paths); 959 } 960 961 } // namespace network_icon 962 } // namespace ui 963