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