Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ash / system / chromeos / power / tray_power.cc
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/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/base/accessibility/accessible_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"
36
37 using message_center::MessageCenter;
38 using message_center::Notification;
39
40 namespace ash {
41 namespace internal {
42 namespace tray {
43
44 namespace {
45
46 const int kMaxSpringChargerAccessibilityNotifyCount = 3;
47 const int kSpringChargerAccessibilityTimerFirstTimeNotifyInSeconds = 30;
48 const int kSpringChargerAccessibilityTimerRepeatInMinutes = 5;
49
50 }
51
52 // This view is used only for the tray.
53 class PowerTrayView : public views::ImageView {
54  public:
55   PowerTrayView()
56       : spring_charger_spoken_notification_count_(0) {
57     UpdateImage();
58   }
59
60   virtual ~PowerTrayView() {
61   }
62
63   // Overriden from views::View.
64   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
65     state->name = accessible_name_;
66     state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
67   }
68
69   void UpdateStatus(bool battery_alert) {
70     UpdateImage();
71     SetVisible(PowerStatus::Get()->IsBatteryPresent());
72
73     if (battery_alert) {
74       accessible_name_ = PowerStatus::Get()->GetAccessibleNameString();
75       NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
76     }
77   }
78
79   void SetupNotifyBadCharger() {
80     // Poll with a shorter duration timer to notify the charger issue
81     // for the first time after the charger dialog is displayed.
82     spring_charger_accessility_timer_.Start(
83         FROM_HERE, base::TimeDelta::FromSeconds(
84             kSpringChargerAccessibilityTimerFirstTimeNotifyInSeconds),
85         this, &PowerTrayView::NotifyChargerIssue);
86   }
87
88  private:
89   void UpdateImage() {
90     SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
91   }
92
93   void NotifyChargerIssue() {
94     if (!Shell::GetInstance()->accessibility_delegate()->
95             IsSpokenFeedbackEnabled())
96       return;
97
98     if (!Shell::GetInstance()->system_tray_delegate()->
99             IsSpringChargerReplacementDialogVisible()) {
100       spring_charger_accessility_timer_.Stop();
101       return;
102     }
103
104     accessible_name_ =  ui::ResourceBundle::GetSharedInstance().
105         GetLocalizedString(IDS_CHARGER_REPLACEMENT_ACCESSIBILTY_NOTIFICATION);
106     NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
107     ++spring_charger_spoken_notification_count_;
108
109     if (spring_charger_spoken_notification_count_ == 1) {
110       // After notify the charger issue for the first time, repeat the
111       // notification with a longer duration timer.
112       spring_charger_accessility_timer_.Stop();
113       spring_charger_accessility_timer_.Start(
114           FROM_HERE, base::TimeDelta::FromMinutes(
115               kSpringChargerAccessibilityTimerRepeatInMinutes),
116           this, &PowerTrayView::NotifyChargerIssue);
117     } else if (spring_charger_spoken_notification_count_ >=
118         kMaxSpringChargerAccessibilityNotifyCount) {
119       spring_charger_accessility_timer_.Stop();
120     }
121   }
122
123   base::string16 accessible_name_;
124
125   // Tracks how many times the original spring charger accessibility
126   // notification has been spoken.
127   int spring_charger_spoken_notification_count_;
128
129   base::RepeatingTimer<PowerTrayView> spring_charger_accessility_timer_;
130
131   DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
132 };
133
134 class PowerNotificationView : public TrayNotificationView {
135  public:
136   explicit PowerNotificationView(TrayPower* owner)
137       : TrayNotificationView(owner, 0) {
138     power_status_view_ =
139         new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
140     InitView(power_status_view_);
141   }
142
143   void UpdateStatus() {
144     SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
145   }
146
147  private:
148   PowerStatusView* power_status_view_;
149
150   DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
151 };
152
153 }  // namespace tray
154
155 using tray::PowerNotificationView;
156
157 const int TrayPower::kCriticalMinutes = 5;
158 const int TrayPower::kLowPowerMinutes = 15;
159 const int TrayPower::kNoWarningMinutes = 30;
160 const int TrayPower::kCriticalPercentage = 5;
161 const int TrayPower::kLowPowerPercentage = 10;
162 const int TrayPower::kNoWarningPercentage = 15;
163
164 TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
165     : SystemTrayItem(system_tray),
166       message_center_(message_center),
167       power_tray_(NULL),
168       notification_view_(NULL),
169       notification_state_(NOTIFICATION_NONE),
170       usb_charger_was_connected_(false),
171       line_power_was_connected_(false) {
172   PowerStatus::Get()->AddObserver(this);
173 }
174
175 TrayPower::~TrayPower() {
176   PowerStatus::Get()->RemoveObserver(this);
177 }
178
179 views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
180   // There may not be enough information when this is created about whether
181   // there is a battery or not. So always create this, and adjust visibility as
182   // necessary.
183   CHECK(power_tray_ == NULL);
184   power_tray_ = new tray::PowerTrayView();
185   power_tray_->UpdateStatus(false);
186   return power_tray_;
187 }
188
189 views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
190   // Make sure icon status is up-to-date. (Also triggers stub activation).
191   PowerStatus::Get()->RequestStatusUpdate();
192   return NULL;
193 }
194
195 views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
196   CHECK(notification_view_ == NULL);
197   if (!PowerStatus::Get()->IsBatteryPresent())
198     return NULL;
199
200   notification_view_ = new PowerNotificationView(this);
201   notification_view_->UpdateStatus();
202
203   return notification_view_;
204 }
205
206 void TrayPower::DestroyTrayView() {
207   power_tray_ = NULL;
208 }
209
210 void TrayPower::DestroyDefaultView() {
211 }
212
213 void TrayPower::DestroyNotificationView() {
214   notification_view_ = NULL;
215 }
216
217 void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
218 }
219
220 void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
221   SetTrayImageItemBorder(power_tray_, alignment);
222 }
223
224 void TrayPower::OnPowerStatusChanged() {
225   RecordChargerType();
226
227   if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
228     if (ash::Shell::GetInstance()->system_tray_delegate()->
229             ShowSpringChargerReplacementDialog()) {
230       power_tray_->SetupNotifyBadCharger();
231     }
232   }
233
234   bool battery_alert = UpdateNotificationState();
235   if (power_tray_)
236     power_tray_->UpdateStatus(battery_alert);
237   if (notification_view_)
238     notification_view_->UpdateStatus();
239
240   // Factory testing may place the battery into unusual states.
241   if (CommandLine::ForCurrentProcess()->HasSwitch(
242           ash::switches::kAshHideNotificationsForFactory))
243     return;
244
245   if (ash::switches::UseUsbChargerNotification())
246     MaybeShowUsbChargerNotification();
247
248   if (battery_alert)
249     ShowNotificationView();
250   else if (notification_state_ == NOTIFICATION_NONE)
251     HideNotificationView();
252
253   usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
254   line_power_was_connected_ = PowerStatus::Get()->IsLinePowerConnected();
255 }
256
257 bool TrayPower::MaybeShowUsbChargerNotification() {
258   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
259   const char kNotificationId[] = "usb-charger";
260   bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
261
262   // Check for a USB charger being connected.
263   if (usb_charger_is_connected && !usb_charger_was_connected_) {
264     scoped_ptr<Notification> notification(new Notification(
265         message_center::NOTIFICATION_TYPE_SIMPLE,
266         kNotificationId,
267         rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
268         rb.GetLocalizedString(
269             IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE_SHORT),
270         rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
271         base::string16(),
272         message_center::NotifierId(
273             message_center::NotifierId::SYSTEM_COMPONENT,
274             system_notifier::kNotifierPower),
275         message_center::RichNotificationData(),
276         NULL));
277     message_center_->AddNotification(notification.Pass());
278     return true;
279   }
280
281   // Check for unplug of a USB charger while the USB charger notification is
282   // showing.
283   if (!usb_charger_is_connected && usb_charger_was_connected_) {
284     message_center_->RemoveNotification(kNotificationId, false);
285     return true;
286   }
287   return false;
288 }
289
290 bool TrayPower::UpdateNotificationState() {
291   const PowerStatus& status = *PowerStatus::Get();
292   if (!status.IsBatteryPresent() ||
293       status.IsBatteryTimeBeingCalculated() ||
294       status.IsMainsChargerConnected() ||
295       status.IsOriginalSpringChargerConnected()) {
296     notification_state_ = NOTIFICATION_NONE;
297     return false;
298   }
299
300   return status.IsUsbChargerConnected() ?
301       UpdateNotificationStateForRemainingPercentage() :
302       UpdateNotificationStateForRemainingTime();
303 }
304
305 bool TrayPower::UpdateNotificationStateForRemainingTime() {
306   // The notification includes a rounded minutes value, so round the estimate
307   // received from the power manager to match.
308   const int remaining_minutes = static_cast<int>(
309       PowerStatus::Get()->GetBatteryTimeToEmpty().InSecondsF() / 60.0 + 0.5);
310
311   if (remaining_minutes >= kNoWarningMinutes ||
312       PowerStatus::Get()->IsBatteryFull()) {
313     notification_state_ = NOTIFICATION_NONE;
314     return false;
315   }
316
317   switch (notification_state_) {
318     case NOTIFICATION_NONE:
319       if (remaining_minutes <= kCriticalMinutes) {
320         notification_state_ = NOTIFICATION_CRITICAL;
321         return true;
322       }
323       if (remaining_minutes <= kLowPowerMinutes) {
324         notification_state_ = NOTIFICATION_LOW_POWER;
325         return true;
326       }
327       return false;
328     case NOTIFICATION_LOW_POWER:
329       if (remaining_minutes <= kCriticalMinutes) {
330         notification_state_ = NOTIFICATION_CRITICAL;
331         return true;
332       }
333       return false;
334     case NOTIFICATION_CRITICAL:
335       return false;
336   }
337   NOTREACHED();
338   return false;
339 }
340
341 bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
342   // The notification includes a rounded percentage, so round the value received
343   // from the power manager to match.
344   const int remaining_percentage =
345       PowerStatus::Get()->GetRoundedBatteryPercent();
346
347   if (remaining_percentage >= kNoWarningPercentage ||
348       PowerStatus::Get()->IsBatteryFull()) {
349     notification_state_ = NOTIFICATION_NONE;
350     return false;
351   }
352
353   switch (notification_state_) {
354     case NOTIFICATION_NONE:
355       if (remaining_percentage <= kCriticalPercentage) {
356         notification_state_ = NOTIFICATION_CRITICAL;
357         return true;
358       }
359       if (remaining_percentage <= kLowPowerPercentage) {
360         notification_state_ = NOTIFICATION_LOW_POWER;
361         return true;
362       }
363       return false;
364     case NOTIFICATION_LOW_POWER:
365       if (remaining_percentage <= kCriticalPercentage) {
366         notification_state_ = NOTIFICATION_CRITICAL;
367         return true;
368       }
369       return false;
370     case NOTIFICATION_CRITICAL:
371       return false;
372   }
373   NOTREACHED();
374   return false;
375 }
376
377 void TrayPower::RecordChargerType() {
378   if (!PowerStatus::Get()->IsLinePowerConnected() ||
379       line_power_was_connected_)
380     return;
381
382   ChargerType current_charger = UNKNOWN_CHARGER;
383   if (PowerStatus::Get()->IsMainsChargerConnected()) {
384     current_charger = MAINS_CHARGER;
385   } else if (PowerStatus::Get()->IsUsbChargerConnected()) {
386     current_charger = USB_CHARGER;
387   } else if (PowerStatus::Get()->IsOriginalSpringChargerConnected()) {
388     current_charger =
389         ash::Shell::GetInstance()->system_tray_delegate()->
390             HasUserConfirmedSafeSpringCharger() ?
391         SAFE_SPRING_CHARGER : UNCONFIRMED_SPRING_CHARGER;
392   }
393
394   if (current_charger != UNKNOWN_CHARGER) {
395     UMA_HISTOGRAM_ENUMERATION("Power.ChargerType",
396                               current_charger,
397                               CHARGER_TYPE_COUNT);
398   }
399 }
400
401 }  // namespace internal
402 }  // namespace ash