root/ash/system/chromeos/network/network_icon.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. image
  2. GetIconMapInstance
  3. GetIconMap
  4. PurgeIconMap
  5. GetEmptyBitmap
  6. GetImageForScale
  7. badges_
  8. GetImageForScale
  9. IconTypeIsDark
  10. IconTypeHasVPNBadge
  11. NumImagesForType
  12. BaseImageForType
  13. ImageTypeForNetworkType
  14. GetImageForIndex
  15. GetConnectedImage
  16. GetDisconnectedImage
  17. ConnectingWirelessImage
  18. ConnectingVpnImage
  19. ConnectingVpnBadge
  20. StrengthIndex
  21. GetStrengthIndex
  22. BadgeForNetworkTechnology
  23. BadgeForVPN
  24. GetIcon
  25. GetConnectingVpnImage
  26. GetConnectingImage
  27. behind_captive_portal_
  28. Update
  29. UpdateWirelessStrengthIndex
  30. UpdateCellularState
  31. UpdatePortalState
  32. UpdateVPNBadge
  33. GetBadges
  34. GenerateImage
  35. GetImageForNetwork
  36. GetImageForConnectedNetwork
  37. GetImageForConnectingNetwork
  38. GetImageForDisconnectedNetwork
  39. GetLabelForNetwork
  40. GetCellularUninitializedMsg
  41. GetDefaultNetworkImageAndLabel
  42. PurgeNetworkIconCache

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/chromeos/network/network_icon.h"

#include "ash/shell.h"
#include "ash/system/chromeos/network/network_icon_animation.h"
#include "ash/system/chromeos/network/network_icon_animation_observer.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_connection_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/shill_property_util.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/image/image_skia_source.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size_conversions.h"

using chromeos::DeviceState;
using chromeos::NetworkConnectionHandler;
using chromeos::NetworkHandler;
using chromeos::NetworkState;
using chromeos::NetworkStateHandler;
using chromeos::NetworkTypePattern;

namespace ash {
namespace network_icon {

namespace {

//------------------------------------------------------------------------------
// Struct to pass icon badges to NetworkIconImageSource.
struct Badges {
  Badges()
      : top_left(NULL),
        top_right(NULL),
        bottom_left(NULL),
        bottom_right(NULL) {
  }
  const gfx::ImageSkia* top_left;
  const gfx::ImageSkia* top_right;
  const gfx::ImageSkia* bottom_left;
  const gfx::ImageSkia* bottom_right;
};

//------------------------------------------------------------------------------
// class used for maintaining a map of network state and images.
class NetworkIconImpl {
 public:
  explicit NetworkIconImpl(IconType icon_type);

  // Determines whether or not the associated network might be dirty and if so
  // updates and generates the icon. Does nothing if network no longer exists.
  void Update(const chromeos::NetworkState* network);

  const gfx::ImageSkia& image() const { return image_; }

 private:
  // Updates |strength_index_| for wireless networks. Returns true if changed.
  bool UpdateWirelessStrengthIndex(const chromeos::NetworkState* network);

  // Updates the local state for cellular networks. Returns true if changed.
  bool UpdateCellularState(const chromeos::NetworkState* network);

  // Updates the portal state for wireless networks. Returns true if changed.
  bool UpdatePortalState(const chromeos::NetworkState* network);

  // Updates the VPN badge. Returns true if changed.
  bool UpdateVPNBadge();

  // Gets |badges| based on |network| and the current state.
  void GetBadges(const NetworkState* network, Badges* badges);

  // Gets the appropriate icon and badges and composites the image.
  void GenerateImage(const chromeos::NetworkState* network);

  // Defines color theme and VPN badging
  const IconType icon_type_;

  // Cached state of the network when the icon was last generated.
  std::string state_;

  // Cached strength index of the network when the icon was last generated.
  int strength_index_;

  // Cached technology badge for the network when the icon was last generated.
  const gfx::ImageSkia* technology_badge_;

  // Cached vpn badge for the network when the icon was last generated.
  const gfx::ImageSkia* vpn_badge_;

