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