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