  // Cached roaming state of the network when the icon was last generated.
  std::string roaming_state_;

  // Cached portal state of the network when the icon was last generated.
  bool behind_captive_portal_;

  // Generated icon image.
  gfx::ImageSkia image_;

  DISALLOW_COPY_AND_ASSIGN(NetworkIconImpl);
};

//------------------------------------------------------------------------------
// Maintain a static (global) icon map. Note: Icons are never destroyed;
// it is assumed that a finite and reasonable number of network icons will be
// created during a session.

typedef std::map<std::string, NetworkIconImpl*> NetworkIconMap;

NetworkIconMap* GetIconMapInstance(IconType icon_type, bool create) {
  typedef std::map<IconType, NetworkIconMap*> IconTypeMap;
  static IconTypeMap* s_icon_map = NULL;
  if (s_icon_map == NULL) {
    if (!create)
      return NULL;
    s_icon_map = new IconTypeMap;
  }
  if (s_icon_map->count(icon_type) == 0) {
    if (!create)
      return NULL;
    (*s_icon_map)[icon_type] = new NetworkIconMap;
  }
  return (*s_icon_map)[icon_type];
}

NetworkIconMap* GetIconMap(IconType icon_type) {
  return GetIconMapInstance(icon_type, true);
}

void PurgeIconMap(IconType icon_type,
                  const std::set<std::string>& network_paths) {
  NetworkIconMap* icon_map = GetIconMapInstance(icon_type, false);
  if (!icon_map)
    return;
  for (NetworkIconMap::iterator loop_iter = icon_map->begin();
       loop_iter != icon_map->end(); ) {
    NetworkIconMap::iterator cur_iter = loop_iter++;
    if (network_paths.count(cur_iter->first) == 0) {
      delete cur_iter->second;
      icon_map->erase(cur_iter);
    }
  }
}

//------------------------------------------------------------------------------
// Utilities for generating icon images.

// 'NONE' will default to ARCS behavior where appropriate (e.g. no network or
// if a new type gets added).
enum ImageType {
  ARCS,
  BARS,
  NONE
};

// Amount to fade icons while connecting.
const double kConnectingImageAlpha = 0.5;

// Images for strength bars for wired networks.
const int kNumBarsImages = 5;

// Imagaes for strength arcs for wireless networks.
const int kNumArcsImages = 5;

// Number of discrete images to use for alpha fade animation
const int kNumFadeImages = 10;

//------------------------------------------------------------------------------
// Classes for generating scaled images.

const SkBitmap GetEmptyBitmap(const gfx::Size pixel_size) {
  typedef std::pair<int, int> SizeKey;
  typedef std::map<SizeKey, SkBitmap> SizeBitmapMap;
  static SizeBitmapMap* s_empty_bitmaps = new SizeBitmapMap;

  SizeKey key(pixel_size.width(), pixel_size.height());

  SizeBitmapMap::iterator iter = s_empty_bitmaps->find(key);
  if (iter != s_empty_bitmaps->end())
    return iter->second;

  SkBitmap empty;
  empty.setConfig(SkBitmap::kARGB_8888_Config, key.first, key.second);
  empty.allocPixels();
  empty.eraseARGB(0, 0, 0, 0);
  (*s_empty_bitmaps)[key] = empty;
  return empty;
}

class EmptyImageSource: public gfx::ImageSkiaSource {
 public:
  explicit EmptyImageSource(const gfx::Size& size)
      : size_(size) {
  }

  virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
    gfx::Size pixel_size = gfx::ToFlooredSize(gfx::ScaleSize(size_, scale));
    SkBitmap empty_bitmap = GetEmptyBitmap(pixel_size);
    return gfx::ImageSkiaRep(empty_bitmap, scale);
  }

 private:
  const gfx::Size size_;

  DISALLOW_COPY_AND_ASSIGN(EmptyImageSource);
};

// This defines how we assemble a network icon.
class NetworkIconImageSource : public gfx::ImageSkiaSource {
 public:
  NetworkIconImageSource(const gfx::ImageSkia& icon, const Badges& badges)
      : icon_(icon),
        badges_(badges) {
  }
  virtual ~NetworkIconImageSource() {}

