Home | History | Annotate | Download | only in network
      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