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