  // TODO(pkotwicz): Figure out what to do when a new image resolution becomes
  // available.
  virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
    gfx::ImageSkiaRep icon_rep = icon_.GetRepresentation(scale);
    if (icon_rep.is_null())
      return gfx::ImageSkiaRep();
    gfx::Canvas canvas(icon_rep, false);
    if (badges_.top_left)
      canvas.DrawImageInt(*badges_.top_left, 0, 0);
    if (badges_.top_right)
      canvas.DrawImageInt(*badges_.top_right,
                          icon_.width() - badges_.top_right->width(), 0);
    if (badges_.bottom_left) {
      canvas.DrawImageInt(*badges_.bottom_left,
                          0, icon_.height() - badges_.bottom_left->height());
    }
    if (badges_.bottom_right) {
      canvas.DrawImageInt(*badges_.bottom_right,
                          icon_.width() - badges_.bottom_right->width(),
                          icon_.height() - badges_.bottom_right->height());
    }
    return canvas.ExtractImageRep();
  }

 private:
  const gfx::ImageSkia icon_;
  const Badges badges_;

  DISALLOW_COPY_AND_ASSIGN(NetworkIconImageSource);
};

//------------------------------------------------------------------------------
// Utilities for extracting icon images.

bool IconTypeIsDark(IconType icon_type) {
  return (icon_type != ICON_TYPE_TRAY);
}

bool IconTypeHasVPNBadge(IconType icon_type) {
  return (icon_type != ICON_TYPE_LIST);
}

int NumImagesForType(ImageType type) {
  return (type == BARS) ? kNumBarsImages : kNumArcsImages;
}

gfx::ImageSkia* BaseImageForType(ImageType image_type, IconType icon_type) {
  gfx::ImageSkia* image;
  if (image_type == BARS) {
    image =  ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_BARS_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_BARS_LIGHT);
  } else {
    image =  ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_ARCS_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_ARCS_LIGHT);
  }
  return image;
}

ImageType ImageTypeForNetworkType(const std::string& type) {
  if (type == shill::kTypeWifi)
    return ARCS;
  else if (type == shill::kTypeCellular || type == shill::kTypeWimax)
    return BARS;
  return NONE;
}

gfx::ImageSkia GetImageForIndex(ImageType image_type,
                                IconType icon_type,
                                int index) {
  int num_images = NumImagesForType(image_type);
  if (index < 0 || index >= num_images)
    return gfx::ImageSkia();
  gfx::ImageSkia* images = BaseImageForType(image_type, icon_type);
  int width = images->width();
  int height = images->height() / num_images;
  return gfx::ImageSkiaOperations::ExtractSubset(*images,
      gfx::Rect(0, index * height, width, height));
}

const gfx::ImageSkia GetConnectedImage(const std::string& type,
                                       IconType icon_type) {
  if (type == shill::kTypeVPN) {
    return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IDR_AURA_UBER_TRAY_NETWORK_VPN);
  }
  ImageType image_type = ImageTypeForNetworkType(type);
  const int connected_index = NumImagesForType(image_type) - 1;
  return GetImageForIndex(image_type, icon_type, connected_index);
}

const gfx::ImageSkia GetDisconnectedImage(const std::string& type,
                                          IconType icon_type) {
  if (type == shill::kTypeVPN) {
    // Note: same as connected image, shouldn't normally be seen.
    return *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IDR_AURA_UBER_TRAY_NETWORK_VPN);
  }
  ImageType image_type = ImageTypeForNetworkType(type);
  const int disconnected_index = 0;
  return GetImageForIndex(image_type, icon_type, disconnected_index);
}

