- add sources.
[platform/framework/web/crosswalk.git] / src / ash / system / chromeos / network / network_icon.cc
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 "chromeos/network/shill_property_util.h"
16 #include "grit/ash_resources.h"
17 #include "grit/ash_strings.h"
18 #include "third_party/cros_system_api/dbus/service_constants.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/base/resource/resource_bundle.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::NetworkState;
31 using chromeos::NetworkStateHandler;
32 using chromeos::NetworkTypePattern;
33
34 namespace ash {
35 namespace network_icon {
36
37 namespace {
38
39 //------------------------------------------------------------------------------
40 // Struct to pass icon badges to NetworkIconImageSource.
41 struct Badges {
42   Badges()
43       : top_left(NULL),
44         top_right(NULL),
45         bottom_left(NULL),
46         bottom_right(NULL) {
47   }
48   const gfx::ImageSkia* top_left;
49   const gfx::ImageSkia* top_right;
50   const gfx::ImageSkia* bottom_left;
51   const gfx::ImageSkia* bottom_right;
52 };
53
54 //------------------------------------------------------------------------------
55 // class used for maintaining a map of network state and images.
56 class NetworkIconImpl {
57  public:
58   explicit NetworkIconImpl(IconType icon_type);
59
60   // Determines whether or not the associated network might be dirty and if so
61   // updates and generates the icon. Does nothing if network no longer exists.
62   void Update(const chromeos::NetworkState* network);
63
64   const gfx::ImageSkia& image() const { return image_; }
65
66  private:
67   // Updates |strength_index_| for wireless networks. Returns true if changed.
68   bool UpdateWirelessStrengthIndex(const chromeos::NetworkState* network);
69
70   // Updates the local state for cellular networks. Returns true if changed.
71   bool UpdateCellularState(const chromeos::NetworkState* network);
72
73   // Updates the VPN badge. Returns true if changed.
74   bool UpdateVPNBadge();
75
76   // Gets |badges| based on |network| and the current state.
77   void GetBadges(const NetworkState* network, Badges* badges);
78
79   // Gets the appropriate icon and badges and composites the image.
80   void GenerateImage(const chromeos::NetworkState* network);
81
82   // Defines color theme and VPN badging
83   const IconType icon_type_;
84
85   // Cached state of the network when the icon was last generated.
86   std::string state_;
87
88   // Cached strength index of the network when the icon was last generated.
89   int strength_index_;
90
91   // Cached technology badge for the network when the icon was last generated.
92   const gfx::ImageSkia* technology_badge_;
93
94   // Cached vpn badge for the network when the icon was last generated.
95   const gfx::ImageSkia* vpn_badge_;
96
97   // Cached roaming state of the network when the icon was last generated.
98   std::string roaming_state_;
99
100   // Generated icon image.
101   gfx::ImageSkia image_;
102
103   DISALLOW_COPY_AND_ASSIGN(NetworkIconImpl);
104 };
105
106 //------------------------------------------------------------------------------
107 // Maintain a static (global) icon map. Note: Icons are never destroyed;
108 // it is assumed that a finite and reasonable number of network icons will be
109 // created during a session.
110
111 typedef std::map<std::string, NetworkIconImpl*> NetworkIconMap;
112
113 NetworkIconMap* GetIconMapInstance(IconType icon_type, bool create) {
114   typedef std::map<IconType, NetworkIconMap*> IconTypeMap;
115   static IconTypeMap* s_icon_map = NULL;
116   if (s_icon_map == NULL) {
117     if (!create)
118       return NULL;
119     s_icon_map = new IconTypeMap;
120   }
121   if (s_icon_map->count(icon_type) == 0) {
122     if (!create)
123       return NULL;
124     (*s_icon_map)[icon_type] = new NetworkIconMap;
125   }
126   return (*s_icon_map)[icon_type];
127 }
128
129 NetworkIconMap* GetIconMap(IconType icon_type) {
130   return GetIconMapInstance(icon_type, true);
131 }
132
133 void PurgeIconMap(IconType icon_type,
134                   const std::set<std::string>& network_paths) {
135   NetworkIconMap* icon_map = GetIconMapInstance(icon_type, false);
136   if (!icon_map)
137     return;
138   for (NetworkIconMap::iterator loop_iter = icon_map->begin();
139        loop_iter != icon_map->end(); ) {
140     NetworkIconMap::iterator cur_iter = loop_iter++;
141     if (network_paths.count(cur_iter->first) == 0) {
142       delete cur_iter->second;
143       icon_map->erase(cur_iter);
144     }
145   }
146 }
147
148 //------------------------------------------------------------------------------
149 // Utilities for generating icon images.
150
151 // 'NONE' will default to ARCS behavior where appropriate (e.g. no network or
152 // if a new type gets added).
153 enum ImageType {
154   ARCS,
155   BARS,
156   NONE
157 };
158
159 // Amount to fade icons while connecting.
160 const double kConnectingImageAlpha = 0.5;
161
162 // Images for strength bars for wired networks.
163 const int kNumBarsImages = 5;
164
165 // Imagaes for strength arcs for wireless networks.
166 const int kNumArcsImages = 5;
167
168 // Number of discrete images to use for alpha fade animation
169 const int kNumFadeImages = 10;
170
171 //------------------------------------------------------------------------------
172 // Classes for generating scaled images.
173
174 const SkBitmap GetEmptyBitmap(const gfx::Size pixel_size) {
175   typedef std::pair<int, int> SizeKey;
176   typedef std::map<SizeKey, SkBitmap> SizeBitmapMap;
177   static SizeBitmapMap* s_empty_bitmaps = new SizeBitmapMap;
178
179   SizeKey key(pixel_size.width(), pixel_size.height());
180
181   SizeBitmapMap::iterator iter = s_empty_bitmaps->find(key);
182   if (iter != s_empty_bitmaps->end())
183     return iter->second;
184
185   SkBitmap empty;
186   empty.setConfig(SkBitmap::kARGB_8888_Config, key.first, key.second);
187   empty.allocPixels();
188   empty.eraseARGB(0, 0, 0, 0);
189   (*s_empty_bitmaps)[key] = empty;
190   return empty;
191 }
192
193 class EmptyImageSource: public gfx::ImageSkiaSource {
194  public:
195   explicit EmptyImageSource(const gfx::Size& size)
196       : size_(size) {
197   }
198
199   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
200     gfx::Size pixel_size = gfx::ToFlooredSize(gfx::ScaleSize(size_, scale));
201     SkBitmap empty_bitmap = GetEmptyBitmap(pixel_size);
202     return gfx::ImageSkiaRep(empty_bitmap, scale);
203   }
204
205  private:
206   const gfx::Size size_;
207
208   DISALLOW_COPY_AND_ASSIGN(EmptyImageSource);
209 };
210
211 // This defines how we assemble a network icon.
212 class NetworkIconImageSource : public gfx::ImageSkiaSource {
213  public:
214   NetworkIconImageSource(const gfx::ImageSkia& icon, const Badges& badges)
215       : icon_(icon),
216         badges_(badges) {
217   }
218   virtual ~NetworkIconImageSource() {}
219
220   // TODO(pkotwicz): Figure out what to do when a new image resolution becomes
221   // available.
222   virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
223     gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
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 == shill::kTypeWifi)
284     return ARCS;
285   else if (type == shill::kTypeCellular || type == shill::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 == shill::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 == shill::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 == shill::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 == shill::kNetworkTechnology1Xrtt) {
417     id = IDR_AURA_UBER_TRAY_NETWORK_1X;
418   } else if (technology == shill::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 == shill::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 == shill::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 == shill::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 == shill::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 == shill::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 == shill::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 == shill::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   if (network->Matches(NetworkTypePattern::Ethernet())) {
467     return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
468   } else if (network->Matches(NetworkTypePattern::Wireless())) {
469     DCHECK(strength_index > 0);
470     return GetImageForIndex(
471         ImageTypeForNetworkType(network->type()), icon_type, strength_index);
472   } else if (network->Matches(NetworkTypePattern::VPN())) {
473     return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN);
474   } else {
475     LOG(WARNING) << "Request for icon for unsupported type: "
476                  << network->type();
477     return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
478   }
479 }
480
481 //------------------------------------------------------------------------------
482 // Get connecting images
483
484 gfx::ImageSkia GetConnectingVpnImage(IconType icon_type) {
485   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
486   const NetworkState* connected_network = NULL;
487   if (icon_type == ICON_TYPE_TRAY) {
488     connected_network =
489         handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
490   }
491   double animation = NetworkIconAnimation::GetInstance()->GetAnimation();
492
493   if (connected_network) {
494     gfx::ImageSkia icon = GetImageForNetwork(connected_network, icon_type);
495     Badges badges;
496     badges.bottom_left = ConnectingVpnBadge(animation);
497     return gfx::ImageSkia(
498         new NetworkIconImageSource(icon, badges), icon.size());
499   } else {
500     gfx::ImageSkia* icon = ConnectingVpnImage(animation);
501     return gfx::ImageSkia(
502         new NetworkIconImageSource(*icon, Badges()), icon->size());
503   }
504 }
505
506 gfx::ImageSkia GetConnectingImage(const std::string& network_type,
507                                   IconType icon_type) {
508   if (network_type == shill::kTypeVPN)
509     return GetConnectingVpnImage(icon_type);
510
511   ImageType image_type = ImageTypeForNetworkType(network_type);
512   double animation = NetworkIconAnimation::GetInstance()->GetAnimation();
513
514   gfx::ImageSkia* icon = ConnectingWirelessImage(
515       image_type, icon_type, animation);
516   return gfx::ImageSkia(
517       new NetworkIconImageSource(*icon, Badges()), icon->size());
518 }
519
520 }  // namespace
521
522 //------------------------------------------------------------------------------
523 // NetworkIconImpl
524
525 NetworkIconImpl::NetworkIconImpl(IconType icon_type)
526     : icon_type_(icon_type),
527       strength_index_(-1),
528       technology_badge_(NULL),
529       vpn_badge_(NULL) {
530   // Default image
531   image_ = GetDisconnectedImage(shill::kTypeWifi, icon_type);
532 }
533
534 void NetworkIconImpl::Update(const NetworkState* network) {
535   DCHECK(network);
536   // Determine whether or not we need to update the icon.
537   bool dirty = image_.isNull();
538
539   // If the network state has changed, the icon needs updating.
540   if (state_ != network->connection_state()) {
541     state_ = network->connection_state();
542     dirty = true;
543   }
544
545   if (network->Matches(NetworkTypePattern::Wireless()))
546     dirty |= UpdateWirelessStrengthIndex(network);
547
548   if (network->Matches(NetworkTypePattern::Cellular()))
549     dirty |= UpdateCellularState(network);
550
551   if (IconTypeHasVPNBadge(icon_type_) &&
552       network->Matches(NetworkTypePattern::NonVirtual())) {
553     dirty |= UpdateVPNBadge();
554   }
555
556   if (dirty) {
557     // Set the icon and badges based on the network and generate the image.
558     GenerateImage(network);
559   }
560 }
561
562 bool NetworkIconImpl::UpdateWirelessStrengthIndex(const NetworkState* network) {
563   int index = GetStrengthIndex(network);
564   if (index != strength_index_) {
565     strength_index_ = index;
566     return true;
567   }
568   return false;
569 }
570
571 bool NetworkIconImpl::UpdateCellularState(const NetworkState* network) {
572   bool dirty = false;
573   const gfx::ImageSkia* technology_badge =
574       BadgeForNetworkTechnology(network, icon_type_);
575   if (technology_badge != technology_badge_) {
576     technology_badge_ = technology_badge;
577     dirty = true;
578   }
579   std::string roaming_state = network->roaming();
580   if (roaming_state != roaming_state_) {
581     roaming_state_ = roaming_state;
582     dirty = true;
583   }
584   return dirty;
585 }
586
587 bool NetworkIconImpl::UpdateVPNBadge() {
588   const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
589       ConnectedNetworkByType(NetworkTypePattern::VPN());
590   if (vpn && vpn_badge_ == NULL) {
591     vpn_badge_ = BadgeForVPN(icon_type_);
592     return true;
593   } else if (!vpn && vpn_badge_ != NULL) {
594     vpn_badge_ = NULL;
595     return true;
596   }
597   return false;
598 }
599
600 void NetworkIconImpl::GetBadges(const NetworkState* network, Badges* badges) {
601   DCHECK(network);
602   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
603   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
604
605   const std::string& type = network->type();
606   if (type == shill::kTypeWifi) {
607     if (network->security() != shill::kSecurityNone &&
608         IconTypeIsDark(icon_type_)) {
609       badges->bottom_right = rb.GetImageSkiaNamed(
610           IDR_AURA_UBER_TRAY_NETWORK_SECURE_DARK);
611     }
612   } else if (type == shill::kTypeWimax) {
613     technology_badge_ = rb.GetImageSkiaNamed(
614         IconTypeIsDark(icon_type_) ?
615         IDR_AURA_UBER_TRAY_NETWORK_4G_DARK :
616         IDR_AURA_UBER_TRAY_NETWORK_4G_LIGHT);
617   } else if (type == shill::kTypeCellular) {
618     if (network->roaming() == shill::kRoamingStateRoaming) {
619       // For networks that are always in roaming don't show roaming badge.
620       const DeviceState* device =
621           handler->GetDeviceState(network->device_path());
622       LOG_IF(WARNING, !device) << "Could not find device state for "
623                                << network->device_path();
624       if (!device || !device->provider_requires_roaming()) {
625         badges->bottom_right = rb.GetImageSkiaNamed(
626             IconTypeIsDark(icon_type_) ?
627             IDR_AURA_UBER_TRAY_NETWORK_ROAMING_DARK :
628             IDR_AURA_UBER_TRAY_NETWORK_ROAMING_LIGHT);
629       }
630     }
631   }
632   if (!network->IsConnectingState()) {
633     badges->top_left = technology_badge_;
634     badges->bottom_left = vpn_badge_;
635   }
636 }
637
638 void NetworkIconImpl::GenerateImage(const NetworkState* network) {
639   DCHECK(network);
640   gfx::ImageSkia icon = GetIcon(network, icon_type_, strength_index_);
641   Badges badges;
642   GetBadges(network, &badges);
643   image_ = gfx::ImageSkia(
644       new NetworkIconImageSource(icon, badges), icon.size());
645 }
646
647 //------------------------------------------------------------------------------
648 // Public interface
649
650 gfx::ImageSkia GetImageForNetwork(const NetworkState* network,
651                                   IconType icon_type) {
652   DCHECK(network);
653   // Handle connecting icons.
654   if (network->IsConnectingState())
655     return GetConnectingImage(network->type(), icon_type);
656
657   // Find or add the icon.
658   NetworkIconMap* icon_map = GetIconMap(icon_type);
659   NetworkIconImpl* icon;
660   NetworkIconMap::iterator iter = icon_map->find(network->path());
661   if (iter == icon_map->end()) {
662     icon = new NetworkIconImpl(icon_type);
663     icon_map->insert(std::make_pair(network->path(), icon));
664   } else {
665     icon = iter->second;
666   }
667
668   // Update and return the icon's image.
669   icon->Update(network);
670   return icon->image();
671 }
672
673 gfx::ImageSkia GetImageForConnectedNetwork(IconType icon_type,
674                                            const std::string& network_type) {
675   return GetConnectedImage(network_type, icon_type);
676 }
677
678 gfx::ImageSkia GetImageForConnectingNetwork(IconType icon_type,
679                                             const std::string& network_type) {
680   return GetConnectingImage(network_type, icon_type);
681 }
682
683 gfx::ImageSkia GetImageForDisconnectedNetwork(IconType icon_type,
684                                               const std::string& network_type) {
685   return GetDisconnectedImage(network_type, icon_type);
686 }
687
688 base::string16 GetLabelForNetwork(const chromeos::NetworkState* network,
689                                   IconType icon_type) {
690   DCHECK(network);
691   std::string activation_state = network->activation_state();
692   if (icon_type == ICON_TYPE_LIST) {
693     // Show "<network>: [Connecting|Activating]..."
694     if (network->IsConnectingState()) {
695       return l10n_util::GetStringFUTF16(
696           IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING,
697           UTF8ToUTF16(network->name()));
698     }
699     if (activation_state == shill::kActivationStateActivating) {
700       return l10n_util::GetStringFUTF16(
701           IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING,
702           UTF8ToUTF16(network->name()));
703     }
704     // Show "Activate <network>" in list view only.
705     if (activation_state == shill::kActivationStateNotActivated ||
706         activation_state == shill::kActivationStatePartiallyActivated) {
707       return l10n_util::GetStringFUTF16(
708           IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE,
709           UTF8ToUTF16(network->name()));
710     }
711   } else {
712     // Show "[Connected to|Connecting to|Activating] <network>" (non-list view).
713     if (network->IsConnectedState()) {
714       return l10n_util::GetStringFUTF16(
715           IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, UTF8ToUTF16(network->name()));
716     }
717     if (network->IsConnectingState()) {
718       return l10n_util::GetStringFUTF16(
719           IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING, UTF8ToUTF16(network->name()));
720     }
721     if (activation_state == shill::kActivationStateActivating) {
722       return l10n_util::GetStringFUTF16(
723           IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING, UTF8ToUTF16(network->name()));
724     }
725   }
726
727   // Otherwise just show the network name or 'Ethernet'.
728   if (network->Matches(NetworkTypePattern::Ethernet())) {
729     return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ETHERNET);
730   } else {
731     return UTF8ToUTF16(network->name());
732   }
733 }
734
735 int GetCellularUninitializedMsg() {
736   static base::Time s_uninitialized_state_time;
737   static int s_uninitialized_msg(0);
738
739   NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
740   if (handler->GetTechnologyState(NetworkTypePattern::Mobile())
741       == NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) {
742     s_uninitialized_msg = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR;
743     s_uninitialized_state_time = base::Time::Now();
744     return s_uninitialized_msg;
745   } else if (handler->GetScanningByType(NetworkTypePattern::Mobile())) {
746     s_uninitialized_msg = IDS_ASH_STATUS_TRAY_CELLULAR_SCANNING;
747     s_uninitialized_state_time = base::Time::Now();
748     return s_uninitialized_msg;
749   }
750   // There can be a delay between leaving the Initializing state and when
751   // a Cellular device shows up, so keep showing the initializing
752   // animation for a bit to avoid flashing the disconnect icon.
753   const int kInitializingDelaySeconds = 1;
754   base::TimeDelta dtime = base::Time::Now() - s_uninitialized_state_time;
755   if (dtime.InSeconds() < kInitializingDelaySeconds)
756     return s_uninitialized_msg;
757   return 0;
758 }
759
760 void GetDefaultNetworkImageAndLabel(IconType icon_type,
761                                     gfx::ImageSkia* image,
762                                     base::string16* label,
763                                     bool* animating) {
764   NetworkStateHandler* state_handler =
765       NetworkHandler::Get()->network_state_handler();
766   NetworkConnectionHandler* connect_handler =
767       NetworkHandler::Get()->network_connection_handler();
768   const NetworkState* connected_network =
769       state_handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
770   const NetworkState* connecting_network =
771       state_handler->ConnectingNetworkByType(NetworkTypePattern::Wireless());
772   if (!connecting_network && icon_type == ICON_TYPE_TRAY) {
773     connecting_network =
774         state_handler->ConnectingNetworkByType(NetworkTypePattern::VPN());
775   }
776
777   const NetworkState* network;
778   // If we are connecting to a network, and there is either no connected
779   // network, or the connection was user requested, use the connecting
780   // network.
781   if (connecting_network &&
782       (!connected_network ||
783        connect_handler->HasConnectingNetwork(connecting_network->path()))) {
784     network = connecting_network;
785   } else {
786     network = connected_network;
787   }
788
789   // Don't show ethernet in the tray
790   if (icon_type == ICON_TYPE_TRAY && network &&
791       network->Matches(NetworkTypePattern::Ethernet())) {
792     *image = gfx::ImageSkia();
793     *animating = false;
794     return;
795   }
796
797   if (!network) {
798     // If no connecting network, check if we are activating a network.
799     const NetworkState* mobile_network =
800         state_handler->FirstNetworkByType(NetworkTypePattern::Mobile());
801     if (mobile_network && (mobile_network->activation_state() ==
802                            shill::kActivationStateActivating)) {
803       network = mobile_network;
804     }
805   }
806   if (!network) {
807     // If no connecting network, check for cellular initializing.
808     int uninitialized_msg = GetCellularUninitializedMsg();
809     if (uninitialized_msg != 0) {
810       *image = GetImageForConnectingNetwork(icon_type, shill::kTypeCellular);
811       if (label)
812         *label = l10n_util::GetStringUTF16(uninitialized_msg);
813       *animating = true;
814     } else {
815       // Otherwise show the disconnected wifi icon.
816       *image = GetImageForDisconnectedNetwork(icon_type, shill::kTypeWifi);
817       if (label) {
818         *label = l10n_util::GetStringUTF16(
819             IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED);
820       }
821       *animating = false;
822     }
823     return;
824   }
825   *animating = network->IsConnectingState();
826   // Get icon and label for connected or connecting network.
827   *image = GetImageForNetwork(network, icon_type);
828   if (label)
829     *label = GetLabelForNetwork(network, icon_type);
830 }
831
832 void PurgeNetworkIconCache() {
833   NetworkStateHandler::NetworkStateList networks;
834   NetworkHandler::Get()->network_state_handler()->GetNetworkList(&networks);
835   std::set<std::string> network_paths;
836   for (NetworkStateHandler::NetworkStateList::iterator iter = networks.begin();
837        iter != networks.end(); ++iter) {
838     network_paths.insert((*iter)->path());
839   }
840   PurgeIconMap(ICON_TYPE_TRAY, network_paths);
841   PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_paths);
842   PurgeIconMap(ICON_TYPE_LIST, network_paths);
843 }
844
845 }  // namespace network_icon
846 }  // namespace ash