Home | History | Annotate | Download | only in power
      1 // Copyright (c) 2013 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/power/power_status.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 
     10 #include "ash/shell.h"
     11 #include "ash/shell_delegate.h"
     12 #include "base/logging.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chromeos/dbus/dbus_thread_manager.h"
     16 #include "chromeos/dbus/power_manager_client.h"
     17 #include "grit/ash_resources.h"
     18 #include "grit/ash_strings.h"
     19 #include "ui/base/l10n/l10n_util.h"
     20 #include "ui/base/l10n/time_format.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "ui/gfx/image/image.h"
     23 #include "ui/gfx/image/image_skia_operations.h"
     24 #include "ui/gfx/rect.h"
     25 
     26 namespace ash {
     27 namespace {
     28 
     29 // Updates |proto| to ensure that its fields are consistent.
     30 void SanitizeProto(power_manager::PowerSupplyProperties* proto) {
     31   DCHECK(proto);
     32 
     33   if (proto->battery_state() ==
     34       power_manager::PowerSupplyProperties_BatteryState_FULL)
     35     proto->set_battery_percent(100.0);
     36 
     37   if (!proto->is_calculating_battery_time()) {
     38     const bool on_line_power = proto->external_power() !=
     39         power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
     40     if ((on_line_power && proto->battery_time_to_full_sec() < 0) ||
     41         (!on_line_power && proto->battery_time_to_empty_sec() < 0))
     42       proto->set_is_calculating_battery_time(true);
     43   }
     44 }
     45 
     46 base::string16 GetBatteryTimeAccessibilityString(int hour, int min) {
     47   DCHECK(hour || min);
     48   if (hour && !min) {
     49     return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
     50                                   ui::TimeFormat::LENGTH_LONG,
     51                                   base::TimeDelta::FromHours(hour));
     52   }
     53   if (min && !hour) {
     54     return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
     55                                   ui::TimeFormat::LENGTH_LONG,
     56                                   base::TimeDelta::FromMinutes(min));
     57   }
     58   return l10n_util::GetStringFUTF16(
     59       IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE,
     60       ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
     61                              ui::TimeFormat::LENGTH_LONG,
     62                              base::TimeDelta::FromHours(hour)),
     63       ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
     64                              ui::TimeFormat::LENGTH_LONG,
     65                              base::TimeDelta::FromMinutes(min)));
     66 }
     67 
     68 static PowerStatus* g_power_status = NULL;
     69 
     70 // Minimum battery percentage rendered in UI.
     71 const int kMinBatteryPercent = 1;
     72 
     73 // Width and height of battery images.
     74 const int kBatteryImageHeight = 25;
     75 const int kBatteryImageWidth = 25;
     76 
     77 // Number of different power states.
     78 const int kNumPowerImages = 15;
     79 
     80 }  // namespace
     81 
     82 const int PowerStatus::kMaxBatteryTimeToDisplaySec = 24 * 60 * 60;
     83 
     84 // static
     85 void PowerStatus::Initialize() {
     86   CHECK(!g_power_status);
     87   g_power_status = new PowerStatus();
     88 }
     89 
     90 // static
     91 void PowerStatus::Shutdown() {
     92   CHECK(g_power_status);
     93   delete g_power_status;
     94   g_power_status = NULL;
     95 }
     96 
     97 // static
     98 bool PowerStatus::IsInitialized() {
     99   return g_power_status != NULL;
    100 }
    101 
    102 // static
    103 PowerStatus* PowerStatus::Get() {
    104   CHECK(g_power_status) << "PowerStatus::Get() called before Initialize().";
    105   return g_power_status;
    106 }
    107 
    108 // static
    109 bool PowerStatus::ShouldDisplayBatteryTime(const base::TimeDelta& time) {
    110   return time >= base::TimeDelta::FromMinutes(1) &&
    111       time.InSeconds() <= kMaxBatteryTimeToDisplaySec;
    112 }
    113 
    114 // static
    115 void PowerStatus::SplitTimeIntoHoursAndMinutes(const base::TimeDelta& time,
    116                                                int* hours,
    117                                                int* minutes) {
    118   DCHECK(hours);
    119   DCHECK(minutes);
    120   const int total_minutes = static_cast<int>(time.InSecondsF() / 60 + 0.5);
    121   *hours = total_minutes / 60;
    122   *minutes = total_minutes % 60;
    123 }
    124 
    125 void PowerStatus::AddObserver(Observer* observer) {
    126   DCHECK(observer);
    127   observers_.AddObserver(observer);
    128 }
    129 
    130 void PowerStatus::RemoveObserver(Observer* observer) {
    131   DCHECK(observer);
    132   observers_.RemoveObserver(observer);
    133 }
    134 
    135 void PowerStatus::RequestStatusUpdate() {
    136   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    137       RequestStatusUpdate();
    138 }
    139 
    140 bool PowerStatus::IsBatteryPresent() const {
    141   return proto_.battery_state() !=
    142       power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
    143 }
    144 
    145 bool PowerStatus::IsBatteryFull() const {
    146   return proto_.battery_state() ==
    147       power_manager::PowerSupplyProperties_BatteryState_FULL;
    148 }
    149 
    150 bool PowerStatus::IsBatteryCharging() const {
    151   return proto_.battery_state() ==
    152       power_manager::PowerSupplyProperties_BatteryState_CHARGING;
    153 }
    154 
    155 bool PowerStatus::IsBatteryDischargingOnLinePower() const {
    156   return IsLinePowerConnected() && proto_.battery_state() ==
    157       power_manager::PowerSupplyProperties_BatteryState_DISCHARGING;
    158 }
    159 
    160 double PowerStatus::GetBatteryPercent() const {
    161   return proto_.battery_percent();
    162 }
    163 
    164 int PowerStatus::GetRoundedBatteryPercent() const {
    165   return std::max(kMinBatteryPercent,
    166       static_cast<int>(GetBatteryPercent() + 0.5));
    167 }
    168 
    169 bool PowerStatus::IsBatteryTimeBeingCalculated() const {
    170   return proto_.is_calculating_battery_time();
    171 }
    172 
    173 base::TimeDelta PowerStatus::GetBatteryTimeToEmpty() const {
    174   return base::TimeDelta::FromSeconds(proto_.battery_time_to_empty_sec());
    175 }
    176 
    177 base::TimeDelta PowerStatus::GetBatteryTimeToFull() const {
    178   return base::TimeDelta::FromSeconds(proto_.battery_time_to_full_sec());
    179 }
    180 
    181 bool PowerStatus::IsLinePowerConnected() const {
    182   return proto_.external_power() !=
    183       power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED;
    184 }
    185 
    186 bool PowerStatus::IsMainsChargerConnected() const {
    187   return proto_.external_power() ==
    188       power_manager::PowerSupplyProperties_ExternalPower_AC;
    189 }
    190 
    191 bool PowerStatus::IsUsbChargerConnected() const {
    192   return proto_.external_power() ==
    193       power_manager::PowerSupplyProperties_ExternalPower_USB;
    194 }
    195 
    196 bool PowerStatus::IsOriginalSpringChargerConnected() const {
    197   return proto_.external_power() == power_manager::
    198       PowerSupplyProperties_ExternalPower_ORIGINAL_SPRING_CHARGER;
    199 }
    200 
    201 gfx::ImageSkia PowerStatus::GetBatteryImage(IconSet icon_set) const {
    202   gfx::Image all;
    203   if (IsUsbChargerConnected()) {
    204     all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
    205         icon_set == ICON_DARK ?
    206         IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE_DARK :
    207         IDR_AURA_UBER_TRAY_POWER_SMALL_CHARGING_UNRELIABLE);
    208   } else {
    209     all = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
    210         icon_set == ICON_DARK ?
    211         IDR_AURA_UBER_TRAY_POWER_SMALL_DARK : IDR_AURA_UBER_TRAY_POWER_SMALL);
    212   }
    213 
    214   // Get the horizontal offset in the battery icon array image. The USB /
    215   // "unreliable charging" image has a single column of icons; the other
    216   // image contains a "battery" column on the left and a "line power"
    217   // column on the right.
    218   int offset = IsUsbChargerConnected() ? 0 : (IsLinePowerConnected() ? 1 : 0);
    219 
    220   // Get the vertical offset corresponding to the current battery level.
    221   int index = -1;
    222   if (GetBatteryPercent() >= 100.0) {
    223     index = kNumPowerImages - 1;
    224   } else if (!IsBatteryPresent()) {
    225     index = kNumPowerImages;
    226   } else {
    227     index = static_cast<int>(
    228         GetBatteryPercent() / 100.0 * (kNumPowerImages - 1));
    229     index = std::max(std::min(index, kNumPowerImages - 2), 0);
    230   }
    231 
    232   gfx::Rect region(
    233       offset * kBatteryImageWidth, index * kBatteryImageHeight,
    234       kBatteryImageWidth, kBatteryImageHeight);
    235   return gfx::ImageSkiaOperations::ExtractSubset(*all.ToImageSkia(), region);
    236 }
    237 
    238 base::string16 PowerStatus::GetAccessibleNameString() const {
    239   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    240   if (IsBatteryFull()) {
    241     return rb.GetLocalizedString(
    242         IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE);
    243   }
    244 
    245   base::string16 battery_percentage_accessible = l10n_util::GetStringFUTF16(
    246       IsBatteryCharging() ?
    247       IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE :
    248       IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE,
    249       base::IntToString16(GetRoundedBatteryPercent()));
    250   base::string16 battery_time_accessible = base::string16();
    251   const base::TimeDelta time = IsBatteryCharging() ? GetBatteryTimeToFull() :
    252       GetBatteryTimeToEmpty();
    253 
    254   if (IsUsbChargerConnected()) {
    255     battery_time_accessible = rb.GetLocalizedString(
    256         IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE);
    257   } else if (IsBatteryTimeBeingCalculated()) {
    258     battery_time_accessible = rb.GetLocalizedString(
    259         IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE);
    260   } else if (ShouldDisplayBatteryTime(time) &&
    261              !IsBatteryDischargingOnLinePower()) {
    262     int hour = 0, min = 0;
    263     PowerStatus::SplitTimeIntoHoursAndMinutes(time, &hour, &min);
    264     base::string16 minute = min < 10 ?
    265         base::ASCIIToUTF16("0") + base::IntToString16(min) :
    266         base::IntToString16(min);
    267     battery_time_accessible =
    268         l10n_util::GetStringFUTF16(
    269             IsBatteryCharging() ?
    270             IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE :
    271             IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE,
    272             GetBatteryTimeAccessibilityString(hour, min));
    273   }
    274   return battery_time_accessible.empty() ?
    275       battery_percentage_accessible :
    276       battery_percentage_accessible + base::ASCIIToUTF16(". ") +
    277       battery_time_accessible;
    278 }
    279 
    280 PowerStatus::PowerStatus() {
    281   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    282       AddObserver(this);
    283   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    284       RequestStatusUpdate();
    285 }
    286 
    287 PowerStatus::~PowerStatus() {
    288   chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
    289       RemoveObserver(this);
    290 }
    291 
    292 void PowerStatus::SetProtoForTesting(
    293     const power_manager::PowerSupplyProperties& proto) {
    294   proto_ = proto;
    295   SanitizeProto(&proto_);
    296 }
    297 
    298 void PowerStatus::PowerChanged(
    299     const power_manager::PowerSupplyProperties& proto) {
    300   proto_ = proto;
    301   SanitizeProto(&proto_);
    302   FOR_EACH_OBSERVER(Observer, observers_, OnPowerStatusChanged());
    303 }
    304 
    305 }  // namespace ash
    306