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