gfx::ImageSkia* ConnectingWirelessImage(ImageType image_type,
                                        IconType icon_type,
                                        double animation) {
  static gfx::ImageSkia* s_bars_images_dark[kNumBarsImages - 1];
  static gfx::ImageSkia* s_bars_images_light[kNumBarsImages - 1];
  static gfx::ImageSkia* s_arcs_images_dark[kNumArcsImages - 1];
  static gfx::ImageSkia* s_arcs_images_light[kNumArcsImages - 1];
  int image_count = NumImagesForType(image_type) - 1;
  int index = animation * nextafter(static_cast<float>(image_count), 0);
  index = std::max(std::min(index, image_count - 1), 0);
  gfx::ImageSkia** images;
  bool dark = IconTypeIsDark(icon_type);
  if (image_type == BARS)
    images = dark ? s_bars_images_dark : s_bars_images_light;
  else
    images = dark ? s_arcs_images_dark : s_arcs_images_light;
  if (!images[index]) {
    // Lazily cache images.
    gfx::ImageSkia source = GetImageForIndex(image_type, icon_type, index + 1);
    images[index] = new gfx::ImageSkia(
        gfx::ImageSkiaOperations::CreateBlendedImage(
            gfx::ImageSkia(new EmptyImageSource(source.size()), source.size()),
            source,
            kConnectingImageAlpha));
  }
  return images[index];
}

gfx::ImageSkia* ConnectingVpnImage(double animation) {
  int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0);
  static gfx::ImageSkia* s_vpn_images[kNumFadeImages];
  if (!s_vpn_images[index]) {
    // Lazily cache images.
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    gfx::ImageSkia* icon = rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN);
    s_vpn_images[index] = new gfx::ImageSkia(
        gfx::ImageSkiaOperations::CreateBlendedImage(
            gfx::ImageSkia(new EmptyImageSource(icon->size()), icon->size()),
            *icon,
            animation));
  }
  return s_vpn_images[index];
}

gfx::ImageSkia* ConnectingVpnBadge(double animation) {
  int index = animation * nextafter(static_cast<float>(kNumFadeImages), 0);
  static gfx::ImageSkia* s_vpn_badges[kNumFadeImages];
  if (!s_vpn_badges[index]) {
    // Lazily cache images.
    ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    gfx::ImageSkia* icon =
        rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);  // For size
    gfx::ImageSkia* badge =
        rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE);
    s_vpn_badges[index] = new gfx::ImageSkia(
        gfx::ImageSkiaOperations::CreateBlendedImage(
            gfx::ImageSkia(new EmptyImageSource(icon->size()), icon->size()),
            *badge,
            animation));
  }
  return s_vpn_badges[index];
}

int StrengthIndex(int strength, int count) {
  // Return an index in the range [1, count-1].
  const float findex = (static_cast<float>(strength) / 100.0f) *
      nextafter(static_cast<float>(count - 1), 0);
  int index = 1 + static_cast<int>(findex);
  index = std::max(std::min(index, count - 1), 1);
  return index;
}

int GetStrengthIndex(const NetworkState* network) {
  ImageType image_type = ImageTypeForNetworkType(network->type());
  if (image_type == ARCS)
    return StrengthIndex(network->signal_strength(), kNumArcsImages);
  else if (image_type == BARS)
    return StrengthIndex(network->signal_strength(), kNumBarsImages);
  return 0;
}

const gfx::ImageSkia* BadgeForNetworkTechnology(const NetworkState* network,
                                                IconType icon_type) {
  const int kUnknownBadgeType = -1;
  int id = kUnknownBadgeType;
  const std::string& technology = network->network_technology();
  if (technology == shill::kNetworkTechnologyEvdo) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_EVDO_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_EVDO_LIGHT;
  } else if (technology == shill::kNetworkTechnology1Xrtt) {
    id = IDR_AURA_UBER_TRAY_NETWORK_1X;
  } else if (technology == shill::kNetworkTechnologyGprs) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT;
  } else if (technology == shill::kNetworkTechnologyEdge) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_EDGE_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_EDGE_LIGHT;
  } else if (technology == shill::kNetworkTechnologyUmts) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_3G_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_3G_LIGHT;
  } else if (technology == shill::kNetworkTechnologyHspa) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_HSPA_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_HSPA_LIGHT;
  } else if (technology == shill::kNetworkTechnologyHspaPlus) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_HSPA_PLUS_LIGHT;
  } else if (technology == shill::kNetworkTechnologyLte) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_LTE_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_LTE_LIGHT;
  } else if (technology == shill::kNetworkTechnologyLteAdvanced) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_LTE_ADVANCED_LIGHT;
  } else if (technology == shill::kNetworkTechnologyGsm) {
    id = IconTypeIsDark(icon_type) ?
        IDR_AURA_UBER_TRAY_NETWORK_GPRS_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_GPRS_LIGHT;
  }
  if (id == kUnknownBadgeType)
    return NULL;
  else
    return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id);
}

