123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- // Copyright (c) 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/power/peripheral_battery_notifier.h"
- #include <string>
- #include <vector>
- #include "ash/constants/ash_features.h"
- #include "ash/constants/notifier_catalogs.h"
- #include "ash/power/hid_battery_util.h"
- #include "ash/public/cpp/notification_utils.h"
- #include "ash/public/cpp/system_tray_client.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/system/model/system_tray_model.h"
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/time/time.h"
- #include "device/bluetooth/bluetooth_adapter_factory.h"
- #include "device/bluetooth/bluetooth_device.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/base/resource/resource_bundle.h"
- #include "ui/events/devices/device_data_manager.h"
- #include "ui/events/devices/touchscreen_device.h"
- #include "ui/gfx/image/image.h"
- #include "ui/message_center/message_center.h"
- #include "ui/message_center/public/cpp/notification.h"
- #include "ui/message_center/public/cpp/notification_delegate.h"
- namespace ash {
- namespace {
- // When a peripheral device's battery level is <= kLowBatteryLevel, consider
- // it to be in low battery condition.
- const uint8_t kLowBatteryLevel = 16;
- // Don't show 2 low battery notification within |kNotificationInterval|.
- constexpr base::TimeDelta kNotificationInterval = base::Seconds(60);
- constexpr char kNotifierStylusBattery[] = "ash.stylus-battery";
- constexpr char kNotificationOriginUrl[] = "chrome://peripheral-battery";
- constexpr char kNotifierNonStylusBattery[] = "power.peripheral-battery";
- // Prefix added to the key of a device to generate a unique ID when posting
- // a notification to the Message Center.
- constexpr char kPeripheralDeviceIdPrefix[] = "battery_notification-";
- // Struct containing parameters for the notification which vary between the
- // stylus notifications and the non stylus notifications.
- struct NotificationParams {
- std::string id;
- std::u16string title;
- std::u16string message;
- std::string notifier_name;
- GURL url;
- const gfx::VectorIcon* icon;
- };
- NotificationParams GetNonStylusNotificationParams(const std::string& map_key,
- const std::u16string& name,
- uint8_t battery_level,
- bool is_bluetooth) {
- return NotificationParams{
- kPeripheralDeviceIdPrefix + map_key,
- name,
- l10n_util::GetStringFUTF16Int(
- IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT, battery_level),
- kNotifierNonStylusBattery,
- GURL(kNotificationOriginUrl),
- is_bluetooth ? &kNotificationBluetoothBatteryWarningIcon
- : &kNotificationBatteryCriticalIcon};
- }
- NotificationParams GetStylusNotificationParams() {
- return NotificationParams{
- PeripheralBatteryNotifier::kStylusNotificationId,
- l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_TITLE),
- l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_BODY),
- kNotifierStylusBattery,
- GURL(),
- &kNotificationStylusBatteryWarningIcon};
- }
- } // namespace
- const char PeripheralBatteryNotifier::kStylusNotificationId[] =
- "stylus-battery";
- PeripheralBatteryNotifier::NotificationInfo::NotificationInfo() = default;
- PeripheralBatteryNotifier::NotificationInfo::NotificationInfo(
- absl::optional<uint8_t> level,
- base::TimeTicks last_notification_timestamp)
- : level(level),
- last_notification_timestamp(last_notification_timestamp),
- ever_notified(false) {}
- PeripheralBatteryNotifier::NotificationInfo::~NotificationInfo() = default;
- PeripheralBatteryNotifier::NotificationInfo::NotificationInfo(
- const NotificationInfo& info) {
- level = info.level;
- last_notification_timestamp = info.last_notification_timestamp;
- ever_notified = info.ever_notified;
- }
- PeripheralBatteryNotifier::PeripheralBatteryNotifier(
- PeripheralBatteryListener* listener)
- : peripheral_battery_listener_(listener) {
- peripheral_battery_listener_->AddObserver(this);
- }
- PeripheralBatteryNotifier::~PeripheralBatteryNotifier() {
- peripheral_battery_listener_->RemoveObserver(this);
- }
- void PeripheralBatteryNotifier::OnUpdatedBatteryLevel(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- if ((battery_info.type == PeripheralBatteryListener::BatteryInfo::
- PeripheralType::kStylusViaCharger ||
- battery_info.type == PeripheralBatteryListener::BatteryInfo::
- PeripheralType::kStylusViaScreen) &&
- !ash::features::IsStylusBatteryStatusEnabled()) {
- return;
- }
- // TODO(b/187703348): it is worth listening to charger events if they
- // might remove the notification: we want to clear it as soon as
- // we believe the battery to have been charged, or at least starting
- // charging.
- if (battery_info.type == PeripheralBatteryListener::BatteryInfo::
- PeripheralType::kStylusViaCharger) {
- return;
- }
- UpdateBattery(battery_info);
- }
- void PeripheralBatteryNotifier::OnAddingBattery(
- const PeripheralBatteryListener::BatteryInfo& battery) {}
- void PeripheralBatteryNotifier::OnRemovingBattery(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- const std::string& map_key = battery_info.key;
- CancelNotification(battery_info);
- battery_notifications_.erase(map_key);
- }
- void PeripheralBatteryNotifier::UpdateBattery(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- if (!battery_info.level || !battery_info.battery_report_eligible) {
- CancelNotification(battery_info);
- return;
- }
- const std::string& map_key = battery_info.key;
- bool was_old_battery_level_low = false;
- auto it = battery_notifications_.find(map_key);
- if (it == battery_notifications_.end()) {
- NotificationInfo new_notification_info;
- new_notification_info.level = battery_info.level;
- new_notification_info.last_notification_timestamp =
- battery_info.last_update_timestamp;
- new_notification_info.ever_notified = false;
- battery_notifications_[map_key] = new_notification_info;
- } else {
- NotificationInfo& existing_notification_info = it->second;
- absl::optional<uint8_t> old_level = existing_notification_info.level;
- was_old_battery_level_low = old_level && *old_level <= kLowBatteryLevel;
- existing_notification_info.level = battery_info.level;
- }
- if (*battery_info.level > kLowBatteryLevel) {
- CancelNotification(battery_info);
- return;
- }
- // If low battery was on record, check if there is a notification, otherwise
- // the user dismissed it and we shouldn't create another one.
- if (was_old_battery_level_low)
- UpdateBatteryNotificationIfVisible(battery_info);
- else
- ShowNotification(battery_info);
- }
- void PeripheralBatteryNotifier::UpdateBatteryNotificationIfVisible(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- const std::string& map_key = battery_info.key;
- std::string notification_map_key =
- (battery_info.type ==
- PeripheralBatteryListener::BatteryInfo::PeripheralType::kStylusViaScreen)
- ? kStylusNotificationId
- : (kPeripheralDeviceIdPrefix + map_key);
- message_center::Notification* notification =
- message_center::MessageCenter::Get()->FindVisibleNotificationById(
- notification_map_key);
- if (notification)
- ShowOrUpdateNotification(battery_info);
- }
- void PeripheralBatteryNotifier::ShowNotification(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- const std::string& map_key = battery_info.key;
- NotificationInfo& notification_info = battery_notifications_[map_key];
- base::TimeTicks now = base::TimeTicks::Now();
- if (!notification_info.ever_notified ||
- (now - notification_info.last_notification_timestamp >=
- kNotificationInterval)) {
- ShowOrUpdateNotification(battery_info);
- notification_info.last_notification_timestamp = base::TimeTicks::Now();
- notification_info.ever_notified = true;
- }
- }
- void PeripheralBatteryNotifier::ShowOrUpdateNotification(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- const std::string& map_key = battery_info.key;
- // Stylus battery notifications differ slightly.
- NotificationParams params =
- (battery_info.type ==
- PeripheralBatteryListener::BatteryInfo::PeripheralType::kStylusViaScreen)
- ? GetStylusNotificationParams()
- : GetNonStylusNotificationParams(
- map_key, battery_info.name, *battery_info.level,
- !battery_info.bluetooth_address.empty());
- auto delegate = base::MakeRefCounted<
- message_center::HandleNotificationClickDelegate>(base::BindRepeating(
- [](const NotificationParams& params) {
- Shell::Get()->system_tray_model()->client()->ShowBluetoothSettings();
- message_center::MessageCenter::Get()->RemoveNotification(
- params.id, /*by_user=*/false);
- },
- params));
- auto notification = CreateSystemNotification(
- message_center::NOTIFICATION_TYPE_SIMPLE, params.id, params.title,
- params.message, std::u16string(), params.url,
- message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
- params.notifier_name,
- NotificationCatalogName::kPeripheralBattery),
- message_center::RichNotificationData(), std::move(delegate), *params.icon,
- message_center::SystemNotificationWarningLevel::WARNING);
- message_center::MessageCenter::Get()->AddNotification(
- std::move(notification));
- }
- void PeripheralBatteryNotifier::CancelNotification(
- const PeripheralBatteryListener::BatteryInfo& battery_info) {
- const std::string& map_key = battery_info.key;
- const auto it = battery_notifications_.find(map_key);
- if (it != battery_notifications_.end()) {
- std::string notification_map_key =
- (battery_info.type == PeripheralBatteryListener::BatteryInfo::
- PeripheralType::kStylusViaScreen)
- ? kStylusNotificationId
- : (kPeripheralDeviceIdPrefix + map_key);
- message_center::MessageCenter::Get()->RemoveNotification(
- notification_map_key, /*by_user=*/false);
- // Resetting this value allows a new low battery level to post a
- // notification if the old one was also under the threshold.
- it->second.level.reset();
- }
- }
- } // namespace ash
|