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 {
Badgesash::network_icon::__anonad3de9820111::Badges42 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
image() const64 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
GetIconMapInstance(IconType icon_type,bool create)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
GetIconMap(IconType icon_type)129 NetworkIconMap* GetIconMap(IconType icon_type) {
130 return GetIconMapInstance(icon_type, true);
131 }
132
PurgeIconMap(IconType icon_type,const std::set<std::string> & network_paths)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
GetEmptyBitmap(const gfx::Size pixel_size)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:
EmptyImageSource(const gfx::Size & size)195 explicit EmptyImageSource(const gfx::Size& size)
196 : size_(size) {
197 }
198
GetImageForScale(float scale)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:
NetworkIconImageSource(const gfx::ImageSkia & icon,const Badges & badges)214 NetworkIconImageSource(const gfx::ImageSkia& icon, const Badges& badges)
215 : icon_(icon),
216 badges_(badges) {
217 }
~NetworkIconImageSource()218 virtual ~NetworkIconImageSource() {}
219
220 // TODO(pkotwicz): Figure out what to do when a new image resolution becomes
221 // available.
GetImageForScale(float scale)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
IconTypeIsDark(IconType icon_type)254 bool IconTypeIsDark(IconType icon_type) {
255 return (icon_type != ICON_TYPE_TRAY);
256 }
257
IconTypeHasVPNBadge(IconType icon_type)258 bool IconTypeHasVPNBadge(IconType icon_type) {
259 return (icon_type != ICON_TYPE_LIST);
260 }
261
NumImagesForType(ImageType type)262 int NumImagesForType(ImageType type) {
263 return (type == BARS) ? kNumBarsImages : kNumArcsImages;
264 }
265
BaseImageForType(ImageType image_type,IconType icon_type)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
ImageTypeForNetworkType(const std::string & type)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
GetImageForIndex(ImageType image_type,IconType icon_type,int index)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
GetConnectedImage(const std::string & type,IconType icon_type)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
GetDisconnectedImage(const std::string & type,IconType icon_type)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
ConnectingWirelessImage(ImageType image_type,IconType icon_type,double animation)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
ConnectingVpnImage(double animation)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
ConnectingVpnBadge(double animation)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
StrengthIndex(int strength,int count)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
GetStrengthIndex(const NetworkState * network)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
BadgeForNetworkTechnology(const NetworkState * network,IconType icon_type)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
BadgeForVPN(IconType icon_type)457 const gfx::ImageSkia* BadgeForVPN(IconType icon_type) {
458 return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
459 IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE);
460 }
461
GetIcon(const NetworkState * network,IconType icon_type,int strength_index)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
GetConnectingVpnImage(IconType icon_type)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
GetConnectingImage(const std::string & network_type,IconType icon_type)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
NetworkIconImpl(IconType icon_type)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
Update(const NetworkState * network)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
UpdateWirelessStrengthIndex(const NetworkState * network)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
UpdateCellularState(const NetworkState * network)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
UpdateVPNBadge()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
GetBadges(const NetworkState * network,Badges * badges)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
GenerateImage(const NetworkState * network)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
GetImageForNetwork(const NetworkState * network,IconType icon_type)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
GetImageForConnectedNetwork(IconType icon_type,const std::string & network_type)673 gfx::ImageSkia GetImageForConnectedNetwork(IconType icon_type,
674 const std::string& network_type) {
675 return GetConnectedImage(network_type, icon_type);
676 }
677
GetImageForConnectingNetwork(IconType icon_type,const std::string & network_type)678 gfx::ImageSkia GetImageForConnectingNetwork(IconType icon_type,
679 const std::string& network_type) {
680 return GetConnectingImage(network_type, icon_type);
681 }
682
GetImageForDisconnectedNetwork(IconType icon_type,const std::string & network_type)683 gfx::ImageSkia GetImageForDisconnectedNetwork(IconType icon_type,
684 const std::string& network_type) {
685 return GetDisconnectedImage(network_type, icon_type);
686 }
687
GetLabelForNetwork(const chromeos::NetworkState * network,IconType icon_type)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
GetCellularUninitializedMsg()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
GetDefaultNetworkImageAndLabel(IconType icon_type,gfx::ImageSkia * image,base::string16 * label,bool * animating)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
PurgeNetworkIconCache()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
847