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