const gfx::ImageSkia* BadgeForVPN(IconType icon_type) {
  return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
      IDR_AURA_UBER_TRAY_NETWORK_VPN_BADGE);
}

gfx::ImageSkia GetIcon(const NetworkState* network,
                       IconType icon_type,
                       int strength_index) {
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  if (network->Matches(NetworkTypePattern::Ethernet())) {
    return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
  } else if (network->Matches(NetworkTypePattern::Wireless())) {
    DCHECK(strength_index > 0);
    return GetImageForIndex(
        ImageTypeForNetworkType(network->type()), icon_type, strength_index);
  } else if (network->Matches(NetworkTypePattern::VPN())) {
    return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_VPN);
  } else {
    LOG(WARNING) << "Request for icon for unsupported type: "
                 << network->type();
    return *rb.GetImageSkiaNamed(IDR_AURA_UBER_TRAY_NETWORK_WIRED);
  }
}

//------------------------------------------------------------------------------
// Get connecting images

gfx::ImageSkia GetConnectingVpnImage(IconType icon_type) {
  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
  const NetworkState* connected_network = NULL;
  if (icon_type == ICON_TYPE_TRAY) {
    connected_network =
        handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
  }
  double animation = NetworkIconAnimation::GetInstance()->GetAnimation();

  if (connected_network) {
    gfx::ImageSkia icon = GetImageForNetwork(connected_network, icon_type);
    Badges badges;
    badges.bottom_left = ConnectingVpnBadge(animation);
    return gfx::ImageSkia(
        new NetworkIconImageSource(icon, badges), icon.size());
  } else {
    gfx::ImageSkia* icon = ConnectingVpnImage(animation);
    return gfx::ImageSkia(
        new NetworkIconImageSource(*icon, Badges()), icon->size());
  }
}

gfx::ImageSkia GetConnectingImage(const std::string& network_type,
                                  IconType icon_type) {
  if (network_type == shill::kTypeVPN)
    return GetConnectingVpnImage(icon_type);

  ImageType image_type = ImageTypeForNetworkType(network_type);
  double animation = NetworkIconAnimation::GetInstance()->GetAnimation();

  gfx::ImageSkia* icon = ConnectingWirelessImage(
      image_type, icon_type, animation);
  return gfx::ImageSkia(
      new NetworkIconImageSource(*icon, Badges()), icon->size());
}

}  // namespace

//------------------------------------------------------------------------------
// NetworkIconImpl

NetworkIconImpl::NetworkIconImpl(IconType icon_type)
    : icon_type_(icon_type),
      strength_index_(-1),
      technology_badge_(NULL),
      vpn_badge_(NULL),
      behind_captive_portal_(false) {
  // Default image
  image_ = GetDisconnectedImage(shill::kTypeWifi, icon_type);
}

void NetworkIconImpl::Update(const NetworkState* network) {
  DCHECK(network);
  // Determine whether or not we need to update the icon.
  bool dirty = image_.isNull();

  // If the network state has changed, the icon needs updating.
  if (state_ != network->connection_state()) {
    state_ = network->connection_state();
    dirty = true;
  }

  dirty |= UpdatePortalState(network);

  if (network->Matches(NetworkTypePattern::Wireless())) {
    dirty |= UpdateWirelessStrengthIndex(network);
  }

  if (network->Matches(NetworkTypePattern::Cellular()))
    dirty |= UpdateCellularState(network);

  if (IconTypeHasVPNBadge(icon_type_) &&
      network->Matches(NetworkTypePattern::NonVirtual())) {
    dirty |= UpdateVPNBadge();
  }

  if (dirty) {
    // Set the icon and badges based on the network and generate the image.
    GenerateImage(network);
  }
}

