derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 1 | // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
James Cook | a35a1e2 | 2017-04-08 02:33:08 | [diff] [blame] | 5 | #include "ash/system/power/power_status.h" |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 6 | |
| 7 | #include <algorithm> |
| 8 | #include <cmath> |
| 9 | |
Evan Stade | 8ff28db | 2018-05-02 17:15:57 | [diff] [blame] | 10 | #include "ash/public/cpp/power_utils.h" |
estade | 81c6985 | 2016-09-08 21:25:59 | [diff] [blame] | 11 | #include "ash/resources/vector_icons/vector_icons.h" |
jamescook | e45f811 | 2017-03-02 16:45:42 | [diff] [blame] | 12 | #include "ash/strings/grit/ash_strings.h" |
Andre Le | f08dbd5 | 2020-11-02 17:12:03 | [diff] [blame] | 13 | #include "ash/system/power/battery_image_source.h" |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 14 | #include "base/i18n/number_formatting.h" |
| 15 | #include "base/i18n/time_formatting.h" |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 16 | #include "base/logging.h" |
Daniel Cheng | 23eed1d | 2017-08-18 02:42:31 | [diff] [blame] | 17 | #include "base/memory/ptr_util.h" |
Peter Kasting | 8b80a9b | 2019-09-09 20:27:23 | [diff] [blame] | 18 | #include "base/numerics/ranges.h" |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 19 | #include "base/strings/string_number_conversions.h" |
| 20 | #include "base/strings/utf_string_conversions.h" |
Steven Bennetts | 3330b9f | 2019-03-15 20:24:13 | [diff] [blame] | 21 | #include "chromeos/dbus/power/power_manager_client.h" |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 22 | #include "ui/base/l10n/l10n_util.h" |
mukai@chromium.org | 9d613a9 | 2013-08-08 01:07:29 | [diff] [blame] | 23 | #include "ui/base/l10n/time_format.h" |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 24 | #include "ui/display/display.h" |
| 25 | #include "ui/display/screen.h" |
Manu Cornet | 187c86f | 2018-12-12 19:29:16 | [diff] [blame] | 26 | #include "ui/gfx/color_palette.h" |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 27 | #include "ui/gfx/image/image.h" |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 28 | |
| 29 | namespace ash { |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 30 | namespace { |
| 31 | |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 32 | static PowerStatus* g_power_status = nullptr; |
| 33 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 34 | std::u16string GetBatteryTimeAccessibilityString(int hour, int min) { |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 35 | DCHECK(hour || min); |
| 36 | if (hour && !min) { |
tnagel@chromium.org | b6cc963b | 2014-02-27 15:32:23 | [diff] [blame] | 37 | return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, |
| 38 | ui::TimeFormat::LENGTH_LONG, |
| 39 | base::TimeDelta::FromHours(hour)); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 40 | } |
| 41 | if (min && !hour) { |
tnagel@chromium.org | b6cc963b | 2014-02-27 15:32:23 | [diff] [blame] | 42 | return ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, |
| 43 | ui::TimeFormat::LENGTH_LONG, |
| 44 | base::TimeDelta::FromMinutes(min)); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 45 | } |
| 46 | return l10n_util::GetStringFUTF16( |
| 47 | IDS_ASH_STATUS_TRAY_BATTERY_TIME_ACCESSIBLE, |
tnagel@chromium.org | b6cc963b | 2014-02-27 15:32:23 | [diff] [blame] | 48 | ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, |
| 49 | ui::TimeFormat::LENGTH_LONG, |
| 50 | base::TimeDelta::FromHours(hour)), |
| 51 | ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION, |
| 52 | ui::TimeFormat::LENGTH_LONG, |
| 53 | base::TimeDelta::FromMinutes(min))); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 54 | } |
| 55 | |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 56 | int PowerSourceToMessageID( |
| 57 | const power_manager::PowerSupplyProperties_PowerSource& source) { |
| 58 | switch (source.port()) { |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 59 | case power_manager::PowerSupplyProperties_PowerSource_Port_UNKNOWN: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 60 | return IDS_ASH_POWER_SOURCE_PORT_UNKNOWN; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 61 | case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 62 | return IDS_ASH_POWER_SOURCE_PORT_LEFT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 63 | case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 64 | return IDS_ASH_POWER_SOURCE_PORT_RIGHT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 65 | case power_manager::PowerSupplyProperties_PowerSource_Port_BACK: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 66 | return IDS_ASH_POWER_SOURCE_PORT_BACK; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 67 | case power_manager::PowerSupplyProperties_PowerSource_Port_FRONT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 68 | return IDS_ASH_POWER_SOURCE_PORT_FRONT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 69 | case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_FRONT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 70 | return IDS_ASH_POWER_SOURCE_PORT_LEFT_FRONT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 71 | case power_manager::PowerSupplyProperties_PowerSource_Port_LEFT_BACK: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 72 | return IDS_ASH_POWER_SOURCE_PORT_LEFT_BACK; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 73 | case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_FRONT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 74 | return IDS_ASH_POWER_SOURCE_PORT_RIGHT_FRONT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 75 | case power_manager::PowerSupplyProperties_PowerSource_Port_RIGHT_BACK: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 76 | return IDS_ASH_POWER_SOURCE_PORT_RIGHT_BACK; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 77 | case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_LEFT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 78 | return IDS_ASH_POWER_SOURCE_PORT_BACK_LEFT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 79 | case power_manager::PowerSupplyProperties_PowerSource_Port_BACK_RIGHT: |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 80 | return IDS_ASH_POWER_SOURCE_PORT_BACK_RIGHT; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 81 | } |
| 82 | NOTREACHED(); |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 83 | return 0; |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 84 | } |
| 85 | |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 86 | } // namespace |
| 87 | |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 88 | bool PowerStatus::BatteryImageInfo::ApproximatelyEqual( |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 89 | const BatteryImageInfo& o) const { |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 90 | // 100% is distinct from all else. |
| 91 | if ((charge_percent != o.charge_percent) && |
| 92 | (charge_percent == 100 || o.charge_percent == 100)) { |
| 93 | return false; |
| 94 | } |
| 95 | |
| 96 | // Otherwise, consider close values such as 42% and 45% as about the same. |
Nico Weber | ffbc2bd | 2019-10-02 20:22:58 | [diff] [blame] | 97 | return icon_badge == o.icon_badge && alert_if_low == o.alert_if_low && |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 98 | std::abs(charge_percent - o.charge_percent) < 5; |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 99 | } |
| 100 | |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 101 | const int PowerStatus::kMaxBatteryTimeToDisplaySec = 24 * 60 * 60; |
| 102 | |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 103 | const double PowerStatus::kCriticalBatteryChargePercentage = 5; |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 104 | |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 105 | // static |
| 106 | void PowerStatus::Initialize() { |
| 107 | CHECK(!g_power_status); |
| 108 | g_power_status = new PowerStatus(); |
| 109 | } |
| 110 | |
| 111 | // static |
| 112 | void PowerStatus::Shutdown() { |
| 113 | CHECK(g_power_status); |
| 114 | delete g_power_status; |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 115 | g_power_status = nullptr; |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | // static |
| 119 | bool PowerStatus::IsInitialized() { |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 120 | return g_power_status != nullptr; |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | // static |
| 124 | PowerStatus* PowerStatus::Get() { |
| 125 | CHECK(g_power_status) << "PowerStatus::Get() called before Initialize()."; |
| 126 | return g_power_status; |
| 127 | } |
| 128 | |
| 129 | void PowerStatus::AddObserver(Observer* observer) { |
| 130 | DCHECK(observer); |
| 131 | observers_.AddObserver(observer); |
| 132 | } |
| 133 | |
| 134 | void PowerStatus::RemoveObserver(Observer* observer) { |
| 135 | DCHECK(observer); |
| 136 | observers_.RemoveObserver(observer); |
| 137 | } |
| 138 | |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 139 | void PowerStatus::RequestStatusUpdate() { |
Evan Stade | 523f7fc | 2019-03-02 19:20:51 | [diff] [blame] | 140 | chromeos::PowerManagerClient::Get()->RequestStatusUpdate(); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | bool PowerStatus::IsBatteryPresent() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 144 | return proto_.battery_state() != |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 145 | power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 146 | } |
| 147 | |
| 148 | bool PowerStatus::IsBatteryFull() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 149 | return proto_.battery_state() == |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 150 | power_manager::PowerSupplyProperties_BatteryState_FULL; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 151 | } |
| 152 | |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 153 | bool PowerStatus::IsBatteryCharging() const { |
| 154 | return proto_.battery_state() == |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 155 | power_manager::PowerSupplyProperties_BatteryState_CHARGING; |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | bool PowerStatus::IsBatteryDischargingOnLinePower() const { |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 159 | return IsLinePowerConnected() && |
| 160 | proto_.battery_state() == |
| 161 | power_manager::PowerSupplyProperties_BatteryState_DISCHARGING; |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 162 | } |
| 163 | |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 164 | double PowerStatus::GetBatteryPercent() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 165 | return proto_.battery_percent(); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | int PowerStatus::GetRoundedBatteryPercent() const { |
Evan Stade | 8ff28db | 2018-05-02 17:15:57 | [diff] [blame] | 169 | return power_utils::GetRoundedBatteryPercent(GetBatteryPercent()); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | bool PowerStatus::IsBatteryTimeBeingCalculated() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 173 | return proto_.is_calculating_battery_time(); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 174 | } |
| 175 | |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 176 | base::Optional<base::TimeDelta> PowerStatus::GetBatteryTimeToEmpty() const { |
| 177 | // powerd omits the field if no battery is present and sends -1 if it couldn't |
| 178 | // compute a reasonable estimate. |
| 179 | if (!proto_.has_battery_time_to_empty_sec() || |
| 180 | proto_.battery_time_to_empty_sec() < 0) { |
| 181 | return base::nullopt; |
| 182 | } |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 183 | return base::TimeDelta::FromSeconds(proto_.battery_time_to_empty_sec()); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 184 | } |
| 185 | |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 186 | base::Optional<base::TimeDelta> PowerStatus::GetBatteryTimeToFull() const { |
| 187 | // powerd omits the field if no battery is present and sends -1 if it couldn't |
| 188 | // compute a reasonable estimate. |
| 189 | if (!proto_.has_battery_time_to_full_sec() || |
| 190 | proto_.battery_time_to_full_sec() < 0) { |
| 191 | return base::nullopt; |
| 192 | } |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 193 | return base::TimeDelta::FromSeconds(proto_.battery_time_to_full_sec()); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | bool PowerStatus::IsLinePowerConnected() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 197 | return proto_.external_power() != |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 198 | power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 199 | } |
| 200 | |
| 201 | bool PowerStatus::IsMainsChargerConnected() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 202 | return proto_.external_power() == |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 203 | power_manager::PowerSupplyProperties_ExternalPower_AC; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | bool PowerStatus::IsUsbChargerConnected() const { |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 207 | return proto_.external_power() == |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 208 | power_manager::PowerSupplyProperties_ExternalPower_USB; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 209 | } |
| 210 | |
michaelpg | 08e6576 | 2015-06-13 06:47:40 | [diff] [blame] | 211 | bool PowerStatus::SupportsDualRoleDevices() const { |
| 212 | return proto_.supports_dual_role_devices(); |
| 213 | } |
| 214 | |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 215 | bool PowerStatus::HasDualRoleDevices() const { |
| 216 | if (!SupportsDualRoleDevices()) |
| 217 | return false; |
| 218 | |
| 219 | for (int i = 0; i < proto_.available_external_power_source_size(); i++) { |
| 220 | if (!proto_.available_external_power_source(i).active_by_default()) |
| 221 | return true; |
| 222 | } |
| 223 | return false; |
| 224 | } |
| 225 | |
| 226 | std::vector<PowerStatus::PowerSource> PowerStatus::GetPowerSources() const { |
| 227 | std::vector<PowerSource> sources; |
| 228 | for (int i = 0; i < proto_.available_external_power_source_size(); i++) { |
| 229 | const auto& source = proto_.available_external_power_source(i); |
| 230 | sources.push_back( |
| 231 | {source.id(), |
| 232 | source.active_by_default() ? DEDICATED_CHARGER : DUAL_ROLE_USB, |
michaelpg | 6ded5ea | 2015-12-11 18:07:30 | [diff] [blame] | 233 | PowerSourceToMessageID(source)}); |
michaelpg | 69b6f66 | 2015-07-27 20:42:02 | [diff] [blame] | 234 | } |
| 235 | return sources; |
| 236 | } |
| 237 | |
| 238 | std::string PowerStatus::GetCurrentPowerSourceID() const { |
| 239 | return proto_.external_power_source_id(); |
| 240 | } |
| 241 | |
danakj | df0952b | 2017-04-03 18:38:39 | [diff] [blame] | 242 | PowerStatus::BatteryImageInfo PowerStatus::GetBatteryImageInfo() const { |
derat | 2eb9055a | 2016-04-13 00:10:41 | [diff] [blame] | 243 | BatteryImageInfo info; |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 244 | CalculateBatteryImageInfo(&info); |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 245 | return info; |
| 246 | } |
derat | 2eb9055a | 2016-04-13 00:10:41 | [diff] [blame] | 247 | |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 248 | void PowerStatus::CalculateBatteryImageInfo(BatteryImageInfo* info) const { |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 249 | info->alert_if_low = !IsLinePowerConnected(); |
| 250 | |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 251 | if (!IsUsbChargerConnected() && !IsBatteryPresent()) { |
Tetsui Ohkubo | d013252 | 2018-10-29 03:08:37 | [diff] [blame] | 252 | info->icon_badge = &kUnifiedMenuBatteryXIcon; |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 253 | info->badge_outline = &kUnifiedMenuBatteryXOutlineIcon; |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 254 | info->charge_percent = 0; |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 255 | return; |
| 256 | } |
| 257 | |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 258 | if (IsUsbChargerConnected()) { |
Tetsui Ohkubo | d013252 | 2018-10-29 03:08:37 | [diff] [blame] | 259 | info->icon_badge = &kUnifiedMenuBatteryUnreliableIcon; |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 260 | info->badge_outline = &kUnifiedMenuBatteryUnreliableOutlineIcon; |
| 261 | } else if (IsLinePowerConnected()) { |
Tetsui Ohkubo | d013252 | 2018-10-29 03:08:37 | [diff] [blame] | 262 | info->icon_badge = &kUnifiedMenuBatteryBoltIcon; |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 263 | info->badge_outline = &kUnifiedMenuBatteryBoltOutlineIcon; |
| 264 | } else { |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 265 | info->icon_badge = nullptr; |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 266 | info->badge_outline = nullptr; |
| 267 | } |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 268 | |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 269 | info->charge_percent = GetBatteryPercent(); |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 270 | |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 271 | // Use an alert badge if the battery is critically low and does not already |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 272 | // have a badge assigned. |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 273 | if (GetBatteryPercent() < kCriticalBatteryChargePercentage && |
| 274 | !info->icon_badge) { |
Tetsui Ohkubo | d013252 | 2018-10-29 03:08:37 | [diff] [blame] | 275 | info->icon_badge = &kUnifiedMenuBatteryAlertIcon; |
Ahmed Mehfooz | 455697a | 2020-07-12 05:23:09 | [diff] [blame] | 276 | info->badge_outline = &kUnifiedMenuBatteryAlertOutlineIcon; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 277 | } |
derat | 2eb9055a | 2016-04-13 00:10:41 | [diff] [blame] | 278 | } |
| 279 | |
danakj | df0952b | 2017-04-03 18:38:39 | [diff] [blame] | 280 | // static |
Evan Stade | c2db857 | 2017-06-21 15:08:32 | [diff] [blame] | 281 | gfx::ImageSkia PowerStatus::GetBatteryImage(const BatteryImageInfo& info, |
| 282 | int height, |
| 283 | SkColor bg_color, |
| 284 | SkColor fg_color) { |
| 285 | auto* source = new BatteryImageSource(info, height, bg_color, fg_color); |
Daniel Cheng | 23eed1d | 2017-08-18 02:42:31 | [diff] [blame] | 286 | return gfx::ImageSkia(base::WrapUnique(source), source->size()); |
tdanderson | 7da397a | 2016-06-22 19:06:39 | [diff] [blame] | 287 | } |
| 288 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 289 | std::u16string PowerStatus::GetAccessibleNameString( |
evy@chromium.org | 04c3b458 | 2014-07-23 00:09:24 | [diff] [blame] | 290 | bool full_description) const { |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 291 | if (IsBatteryFull()) { |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 292 | return l10n_util::GetStringUTF16( |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 293 | IDS_ASH_STATUS_TRAY_BATTERY_FULL_CHARGE_ACCESSIBLE); |
| 294 | } |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 295 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 296 | std::u16string battery_percentage_accessible = l10n_util::GetStringFUTF16( |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 297 | IsBatteryCharging() |
| 298 | ? IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_CHARGING_ACCESSIBLE |
| 299 | : IDS_ASH_STATUS_TRAY_BATTERY_PERCENT_ACCESSIBLE, |
Raul Tambre | 73f90694 | 2019-02-11 16:47:21 | [diff] [blame] | 300 | base::NumberToString16(GetRoundedBatteryPercent())); |
evy@chromium.org | 04c3b458 | 2014-07-23 00:09:24 | [diff] [blame] | 301 | if (!full_description) |
| 302 | return battery_percentage_accessible; |
| 303 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 304 | std::u16string battery_time_accessible = std::u16string(); |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 305 | const base::Optional<base::TimeDelta> time = |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 306 | IsBatteryCharging() ? GetBatteryTimeToFull() : GetBatteryTimeToEmpty(); |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 307 | |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 308 | if (IsUsbChargerConnected()) { |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 309 | battery_time_accessible = l10n_util::GetStringUTF16( |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 310 | IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE_ACCESSIBLE); |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 311 | } else if (IsBatteryTimeBeingCalculated()) { |
estade | 6d2bd8f | 2017-03-28 03:16:13 | [diff] [blame] | 312 | battery_time_accessible = l10n_util::GetStringUTF16( |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 313 | IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING_ACCESSIBLE); |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 314 | } else if (time && power_utils::ShouldDisplayBatteryTime(*time) && |
derat@chromium.org | a8b4575 | 2013-07-10 06:32:02 | [diff] [blame] | 315 | !IsBatteryDischargingOnLinePower()) { |
| 316 | int hour = 0, min = 0; |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 317 | power_utils::SplitTimeIntoHoursAndMinutes(*time, &hour, &min); |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 318 | std::u16string minute = |
Raul Tambre | 73f90694 | 2019-02-11 16:47:21 | [diff] [blame] | 319 | min < 10 ? base::ASCIIToUTF16("0") + base::NumberToString16(min) |
| 320 | : base::NumberToString16(min); |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 321 | battery_time_accessible = l10n_util::GetStringFUTF16( |
| 322 | IsBatteryCharging() |
| 323 | ? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_ACCESSIBLE |
| 324 | : IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_ACCESSIBLE, |
| 325 | GetBatteryTimeAccessibilityString(hour, min)); |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 326 | } |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 327 | return battery_time_accessible.empty() |
| 328 | ? battery_percentage_accessible |
Sammie Quon | cf5561e | 2017-09-27 23:22:13 | [diff] [blame] | 329 | : battery_percentage_accessible + base::ASCIIToUTF16(" ") + |
jamescook | b8dcef52 | 2016-06-25 14:42:55 | [diff] [blame] | 330 | battery_time_accessible; |
derat@chromium.org | 349541e | 2013-06-29 20:56:52 | [diff] [blame] | 331 | } |
| 332 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 333 | std::pair<std::u16string, std::u16string> PowerStatus::GetStatusStrings() |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 334 | const { |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 335 | std::u16string percentage; |
| 336 | std::u16string status; |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 337 | if (IsBatteryFull()) { |
| 338 | status = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BATTERY_FULL); |
| 339 | } else { |
| 340 | percentage = base::FormatPercent(GetRoundedBatteryPercent()); |
| 341 | if (IsUsbChargerConnected()) { |
| 342 | status = l10n_util::GetStringUTF16( |
| 343 | IDS_ASH_STATUS_TRAY_BATTERY_CHARGING_UNRELIABLE); |
| 344 | } else if (IsBatteryTimeBeingCalculated()) { |
| 345 | status = |
| 346 | l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_BATTERY_CALCULATING); |
| 347 | } else { |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 348 | base::Optional<base::TimeDelta> time = IsBatteryCharging() |
| 349 | ? GetBatteryTimeToFull() |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 350 | : GetBatteryTimeToEmpty(); |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 351 | if (time && power_utils::ShouldDisplayBatteryTime(*time) && |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 352 | !IsBatteryDischargingOnLinePower()) { |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 353 | std::u16string duration; |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 354 | if (!base::TimeDurationFormat(*time, base::DURATION_WIDTH_NUMERIC, |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 355 | &duration)) |
Daniel Erat | 572a042 | 2019-02-11 22:04:02 | [diff] [blame] | 356 | LOG(ERROR) << "Failed to format duration " << *time; |
Tetsui Ohkubo | b264d4d4 | 2018-03-12 07:56:17 | [diff] [blame] | 357 | status = l10n_util::GetStringFUTF16( |
| 358 | IsBatteryCharging() |
| 359 | ? IDS_ASH_STATUS_TRAY_BATTERY_TIME_UNTIL_FULL_SHORT |
| 360 | : IDS_ASH_STATUS_TRAY_BATTERY_TIME_LEFT_SHORT, |
| 361 | duration); |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | return std::make_pair(percentage, status); |
| 367 | } |
| 368 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame^] | 369 | std::u16string PowerStatus::GetInlinedStatusString() const { |
| 370 | std::u16string percentage_text; |
| 371 | std::u16string status_text; |
Tatsuhisa Yamaguchi | bb76e2f | 2018-10-31 06:41:34 | [diff] [blame] | 372 | std::tie(percentage_text, status_text) = GetStatusStrings(); |
| 373 | |
| 374 | if (!percentage_text.empty() && !status_text.empty()) { |
| 375 | return percentage_text + |
| 376 | l10n_util::GetStringUTF16( |
| 377 | IDS_ASH_STATUS_TRAY_BATTERY_STATUS_SEPARATOR) + |
| 378 | status_text; |
| 379 | } else if (!percentage_text.empty()) { |
| 380 | return percentage_text; |
| 381 | } else { |
| 382 | return status_text; |
| 383 | } |
| 384 | } |
| 385 | |
Sam McNally | 366a2194 | 2020-09-18 04:02:53 | [diff] [blame] | 386 | double PowerStatus::GetPreferredMinimumPower() const { |
Sam McNally | 9d5a10f | 2020-09-25 04:09:28 | [diff] [blame] | 387 | return proto_.preferred_minimum_external_power(); |
Sam McNally | 366a2194 | 2020-09-18 04:02:53 | [diff] [blame] | 388 | } |
| 389 | |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 390 | PowerStatus::PowerStatus() { |
Evan Stade | 523f7fc | 2019-03-02 19:20:51 | [diff] [blame] | 391 | chromeos::PowerManagerClient::Get()->AddObserver(this); |
| 392 | chromeos::PowerManagerClient::Get()->RequestStatusUpdate(); |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 393 | } |
| 394 | |
| 395 | PowerStatus::~PowerStatus() { |
Evan Stade | 523f7fc | 2019-03-02 19:20:51 | [diff] [blame] | 396 | chromeos::PowerManagerClient::Get()->RemoveObserver(this); |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 397 | } |
| 398 | |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 399 | void PowerStatus::SetProtoForTesting( |
| 400 | const power_manager::PowerSupplyProperties& proto) { |
| 401 | proto_ = proto; |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 402 | } |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 403 | |
derat@chromium.org | 9fcb3e2 | 2013-07-03 00:35:55 | [diff] [blame] | 404 | void PowerStatus::PowerChanged( |
| 405 | const power_manager::PowerSupplyProperties& proto) { |
| 406 | proto_ = proto; |
ericwilligers | 5c57931 | 2016-10-14 20:07:33 | [diff] [blame] | 407 | for (auto& observer : observers_) |
| 408 | observer.OnPowerStatusChanged(); |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 409 | } |
| 410 | |
derat@chromium.org | 37ce571b | 2013-06-28 05:28:45 | [diff] [blame] | 411 | } // namespace ash |