tray_power.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/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"
31
32using message_center::MessageCenter;
33using message_center::Notification;
34
35namespace ash {
36namespace internal {
37
38namespace {
39// Notification times.
40const int kCriticalSeconds = 5 * 60;
41const int kLowPowerSeconds = 15 * 60;
42const int kNoWarningSeconds = 30 * 60;
43// Notification in battery percentage.
44const double kCriticalPercentage = 5.0;
45const double kLowPowerPercentage = 10.0;
46const double kNoWarningPercentage = 15.0;
47
48}  // namespace
49
50namespace tray {
51
52// This view is used only for the tray.
53class PowerTrayView : public views::ImageView {
54 public:
55  PowerTrayView() {
56    UpdateImage();
57  }
58
59  virtual ~PowerTrayView() {
60  }
61
62  // Overriden from views::View.
63  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE {
64    state->name = accessible_name_;
65    state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
66  }
67
68  void UpdateStatus(bool battery_alert) {
69    UpdateImage();
70    SetVisible(PowerStatus::Get()->IsBatteryPresent());
71
72    if (battery_alert) {
73      accessible_name_ = PowerStatus::Get()->GetAccessibleNameString();
74      NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true);
75    }
76  }
77
78 private:
79  void UpdateImage() {
80    SetImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_LIGHT));
81  }
82
83  base::string16 accessible_name_;
84
85  DISALLOW_COPY_AND_ASSIGN(PowerTrayView);
86};
87
88class PowerNotificationView : public TrayNotificationView {
89 public:
90  explicit PowerNotificationView(TrayPower* owner)
91      : TrayNotificationView(owner, 0) {
92    power_status_view_ =
93        new PowerStatusView(PowerStatusView::VIEW_NOTIFICATION, true);
94    InitView(power_status_view_);
95  }
96
97  void UpdateStatus() {
98    SetIconImage(PowerStatus::Get()->GetBatteryImage(PowerStatus::ICON_DARK));
99  }
100
101 private:
102  PowerStatusView* power_status_view_;
103
104  DISALLOW_COPY_AND_ASSIGN(PowerNotificationView);
105};
106
107}  // namespace tray
108
109using tray::PowerNotificationView;
110
111TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
112    : SystemTrayItem(system_tray),
113      message_center_(message_center),
114      power_tray_(NULL),
115      notification_view_(NULL),
116      notification_state_(NOTIFICATION_NONE),
117      usb_charger_was_connected_(false) {
118  PowerStatus::Get()->AddObserver(this);
119}
120
121TrayPower::~TrayPower() {
122  PowerStatus::Get()->RemoveObserver(this);
123}
124
125views::View* TrayPower::CreateTrayView(user::LoginStatus status) {
126  // There may not be enough information when this is created about whether
127  // there is a battery or not. So always create this, and adjust visibility as
128  // necessary.
129  CHECK(power_tray_ == NULL);
130  power_tray_ = new tray::PowerTrayView();
131  power_tray_->UpdateStatus(false);
132  return power_tray_;
133}
134
135views::View* TrayPower::CreateDefaultView(user::LoginStatus status) {
136  // Make sure icon status is up-to-date. (Also triggers stub activation).
137  PowerStatus::Get()->RequestStatusUpdate();
138  return NULL;
139}
140
141views::View* TrayPower::CreateNotificationView(user::LoginStatus status) {
142  CHECK(notification_view_ == NULL);
143  if (!PowerStatus::Get()->IsBatteryPresent())
144    return NULL;
145
146  notification_view_ = new PowerNotificationView(this);
147  notification_view_->UpdateStatus();
148
149  return notification_view_;
150}
151
152void TrayPower::DestroyTrayView() {
153  power_tray_ = NULL;
154}
155
156void TrayPower::DestroyDefaultView() {
157}
158
159void TrayPower::DestroyNotificationView() {
160  notification_view_ = NULL;
161}
162
163void TrayPower::UpdateAfterLoginStatusChange(user::LoginStatus status) {
164}
165
166void TrayPower::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
167  SetTrayImageItemBorder(power_tray_, alignment);
168}
169
170void TrayPower::OnPowerStatusChanged() {
171  bool battery_alert = UpdateNotificationState();
172  if (power_tray_)
173    power_tray_->UpdateStatus(battery_alert);
174  if (notification_view_)
175    notification_view_->UpdateStatus();
176
177  // Factory testing may place the battery into unusual states.
178  if (CommandLine::ForCurrentProcess()->HasSwitch(
179          ash::switches::kAshHideNotificationsForFactory))
180    return;
181
182  if (ash::switches::UseUsbChargerNotification())
183    MaybeShowUsbChargerNotification();
184
185  if (battery_alert)
186    ShowNotificationView();
187  else if (notification_state_ == NOTIFICATION_NONE)
188    HideNotificationView();
189
190  usb_charger_was_connected_ = PowerStatus::Get()->IsUsbChargerConnected();
191}
192
193bool TrayPower::MaybeShowUsbChargerNotification() {
194  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
195  const char kNotificationId[] = "usb-charger";
196  bool usb_charger_is_connected = PowerStatus::Get()->IsUsbChargerConnected();
197
198  // Check for a USB charger being connected.
199  if (usb_charger_is_connected && !usb_charger_was_connected_) {
200    scoped_ptr<Notification> notification(new Notification(
201        message_center::NOTIFICATION_TYPE_SIMPLE,
202        kNotificationId,
203        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
204        rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE),
205        rb.GetImageNamed(IDR_AURA_NOTIFICATION_LOW_POWER_CHARGER),
206        base::string16(),
207        message_center::NotifierId(system_notifier::NOTIFIER_POWER),
208        message_center::RichNotificationData(),
209        NULL));
210    message_center_->AddNotification(notification.Pass());
211    return true;
212  }
213
214  // Check for unplug of a USB charger while the USB charger notification is
215  // showing.
216  if (!usb_charger_is_connected && usb_charger_was_connected_) {
217    message_center_->RemoveNotification(kNotificationId, false);
218    return true;
219  }
220  return false;
221}
222
223bool TrayPower::UpdateNotificationState() {
224  const PowerStatus& status = *PowerStatus::Get();
225  if (!status.IsBatteryPresent() ||
226      status.IsBatteryTimeBeingCalculated() ||
227      status.IsMainsChargerConnected()) {
228    notification_state_ = NOTIFICATION_NONE;
229    return false;
230  }
231
232  return status.IsUsbChargerConnected() ?
233      UpdateNotificationStateForRemainingPercentage() :
234      UpdateNotificationStateForRemainingTime();
235}
236
237bool TrayPower::UpdateNotificationStateForRemainingTime() {
238  const int remaining_seconds =
239      PowerStatus::Get()->GetBatteryTimeToEmpty().InSeconds();
240
241  if (remaining_seconds >= kNoWarningSeconds) {
242    notification_state_ = NOTIFICATION_NONE;
243    return false;
244  }
245
246  switch (notification_state_) {
247    case NOTIFICATION_NONE:
248      if (remaining_seconds <= kCriticalSeconds) {
249        notification_state_ = NOTIFICATION_CRITICAL;
250        return true;
251      }
252      if (remaining_seconds <= kLowPowerSeconds) {
253        notification_state_ = NOTIFICATION_LOW_POWER;
254        return true;
255      }
256      return false;
257    case NOTIFICATION_LOW_POWER:
258      if (remaining_seconds <= kCriticalSeconds) {
259        notification_state_ = NOTIFICATION_CRITICAL;
260        return true;
261      }
262      return false;
263    case NOTIFICATION_CRITICAL:
264      return false;
265  }
266  NOTREACHED();
267  return false;
268}
269
270bool TrayPower::UpdateNotificationStateForRemainingPercentage() {
271  const double remaining_percentage = PowerStatus::Get()->GetBatteryPercent();
272
273  if (remaining_percentage > kNoWarningPercentage) {
274    notification_state_ = NOTIFICATION_NONE;
275    return false;
276  }
277
278  switch (notification_state_) {
279    case NOTIFICATION_NONE:
280      if (remaining_percentage <= kCriticalPercentage) {
281        notification_state_ = NOTIFICATION_CRITICAL;
282        return true;
283      }
284      if (remaining_percentage <= kLowPowerPercentage) {
285        notification_state_ = NOTIFICATION_LOW_POWER;
286        return true;
287      }
288      return false;
289    case NOTIFICATION_LOW_POWER:
290      if (remaining_percentage <= kCriticalPercentage) {
291        notification_state_ = NOTIFICATION_CRITICAL;
292        return true;
293      }
294      return false;
295    case NOTIFICATION_CRITICAL:
296      return false;
297  }
298  NOTREACHED();
299  return false;
300}
301
302}  // namespace internal
303}  // namespace ash
304