bool NetworkIconImpl::UpdateWirelessStrengthIndex(const NetworkState* network) {
  int index = GetStrengthIndex(network);
  if (index != strength_index_) {
    strength_index_ = index;
    return true;
  }
  return false;
}

bool NetworkIconImpl::UpdateCellularState(const NetworkState* network) {
  bool dirty = false;
  const gfx::ImageSkia* technology_badge =
      BadgeForNetworkTechnology(network, icon_type_);
  if (technology_badge != technology_badge_) {
    technology_badge_ = technology_badge;
    dirty = true;
  }
  std::string roaming_state = network->roaming();
  if (roaming_state != roaming_state_) {
    roaming_state_ = roaming_state;
    dirty = true;
  }
  return dirty;
}

bool NetworkIconImpl::UpdatePortalState(const NetworkState* network) {
  bool behind_captive_portal = false;
  if (network) {
    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
    behind_captive_portal =
        delegate->IsNetworkBehindCaptivePortal(network->path());
  }

  if (behind_captive_portal == behind_captive_portal_)
    return false;
  behind_captive_portal_ = behind_captive_portal;
  return true;
}

bool NetworkIconImpl::UpdateVPNBadge() {
  const NetworkState* vpn = NetworkHandler::Get()->network_state_handler()->
      ConnectedNetworkByType(NetworkTypePattern::VPN());
  if (vpn && vpn_badge_ == NULL) {
    vpn_badge_ = BadgeForVPN(icon_type_);
    return true;
  } else if (!vpn && vpn_badge_ != NULL) {
    vpn_badge_ = NULL;
    return true;
  }
  return false;
}

void NetworkIconImpl::GetBadges(const NetworkState* network, Badges* badges) {
  DCHECK(network);
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();

  const std::string& type = network->type();
  if (type == shill::kTypeWifi) {
    if (network->security() != shill::kSecurityNone &&
        IconTypeIsDark(icon_type_)) {
      badges->bottom_right = rb.GetImageSkiaNamed(
          IDR_AURA_UBER_TRAY_NETWORK_SECURE_DARK);
    }
  } else if (type == shill::kTypeWimax) {
    technology_badge_ = rb.GetImageSkiaNamed(
        IconTypeIsDark(icon_type_) ?
        IDR_AURA_UBER_TRAY_NETWORK_4G_DARK :
        IDR_AURA_UBER_TRAY_NETWORK_4G_LIGHT);
  } else if (type == shill::kTypeCellular) {
    if (network->roaming() == shill::kRoamingStateRoaming) {
      // For networks that are always in roaming don't show roaming badge.
      const DeviceState* device =
          handler->GetDeviceState(network->device_path());
      LOG_IF(WARNING, !device) << "Could not find device state for "
                               << network->device_path();
      if (!device || !device->provider_requires_roaming()) {
        badges->bottom_right = rb.GetImageSkiaNamed(
            IconTypeIsDark(icon_type_) ?
            IDR_AURA_UBER_TRAY_NETWORK_ROAMING_DARK :
            IDR_AURA_UBER_TRAY_NETWORK_ROAMING_LIGHT);
      }
    }
  }
  if (!network->IsConnectingState()) {
    badges->top_left = technology_badge_;
    badges->bottom_left = vpn_badge_;
  }

  if (behind_captive_portal_) {
    gfx::ImageSkia* badge = rb.GetImageSkiaNamed(
       IconTypeIsDark(icon_type_) ?
       IDR_AURA_UBER_TRAY_NETWORK_PORTAL_DARK :
       IDR_AURA_UBER_TRAY_NETWORK_PORTAL_LIGHT);
    badges->bottom_right = badge;
  }
}

