OLD | NEW |
| (Empty) |
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/common/system/chromeos/network/network_icon.h" | |
6 | |
7 #include "ash/common/system/chromeos/network/network_icon_animation.h" | |
8 #include "ash/common/system/chromeos/network/network_icon_animation_observer.h" | |
9 #include "ash/common/system/tray/tray_constants.h" | |
10 #include "ash/resources/grit/ash_resources.h" | |
11 #include "ash/resources/vector_icons/vector_icons.h" | |
12 #include "ash/strings/grit/ash_strings.h" | |
13 #include "base/macros.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "chromeos/network/device_state.h" | |
16 #include "chromeos/network/network_connection_handler.h" | |
17 #include "chromeos/network/network_state.h" | |
18 #include "chromeos/network/network_state_handler.h" | |
19 #include "chromeos/network/portal_detector/network_portal_detector.h" | |
20 #include "third_party/cros_system_api/dbus/service_constants.h" | |
21 #include "third_party/skia/include/core/SkPaint.h" | |
22 #include "third_party/skia/include/core/SkPath.h" | |
23 #include "ui/base/l10n/l10n_util.h" | |
24 #include "ui/base/resource/resource_bundle.h" | |
25 #include "ui/gfx/canvas.h" | |
26 #include "ui/gfx/color_palette.h" | |
27 #include "ui/gfx/geometry/insets.h" | |
28 #include "ui/gfx/geometry/rect.h" | |
29 #include "ui/gfx/geometry/size_conversions.h" | |
30 #include "ui/gfx/image/canvas_image_source.h" | |
31 #include "ui/gfx/image/image_skia_operations.h" | |
32 #include "ui/gfx/image/image_skia_source.h" | |
33 #include "ui/gfx/paint_vector_icon.h" | |
34 #include "ui/gfx/skia_util.h" | |
35 #include "ui/gfx/vector_icon_types.h" | |
36 | |
37 using chromeos::DeviceState; | |
38 using chromeos::NetworkConnectionHandler; | |
39 using chromeos::NetworkHandler; | |
40 using chromeos::NetworkPortalDetector; | |
41 using chromeos::NetworkState; | |
42 using chromeos::NetworkStateHandler; | |
43 using chromeos::NetworkTypePattern; | |
44 | |
45 namespace ash { | |
46 namespace network_icon { | |
47 | |
48 namespace { | |
49 | |
50 // Constants for offseting the badge displayed on top of the signal strength | |
51 // icon. The badge will extend outside of the base icon bounds by these amounts. | |
52 // All values are in dp. | |
53 | |
54 // The badge offsets are different depending on whether the icon is in the tray | |
55 // or menu. | |
56 const int kTrayIconBadgeOffset = 3; | |
57 const int kMenuIconBadgeOffset = 2; | |
58 | |
59 //------------------------------------------------------------------------------ | |
60 // Struct to pass icon badges to NetworkIconImageSource. | |
61 struct Badges { | |
62 gfx::ImageSkia top_left; | |
63 gfx::ImageSkia top_right; | |
64 gfx::ImageSkia bottom_left; | |
65 gfx::ImageSkia bottom_right; | |
66 }; | |
67 | |
68 //------------------------------------------------------------------------------ | |
69 // class used for maintaining a map of network state and images. | |
70 class NetworkIconImpl { | |
71 public: | |
72 NetworkIconImpl(const std::string& path, IconType icon_type); | |
73 | |
74 // Determines whether or not the associated network might be dirty and if so | |
75 // updates and generates the icon. Does nothing if network no longer exists. | |
76 void Update(const chromeos::NetworkState* network); | |
77 | |
78 const gfx::ImageSkia& image() const { return image_; } | |
79 | |
80 private: | |
81 // Updates |strength_index_| for wireless networks. Returns true if changed. | |
82 bool UpdateWirelessStrengthIndex(const chromeos::NetworkState* network); | |
83 | |
84 // Updates the local state for cellular networks. Returns true if changed. | |
85 bool UpdateCellularState(const chromeos::NetworkState* network); | |
86 | |
87 // Updates the portal state for wireless networks. Returns true if changed. | |
88 bool UpdatePortalState(const chromeos::NetworkState* network); | |
89 | |
90 // Updates the VPN badge. Returns true if changed. | |
91 bool UpdateVPNBadge(); | |
92 | |
93 // Gets |badges| based on |network| and the current state. | |
94 void GetBadges(const NetworkState* network, Badges* badges); | |
95 | |
96 // Gets the appropriate icon and badges and composites the image. | |
97 void GenerateImage(const chromeos::NetworkState* network); | |
98 | |
99 // Network path, used for debugging. | |
100 std::string network_path_; | |
101 | |
102 // Defines color theme and VPN badging | |
103 const IconType icon_type_; | |
104 | |
105 // Cached state of the network when the icon was last generated. | |
106 std::string state_; | |
107 | |
108 // Cached strength index of the network when the icon was last generated. | |
109 int strength_index_; | |
110 | |
111 // Cached technology badge for the network when the icon was last generated. | |
112 gfx::ImageSkia technology_badge_; | |
113 | |
114 // Cached vpn badge for the network when the icon was last generated. | |
115 gfx::ImageSkia vpn_badge_; | |
116 | |
117 // Cached roaming state of the network when the icon was last generated. | |
118 std::string roaming_state_; | |
119 | |
120 // Cached portal state of the network when the icon was last generated. | |
121 bool behind_captive_portal_; | |
122 | |
123 // Generated icon image. | |
124 gfx::ImageSkia image_; | |
125 | |
126 DISALLOW_COPY_AND_ASSIGN(NetworkIconImpl); | |
127 }; | |
128 | |
129 //------------------------------------------------------------------------------ | |
130 // Maintain a static (global) icon map. Note: Icons are never destroyed; | |
131 // it is assumed that a finite and reasonable number of network icons will be | |
132 // created during a session. | |
133 | |
134 typedef std::map<std::string, NetworkIconImpl*> NetworkIconMap; | |
135 | |
136 NetworkIconMap* GetIconMapInstance(IconType icon_type, bool create) { | |
137 typedef std::map<IconType, NetworkIconMap*> IconTypeMap; | |
138 static IconTypeMap* s_icon_map = nullptr; | |
139 if (s_icon_map == nullptr) { | |
140 if (!create) | |
141 return nullptr; | |
142 s_icon_map = new IconTypeMap; | |
143 } | |
144 if (s_icon_map->count(icon_type) == 0) { | |
145 if (!create) | |
146 return nullptr; | |
147 (*s_icon_map)[icon_type] = new NetworkIconMap; | |
148 } | |
149 return (*s_icon_map)[icon_type]; | |
150 } | |
151 | |
152 NetworkIconMap* GetIconMap(IconType icon_type) { | |
153 return GetIconMapInstance(icon_type, true); | |
154 } | |
155 | |
156 void PurgeIconMap(IconType icon_type, | |
157 const std::set<std::string>& network_paths) { | |
158 NetworkIconMap* icon_map = GetIconMapInstance(icon_type, false); | |
159 if (!icon_map) | |
160 return; | |
161 for (NetworkIconMap::iterator loop_iter = icon_map->begin(); | |
162 loop_iter != icon_map->end();) { | |
163 NetworkIconMap::iterator cur_iter = loop_iter++; | |
164 if (network_paths.count(cur_iter->first) == 0) { | |
165 delete cur_iter->second; | |
166 icon_map->erase(cur_iter); | |
167 } | |
168 } | |
169 } | |
170 | |
171 //------------------------------------------------------------------------------ | |
172 // Utilities for generating icon images. | |
173 | |
174 // 'NONE' will default to ARCS behavior where appropriate (e.g. no network or | |
175 // if a new type gets added). | |
176 enum ImageType { ARCS, BARS, NONE }; | |
177 | |
178 // Amount to fade icons while connecting. | |
179 const double kConnectingImageAlpha = 0.5; | |
180 | |
181 // Images for strength arcs for wireless networks or strength bars for cellular | |
182 // networks. | |
183 const int kNumNetworkImages = 5; | |
184 | |
185 // Number of discrete images to use for alpha fade animation | |
186 const int kNumFadeImages = 10; | |
187 | |
188 SkColor GetDefaultColorForIconType(IconType icon_type) { | |
189 return icon_type == ICON_TYPE_TRAY ? kTrayIconColor : kMenuIconColor; | |
190 } | |
191 | |
192 bool IconTypeIsDark(IconType icon_type) { | |
193 return (icon_type != ICON_TYPE_TRAY); | |
194 } | |
195 | |
196 bool IconTypeHasVPNBadge(IconType icon_type) { | |
197 return (icon_type != ICON_TYPE_LIST && icon_type != ICON_TYPE_MENU_LIST); | |
198 } | |
199 | |
200 // This defines how we assemble a network icon. | |
201 class NetworkIconImageSource : public gfx::CanvasImageSource { | |
202 public: | |
203 static gfx::ImageSkia CreateImage(const gfx::ImageSkia& icon, | |
204 const Badges& badges) { | |
205 auto* source = new NetworkIconImageSource(icon, badges); | |
206 return gfx::ImageSkia(source, source->size()); | |
207 } | |
208 | |
209 // gfx::CanvasImageSource: | |
210 void Draw(gfx::Canvas* canvas) override { | |
211 const int width = size().width(); | |
212 const int height = size().height(); | |
213 | |
214 // The base icon is centered in both dimensions. | |
215 const int icon_y = (height - icon_.height()) / 2; | |
216 canvas->DrawImageInt(icon_, (width - icon_.width()) / 2, icon_y); | |
217 | |
218 // The badges are flush against the edges of the canvas, except at the top, | |
219 // where the badge is only 1dp higher than the base image. | |
220 const int top_badge_y = icon_y - 1; | |
221 if (!badges_.top_left.isNull()) | |
222 canvas->DrawImageInt(badges_.top_left, 0, top_badge_y); | |
223 if (!badges_.top_right.isNull()) { | |
224 canvas->DrawImageInt(badges_.top_right, width - badges_.top_right.width(), | |
225 top_badge_y); | |
226 } | |
227 if (!badges_.bottom_left.isNull()) { | |
228 canvas->DrawImageInt(badges_.bottom_left, 0, | |
229 height - badges_.bottom_left.height()); | |
230 } | |
231 if (!badges_.bottom_right.isNull()) { | |
232 canvas->DrawImageInt(badges_.bottom_right, | |
233 width - badges_.bottom_right.width(), | |
234 height - badges_.bottom_right.height()); | |
235 } | |
236 } | |
237 | |
238 bool HasRepresentationAtAllScales() const override { return true; } | |
239 | |
240 private: | |
241 NetworkIconImageSource(const gfx::ImageSkia& icon, const Badges& badges) | |
242 : CanvasImageSource(GetSizeForBaseIconSize(icon.size()), false), | |
243 icon_(icon), | |
244 badges_(badges) {} | |
245 ~NetworkIconImageSource() override {} | |
246 | |
247 static gfx::Size GetSizeForBaseIconSize(const gfx::Size& base_icon_size) { | |
248 gfx::Size size = base_icon_size; | |
249 const int badge_offset = base_icon_size.width() == kTrayIconSize | |
250 ? kTrayIconBadgeOffset | |
251 : kMenuIconBadgeOffset; | |
252 size.Enlarge(badge_offset * 2, badge_offset * 2); | |
253 return size; | |
254 } | |
255 | |
256 const gfx::ImageSkia icon_; | |
257 const Badges badges_; | |
258 | |
259 DISALLOW_COPY_AND_ASSIGN(NetworkIconImageSource); | |
260 }; | |
261 | |
262 // Depicts a given signal strength using arcs (e.g. for WiFi connections) or | |
263 // bars (e.g. for cell connections). | |
264 class SignalStrengthImageSource : public gfx::CanvasImageSource { | |
265 public: | |
266 SignalStrengthImageSource(ImageType image_type, | |
267 IconType icon_type, | |
268 int signal_strength) | |
269 : CanvasImageSource(GetSizeForIconType(icon_type), false), | |
270 image_type_(image_type), | |
271 icon_type_(icon_type), | |
272 color_(GetDefaultColorForIconType(icon_type_)), | |
273 signal_strength_(signal_strength) { | |
274 if (image_type_ == NONE) | |
275 image_type_ = ARCS; | |
276 | |
277 DCHECK_GE(signal_strength, 0); | |
278 DCHECK_LT(signal_strength, kNumNetworkImages); | |
279 } | |
280 ~SignalStrengthImageSource() override {} | |
281 | |
282 void set_color(SkColor color) { color_ = color; } | |
283 | |
284 // gfx::CanvasImageSource: | |
285 void Draw(gfx::Canvas* canvas) override { | |
286 if (image_type_ == ARCS) | |
287 DrawArcs(canvas); | |
288 else | |
289 DrawBars(canvas); | |
290 } | |
291 | |
292 bool HasRepresentationAtAllScales() const override { return true; } | |
293 | |
294 private: | |
295 static gfx::Size GetSizeForIconType(IconType icon_type) { | |
296 int side = icon_type == ICON_TYPE_TRAY ? kTrayIconSize : kMenuIconSize; | |
297 return gfx::Size(side, side); | |
298 } | |
299 | |
300 void DrawArcs(gfx::Canvas* canvas) { | |
301 gfx::RectF oval_bounds((gfx::Rect(size()))); | |
302 oval_bounds.Inset(gfx::Insets(kIconInset)); | |
303 // Double the width and height. The new midpoint should be the former | |
304 // bottom center. | |
305 oval_bounds.Inset(-oval_bounds.width() / 2, 0, -oval_bounds.width() / 2, | |
306 -oval_bounds.height()); | |
307 | |
308 const SkScalar kAngleAboveHorizontal = 51.f; | |
309 const SkScalar kStartAngle = 180.f + kAngleAboveHorizontal; | |
310 const SkScalar kSweepAngle = 180.f - 2 * kAngleAboveHorizontal; | |
311 | |
312 cc::PaintFlags flags; | |
313 flags.setAntiAlias(true); | |
314 flags.setStyle(cc::PaintFlags::kFill_Style); | |
315 // Background. Skip drawing for full signal. | |
316 if (signal_strength_ != kNumNetworkImages - 1) { | |
317 flags.setColor(SkColorSetA(color_, kBgAlpha)); | |
318 canvas->sk_canvas()->drawArc(gfx::RectFToSkRect(oval_bounds), kStartAngle, | |
319 kSweepAngle, true, flags); | |
320 } | |
321 // Foreground (signal strength). | |
322 if (signal_strength_ != 0) { | |
323 flags.setColor(color_); | |
324 // Percent of the height of the background wedge that we draw the | |
325 // foreground wedge, indexed by signal strength. | |
326 static const float kWedgeHeightPercentages[] = {0.f, 0.375f, 0.5833f, | |
327 0.75f, 1.f}; | |
328 const float wedge_percent = kWedgeHeightPercentages[signal_strength_]; | |
329 oval_bounds.Inset( | |
330 gfx::InsetsF((oval_bounds.height() / 2) * (1.f - wedge_percent))); | |
331 canvas->sk_canvas()->drawArc(gfx::RectFToSkRect(oval_bounds), kStartAngle, | |
332 kSweepAngle, true, flags); | |
333 } | |
334 } | |
335 | |
336 void DrawBars(gfx::Canvas* canvas) { | |
337 // Undo the canvas's device scaling and round values to the nearest whole | |
338 // number so we can draw on exact pixel boundaries. | |
339 const float dsf = canvas->UndoDeviceScaleFactor(); | |
340 auto scale = [dsf](SkScalar dimension) { | |
341 return std::round(dimension * dsf); | |
342 }; | |
343 | |
344 // Length of short side of an isosceles right triangle, in dip. | |
345 const SkScalar kFullTriangleSide = | |
346 SkIntToScalar(size().width()) - kIconInset * 2; | |
347 | |
348 auto make_triangle = [scale, kFullTriangleSide](SkScalar side) { | |
349 SkPath triangle; | |
350 triangle.moveTo(scale(kIconInset), scale(kIconInset + kFullTriangleSide)); | |
351 triangle.rLineTo(scale(side), 0); | |
352 triangle.rLineTo(0, -scale(side)); | |
353 triangle.close(); | |
354 return triangle; | |
355 }; | |
356 | |
357 cc::PaintFlags flags; | |
358 flags.setAntiAlias(true); | |
359 flags.setStyle(cc::PaintFlags::kFill_Style); | |
360 // Background. Skip drawing for full signal. | |
361 if (signal_strength_ != kNumNetworkImages - 1) { | |
362 flags.setColor(SkColorSetA(color_, kBgAlpha)); | |
363 canvas->DrawPath(make_triangle(kFullTriangleSide), flags); | |
364 } | |
365 // Foreground (signal strength). | |
366 if (signal_strength_ != 0) { | |
367 flags.setColor(color_); | |
368 // As a percentage of the bg triangle, the length of one of the short | |
369 // sides of the fg triangle, indexed by signal strength. | |
370 static const float kTriangleSidePercents[] = {0.f, 0.5f, 0.625f, 0.75f, | |
371 1.f}; | |
372 canvas->DrawPath(make_triangle(kTriangleSidePercents[signal_strength_] * | |
373 kFullTriangleSide), | |
374 flags); | |
375 } | |
376 } | |
377 | |
378 ImageType image_type_; | |
379 IconType icon_type_; | |
380 SkColor color_; | |
381 | |
382 // On a scale of 0 to kNum{Arcs,Bars}Images - 1, how connected we are. | |
383 int signal_strength_; | |
384 | |
385 // Padding between outside of icon and edge of the canvas, in dp. This value | |
386 // stays the same regardless of the canvas size (which depends on | |
387 // |icon_type_|). | |
388 static constexpr int kIconInset = 2; | |
389 | |
390 // TODO(estade): share this alpha with other things in ash (battery, etc.). | |
391 // See crbug.com/623987 and crbug.com/632827 | |
392 static constexpr int kBgAlpha = 0x4D; | |
393 | |
394 DISALLOW_COPY_AND_ASSIGN(SignalStrengthImageSource); | |
395 }; | |
396 | |
397 //------------------------------------------------------------------------------ | |
398 // Utilities for extracting icon images. | |
399 | |
400 ImageType ImageTypeForNetworkType(const std::string& type) { | |
401 if (type == shill::kTypeWifi) | |
402 return ARCS; | |
403 else if (type == shill::kTypeCellular || type == shill::kTypeWimax) | |
404 return BARS; | |
405 return NONE; | |
406 } | |
407 | |
408 gfx::ImageSkia GetImageForIndex(ImageType image_type, | |
409 IconType icon_type, | |
410 int index) { | |
411 gfx::CanvasImageSource* source = | |
412 new SignalStrengthImageSource(image_type, icon_type, index); | |
413 return gfx::ImageSkia(source, source->size()); | |
414 } | |
415 | |
416 const gfx::ImageSkia GetDisconnectedImage(IconType icon_type, | |
417 const std::string& network_type) { | |
418 DCHECK_NE(shill::kTypeVPN, network_type); | |
419 ImageType image_type = ImageTypeForNetworkType(network_type); | |
420 const int disconnected_index = 0; | |
421 return GetImageForIndex(image_type, icon_type, disconnected_index); | |
422 } | |
423 | |
424 gfx::ImageSkia* ConnectingWirelessImage(ImageType image_type, | |
425 IconType icon_type, | |
426 double animation) { | |
427 static const int kImageCount = kNumNetworkImages - 1; | |
428 static gfx::ImageSkia* s_bars_images_dark[kImageCount]; | |
429 static gfx::ImageSkia* s_bars_images_light[kImageCount]; | |
430 static gfx::ImageSkia* s_arcs_images_dark[kImageCount]; | |
431 static gfx::ImageSkia* s_arcs_images_light[kImageCount]; | |
432 int index = animation * nextafter(static_cast<float>(kImageCount), 0); | |
433 index = std::max(std::min(index, kImageCount - 1), 0); | |
434 gfx::ImageSkia** images; | |
435 bool dark = IconTypeIsDark(icon_type); | |
436 if (image_type == BARS) | |
437 images = dark ? s_bars_images_dark : s_bars_images_light; | |
438 else | |
439 images = dark ? s_arcs_images_dark : s_arcs_images_light; | |
440 if (!images[index]) { | |
441 // Lazily cache images. | |
442 // TODO(estade): should the alpha be applied in SignalStrengthImageSource? | |
443 gfx::ImageSkia source = GetImageForIndex(image_type, icon_type, index + 1); | |
444 images[index] = | |
445 new gfx::ImageSkia(gfx::ImageSkiaOperations::CreateTransparentImage( | |
446 source, kConnectingImageAlpha)); | |
447 } | |
448 return images[index]; | |
449 } | |
450 | |
451 gfx::ImageSkia ConnectingVpnImage(double animation) { | |
452 int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0); | |
453 static gfx::ImageSkia* s_vpn_images[kNumFadeImages]; | |
454 if (!s_vpn_images[index]) { | |
455 // Lazily cache images. | |
456 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
457 // TODO(estade): update this icon to MD. See crbug.com/690176 | |
458 gfx::ImageSkia* icon = rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN); | |
459 s_vpn_images[index] = new gfx::ImageSkia( | |
460 gfx::ImageSkiaOperations::CreateTransparentImage(*icon, animation)); | |
461 } | |
462 return *s_vpn_images[index]; | |
463 } | |
464 | |
465 gfx::ImageSkia ConnectingVpnBadge(double animation, IconType icon_type) { | |
466 int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0); | |
467 static gfx::ImageSkia* s_vpn_badges[kNumFadeImages]; | |
468 if (!s_vpn_badges[index]) { | |
469 // Lazily cache images. | |
470 gfx::ImageSkia badge = gfx::CreateVectorIcon( | |
471 kNetworkBadgeVpnIcon, GetDefaultColorForIconType(icon_type)); | |
472 s_vpn_badges[index] = new gfx::ImageSkia( | |
473 gfx::ImageSkiaOperations::CreateTransparentImage(badge, animation)); | |
474 } | |
475 return *s_vpn_badges[index]; | |
476 } | |
477 | |
478 int StrengthIndex(int strength) { | |
479 // Return an index in the range [1, kNumNetworkImages - 1]. | |
480 const float findex = (static_cast<float>(strength) / 100.0f) * | |
481 nextafter(static_cast<float>(kNumNetworkImages - 1), 0); | |
482 int index = 1 + static_cast<int>(findex); | |
483 index = std::max(std::min(index, kNumNetworkImages - 1), 1); | |
484 return index; | |
485 } | |
486 | |
487 gfx::ImageSkia BadgeForNetworkTechnology(const NetworkState* network, | |
488 IconType icon_type) { | |
489 const std::string& technology = network->network_technology(); | |
490 const gfx::VectorIcon* icon = &gfx::kNoneIcon; | |
491 if (technology == shill::kNetworkTechnologyEvdo) { | |
492 icon = &kNetworkBadgeTechnologyEvdoIcon; | |
493 } else if (technology == shill::kNetworkTechnology1Xrtt) { | |
494 icon = &kNetworkBadgeTechnology1xIcon; | |
495 } else if (technology == shill::kNetworkTechnologyGprs || | |
496 technology == shill::kNetworkTechnologyGsm) { | |
497 icon = &kNetworkBadgeTechnologyGprsIcon; | |
498 } else if (technology == shill::kNetworkTechnologyEdge) { | |
499 icon = &kNetworkBadgeTechnologyEdgeIcon; | |
500 } else if (technology == shill::kNetworkTechnologyUmts) { | |
501 icon = &kNetworkBadgeTechnology3gIcon; | |
502 } else if (technology == shill::kNetworkTechnologyHspa) { | |
503 icon = &kNetworkBadgeTechnologyHspaIcon; | |
504 } else if (technology == shill::kNetworkTechnologyHspaPlus) { | |
505 icon = &kNetworkBadgeTechnologyHspaPlusIcon; | |
506 } else if (technology == shill::kNetworkTechnologyLte) { | |
507 icon = &kNetworkBadgeTechnologyLteIcon; | |
508 } else if (technology == shill::kNetworkTechnologyLteAdvanced) { | |
509 icon = &kNetworkBadgeTechnologyLteAdvancedIcon; | |
510 } else { | |
511 return gfx::ImageSkia(); | |
512 } | |
513 return gfx::CreateVectorIcon(*icon, GetDefaultColorForIconType(icon_type)); | |
514 } | |
515 | |
516 gfx::ImageSkia GetIcon(const NetworkState* network, | |
517 IconType icon_type, | |
518 int strength_index) { | |
519 if (network->Matches(NetworkTypePattern::Ethernet())) { | |
520 DCHECK_NE(ICON_TYPE_TRAY, icon_type); | |
521 return gfx::CreateVectorIcon(kNetworkEthernetIcon, | |
522 GetDefaultColorForIconType(ICON_TYPE_LIST)); | |
523 } else if (network->Matches(NetworkTypePattern::Wireless())) { | |
524 DCHECK(strength_index > 0); | |
525 return GetImageForIndex(ImageTypeForNetworkType(network->type()), icon_type, | |
526 strength_index); | |
527 } else if (network->Matches(NetworkTypePattern::VPN())) { | |
528 DCHECK_NE(ICON_TYPE_TRAY, icon_type); | |
529 return gfx::CreateVectorIcon(kNetworkVpnIcon, | |
530 GetDefaultColorForIconType(ICON_TYPE_LIST)); | |
531 } | |
532 | |
533 NOTREACHED() << "Request for icon for unsupported type: " << network->type(); | |
534 return gfx::ImageSkia(); | |
535 } | |
536 | |
537 //------------------------------------------------------------------------------ | |
538 // Get connecting images | |
539 | |
540 gfx::ImageSkia GetConnectingVpnImage(IconType icon_type) { | |
541 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); | |
542 const NetworkState* connected_network = nullptr; | |
543 if (icon_type == ICON_TYPE_TRAY) { | |
544 connected_network = | |
545 handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual()); | |
546 } | |
547 double animation = NetworkIconAnimation::GetInstance()->GetAnimation(); | |
548 | |
549 gfx::ImageSkia icon; | |
550 Badges badges; | |
551 if (connected_network) { | |
552 icon = GetImageForNetwork(connected_network, icon_type); | |
553 badges.bottom_left = ConnectingVpnBadge(animation, icon_type); | |
554 } else { | |
555 icon = ConnectingVpnImage(animation); | |
556 } | |
557 return NetworkIconImageSource::CreateImage(icon, badges); | |
558 } | |
559 | |
560 gfx::ImageSkia GetConnectingImage(IconType icon_type, | |
561 const std::string& network_type) { | |
562 if (network_type == shill::kTypeVPN) | |
563 return GetConnectingVpnImage(icon_type); | |
564 | |
565 ImageType image_type = ImageTypeForNetworkType(network_type); | |
566 double animation = NetworkIconAnimation::GetInstance()->GetAnimation(); | |
567 | |
568 return NetworkIconImageSource::CreateImage( | |
569 *ConnectingWirelessImage(image_type, icon_type, animation), Badges()); | |
570 } | |
571 | |
572 } // namespace | |
573 | |
574 //------------------------------------------------------------------------------ | |
575 // NetworkIconImpl | |
576 | |
577 NetworkIconImpl::NetworkIconImpl(const std::string& path, IconType icon_type) | |
578 : network_path_(path), | |
579 icon_type_(icon_type), | |
580 strength_index_(-1), | |
581 behind_captive_portal_(false) { | |
582 // Default image | |
583 image_ = GetDisconnectedImage(icon_type, shill::kTypeWifi); | |
584 } | |
585 | |
586 void NetworkIconImpl::Update(const NetworkState* network) { | |
587 DCHECK(network); | |
588 // Determine whether or not we need to update the icon. | |
589 bool dirty = image_.isNull(); | |
590 | |
591 // If the network state has changed, the icon needs updating. | |
592 if (state_ != network->connection_state()) { | |
593 state_ = network->connection_state(); | |
594 dirty = true; | |
595 } | |
596 | |
597 dirty |= UpdatePortalState(network); | |
598 | |
599 if (network->Matches(NetworkTypePattern::Wireless())) { | |
600 dirty |= UpdateWirelessStrengthIndex(network); | |
601 } | |
602 | |
603 if (network->Matches(NetworkTypePattern::Cellular())) | |
604 dirty |= UpdateCellularState(network); | |
605 | |
606 if (IconTypeHasVPNBadge(icon_type_) && | |
607 network->Matches(NetworkTypePattern::NonVirtual())) { | |
608 dirty |= UpdateVPNBadge(); | |
609 } | |
610 | |
611 if (dirty) { | |
612 // Set the icon and badges based on the network and generate the image. | |
613 GenerateImage(network); | |
614 } | |
615 } | |
616 | |
617 bool NetworkIconImpl::UpdateWirelessStrengthIndex(const NetworkState* network) { | |
618 int index = StrengthIndex(network->signal_strength()); | |
619 if (index != strength_index_) { | |
620 strength_index_ = index; | |
621 return true; | |
622 } | |
623 return false; | |
624 } | |
625 | |
626 bool NetworkIconImpl::UpdateCellularState(const NetworkState* network) { | |
627 bool dirty = false; | |
628 const gfx::ImageSkia technology_badge = | |
629 BadgeForNetworkTechnology(network, icon_type_); | |
630 if (!technology_badge.BackedBySameObjectAs(technology_badge_)) { | |
631 technology_badge_ = technology_badge; | |
632 dirty = true; | |
633 } | |
634 std::string roaming_state = network->roaming(); | |
635 if (roaming_state != roaming_state_) { | |
636 roaming_state_ = roaming_state; | |
637 dirty = true; | |
638 } | |
639 return dirty; | |
640 } | |
641 | |
642 bool NetworkIconImpl::UpdatePortalState(const NetworkState* network) { | |
643 bool behind_captive_portal = false; | |
644 if (network && chromeos::network_portal_detector::IsInitialized()) { | |
645 NetworkPortalDetector::CaptivePortalState state = | |
646 chromeos::network_portal_detector::GetInstance()->GetCaptivePortalState( | |
647 network->guid()); | |
648 behind_captive_portal = | |
649 state.status == NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_PORTAL; | |
650 } | |
651 | |
652 if (behind_captive_portal == behind_captive_portal_) | |
653 return false; | |
654 behind_captive_portal_ = behind_captive_portal; | |
655 return true; | |
656 } | |
657 | |
658 bool NetworkIconImpl::UpdateVPNBadge() { | |
659 const NetworkState* vpn = | |
660 NetworkHandler::Get()->network_state_handler()->ConnectedNetworkByType( | |
661 NetworkTypePattern::VPN()); | |
662 if (vpn && vpn_badge_.isNull()) { | |
663 vpn_badge_ = gfx::CreateVectorIcon(kNetworkBadgeVpnIcon, | |
664 GetDefaultColorForIconType(icon_type_)); | |
665 return true; | |
666 } | |
667 if (!vpn && !vpn_badge_.isNull()) { | |
668 vpn_badge_ = gfx::ImageSkia(); | |
669 return true; | |
670 } | |
671 return false; | |
672 } | |
673 | |
674 void NetworkIconImpl::GetBadges(const NetworkState* network, Badges* badges) { | |
675 DCHECK(network); | |
676 | |
677 const std::string& type = network->type(); | |
678 const SkColor icon_color = GetDefaultColorForIconType(icon_type_); | |
679 if (type == shill::kTypeWifi) { | |
680 if (network->security_class() != shill::kSecurityNone && | |
681 IconTypeIsDark(icon_type_)) { | |
682 badges->bottom_right = | |
683 gfx::CreateVectorIcon(kNetworkBadgeSecureIcon, icon_color); | |
684 } | |
685 } else if (type == shill::kTypeWimax) { | |
686 technology_badge_ = | |
687 gfx::CreateVectorIcon(kNetworkBadgeTechnology4gIcon, icon_color); | |
688 } else if (type == shill::kTypeCellular) { | |
689 if (network->roaming() == shill::kRoamingStateRoaming) { | |
690 // For networks that are always in roaming don't show roaming badge. | |
691 const DeviceState* device = | |
692 NetworkHandler::Get()->network_state_handler()->GetDeviceState( | |
693 network->device_path()); | |
694 LOG_IF(WARNING, !device) << "Could not find device state for " | |
695 << network->device_path(); | |
696 if (!device || !device->provider_requires_roaming()) { | |
697 badges->bottom_right = | |
698 gfx::CreateVectorIcon(kNetworkBadgeRoamingIcon, icon_color); | |
699 } | |
700 } | |
701 } | |
702 if (!network->IsConnectingState()) { | |
703 badges->top_left = technology_badge_; | |
704 badges->bottom_left = vpn_badge_; | |
705 } | |
706 | |
707 if (behind_captive_portal_) { | |
708 badges->bottom_right = | |
709 gfx::CreateVectorIcon(kNetworkBadgeCaptivePortalIcon, icon_color); | |
710 } | |
711 } | |
712 | |
713 void NetworkIconImpl::GenerateImage(const NetworkState* network) { | |
714 DCHECK(network); | |
715 gfx::ImageSkia icon = GetIcon(network, icon_type_, strength_index_); | |
716 Badges badges; | |
717 GetBadges(network, &badges); | |
718 image_ = NetworkIconImageSource::CreateImage(icon, badges); | |
719 } | |
720 | |
721 namespace { | |
722 | |
723 NetworkIconImpl* FindAndUpdateImageImpl(const NetworkState* network, | |
724 IconType icon_type) { | |
725 // Find or add the icon. | |
726 NetworkIconMap* icon_map = GetIconMap(icon_type); | |
727 NetworkIconImpl* icon; | |
728 NetworkIconMap::iterator iter = icon_map->find(network->path()); | |
729 if (iter == icon_map->end()) { | |
730 icon = new NetworkIconImpl(network->path(), icon_type); | |
731 icon_map->insert(std::make_pair(network->path(), icon)); | |
732 } else { | |
733 icon = iter->second; | |
734 } | |
735 | |
736 // Update and return the icon's image. | |
737 icon->Update(network); | |
738 return icon; | |
739 } | |
740 | |
741 } // namespace | |
742 | |
743 //------------------------------------------------------------------------------ | |
744 // Public interface | |
745 | |
746 gfx::ImageSkia GetImageForNetwork(const NetworkState* network, | |
747 IconType icon_type) { | |
748 DCHECK(network); | |
749 if (!network->visible()) | |
750 return GetDisconnectedImage(icon_type, network->type()); | |
751 | |
752 if (network->IsConnectingState()) | |
753 return GetConnectingImage(icon_type, network->type()); | |
754 | |
755 NetworkIconImpl* icon = FindAndUpdateImageImpl(network, icon_type); | |
756 return icon->image(); | |
757 } | |
758 | |
759 gfx::ImageSkia GetImageForConnectedMobileNetwork() { | |
760 ImageType image_type = ImageTypeForNetworkType(shill::kTypeWifi); | |
761 const IconType icon_type = ICON_TYPE_LIST; | |
762 const int connected_index = kNumNetworkImages - 1; | |
763 return GetImageForIndex(image_type, icon_type, connected_index); | |
764 } | |
765 | |
766 gfx::ImageSkia GetImageForDisconnectedCellNetwork() { | |
767 return GetDisconnectedImage(ICON_TYPE_LIST, shill::kTypeCellular); | |
768 } | |
769 | |
770 gfx::ImageSkia GetImageForNewWifiNetwork(SkColor icon_color, | |
771 SkColor badge_color) { | |
772 SignalStrengthImageSource* source = | |
773 new SignalStrengthImageSource(ImageTypeForNetworkType(shill::kTypeWifi), | |
774 ICON_TYPE_LIST, kNumNetworkImages - 1); | |
775 source->set_color(icon_color); | |
776 gfx::ImageSkia icon = gfx::ImageSkia(source, source->size()); | |
777 Badges badges; | |
778 badges.bottom_right = | |
779 gfx::CreateVectorIcon(kNetworkBadgeAddOtherIcon, badge_color); | |
780 return NetworkIconImageSource::CreateImage(icon, badges); | |
781 } | |
782 | |
783 base::string16 GetLabelForNetwork(const chromeos::NetworkState* network, | |
784 IconType icon_type) { | |
785 DCHECK(network); | |
786 std::string activation_state = network->activation_state(); | |
787 if (icon_type == ICON_TYPE_LIST || icon_type == ICON_TYPE_MENU_LIST) { | |
788 // Show "<network>: [Connecting|Activating|Reconnecting]..." | |
789 // TODO(varkha): Remaining states should migrate to secondary status in the | |
790 // network item and no longer be part of the label. | |
791 // See http://crbug.com/676181 . | |
792 if (network->IsReconnecting()) { | |
793 return l10n_util::GetStringFUTF16( | |
794 IDS_ASH_STATUS_TRAY_NETWORK_LIST_RECONNECTING, | |
795 base::UTF8ToUTF16(network->name())); | |
796 } | |
797 if (icon_type != ICON_TYPE_MENU_LIST && network->IsConnectingState()) { | |
798 return l10n_util::GetStringFUTF16( | |
799 IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING, | |
800 base::UTF8ToUTF16(network->name())); | |
801 } | |
802 if (activation_state == shill::kActivationStateActivating) { | |
803 return l10n_util::GetStringFUTF16( | |
804 IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING, | |
805 base::UTF8ToUTF16(network->name())); | |
806 } | |
807 // Show "Activate <network>" in list view only. | |
808 if (activation_state == shill::kActivationStateNotActivated || | |
809 activation_state == shill::kActivationStatePartiallyActivated) { | |
810 return l10n_util::GetStringFUTF16( | |
811 IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE, | |
812 base::UTF8ToUTF16(network->name())); | |
813 } | |
814 } else { | |
815 // Show "[Connected to|Connecting to|Activating|Reconnecting to] <network>" | |
816 // (non-list view). | |
817 if (network->IsReconnecting()) { | |
818 return l10n_util::GetStringFUTF16( | |
819 IDS_ASH_STATUS_TRAY_NETWORK_RECONNECTING, | |
820 base::UTF8ToUTF16(network->name())); | |
821 } | |
822 if (network->IsConnectedState()) { | |
823 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED, | |
824 base::UTF8ToUTF16(network->name())); | |
825 } | |
826 if (network->IsConnectingState()) { | |
827 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING, | |
828 base::UTF8ToUTF16(network->name())); | |
829 } | |
830 if (activation_state == shill::kActivationStateActivating) { | |
831 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING, | |
832 base::UTF8ToUTF16(network->name())); | |
833 } | |
834 } | |
835 | |
836 // Otherwise just show the network name or 'Ethernet'. | |
837 if (network->Matches(NetworkTypePattern::Ethernet())) { | |
838 return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ETHERNET); | |
839 } else { | |
840 return base::UTF8ToUTF16(network->name()); | |
841 } | |
842 } | |
843 | |
844 int GetCellularUninitializedMsg() { | |
845 static base::Time s_uninitialized_state_time; | |
846 static int s_uninitialized_msg(0); | |
847 | |
848 NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler(); | |
849 if (handler->GetTechnologyState(NetworkTypePattern::Mobile()) == | |
850 NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) { | |
851 s_uninitialized_msg = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR; | |
852 s_uninitialized_state_time = base::Time::Now(); | |
853 return s_uninitialized_msg; | |
854 } else if (handler->GetScanningByType(NetworkTypePattern::Mobile())) { | |
855 s_uninitialized_msg = IDS_ASH_STATUS_TRAY_MOBILE_SCANNING; | |
856 s_uninitialized_state_time = base::Time::Now(); | |
857 return s_uninitialized_msg; | |
858 } | |
859 // There can be a delay between leaving the Initializing state and when | |
860 // a Cellular device shows up, so keep showing the initializing | |
861 // animation for a bit to avoid flashing the disconnect icon. | |
862 const int kInitializingDelaySeconds = 1; | |
863 base::TimeDelta dtime = base::Time::Now() - s_uninitialized_state_time; | |
864 if (dtime.InSeconds() < kInitializingDelaySeconds) | |
865 return s_uninitialized_msg; | |
866 return 0; | |
867 } | |
868 | |
869 void GetDefaultNetworkImageAndLabel(IconType icon_type, | |
870 gfx::ImageSkia* image, | |
871 base::string16* label, | |
872 bool* animating) { | |
873 NetworkStateHandler* state_handler = | |
874 NetworkHandler::Get()->network_state_handler(); | |
875 NetworkConnectionHandler* connect_handler = | |
876 NetworkHandler::Get()->network_connection_handler(); | |
877 const NetworkState* connected_network = | |
878 state_handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual()); | |
879 const NetworkState* connecting_network = | |
880 state_handler->ConnectingNetworkByType(NetworkTypePattern::Wireless()); | |
881 if (!connecting_network && icon_type == ICON_TYPE_TRAY) { | |
882 connecting_network = | |
883 state_handler->ConnectingNetworkByType(NetworkTypePattern::VPN()); | |
884 } | |
885 | |
886 const NetworkState* network; | |
887 // If we are connecting to a network, and there is either no connected | |
888 // network, or the connection was user requested, or shill triggered a | |
889 // reconnection, use the connecting network. | |
890 if (connecting_network && | |
891 (!connected_network || connecting_network->IsReconnecting() || | |
892 connect_handler->HasConnectingNetwork(connecting_network->path()))) { | |
893 network = connecting_network; | |
894 } else { | |
895 network = connected_network; | |
896 } | |
897 | |
898 // Don't show ethernet in the tray | |
899 if (icon_type == ICON_TYPE_TRAY && network && | |
900 network->Matches(NetworkTypePattern::Ethernet())) { | |
901 *image = gfx::ImageSkia(); | |
902 *animating = false; | |
903 return; | |
904 } | |
905 | |
906 if (!network) { | |
907 // If no connecting network, check if we are activating a network. | |
908 const NetworkState* mobile_network = | |
909 state_handler->FirstNetworkByType(NetworkTypePattern::Mobile()); | |
910 if (mobile_network && (mobile_network->activation_state() == | |
911 shill::kActivationStateActivating)) { | |
912 network = mobile_network; | |
913 } | |
914 } | |
915 if (!network) { | |
916 // If no connecting network, check for cellular initializing. | |
917 int uninitialized_msg = GetCellularUninitializedMsg(); | |
918 if (uninitialized_msg != 0) { | |
919 *image = GetConnectingImage(icon_type, shill::kTypeCellular); | |
920 if (label) | |
921 *label = l10n_util::GetStringUTF16(uninitialized_msg); | |
922 *animating = true; | |
923 } else { | |
924 // Otherwise show the disconnected wifi icon. | |
925 *image = GetDisconnectedImage(icon_type, shill::kTypeWifi); | |
926 if (label) { | |
927 *label = l10n_util::GetStringUTF16( | |
928 IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED); | |
929 } | |
930 *animating = false; | |
931 } | |
932 return; | |
933 } | |
934 *animating = network->IsConnectingState(); | |
935 // Get icon and label for connected or connecting network. | |
936 *image = GetImageForNetwork(network, icon_type); | |
937 if (label) | |
938 *label = GetLabelForNetwork(network, icon_type); | |
939 } | |
940 | |
941 void PurgeNetworkIconCache() { | |
942 NetworkStateHandler::NetworkStateList networks; | |
943 NetworkHandler::Get()->network_state_handler()->GetVisibleNetworkList( | |
944 &networks); | |
945 std::set<std::string> network_paths; | |
946 for (NetworkStateHandler::NetworkStateList::iterator iter = networks.begin(); | |
947 iter != networks.end(); ++iter) { | |
948 network_paths.insert((*iter)->path()); | |
949 } | |
950 PurgeIconMap(ICON_TYPE_TRAY, network_paths); | |
951 PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_paths); | |
952 PurgeIconMap(ICON_TYPE_LIST, network_paths); | |
953 PurgeIconMap(ICON_TYPE_MENU_LIST, network_paths); | |
954 } | |
955 | |
956 } // namespace network_icon | |
957 } // namespace ash | |
OLD | NEW |