• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "ash/system/chromeos/power/tray_power.h"
6 
7 #include "ash/ash_switches.h"
8 #include "ash/shell.h"
9 #include "ash/system/chromeos/power/power_status_view.h"
10 #include "ash/system/date/date_view.h"
11 #include "ash/system/system_notifier.h"
12 #include "ash/system/tray/system_tray_delegate.h"
13 #include "ash/system/tray/tray_constants.h"
14 #include "ash/system/tray/tray_notification_view.h"
15 #include "ash/system/tray/tray_utils.h"
16 #include "base/command_line.h"
17 #include "base/metrics/histogram.h"
18 #include "grit/ash_resources.h"
19 #include "grit/ash_strings.h"
20 #include "third_party/icu/source/i18n/unicode/fieldpos.h"
21 #include "third_party/icu/source/i18n/unicode/fmtable.h"
22 #include "ui/base/accessibility/accessible_view_state.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/notification.h"
26 #include "ui/views/controls/button/button.h"
27 #include "ui/views/controls/image_view.h"
28 #include "ui/views/controls/label.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/layout/fill_layout.h"
31 #include "ui/views/layout/grid_layout.h"
32 #include "ui/views/view.h"
33 #include "ui/views/widget/widget.h"
34 
35 using message_center::MessageCenter;
36 using message_center::Notification;
37 
38 namespace ash {
39 namespace internal {
40 namespace tray {
41 
42 // This view is used only for the tray.
43 class PowerTrayView : public views::ImageView {
44  public:
PowerTrayView()45   PowerTrayView() {
46     UpdateImage();
47   }
48 
~PowerTrayView()49   virtual ~PowerTrayView() {
50   }
51 
52   // Overriden from views::View.
GetAccessibleState(ui::AccessibleViewState * state)53   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
54     state->name = accessible_name_;
55     state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
56   }
57 
UpdateStatus(bool battery_alert)58   void UpdateStatus(bool battery_alert) {
59     UpdateImage();
60     SetVisible(PowerStatus::Get()->IsBatteryPresent());
61 
62     if (battery_alert) {
63       accessible_name_ = PowerStatus::Get()->GetAccessibleNameString();
64       NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
65     }
66   }
67 
68  private:
UpdateImage()69   void UpdateImage() {
70     SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
71   }
72 
73   base::string16 accessible_name_;
74 
75   DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
76 };
77 
78 class PowerNotificationView : public TrayNotificationView {
79  public:
PowerNotificationView(TrayPower * owner)80   explicit PowerNotificationView(TrayPower* owner)
81       : TrayNotificationView(owner, 0) {
82     power_status_view_ =
83         new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
84     InitView(power_status_view_);
85   }
86 
UpdateStatus()87   void UpdateStatus() {
88     SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
89   }
90 
91  private:
92   PowerStatusView* power_status_view_;
93 
94   DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
95 };
96 
97 }  // namespace tray
98 
99 using tray::PowerNotificationView;
100 
101 const int TrayPower::kCriticalMinutes = 5;
102 const int TrayPower::kLowPowerMinutes = 15;
103 const int TrayPower::kNoWarningMinutes = 30;
104 const int TrayPower::kCriticalPercentage = 5;
105 const int TrayPower::kLowPowerPercentage = 10;
106 const int TrayPower::kNoWarningPercentage = 15;
107 
TrayPower(SystemTray * system_tray,MessageCenter * message_center)108 TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
109     : SystemTrayItem(system_tray),
110       message_center_(message_center),
111       power_tray_(NULL),
112       notification_view_(NULL),
113       notification_state_(NOTIFICATION_NONE),
114       usb_charger_was_connected_(false),
115       line_power_was_connected_(false) {
116   PowerStatus::Get()->AddObserver(this);
117 }
118 
~TrayPower()119 TrayPower::~TrayPower() {
120   PowerStatus::Get()->RemoveObserver(this);
121 }
122 
CreateTrayView(user::LoginStatus status)123 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
124   // There may not be enough information when this is created about whether
125   // there is a battery or not. So always create this, and adjust visibility as
126   // necessary.
127   CHECK(power_tray_ == NULL);
128   power_tray_ = new tray::PowerTrayView();
129   power_tray_->UpdateStatus(false);
130   return power_tray_;
131 }
132 
CreateDefaultView(user::LoginStatus status)133 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
134   // Make sure icon status is up-to-date. (Also triggers stub activation).
135   PowerStatus::Get()->RequestStatusUpdate();
136   return NULL;
137 }
138 
CreateNotificationView(user::LoginStatus status)139 views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
140   CHECK(notification_view_ == NULL);
141   if (!PowerStatus::Get()->IsBatteryPresent())
142     return NULL;
143 
144   notification_view_ = new PowerNotificationView(this);
145   notification_view_->UpdateStatus();
146 
147   return notification_view_;
148 }
149 
DestroyTrayView()150 void TrayPower::DestroyTrayView() {
151   power_tray_ = NULL;
152 }
153 
DestroyDefaultView()154 void TrayPower::DestroyDefaultView() {
155 }
156 
DestroyNotificationView()157 void TrayPower::DestroyNotificationView() {
158   notification_view_ = NULL;
159 }
160 
UpdateAfterLoginStatusChange(user::LoginStatus status)161 void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
162 }
163 
UpdateAfterShelfAlignmentChange(ShelfAlignment alignment)164 void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
165   SetTrayImageItemBorder(power_tray_, alignment);
166 }
167 
OnPowerStatusChanged()168 void TrayPower::OnPowerStatusChanged() {
169   RecordChargerType();
170 
171   if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
172     ash::Shell::GetInstance()->system_tray_delegate()->
173         ShowSpringChargerReplacementDialog();
174   }
175 
176   bool battery_alert = UpdateNotificationState();
177   if (power_tray_)
178     power_tray_->UpdateStatus(battery_alert);
179   if (notification_view_)
180     notification_view_->UpdateStatus();
181 
182   // Factory testing may place the battery into unusual states.
183   if (CommandLine::ForCurrentProcess()->HasSwitch(
184           ash::switches::kAshHideNotificationsForFactory))
185     return;
186 
187   if (ash::switches::UseUsbChargerNotification())
188     MaybeShowUsbChargerNotification();
189 
190   if (battery_alert)
191     ShowNotificationView();
192   else if (notification_state_ == NOTIFICATION_NONE)
193     HideNotificationView();
194 
195   usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
196   line_power_was_connected_ = PowerStatus::Get()->IsLinePowerConnected();
197 }
198 
MaybeShowUsbChargerNotification()199 bool TrayPower::MaybeShowUsbChargerNotification() {
200   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
201   const char kNotificationId[] = "usb-charger";
202   bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
203 
204   // Check for a USB charger being connected.
205   if (usb_charger_is_connected && !usb_charger_was_connected_) {
206     scoped_ptr<Notification> notification(new Notification(
207         message_center::NOTIFICATION_TYPE_SIMPLE,
208         kNotificationId,
209         rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
210         rb.GetLocalizedString(
211             IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
212         rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
213         base::string16(),
214         message_center::NotifierId(
215             message_center::NotifierId::SYSTEM_COMPONENT,
216             system_notifier::kNotifierPower),
217         message_center::RichNotificationData(),
218         NULL));
219     message_center_->AddNotification(notification.Pass());
220     return true;
221   }
222 
223   // Check for unplug of a USB charger while the USB charger notification is
224   // showing.
225   if (!usb_charger_is_connected && usb_charger_was_connected_) {
226     message_center_->RemoveNotification(kNotificationId, false);
227     return true;
228   }
229   return false;
230 }
231 
UpdateNotificationState()232 bool TrayPower::UpdateNotificationState() {
233   const PowerStatus& status = *PowerStatus::Get();
234   if (!status.IsBatteryPresent() ||
235       status.IsBatteryTimeBeingCalculated() ||
236       status.IsMainsChargerConnected() ||
237       status.IsOriginalSpringChargerConnected()) {
238     notification_state_ = NOTIFICATION_NONE;
239     return false;
240   }
241 
242   return status.IsUsbChargerConnected() ?
243       UpdateNotificationStateForRemainingPercentage() :
244       UpdateNotificationStateForRemainingTime();
245 }
246 
UpdateNotificationStateForRemainingTime()247 bool TrayPower::UpdateNotificationStateForRemainingTime() {
248   // The notification includes a rounded minutes value, so round the estimate
249   // received from the power manager to match.
250   const int remaining_minutes = static_cast<int>(
251       PowerStatus::Get()->GetBatteryTimeToEmpty().InSecondsF() / 60.0 + 0.5);
252 
253   if (remaining_minutes >= kNoWarningMinutes ||
254       PowerStatus::Get()->IsBatteryFull()) {
255     notification_state_ = NOTIFICATION_NONE;
256     return false;
257   }
258 
259   switch (notification_state_) {
260     case NOTIFICATION_NONE:
261       if (remaining_minutes <= kCriticalMinutes) {
262         notification_state_ = NOTIFICATION_CRITICAL;
263         return true;
264       }
265       if (remaining_minutes <= kLowPowerMinutes) {
266         notification_state_ = NOTIFICATION_LOW_POWER;
267         return true;
268       }
269       return false;
270     case NOTIFICATION_LOW_POWER:
271       if (remaining_minutes <= kCriticalMinutes) {
272         notification_state_ = NOTIFICATION_CRITICAL;
273         return true;
274       }
275       return false;
276     case NOTIFICATION_CRITICAL:
277       return false;
278   }
279   NOTREACHED();
280   return false;
281 }
282 
UpdateNotificationStateForRemainingPercentage()283 bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
284   // The notification includes a rounded percentage, so round the value received
285   // from the power manager to match.
286   const int remaining_percentage =
287       PowerStatus::Get()->GetRoundedBatteryPercent();
288 
289   if (remaining_percentage >= kNoWarningPercentage ||
290       PowerStatus::Get()->IsBatteryFull()) {
291     notification_state_ = NOTIFICATION_NONE;
292     return false;
293   }
294 
295   switch (notification_state_) {
296     case NOTIFICATION_NONE:
297       if (remaining_percentage <= kCriticalPercentage) {
298         notification_state_ = NOTIFICATION_CRITICAL;
299         return true;
300       }
301       if (remaining_percentage <= kLowPowerPercentage) {
302         notification_state_ = NOTIFICATION_LOW_POWER;
303         return true;
304       }
305       return false;
306     case NOTIFICATION_LOW_POWER:
307       if (remaining_percentage <= kCriticalPercentage) {
308         notification_state_ = NOTIFICATION_CRITICAL;
309         return true;
310       }
311       return false;
312     case NOTIFICATION_CRITICAL:
313       return false;
314   }
315   NOTREACHED();
316   return false;
317 }
318 
RecordChargerType()319 void TrayPower::RecordChargerType() {
320   if (!PowerStatus::Get()->IsLinePowerConnected() ||
321       line_power_was_connected_)
322     return;
323 
324   ChargerType current_charger = UNKNOWN_CHARGER;
325   if (PowerStatus::Get()->IsMainsChargerConnected()) {
326     current_charger = MAINS_CHARGER;
327   } else if (PowerStatus::Get()->IsUsbChargerConnected()) {
328     current_charger = USB_CHARGER;
329   } else if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
330     current_charger =
331         ash::Shell::GetInstance()->system_tray_delegate()->
332             HasUserConfirmedSafeSpringCharger() ?
333         SAFE_SPRING_CHARGER : UNCONFIRMED_SPRING_CHARGER;
334   }
335 
336   if (current_charger != UNKNOWN_CHARGER) {
337     UMA_HISTOGRAM_ENUMERATION("Power.ChargerType",
338                               current_charger,
339                               CHARGER_TYPE_COUNT);
340   }
341 }
342 
343 }  // namespace internal
344 }  // namespace ash
345