void NetworkIconImpl::GenerateImage(const NetworkState* network) {
  DCHECK(network);
  gfx::ImageSkia icon = GetIcon(network, icon_type_, strength_index_);
  Badges badges;
  GetBadges(network, &badges);
  image_ = gfx::ImageSkia(
      new NetworkIconImageSource(icon, badges), icon.size());
}

//------------------------------------------------------------------------------
// Public interface

gfx::ImageSkia GetImageForNetwork(const NetworkState* network,
                                  IconType icon_type) {
  DCHECK(network);
  // Handle connecting icons.
  if (network->IsConnectingState())
    return GetConnectingImage(network->type(), icon_type);

  // Find or add the icon.
  NetworkIconMap* icon_map = GetIconMap(icon_type);
  NetworkIconImpl* icon;
  NetworkIconMap::iterator iter = icon_map->find(network->path());
  if (iter == icon_map->end()) {
    icon = new NetworkIconImpl(icon_type);
    icon_map->insert(std::make_pair(network->path(), icon));
  } else {
    icon = iter->second;
  }

  // Update and return the icon's image.
  icon->Update(network);
  return icon->image();
}

gfx::ImageSkia GetImageForConnectedNetwork(IconType icon_type,
                                           const std::string& network_type) {
  return GetConnectedImage(network_type, icon_type);
}

gfx::ImageSkia GetImageForConnectingNetwork(IconType icon_type,
                                            const std::string& network_type) {
  return GetConnectingImage(network_type, icon_type);
}

gfx::ImageSkia GetImageForDisconnectedNetwork(IconType icon_type,
                                              const std::string& network_type) {
  return GetDisconnectedImage(network_type, icon_type);
}

base::string16 GetLabelForNetwork(const chromeos::NetworkState* network,
                                  IconType icon_type) {
  DCHECK(network);
  std::string activation_state = network->activation_state();
  if (icon_type == ICON_TYPE_LIST) {
    // Show "<network>: [Connecting|Activating]..."
    if (network->IsConnectingState()) {
      return l10n_util::GetStringFUTF16(
          IDS_ASH_STATUS_TRAY_NETWORK_LIST_CONNECTING,
          base::UTF8ToUTF16(network->name()));
    }
    if (activation_state == shill::kActivationStateActivating) {
      return l10n_util::GetStringFUTF16(
          IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATING,
          base::UTF8ToUTF16(network->name()));
    }
    // Show "Activate <network>" in list view only.
    if (activation_state == shill::kActivationStateNotActivated ||
        activation_state == shill::kActivationStatePartiallyActivated) {
      return l10n_util::GetStringFUTF16(
          IDS_ASH_STATUS_TRAY_NETWORK_LIST_ACTIVATE,
          base::UTF8ToUTF16(network->name()));
    }
  } else {
    // Show "[Connected to|Connecting to|Activating] <network>" (non-list view).
    if (network->IsConnectedState()) {
      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTED,
                                        base::UTF8ToUTF16(network->name()));
    }
    if (network->IsConnectingState()) {
      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_CONNECTING,
                                        base::UTF8ToUTF16(network->name()));
    }
    if (activation_state == shill::kActivationStateActivating) {
      return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_NETWORK_ACTIVATING,
                                        base::UTF8ToUTF16(network->name()));
    }
  }

  // Otherwise just show the network name or 'Ethernet'.
  if (network->Matches(NetworkTypePattern::Ethernet())) {
    return l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ETHERNET);
  } else {
    return base::UTF8ToUTF16(network->name());
  }
}

int GetCellularUninitializedMsg() {
  static base::Time s_uninitialized_state_time;
  static int s_uninitialized_msg(0);

  NetworkStateHandler* handler = NetworkHandler::Get()->network_state_handler();
  if (handler->GetTechnologyState(NetworkTypePattern::Mobile())
      == NetworkStateHandler::TECHNOLOGY_UNINITIALIZED) {
    s_uninitialized_msg = IDS_ASH_STATUS_TRAY_INITIALIZING_CELLULAR;
    s_uninitialized_state_time = base::Time::Now();
    return s_uninitialized_msg;
  } else if (handler->GetScanningByType(NetworkTypePattern::Mobile())) {
    s_uninitialized_msg = IDS_ASH_STATUS_TRAY_CELLULAR_SCANNING;
    s_uninitialized_state_time = base::Time::Now();
    return s_uninitialized_msg;
  }
  // There can be a delay between leaving the Initializing state and when
  // a Cellular device shows up, so keep showing the initializing
  // animation for a bit to avoid flashing the disconnect icon.
  const int kInitializingDelaySeconds = 1;
  base::TimeDelta dtime = base::Time::Now() - s_uninitialized_state_time;
  if (dtime.InSeconds() < kInitializingDelaySeconds)
    return s_uninitialized_msg;
  return 0;
}

