Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ash / system / chromeos / power / tray_power.cc
blobcb6d7564da602cf466912010a5f882df77ca31b3
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.
5 #include "ash/system/chromeos/power/tray_power.h"
7 #include "ash/accessibility_delegate.h"
8 #include "ash/ash_switches.h"
9 #include "ash/shell.h"
10 #include "ash/system/chromeos/devicetype_utils.h"
11 #include "ash/system/chromeos/power/battery_notification.h"
12 #include "ash/system/date/date_view.h"
13 #include "ash/system/system_notifier.h"
14 #include "ash/system/tray/system_tray_delegate.h"
15 #include "ash/system/tray/tray_constants.h"
16 #include "ash/system/tray/tray_utils.h"
17 #include "base/command_line.h"
18 #include "base/logging.h"
19 #include "base/metrics/histogram.h"
20 #include "base/time/time.h"
21 #include "grit/ash_resources.h"
22 #include "grit/ash_strings.h"
23 #include "ui/accessibility/ax_view_state.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/message_center/message_center.h"
26 #include "ui/message_center/notification.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/view.h"
30 using message_center::MessageCenter;
31 using message_center::Notification;
33 namespace ash {
34 namespace {
36 std::string GetNotificationStateString(
37 TrayPower::NotificationState notification_state) {
38 switch (notification_state) {
39 case TrayPower::NOTIFICATION_NONE:
40 return "none";
41 case TrayPower::NOTIFICATION_LOW_POWER:
42 return "low power";
43 case TrayPower::NOTIFICATION_CRITICAL:
44 return "critical power";
46 NOTREACHED() << "Unknown state " << notification_state;
47 return "Unknown state";
50 void LogBatteryForUsbCharger(TrayPower::NotificationState state,
51 int battery_percent) {
52 LOG(WARNING) << "Showing " << GetNotificationStateString(state)
53 << " notification. USB charger is connected. "
54 << "Battery percentage: " << battery_percent << "%.";
57 void LogBatteryForNoCharger(TrayPower::NotificationState state,
58 int remaining_minutes) {
59 LOG(WARNING) << "Showing " << GetNotificationStateString(state)
60 << " notification. No charger connected."
61 << " Remaining time: " << remaining_minutes << " minutes.";
64 } // namespace
66 namespace tray {
68 // This view is used only for the tray.
69 class PowerTrayView : public views::ImageView {
70 public:
71 PowerTrayView() {
72 UpdateImage();
75 ~PowerTrayView() override {}
77 // Overriden from views::View.
78 void GetAccessibleState(ui::AXViewState* state) override {
79 state->name = accessible_name_;
80 state->role = ui::AX_ROLE_BUTTON;
83 void UpdateStatus(bool battery_alert) {
84 UpdateImage();
85 SetVisible(PowerStatus::Get()->IsBatteryPresent());
87 if (battery_alert) {
88 accessible_name_ = PowerStatus::Get()->GetAccessibleNameString(true);
89 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
93 private:
94 void UpdateImage() {
95 SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
98 base::string16 accessible_name_;
100 DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
103 } // namespace tray
105 const int TrayPower::kCriticalMinutes = 5;
106 const int TrayPower::kLowPowerMinutes = 15;
107 const int TrayPower::kNoWarningMinutes = 30;
108 const int TrayPower::kCriticalPercentage = 5;
109 const int TrayPower::kLowPowerPercentage = 10;
110 const int TrayPower::kNoWarningPercentage = 15;
112 TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
113 : SystemTrayItem(system_tray),
114 message_center_(message_center),
115 power_tray_(NULL),
116 notification_state_(NOTIFICATION_NONE),
117 usb_charger_was_connected_(false),
118 line_power_was_connected_(false) {
119 PowerStatus::Get()->AddObserver(this);
122 TrayPower::~TrayPower() {
123 PowerStatus::Get()->RemoveObserver(this);
126 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
127 // There may not be enough information when this is created about whether
128 // there is a battery or not. So always create this, and adjust visibility as
129 // necessary.
130 CHECK(power_tray_ == NULL);
131 power_tray_ = new tray::PowerTrayView();
132 power_tray_->UpdateStatus(false);
133 return power_tray_;
136 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
137 // Make sure icon status is up-to-date. (Also triggers stub activation).
138 PowerStatus::Get()->RequestStatusUpdate();
139 return NULL;
142 void TrayPower::DestroyTrayView() {
143 power_tray_ = NULL;
146 void TrayPower::DestroyDefaultView() {
149 void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
152 void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
153 SetTrayImageItemBorder(power_tray_, alignment);
156 void TrayPower::OnPowerStatusChanged() {
157 bool battery_alert = UpdateNotificationState();
158 if (power_tray_)
159 power_tray_->UpdateStatus(battery_alert);
161 // Factory testing may place the battery into unusual states.
162 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
163 ash::switches::kAshHideNotificationsForFactory))
164 return;
166 MaybeShowUsbChargerNotification();
168 if (battery_alert) {
169 // Remove any existing notification so it's dismissed before adding a new
170 // one. Otherwise we might update a "low battery" notification to "critical"
171 // without it being shown again.
172 battery_notification_.reset();
173 battery_notification_.reset(
174 new BatteryNotification(message_center_, notification_state_));
175 } else if (notification_state_ == NOTIFICATION_NONE) {
176 battery_notification_.reset();
177 } else if (battery_notification_.get()) {
178 battery_notification_->Update(notification_state_);
181 usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
182 line_power_was_connected_ = PowerStatus::Get()->IsLinePowerConnected();
185 bool TrayPower::MaybeShowUsbChargerNotification() {
186 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
187 const char kNotificationId[] = "usb-charger";
188 bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
190 // Check for a USB charger being connected.
191 if (usb_charger_is_connected && !usb_charger_was_connected_) {
192 scoped_ptr<Notification> notification(new Notification(
193 message_center::NOTIFICATION_TYPE_SIMPLE,
194 kNotificationId,
195 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
196 ash::SubstituteChromeOSDeviceType(
197 IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
198 rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
199 base::string16(),
200 message_center::NotifierId(
201 message_center::NotifierId::SYSTEM_COMPONENT,
202 system_notifier::kNotifierPower),
203 message_center::RichNotificationData(),
204 NULL));
205 message_center_->AddNotification(notification.Pass());
206 return true;
209 // Check for unplug of a USB charger while the USB charger notification is
210 // showing.
211 if (!usb_charger_is_connected && usb_charger_was_connected_) {
212 message_center_->RemoveNotification(kNotificationId, false);
213 return true;
215 return false;
218 bool TrayPower::UpdateNotificationState() {
219 const PowerStatus& status = *PowerStatus::Get();
220 if (!status.IsBatteryPresent() ||
221 status.IsBatteryTimeBeingCalculated() ||
222 status.IsMainsChargerConnected()) {
223 notification_state_ = NOTIFICATION_NONE;
224 return false;
227 return status.IsUsbChargerConnected() ?
228 UpdateNotificationStateForRemainingPercentage() :
229 UpdateNotificationStateForRemainingTime();
232 bool TrayPower::UpdateNotificationStateForRemainingTime() {
233 // The notification includes a rounded minutes value, so round the estimate
234 // received from the power manager to match.
235 const int remaining_minutes = static_cast<int>(
236 PowerStatus::Get()->GetBatteryTimeToEmpty().InSecondsF() / 60.0 + 0.5);
238 if (remaining_minutes >= kNoWarningMinutes ||
239 PowerStatus::Get()->IsBatteryFull()) {
240 notification_state_ = NOTIFICATION_NONE;
241 return false;
244 switch (notification_state_) {
245 case NOTIFICATION_NONE:
246 if (remaining_minutes <= kCriticalMinutes) {
247 notification_state_ = NOTIFICATION_CRITICAL;
248 LogBatteryForNoCharger(notification_state_, remaining_minutes);
249 return true;
251 if (remaining_minutes <= kLowPowerMinutes) {
252 notification_state_ = NOTIFICATION_LOW_POWER;
253 LogBatteryForNoCharger(notification_state_, remaining_minutes);
254 return true;
256 return false;
257 case NOTIFICATION_LOW_POWER:
258 if (remaining_minutes <= kCriticalMinutes) {
259 notification_state_ = NOTIFICATION_CRITICAL;
260 LogBatteryForNoCharger(notification_state_, remaining_minutes);
261 return true;
263 return false;
264 case NOTIFICATION_CRITICAL:
265 return false;
267 NOTREACHED();
268 return false;
271 bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
272 // The notification includes a rounded percentage, so round the value received
273 // from the power manager to match.
274 const int remaining_percentage =
275 PowerStatus::Get()->GetRoundedBatteryPercent();
277 if (remaining_percentage >= kNoWarningPercentage ||
278 PowerStatus::Get()->IsBatteryFull()) {
279 notification_state_ = NOTIFICATION_NONE;
280 return false;
283 switch (notification_state_) {
284 case NOTIFICATION_NONE:
285 if (remaining_percentage <= kCriticalPercentage) {
286 notification_state_ = NOTIFICATION_CRITICAL;
287 LogBatteryForUsbCharger(notification_state_, remaining_percentage);
288 return true;
290 if (remaining_percentage <= kLowPowerPercentage) {
291 notification_state_ = NOTIFICATION_LOW_POWER;
292 LogBatteryForUsbCharger(notification_state_, remaining_percentage);
293 return true;
295 return false;
296 case NOTIFICATION_LOW_POWER:
297 if (remaining_percentage <= kCriticalPercentage) {
298 notification_state_ = NOTIFICATION_CRITICAL;
299 LogBatteryForUsbCharger(notification_state_, remaining_percentage);
300 return true;
302 return false;
303 case NOTIFICATION_CRITICAL:
304 return false;
306 NOTREACHED();
307 return false;
310 } // namespace ash