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/ash_switches.h"
8 #include "ash/system/chromeos/power/power_status_view.h"
9 #include "ash/system/date/date_view.h"
10 #include "ash/system/system_notifier.h"
11 #include "ash/system/tray/tray_constants.h"
12 #include "ash/system/tray/tray_notification_view.h"
13 #include "ash/system/tray/tray_utils.h"
14 #include "base/command_line.h"
15 #include "grit/ash_resources.h"
16 #include "grit/ash_strings.h"
17 #include "third_party/icu/source/i18n/unicode/fieldpos.h"
18 #include "third_party/icu/source/i18n/unicode/fmtable.h"
19 #include "ui/base/accessibility/accessible_view_state.h"
20 #include "ui/base/resource/resource_bundle.h"
21 #include "ui/message_center/message_center.h"
22 #include "ui/message_center/notification.h"
23 #include "ui/views/controls/button/button.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/label.h"
26 #include "ui/views/layout/box_layout.h"
27 #include "ui/views/layout/fill_layout.h"
28 #include "ui/views/layout/grid_layout.h"
29 #include "ui/views/view.h"
30 #include "ui/views/widget/widget.h"
32 using message_center::MessageCenter;
33 using message_center::Notification;
39 // This view is used only for the tray.
40 class PowerTrayView : public views::ImageView {
46 virtual ~PowerTrayView() {
49 // Overriden from views::View.
50 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
51 state->name = accessible_name_;
52 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
55 void UpdateStatus(bool battery_alert) {
57 SetVisible(PowerStatus::Get()->IsBatteryPresent());
60 accessible_name_ = PowerStatus::Get()->GetAccessibleNameString();
61 NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
67 SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
70 base::string16 accessible_name_;
72 DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
75 class PowerNotificationView : public TrayNotificationView {
77 explicit PowerNotificationView(TrayPower* owner)
78 : TrayNotificationView(owner, 0) {
80 new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
81 InitView(power_status_view_);
85 SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
89 PowerStatusView* power_status_view_;
91 DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
96 using tray::PowerNotificationView;
98 const int TrayPower::kCriticalMinutes = 5;
99 const int TrayPower::kLowPowerMinutes = 15;
100 const int TrayPower::kNoWarningMinutes = 30;
101 const int TrayPower::kCriticalPercentage = 5;
102 const int TrayPower::kLowPowerPercentage = 10;
103 const int TrayPower::kNoWarningPercentage = 15;
105 TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
106 : SystemTrayItem(system_tray),
107 message_center_(message_center),
109 notification_view_(NULL),
110 notification_state_(NOTIFICATION_NONE),
111 usb_charger_was_connected_(false) {
112 PowerStatus::Get()->AddObserver(this);
115 TrayPower::~TrayPower() {
116 PowerStatus::Get()->RemoveObserver(this);
119 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
120 // There may not be enough information when this is created about whether
121 // there is a battery or not. So always create this, and adjust visibility as
123 CHECK(power_tray_ == NULL);
124 power_tray_ = new tray::PowerTrayView();
125 power_tray_->UpdateStatus(false);
129 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
130 // Make sure icon status is up-to-date. (Also triggers stub activation).
131 PowerStatus::Get()->RequestStatusUpdate();
135 views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
136 CHECK(notification_view_ == NULL);
137 if (!PowerStatus::Get()->IsBatteryPresent())
140 notification_view_ = new PowerNotificationView(this);
141 notification_view_->UpdateStatus();
143 return notification_view_;
146 void TrayPower::DestroyTrayView() {
150 void TrayPower::DestroyDefaultView() {
153 void TrayPower::DestroyNotificationView() {
154 notification_view_ = NULL;
157 void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
160 void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
161 SetTrayImageItemBorder(power_tray_, alignment);
164 void TrayPower::OnPowerStatusChanged() {
165 bool battery_alert = UpdateNotificationState();
167 power_tray_->UpdateStatus(battery_alert);
168 if (notification_view_)
169 notification_view_->UpdateStatus();
171 // Factory testing may place the battery into unusual states.
172 if (CommandLine::ForCurrentProcess()->HasSwitch(
173 ash::switches::kAshHideNotificationsForFactory))
176 if (ash::switches::UseUsbChargerNotification())
177 MaybeShowUsbChargerNotification();
180 ShowNotificationView();
181 else if (notification_state_ == NOTIFICATION_NONE)
182 HideNotificationView();
184 usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
187 bool TrayPower::MaybeShowUsbChargerNotification() {
188 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
189 const char kNotificationId[] = "usb-charger";
190 bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
192 // Check for a USB charger being connected.
193 if (usb_charger_is_connected && !usb_charger_was_connected_) {
194 scoped_ptr<Notification> notification(new Notification(
195 message_center::NOTIFICATION_TYPE_SIMPLE,
197 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
198 rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE),
199 rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
201 message_center::NotifierId(system_notifier::NOTIFIER_POWER),
202 message_center::RichNotificationData(),
204 message_center_->AddNotification(notification.Pass());
208 // Check for unplug of a USB charger while the USB charger notification is
210 if (!usb_charger_is_connected && usb_charger_was_connected_) {
211 message_center_->RemoveNotification(kNotificationId, false);
217 bool TrayPower::UpdateNotificationState() {
218 const PowerStatus& status = *PowerStatus::Get();
219 if (!status.IsBatteryPresent() ||
220 status.IsBatteryTimeBeingCalculated() ||
221 status.IsMainsChargerConnected()) {
222 notification_state_ = NOTIFICATION_NONE;
226 return status.IsUsbChargerConnected() ?
227 UpdateNotificationStateForRemainingPercentage() :
228 UpdateNotificationStateForRemainingTime();
231 bool TrayPower::UpdateNotificationStateForRemainingTime() {
232 // The notification includes a rounded minutes value, so round the estimate
233 // received from the power manager to match.
234 const int remaining_minutes = static_cast<int>(
235 PowerStatus::Get()->GetBatteryTimeToEmpty().InSecondsF() / 60.0 + 0.5);
237 if (remaining_minutes >= kNoWarningMinutes) {
238 notification_state_ = NOTIFICATION_NONE;
242 switch (notification_state_) {
243 case NOTIFICATION_NONE:
244 if (remaining_minutes <= kCriticalMinutes) {
245 notification_state_ = NOTIFICATION_CRITICAL;
248 if (remaining_minutes <= kLowPowerMinutes) {
249 notification_state_ = NOTIFICATION_LOW_POWER;
253 case NOTIFICATION_LOW_POWER:
254 if (remaining_minutes <= kCriticalMinutes) {
255 notification_state_ = NOTIFICATION_CRITICAL;
259 case NOTIFICATION_CRITICAL:
266 bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
267 // The notification includes a rounded percentage, so round the value received
268 // from the power manager to match.
269 const int remaining_percentage =
270 PowerStatus::Get()->GetRoundedBatteryPercent();
272 if (remaining_percentage >= kNoWarningPercentage) {
273 notification_state_ = NOTIFICATION_NONE;
277 switch (notification_state_) {
278 case NOTIFICATION_NONE:
279 if (remaining_percentage <= kCriticalPercentage) {
280 notification_state_ = NOTIFICATION_CRITICAL;
283 if (remaining_percentage <= kLowPowerPercentage) {
284 notification_state_ = NOTIFICATION_LOW_POWER;
288 case NOTIFICATION_LOW_POWER:
289 if (remaining_percentage <= kCriticalPercentage) {
290 notification_state_ = NOTIFICATION_CRITICAL;
294 case NOTIFICATION_CRITICAL:
301 } // namespace internal