void GetDefaultNetworkImageAndLabel(IconType icon_type,
                                    gfx::ImageSkia* image,
                                    base::string16* label,
                                    bool* animating) {
  NetworkStateHandler* state_handler =
      NetworkHandler::Get()->network_state_handler();
  NetworkConnectionHandler* connect_handler =
      NetworkHandler::Get()->network_connection_handler();
  const NetworkState* connected_network =
      state_handler->ConnectedNetworkByType(NetworkTypePattern::NonVirtual());
  const NetworkState* connecting_network =
      state_handler->ConnectingNetworkByType(NetworkTypePattern::Wireless());
  if (!connecting_network && icon_type == ICON_TYPE_TRAY) {
    connecting_network =
        state_handler->ConnectingNetworkByType(NetworkTypePattern::VPN());
  }

  const NetworkState* network;
  // If we are connecting to a network, and there is either no connected
  // network, or the connection was user requested, use the connecting
  // network.
  if (connecting_network &&
      (!connected_network ||
       connect_handler->HasConnectingNetwork(connecting_network->path()))) {
    network = connecting_network;
  } else {
    network = connected_network;
  }

  // Don't show ethernet in the tray
  if (icon_type == ICON_TYPE_TRAY && network &&
      network->Matches(NetworkTypePattern::Ethernet())) {
    *image = gfx::ImageSkia();
    *animating = false;
    return;
  }

  if (!network) {
    // If no connecting network, check if we are activating a network.
    const NetworkState* mobile_network =
        state_handler->FirstNetworkByType(NetworkTypePattern::Mobile());
    if (mobile_network && (mobile_network->activation_state() ==
                           shill::kActivationStateActivating)) {
      network = mobile_network;
    }
  }
  if (!network) {
    // If no connecting network, check for cellular initializing.
    int uninitialized_msg = GetCellularUninitializedMsg();
    if (uninitialized_msg != 0) {
      *image = GetImageForConnectingNetwork(icon_type, shill::kTypeCellular);
      if (label)
        *label = l10n_util::GetStringUTF16(uninitialized_msg);
      *animating = true;
    } else {
      // Otherwise show the disconnected wifi icon.
      *image = GetImageForDisconnectedNetwork(icon_type, shill::kTypeWifi);
      if (label) {
        *label = l10n_util::GetStringUTF16(
            IDS_ASH_STATUS_TRAY_NETWORK_NOT_CONNECTED);
      }
      *animating = false;
    }
    return;
  }
  *animating = network->IsConnectingState();
  // Get icon and label for connected or connecting network.
  *image = GetImageForNetwork(network, icon_type);
  if (label)
    *label = GetLabelForNetwork(network, icon_type);
}

void PurgeNetworkIconCache() {
  NetworkStateHandler::NetworkStateList networks;
  NetworkHandler::Get()->network_state_handler()->GetNetworkList(&networks);
  std::set<std::string> network_paths;
  for (NetworkStateHandler::NetworkStateList::iterator iter = networks.begin();
       iter != networks.end(); ++iter) {
    network_paths.insert((*iter)->path());
  }
  PurgeIconMap(ICON_TYPE_TRAY, network_paths);
  PurgeIconMap(ICON_TYPE_DEFAULT_VIEW, network_paths);
  PurgeIconMap(ICON_TYPE_LIST, network_paths);
}

}  // namespace network_icon
}  // namespace ash

/* [<][>][^][v][top][bottom][index][help] */