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