Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ash / system / chromeos / power / tray_power.cc
blobe4f094f40f2c1ca3640a9fe75b94fd02106b0265
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/power/power_status_view.h"
11 #include "ash/system/date/date_view.h"
12 #include "ash/system/system_notifier.h"
13 #include "ash/system/tray/system_tray_delegate.h"
14 #include "ash/system/tray/tray_constants.h"
15 #include "ash/system/tray/tray_notification_view.h"
16 #include "ash/system/tray/tray_utils.h"
17 #include "base/command_line.h"
18 #include "base/metrics/histogram.h"
19 #include "base/time/time.h"
20 #include "grit/ash_resources.h"
21 #include "grit/ash_strings.h"
22 #include "third_party/icu/source/i18n/unicode/fieldpos.h"
23 #include "third_party/icu/source/i18n/unicode/fmtable.h"
24 #include "ui/accessibility/ax_view_state.h"
25 #include "ui/base/resource/resource_bundle.h"
26 #include "ui/message_center/message_center.h"
27 #include "ui/message_center/notification.h"
28 #include "ui/views/controls/button/button.h"
29 #include "ui/views/controls/image_view.h"
30 #include "ui/views/controls/label.h"
31 #include "ui/views/layout/box_layout.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/layout/grid_layout.h"
34 #include "ui/views/view.h"
35 #include "ui/views/widget/widget.h"
37 using message_center::MessageCenter;
38 using message_center::Notification;
40 namespace ash {
41 namespace tray {
42 namespace {
44 const int kMaxSpringChargerAccessibilityNotifyCount = 3;
45 const int kSpringChargerAccessibilityTimerFirstTimeNotifyInSeconds = 30;
46 const int kSpringChargerAccessibilityTimerRepeatInMinutes = 5;
50 // This view is used only for the tray.
51 class PowerTrayView : public views::ImageView {
52 public:
53 PowerTrayView()
54 : spring_charger_spoken_notification_count_(0) {
55 UpdateImage();
58 virtual ~PowerTrayView() {
61 // Overriden from views::View.
62 virtual void GetAccessibleState(ui::AXViewState* state) OVERRIDE {
63 state->name = accessible_name_;
64 state->role = ui::AX_ROLE_BUTTON;
67 void UpdateStatus(bool battery_alert) {
68 UpdateImage();
69 SetVisible(PowerStatus::Get()->IsBatteryPresent());
71 if (battery_alert) {
72 accessible_name_ = PowerStatus::Get()->GetAccessibleNameString(true);
73 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
77 void SetupNotifyBadCharger() {
78 // Poll with a shorter duration timer to notify the charger issue
79 // for the first time after the charger dialog is displayed.
80 spring_charger_accessility_timer_.Start(
81 FROM_HERE, base::TimeDelta::FromSeconds(
82 kSpringChargerAccessibilityTimerFirstTimeNotifyInSeconds),
83 this, &PowerTrayView::NotifyChargerIssue);
86 private:
87 void UpdateImage() {
88 SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
91 void NotifyChargerIssue() {
92 if (!Shell::GetInstance()->accessibility_delegate()->
93 IsSpokenFeedbackEnabled())
94 return;
96 if (!Shell::GetInstance()->system_tray_delegate()->
97 IsSpringChargerReplacementDialogVisible()) {
98 spring_charger_accessility_timer_.Stop();
99 return;
102 accessible_name_ = ui::ResourceBundle::GetSharedInstance().
103 GetLocalizedString(IDS_CHARGER_REPLACEMENT_ACCESSIBILTY_NOTIFICATION);
104 NotifyAccessibilityEvent(ui::AX_EVENT_ALERT, true);
105 ++spring_charger_spoken_notification_count_;
107 if (spring_charger_spoken_notification_count_ == 1) {
108 // After notify the charger issue for the first time, repeat the
109 // notification with a longer duration timer.
110 spring_charger_accessility_timer_.Stop();
111 spring_charger_accessility_timer_.Start(
112 FROM_HERE, base::TimeDelta::FromMinutes(
113 kSpringChargerAccessibilityTimerRepeatInMinutes),
114 this, &PowerTrayView::NotifyChargerIssue);
115 } else if (spring_charger_spoken_notification_count_ >=
116 kMaxSpringChargerAccessibilityNotifyCount) {
117 spring_charger_accessility_timer_.Stop();
121 base::string16 accessible_name_;
123 // Tracks how many times the original spring charger accessibility
124 // notification has been spoken.
125 int spring_charger_spoken_notification_count_;
127 base::RepeatingTimer<PowerTrayView> spring_charger_accessility_timer_;
129 DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
132 class PowerNotificationView : public TrayNotificationView {
133 public:
134 explicit PowerNotificationView(TrayPower* owner)
135 : TrayNotificationView(owner, 0) {
136 power_status_view_ =
137 new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
138 InitView(power_status_view_);
141 void UpdateStatus() {
142 SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
145 private:
146 PowerStatusView* power_status_view_;
148 DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
151 } // namespace tray
153 using tray::PowerNotificationView;
155 const int TrayPower::kCriticalMinutes = 5;
156 const int TrayPower::kLowPowerMinutes = 15;
157 const int TrayPower::kNoWarningMinutes = 30;
158 const int TrayPower::kCriticalPercentage = 5;
159 const int TrayPower::kLowPowerPercentage = 10;
160 const int TrayPower::kNoWarningPercentage = 15;
162 TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
163 : SystemTrayItem(system_tray),
164 message_center_(message_center),
165 power_tray_(NULL),
166 notification_view_(NULL),
167 notification_state_(NOTIFICATION_NONE),
168 usb_charger_was_connected_(false),
169 line_power_was_connected_(false) {
170 PowerStatus::Get()->AddObserver(this);
173 TrayPower::~TrayPower() {
174 PowerStatus::Get()->RemoveObserver(this);
177 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
178 // There may not be enough information when this is created about whether
179 // there is a battery or not. So always create this, and adjust visibility as
180 // necessary.
181 CHECK(power_tray_ == NULL);
182 power_tray_ = new tray::PowerTrayView();
183 power_tray_->UpdateStatus(false);
184 return power_tray_;
187 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
188 // Make sure icon status is up-to-date. (Also triggers stub activation).
189 PowerStatus::Get()->RequestStatusUpdate();
190 return NULL;
193 views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
194 CHECK(notification_view_ == NULL);
195 if (!PowerStatus::Get()->IsBatteryPresent())
196 return NULL;
198 notification_view_ = new PowerNotificationView(this);
199 notification_view_->UpdateStatus();
201 return notification_view_;
204 void TrayPower::DestroyTrayView() {
205 power_tray_ = NULL;
208 void TrayPower::DestroyDefaultView() {
211 void TrayPower::DestroyNotificationView() {
212 notification_view_ = NULL;
215 void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
218 void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
219 SetTrayImageItemBorder(power_tray_, alignment);
222 void TrayPower::OnPowerStatusChanged() {
223 RecordChargerType();
225 if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
226 if (ash::Shell::GetInstance()->system_tray_delegate()->
227 ShowSpringChargerReplacementDialog()) {
228 power_tray_->SetupNotifyBadCharger();
232 bool battery_alert = UpdateNotificationState();
233 if (power_tray_)
234 power_tray_->UpdateStatus(battery_alert);
235 if (notification_view_)
236 notification_view_->UpdateStatus();
238 // Factory testing may place the battery into unusual states.
239 if (CommandLine::ForCurrentProcess()->HasSwitch(
240 ash::switches::kAshHideNotificationsForFactory))
241 return;
243 MaybeShowUsbChargerNotification();
245 if (battery_alert)
246 ShowNotificationView();
247 else if (notification_state_ == NOTIFICATION_NONE)
248 HideNotificationView();
250 usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
251 line_power_was_connected_ = PowerStatus::Get()->IsLinePowerConnected();
254 bool TrayPower::MaybeShowUsbChargerNotification() {
255 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
256 const char kNotificationId[] = "usb-charger";
257 bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
259 // Check for a USB charger being connected.
260 if (usb_charger_is_connected && !usb_charger_was_connected_) {
261 scoped_ptr<Notification> notification(new Notification(
262 message_center::NOTIFICATION_TYPE_SIMPLE,
263 kNotificationId,
264 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
265 rb.GetLocalizedString(
266 IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
267 rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
268 base::string16(),
269 message_center::NotifierId(
270 message_center::NotifierId::SYSTEM_COMPONENT,
271 system_notifier::kNotifierPower),
272 message_center::RichNotificationData(),
273 NULL));
274 message_center_->AddNotification(notification.Pass());
275 return true;
278 // Check for unplug of a USB charger while the USB charger notification is
279 // showing.
280 if (!usb_charger_is_connected && usb_charger_was_connected_) {
281 message_center_->RemoveNotification(kNotificationId, false);
282 return true;
284 return false;
287 bool TrayPower::UpdateNotificationState() {
288 const PowerStatus& status = *PowerStatus::Get();
289 if (!status.IsBatteryPresent() ||
290 status.IsBatteryTimeBeingCalculated() ||
291 status.IsMainsChargerConnected() ||
292 status.IsOriginalSpringChargerConnected()) {
293 notification_state_ = NOTIFICATION_NONE;
294 return false;
297 return status.IsUsbChargerConnected() ?
298 UpdateNotificationStateForRemainingPercentage() :
299 UpdateNotificationStateForRemainingTime();
302 bool TrayPower::UpdateNotificationStateForRemainingTime() {
303 // The notification includes a rounded minutes value, so round the estimate
304 // received from the power manager to match.
305 const int remaining_minutes = static_cast<int>(
306 PowerStatus::Get()->GetBatteryTimeToEmpty().InSecondsF() / 60.0 + 0.5);
308 if (remaining_minutes >= kNoWarningMinutes ||
309 PowerStatus::Get()->IsBatteryFull()) {
310 notification_state_ = NOTIFICATION_NONE;
311 return false;
314 switch (notification_state_) {
315 case NOTIFICATION_NONE:
316 if (remaining_minutes <= kCriticalMinutes) {
317 notification_state_ = NOTIFICATION_CRITICAL;
318 return true;
320 if (remaining_minutes <= kLowPowerMinutes) {
321 notification_state_ = NOTIFICATION_LOW_POWER;
322 return true;
324 return false;
325 case NOTIFICATION_LOW_POWER:
326 if (remaining_minutes <= kCriticalMinutes) {
327 notification_state_ = NOTIFICATION_CRITICAL;
328 return true;
330 return false;
331 case NOTIFICATION_CRITICAL:
332 return false;
334 NOTREACHED();
335 return false;
338 bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
339 // The notification includes a rounded percentage, so round the value received
340 // from the power manager to match.
341 const int remaining_percentage =
342 PowerStatus::Get()->GetRoundedBatteryPercent();
344 if (remaining_percentage >= kNoWarningPercentage ||
345 PowerStatus::Get()->IsBatteryFull()) {
346 notification_state_ = NOTIFICATION_NONE;
347 return false;
350 switch (notification_state_) {
351 case NOTIFICATION_NONE:
352 if (remaining_percentage <= kCriticalPercentage) {
353 notification_state_ = NOTIFICATION_CRITICAL;
354 return true;
356 if (remaining_percentage <= kLowPowerPercentage) {
357 notification_state_ = NOTIFICATION_LOW_POWER;
358 return true;
360 return false;
361 case NOTIFICATION_LOW_POWER:
362 if (remaining_percentage <= kCriticalPercentage) {
363 notification_state_ = NOTIFICATION_CRITICAL;
364 return true;
366 return false;
367 case NOTIFICATION_CRITICAL:
368 return false;
370 NOTREACHED();
371 return false;
374 void TrayPower::RecordChargerType() {
375 if (!PowerStatus::Get()->IsLinePowerConnected() ||
376 line_power_was_connected_)
377 return;
379 ChargerType current_charger = UNKNOWN_CHARGER;
380 if (PowerStatus::Get()->IsMainsChargerConnected()) {
381 current_charger = MAINS_CHARGER;
382 } else if (PowerStatus::Get()->IsUsbChargerConnected()) {
383 current_charger = USB_CHARGER;
384 } else if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
385 current_charger =
386 ash::Shell::GetInstance()->system_tray_delegate()->
387 HasUserConfirmedSafeSpringCharger() ?
388 SAFE_SPRING_CHARGER : UNCONFIRMED_SPRING_CHARGER;
391 if (current_charger != UNKNOWN_CHARGER) {
392 UMA_HISTOGRAM_ENUMERATION("Power.ChargerType",
393 current_charger,
394 CHARGER_TYPE_COUNT);
398 } // namespace ash