summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-20 09:19:10 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-20 09:19:10 +0000
commitc5bab73d6fd22ddf7b107f1dc186ff2f381b4977 (patch)
tree8213999dcb29788ce6d92f23976743852938ac1b
parentf8c8b7bd8a25136a7db96f391b912e958796e1a3 (diff)
downloadchromium_src-c5bab73d6fd22ddf7b107f1dc186ff2f381b4977.zip
chromium_src-c5bab73d6fd22ddf7b107f1dc186ff2f381b4977.tar.gz
chromium_src-c5bab73d6fd22ddf7b107f1dc186ff2f381b4977.tar.bz2
cros: Show notification when low-power charger connected
Encourage the user to plug in the official charger. Also extract FakeMessageCenter into a message_center_test_support target so I can use it for a unit test. BUG=249852 TEST=ash_unittests TrayPowerTest.* Review URL: https://chromiumcodereview.appspot.com/17157007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@207374 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/ash.gyp2
-rw-r--r--ash/ash_chromeos_strings.grdp8
-rw-r--r--ash/ash_switches.cc11
-rw-r--r--ash/ash_switches.h7
-rw-r--r--ash/system/chromeos/power/tray_power.cc59
-rw-r--r--ash/system/chromeos/power/tray_power.h42
-rw-r--r--ash/system/chromeos/power/tray_power_unittest.cc152
-rw-r--r--ash/system/tray/system_tray.cc7
-rw-r--r--ui/message_center/fake_message_center.h2
-rw-r--r--ui/message_center/message_center.gyp22
10 files changed, 294 insertions, 18 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index abbda38..7544aa8 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -625,6 +625,7 @@
'../ui/compositor/compositor.gyp:compositor',
'../ui/keyboard/keyboard.gyp:keyboard',
'../ui/message_center/message_center.gyp:message_center',
+ '../ui/message_center/message_center.gyp:message_center_test_support',
'../ui/ui.gyp:ui',
'../ui/ui.gyp:ui_resources',
'../ui/ui.gyp:ui_test_support',
@@ -694,6 +695,7 @@
'shell/window_watcher.cc',
'shell/window_watcher_unittest.cc',
'system/chromeos/network/network_state_notifier_unittest.cc',
+ 'system/chromeos/power/tray_power_unittest.cc',
'system/tray/system_tray_unittest.cc',
'system/user/tray_user_unittest.cc',
'system/web_notification/web_notification_tray_unittest.cc',
diff --git a/ash/ash_chromeos_strings.grdp b/ash/ash_chromeos_strings.grdp
index 1e99643..5ec7442 100644
--- a/ash/ash_chromeos_strings.grdp
+++ b/ash/ash_chromeos_strings.grdp
@@ -3,6 +3,14 @@
Everything in this file is wrapped in <if expr="pp_ifdef('chromeos')">. -->
<grit-part>
+ <!-- Status tray charging strings. -->
+ <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE" desc="The title of a notification indicating that a low-current USB charger has been connected.">
+ Low-power charger connected
+ </message>
+ <message name="IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE" desc="The message body of a notification indicating that a low-current USB charger has been connected.">
+ Your Chromebook may not charge while it is turned on. Consider using the official charger.
+ </message>
+
<!-- Status Tray Network strings -->
<message name="IDS_ASH_STATUS_TRAY_NETWORK" desc="The label used in the network dialog header.">
Network
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index cc64455..7f9b99b 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -63,6 +63,10 @@ const char kAshDisableUIScaling[] = "ash-disable-ui-scaling";
#if defined(OS_CHROMEOS)
// Disable compositor based mirroring.
const char kAshDisableSoftwareMirroring[] = "ash-disable-software-mirroring";
+
+// Disable the notification when a low-power USB charger is connected.
+const char kAshDisableUsbChargerNotification[] =
+ "ash-disable-usb-charger-notification";
#endif
// Extend the status tray volume item to allow the user to choose an audio
@@ -178,5 +182,12 @@ bool UseAlternateShelfLayout() {
HasSwitch(ash::switches::kAshUseAlternateShelfLayout);
}
+#if defined(OS_CHROMEOS)
+bool UseUsbChargerNotification() {
+ return !CommandLine::ForCurrentProcess()->
+ HasSwitch(ash::switches::kAshDisableUsbChargerNotification);
+}
+#endif
+
} // namespace switches
} // namespace ash
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 6414bd9..f94a01f 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -33,6 +33,7 @@ ASH_EXPORT extern const char kAshDisableDisplayRotation[];
ASH_EXPORT extern const char kAshDisableDragAndDropAppListToLauncher[];
#if defined(OS_CHROMEOS)
ASH_EXPORT extern const char kAshDisableSoftwareMirroring[];
+ASH_EXPORT extern const char kAshDisableUsbChargerNotification[];
#endif
ASH_EXPORT extern const char kAshEnableAudioDeviceMenu[];
ASH_EXPORT extern const char kAshEnableAdvancedGestures[];
@@ -69,6 +70,12 @@ ASH_EXPORT bool ShowAudioDeviceMenu();
// Returns true if the alternate shelf layout should be used.
ASH_EXPORT bool UseAlternateShelfLayout();
+#if defined(OS_CHROMEOS)
+// Returns true if a notification should appear when a low-power USB charger
+// is connected.
+ASH_EXPORT bool UseUsbChargerNotification();
+#endif
+
} // namespace switches
} // namespace ash
diff --git a/ash/system/chromeos/power/tray_power.cc b/ash/system/chromeos/power/tray_power.cc
index 9dc87cc..81ec561 100644
--- a/ash/system/chromeos/power/tray_power.cc
+++ b/ash/system/chromeos/power/tray_power.cc
@@ -29,6 +29,8 @@
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/size.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/notification.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
@@ -40,6 +42,8 @@
using chromeos::PowerManagerHandler;
using chromeos::PowerSupplyStatus;
+using message_center::MessageCenter;
+using message_center::Notification;
namespace ash {
namespace internal {
@@ -201,12 +205,15 @@ class PowerNotificationView : public TrayNotificationView {
using tray::PowerNotificationView;
-TrayPower::TrayPower(SystemTray* system_tray)
+TrayPower::TrayPower(SystemTray* system_tray, MessageCenter* message_center)
: SystemTrayItem(system_tray),
+ message_center_(message_center),
power_tray_(NULL),
notification_view_(NULL),
notification_state_(NOTIFICATION_NONE) {
- PowerManagerHandler::Get()->AddObserver(this);
+ // Tests may not have a PowerManagerHandler.
+ if (PowerManagerHandler::IsInitialized())
+ PowerManagerHandler::Get()->AddObserver(this);
}
TrayPower::~TrayPower() {
@@ -274,6 +281,14 @@ gfx::ImageSkia TrayPower::GetBatteryImage(int image_index,
}
// static
+gfx::Image TrayPower::GetUsbChargerNotificationImage() {
+ // TODO(jamescook): Use a specialized art asset here.
+ gfx::ImageSkia icon =
+ GetBatteryImage(kNumPowerImages - 1, 0, true, ICON_LIGHT);
+ return gfx::Image(icon);
+}
+
+// static
base::string16 TrayPower::GetAccessibleNameString(
const chromeos::PowerSupplyStatus& supply_status) {
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
@@ -400,16 +415,53 @@ void TrayPower::OnPowerStatusChanged(
ash::switches::kAshHideNotificationsForFactory))
return;
+ if (ash::switches::UseUsbChargerNotification())
+ MaybeShowUsbChargerNotification(last_power_supply_status_, status);
+
if (battery_alert)
ShowNotificationView();
else if (notification_state_ == NOTIFICATION_NONE)
HideNotificationView();
+
+ last_power_supply_status_ = status;
}
void TrayPower::RequestStatusUpdate() const {
PowerManagerHandler::Get()->RequestStatusUpdate();
}
+bool TrayPower::MaybeShowUsbChargerNotification(
+ const PowerSupplyStatus& old_status,
+ const PowerSupplyStatus& new_status) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ const char kNotificationId[] = "usb-charger";
+ // Check for a USB charger being connected.
+ if (new_status.battery_state == PowerSupplyStatus::CONNECTED_TO_USB &&
+ old_status.battery_state != PowerSupplyStatus::CONNECTED_TO_USB) {
+ scoped_ptr<Notification> notification(new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE,
+ kNotificationId,
+ rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_TITLE),
+ rb.GetLocalizedString(IDS_ASH_STATUS_TRAY_LOW_POWER_CHARGER_MESSAGE),
+ GetUsbChargerNotificationImage(),
+ base::string16(),
+ std::string(),
+ message_center::RichNotificationData(),
+ NULL));
+ message_center_->AddNotification(notification.Pass());
+ return true;
+ }
+
+ // Check for unplug of a USB charger while the USB charger notification is
+ // showing.
+ if (new_status.battery_state != PowerSupplyStatus::CONNECTED_TO_USB &&
+ old_status.battery_state == PowerSupplyStatus::CONNECTED_TO_USB) {
+ message_center_->RemoveNotification(kNotificationId, false);
+ return true;
+ }
+ return false;
+}
+
bool TrayPower::UpdateNotificationState(
const chromeos::PowerSupplyStatus& status) {
if (!status.battery_is_present ||
@@ -439,7 +491,8 @@ bool TrayPower::UpdateNotificationStateForRemainingTime(int remaining_seconds) {
if (remaining_seconds <= kCriticalSeconds) {
notification_state_ = NOTIFICATION_CRITICAL;
return true;
- } else if (remaining_seconds <= kLowPowerSeconds) {
+ }
+ if (remaining_seconds <= kLowPowerSeconds) {
notification_state_ = NOTIFICATION_LOW_POWER;
return true;
}
diff --git a/ash/system/chromeos/power/tray_power.h b/ash/system/chromeos/power/tray_power.h
index db33f35..7e193c3 100644
--- a/ash/system/chromeos/power/tray_power.h
+++ b/ash/system/chromeos/power/tray_power.h
@@ -11,9 +11,14 @@
class SkBitmap;
namespace gfx {
+class Image;
class ImageSkia;
}
+namespace message_center {
+class MessageCenter;
+}
+
namespace ash {
namespace internal {
@@ -27,10 +32,22 @@ enum IconSet {
ICON_DARK
};
-class TrayPower : public SystemTrayItem,
- public chromeos::PowerManagerHandler::Observer {
+class ASH_EXPORT TrayPower : public SystemTrayItem,
+ public chromeos::PowerManagerHandler::Observer {
public:
- explicit TrayPower(SystemTray* system_tray);
+ // Visible for testing.
+ enum NotificationState {
+ NOTIFICATION_NONE,
+
+ // Low battery charge.
+ NOTIFICATION_LOW_POWER,
+
+ // Critically low battery charge.
+ NOTIFICATION_CRITICAL,
+ };
+
+ TrayPower(SystemTray* system_tray,
+ message_center::MessageCenter* message_center);
virtual ~TrayPower();
// Gets whether battery charging is unreliable for |supply_status|.
@@ -58,6 +75,9 @@ class TrayPower : public SystemTrayItem,
bool charging_unreliable,
IconSet icon_set);
+ // Returns an icon for the USB charger notification.
+ static gfx::Image GetUsbChargerNotificationImage();
+
// Gets the battery accessible string for |supply_status|.
static base::string16 GetAccessibleNameString(
const chromeos::PowerSupplyStatus& supply_status);
@@ -66,11 +86,7 @@ class TrayPower : public SystemTrayItem,
static int GetRoundedBatteryPercentage(double battery_percentage);
private:
- enum NotificationState {
- NOTIFICATION_NONE,
- NOTIFICATION_LOW_POWER,
- NOTIFICATION_CRITICAL
- };
+ friend class TrayPowerTest;
// Overridden from SystemTrayItem.
virtual views::View* CreateTrayView(user::LoginStatus status) OVERRIDE;
@@ -91,16 +107,26 @@ class TrayPower : public SystemTrayItem,
// Requests a power status update.
void RequestStatusUpdate() const;
+ // Show a notification that a low-power USB charger has been connected.
+ // Returns true if a notification was shown or explicitly hidden.
+ bool MaybeShowUsbChargerNotification(
+ const chromeos::PowerSupplyStatus& old_status,
+ const chromeos::PowerSupplyStatus& new_status);
+
// Sets |notification_state_|. Returns true if a notification should be shown.
bool UpdateNotificationState(const chromeos::PowerSupplyStatus& status);
bool UpdateNotificationStateForRemainingTime(int remaining_seconds);
bool UpdateNotificationStateForRemainingPercentage(
double remaining_percentage);
+ message_center::MessageCenter* message_center_; // Not owned.
tray::PowerTrayView* power_tray_;
tray::PowerNotificationView* notification_view_;
NotificationState notification_state_;
+ // Power supply status at the last update.
+ chromeos::PowerSupplyStatus last_power_supply_status_;
+
DISALLOW_COPY_AND_ASSIGN(TrayPower);
};
diff --git a/ash/system/chromeos/power/tray_power_unittest.cc b/ash/system/chromeos/power/tray_power_unittest.cc
new file mode 100644
index 0000000..6fcadab
--- /dev/null
+++ b/ash/system/chromeos/power/tray_power_unittest.cc
@@ -0,0 +1,152 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/chromeos/power/tray_power.h"
+
+#include "ash/ash_switches.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/message_center/fake_message_center.h"
+
+using chromeos::PowerSupplyStatus;
+using message_center::Notification;
+
+namespace {
+
+class MockMessageCenter : public message_center::FakeMessageCenter {
+ public:
+ MockMessageCenter() : add_count_(0), remove_count_(0) {}
+ virtual ~MockMessageCenter() {}
+
+ int add_count() const { return add_count_; }
+ int remove_count() const { return remove_count_; }
+
+ // message_center::FakeMessageCenter overrides:
+ virtual void AddNotification(scoped_ptr<Notification> notification) OVERRIDE {
+ add_count_++;
+ }
+ virtual void RemoveNotification(const std::string& id, bool by_user)
+ OVERRIDE {
+ remove_count_++;
+ }
+
+ private:
+ int add_count_;
+ int remove_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
+};
+
+} // namespace
+
+namespace ash {
+namespace internal {
+
+class TrayPowerTest : public testing::Test {
+ public:
+ TrayPowerTest() {}
+ virtual ~TrayPowerTest() {}
+
+ MockMessageCenter* message_center() { return message_center_.get(); }
+ TrayPower* tray_power() { return tray_power_.get(); }
+
+ // testing::Test overrides:
+ virtual void SetUp() OVERRIDE {
+ message_center_.reset(new MockMessageCenter());
+ tray_power_.reset(new TrayPower(NULL, message_center_.get()));
+ }
+
+ TrayPower::NotificationState notification_state() const {
+ return tray_power_->notification_state_;
+ }
+
+ bool MaybeShowUsbChargerNotification(const PowerSupplyStatus& old_status,
+ const PowerSupplyStatus& new_status) {
+ return tray_power_->MaybeShowUsbChargerNotification(old_status, new_status);
+ }
+
+ bool UpdateNotificationState(const PowerSupplyStatus& status) {
+ return tray_power_->UpdateNotificationState(status);
+ }
+
+ void SetLastPowerStatus(const PowerSupplyStatus& status) {
+ tray_power_->last_power_supply_status_ = status;
+ }
+
+ // Returns a discharging PowerSupplyStatus more appropriate for testing.
+ static PowerSupplyStatus DefaultPowerSupplyStatus() {
+ PowerSupplyStatus status;
+ status.line_power_on = false;
+ status.battery_is_present = true;
+ status.battery_is_full = false;
+ status.battery_seconds_to_empty = 3 * 60 * 60;
+ status.battery_seconds_to_full = 2 * 60 * 60;
+ status.battery_percentage = 50.0;
+ status.is_calculating_battery_time = false;
+ status.battery_state = PowerSupplyStatus::DISCHARGING;
+ return status;
+ }
+
+ private:
+ scoped_ptr<MockMessageCenter> message_center_;
+ scoped_ptr<TrayPower> tray_power_;
+
+ DISALLOW_COPY_AND_ASSIGN(TrayPowerTest);
+};
+
+TEST_F(TrayPowerTest, MaybeShowUsbChargerNotification) {
+ // Notification shows when connecting a USB charger.
+ PowerSupplyStatus discharging = DefaultPowerSupplyStatus();
+ PowerSupplyStatus usb_connected = DefaultPowerSupplyStatus();
+ usb_connected.line_power_on = true;
+ usb_connected.battery_state = PowerSupplyStatus::CONNECTED_TO_USB;
+ EXPECT_TRUE(MaybeShowUsbChargerNotification(discharging, usb_connected));
+ EXPECT_EQ(1, message_center()->add_count());
+
+ // Change in charge does not trigger the notification again.
+ PowerSupplyStatus more_charge = DefaultPowerSupplyStatus();
+ more_charge.line_power_on = true;
+ more_charge.battery_seconds_to_full = 60 * 60;
+ more_charge.battery_percentage = 75.0;
+ more_charge.battery_state = PowerSupplyStatus::CONNECTED_TO_USB;
+ EXPECT_FALSE(MaybeShowUsbChargerNotification(usb_connected, more_charge));
+ EXPECT_EQ(1, message_center()->add_count());
+ EXPECT_EQ(0, message_center()->remove_count());
+
+ // Disconnecting a USB charger with the notification showing should close
+ // the notification.
+ EXPECT_TRUE(MaybeShowUsbChargerNotification(usb_connected, discharging));
+ EXPECT_EQ(1, message_center()->remove_count());
+}
+
+TEST_F(TrayPowerTest, UpdateNotificationState) {
+ // No notifications when no battery present.
+ PowerSupplyStatus no_battery = DefaultPowerSupplyStatus();
+ no_battery.battery_is_present = false;
+ EXPECT_FALSE(UpdateNotificationState(no_battery));
+ EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+ // No notification when calculating remaining battery time.
+ PowerSupplyStatus calculating = DefaultPowerSupplyStatus();
+ calculating.is_calculating_battery_time = true;
+ EXPECT_FALSE(UpdateNotificationState(calculating));
+ EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+ // No notification when charging.
+ PowerSupplyStatus charging = DefaultPowerSupplyStatus();
+ charging.line_power_on = true;
+ charging.battery_state = PowerSupplyStatus::CHARGING;
+ EXPECT_FALSE(UpdateNotificationState(charging));
+ EXPECT_EQ(TrayPower::NOTIFICATION_NONE, notification_state());
+
+ // Critical low battery notification.
+ PowerSupplyStatus critical = DefaultPowerSupplyStatus();
+ critical.battery_seconds_to_empty = 60;
+ critical.battery_percentage = 2.0;
+ EXPECT_TRUE(UpdateNotificationState(critical));
+ EXPECT_EQ(TrayPower::NOTIFICATION_CRITICAL, notification_state());
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index eb183ec..0117c6c 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -58,6 +58,7 @@
#include "ash/system/chromeos/screen_security/screen_share_tray_item.h"
#include "ash/system/chromeos/settings/tray_settings.h"
#include "ash/system/chromeos/tray_display.h"
+#include "ui/message_center/message_center.h"
#endif
using views::TrayBubbleView;
@@ -120,7 +121,8 @@ SystemTray::SystemTray(internal::StatusAreaWidget* status_area_widget)
: internal::TrayBackgroundView(status_area_widget),
items_(),
default_bubble_height_(0),
- hide_notifications_(false) {
+ hide_notifications_(false),
+ tray_accessibility_(NULL) {
SetContentsBackground();
}
@@ -164,7 +166,8 @@ void SystemTray::CreateItems(SystemTrayDelegate* delegate) {
tray_accessibility_ = new internal::TrayAccessibility(this);
AddTrayItem(tray_accessibility_);
#if defined(OS_CHROMEOS)
- AddTrayItem(new internal::TrayPower(this));
+ AddTrayItem(
+ new internal::TrayPower(this, message_center::MessageCenter::Get()));
#endif
#if defined(OS_CHROMEOS)
AddTrayItem(new internal::TrayNetwork(this));
diff --git a/ui/message_center/fake_message_center.h b/ui/message_center/fake_message_center.h
index 19ced60..d118ee7 100644
--- a/ui/message_center/fake_message_center.h
+++ b/ui/message_center/fake_message_center.h
@@ -12,7 +12,7 @@ namespace message_center {
class NotificationDelegate;
// MessageCenter implementation of doing nothing. Useful for tests.
-class FakeMessageCenter : public MessageCenter {
+class MESSAGE_CENTER_EXPORT FakeMessageCenter : public MessageCenter {
public:
FakeMessageCenter();
virtual ~FakeMessageCenter();
diff --git a/ui/message_center/message_center.gyp b/ui/message_center/message_center.gyp
index ee469d2..ef5a0cc 100644
--- a/ui/message_center/message_center.gyp
+++ b/ui/message_center/message_center.gyp
@@ -125,6 +125,23 @@
],
}, # target_name: message_center
{
+ 'target_name': 'message_center_test_support',
+ 'type': 'static_library',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ '../../base/base.gyp:test_support_base',
+ '../../skia/skia.gyp:skia',
+ '../ui.gyp:ui',
+ 'message_center',
+ ],
+ 'sources': [
+ 'fake_message_center.h',
+ 'fake_message_center.cc',
+ 'fake_notifier_settings_provider.h',
+ 'fake_notifier_settings_provider.cc',
+ ],
+ }, # target_name: message_center_test_support
+ {
'target_name': 'message_center_unittests',
'type': 'executable',
'dependencies': [
@@ -135,6 +152,7 @@
'../ui.gyp:run_ui_unittests',
'../ui.gyp:ui',
'message_center',
+ 'message_center_test_support',
],
'sources': [
'cocoa/notification_controller_unittest.mm',
@@ -146,10 +164,6 @@
'cocoa/tray_view_controller_unittest.mm',
'message_center_tray_unittest.cc',
'message_center_impl_unittest.cc',
- 'fake_message_center.h',
- 'fake_message_center.cc',
- 'fake_notifier_settings_provider.h',
- 'fake_notifier_settings_provider.cc',
'notification_list_unittest.cc',
'test/run_all_unittests.cc',
],