peripheral_battery_notifier.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. // Copyright (c) 2013 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. #include "ash/system/power/peripheral_battery_notifier.h"
  5. #include <string>
  6. #include <vector>
  7. #include "ash/constants/ash_features.h"
  8. #include "ash/constants/notifier_catalogs.h"
  9. #include "ash/power/hid_battery_util.h"
  10. #include "ash/public/cpp/notification_utils.h"
  11. #include "ash/public/cpp/system_tray_client.h"
  12. #include "ash/resources/vector_icons/vector_icons.h"
  13. #include "ash/shell.h"
  14. #include "ash/strings/grit/ash_strings.h"
  15. #include "ash/system/model/system_tray_model.h"
  16. #include "base/bind.h"
  17. #include "base/logging.h"
  18. #include "base/strings/string_piece.h"
  19. #include "base/strings/utf_string_conversions.h"
  20. #include "base/time/time.h"
  21. #include "device/bluetooth/bluetooth_adapter_factory.h"
  22. #include "device/bluetooth/bluetooth_device.h"
  23. #include "ui/base/l10n/l10n_util.h"
  24. #include "ui/base/resource/resource_bundle.h"
  25. #include "ui/events/devices/device_data_manager.h"
  26. #include "ui/events/devices/touchscreen_device.h"
  27. #include "ui/gfx/image/image.h"
  28. #include "ui/message_center/message_center.h"
  29. #include "ui/message_center/public/cpp/notification.h"
  30. #include "ui/message_center/public/cpp/notification_delegate.h"
  31. namespace ash {
  32. namespace {
  33. // When a peripheral device's battery level is <= kLowBatteryLevel, consider
  34. // it to be in low battery condition.
  35. const uint8_t kLowBatteryLevel = 16;
  36. // Don't show 2 low battery notification within |kNotificationInterval|.
  37. constexpr base::TimeDelta kNotificationInterval = base::Seconds(60);
  38. constexpr char kNotifierStylusBattery[] = "ash.stylus-battery";
  39. constexpr char kNotificationOriginUrl[] = "chrome://peripheral-battery";
  40. constexpr char kNotifierNonStylusBattery[] = "power.peripheral-battery";
  41. // Prefix added to the key of a device to generate a unique ID when posting
  42. // a notification to the Message Center.
  43. constexpr char kPeripheralDeviceIdPrefix[] = "battery_notification-";
  44. // Struct containing parameters for the notification which vary between the
  45. // stylus notifications and the non stylus notifications.
  46. struct NotificationParams {
  47. std::string id;
  48. std::u16string title;
  49. std::u16string message;
  50. std::string notifier_name;
  51. GURL url;
  52. const gfx::VectorIcon* icon;
  53. };
  54. NotificationParams GetNonStylusNotificationParams(const std::string& map_key,
  55. const std::u16string& name,
  56. uint8_t battery_level,
  57. bool is_bluetooth) {
  58. return NotificationParams{
  59. kPeripheralDeviceIdPrefix + map_key,
  60. name,
  61. l10n_util::GetStringFUTF16Int(
  62. IDS_ASH_LOW_PERIPHERAL_BATTERY_NOTIFICATION_TEXT, battery_level),
  63. kNotifierNonStylusBattery,
  64. GURL(kNotificationOriginUrl),
  65. is_bluetooth ? &kNotificationBluetoothBatteryWarningIcon
  66. : &kNotificationBatteryCriticalIcon};
  67. }
  68. NotificationParams GetStylusNotificationParams() {
  69. return NotificationParams{
  70. PeripheralBatteryNotifier::kStylusNotificationId,
  71. l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_TITLE),
  72. l10n_util::GetStringUTF16(IDS_ASH_LOW_STYLUS_BATTERY_NOTIFICATION_BODY),
  73. kNotifierStylusBattery,
  74. GURL(),
  75. &kNotificationStylusBatteryWarningIcon};
  76. }
  77. } // namespace
  78. const char PeripheralBatteryNotifier::kStylusNotificationId[] =
  79. "stylus-battery";
  80. PeripheralBatteryNotifier::NotificationInfo::NotificationInfo() = default;
  81. PeripheralBatteryNotifier::NotificationInfo::NotificationInfo(
  82. absl::optional<uint8_t> level,
  83. base::TimeTicks last_notification_timestamp)
  84. : level(level),
  85. last_notification_timestamp(last_notification_timestamp),
  86. ever_notified(false) {}
  87. PeripheralBatteryNotifier::NotificationInfo::~NotificationInfo() = default;
  88. PeripheralBatteryNotifier::NotificationInfo::NotificationInfo(
  89. const NotificationInfo& info) {
  90. level = info.level;
  91. last_notification_timestamp = info.last_notification_timestamp;
  92. ever_notified = info.ever_notified;
  93. }
  94. PeripheralBatteryNotifier::PeripheralBatteryNotifier(
  95. PeripheralBatteryListener* listener)
  96. : peripheral_battery_listener_(listener) {
  97. peripheral_battery_listener_->AddObserver(this);
  98. }
  99. PeripheralBatteryNotifier::~PeripheralBatteryNotifier() {
  100. peripheral_battery_listener_->RemoveObserver(this);
  101. }
  102. void PeripheralBatteryNotifier::OnUpdatedBatteryLevel(
  103. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  104. if ((battery_info.type == PeripheralBatteryListener::BatteryInfo::
  105. PeripheralType::kStylusViaCharger ||
  106. battery_info.type == PeripheralBatteryListener::BatteryInfo::
  107. PeripheralType::kStylusViaScreen) &&
  108. !ash::features::IsStylusBatteryStatusEnabled()) {
  109. return;
  110. }
  111. // TODO(b/187703348): it is worth listening to charger events if they
  112. // might remove the notification: we want to clear it as soon as
  113. // we believe the battery to have been charged, or at least starting
  114. // charging.
  115. if (battery_info.type == PeripheralBatteryListener::BatteryInfo::
  116. PeripheralType::kStylusViaCharger) {
  117. return;
  118. }
  119. UpdateBattery(battery_info);
  120. }
  121. void PeripheralBatteryNotifier::OnAddingBattery(
  122. const PeripheralBatteryListener::BatteryInfo& battery) {}
  123. void PeripheralBatteryNotifier::OnRemovingBattery(
  124. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  125. const std::string& map_key = battery_info.key;
  126. CancelNotification(battery_info);
  127. battery_notifications_.erase(map_key);
  128. }
  129. void PeripheralBatteryNotifier::UpdateBattery(
  130. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  131. if (!battery_info.level || !battery_info.battery_report_eligible) {
  132. CancelNotification(battery_info);
  133. return;
  134. }
  135. const std::string& map_key = battery_info.key;
  136. bool was_old_battery_level_low = false;
  137. auto it = battery_notifications_.find(map_key);
  138. if (it == battery_notifications_.end()) {
  139. NotificationInfo new_notification_info;
  140. new_notification_info.level = battery_info.level;
  141. new_notification_info.last_notification_timestamp =
  142. battery_info.last_update_timestamp;
  143. new_notification_info.ever_notified = false;
  144. battery_notifications_[map_key] = new_notification_info;
  145. } else {
  146. NotificationInfo& existing_notification_info = it->second;
  147. absl::optional<uint8_t> old_level = existing_notification_info.level;
  148. was_old_battery_level_low = old_level && *old_level <= kLowBatteryLevel;
  149. existing_notification_info.level = battery_info.level;
  150. }
  151. if (*battery_info.level > kLowBatteryLevel) {
  152. CancelNotification(battery_info);
  153. return;
  154. }
  155. // If low battery was on record, check if there is a notification, otherwise
  156. // the user dismissed it and we shouldn't create another one.
  157. if (was_old_battery_level_low)
  158. UpdateBatteryNotificationIfVisible(battery_info);
  159. else
  160. ShowNotification(battery_info);
  161. }
  162. void PeripheralBatteryNotifier::UpdateBatteryNotificationIfVisible(
  163. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  164. const std::string& map_key = battery_info.key;
  165. std::string notification_map_key =
  166. (battery_info.type ==
  167. PeripheralBatteryListener::BatteryInfo::PeripheralType::kStylusViaScreen)
  168. ? kStylusNotificationId
  169. : (kPeripheralDeviceIdPrefix + map_key);
  170. message_center::Notification* notification =
  171. message_center::MessageCenter::Get()->FindVisibleNotificationById(
  172. notification_map_key);
  173. if (notification)
  174. ShowOrUpdateNotification(battery_info);
  175. }
  176. void PeripheralBatteryNotifier::ShowNotification(
  177. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  178. const std::string& map_key = battery_info.key;
  179. NotificationInfo& notification_info = battery_notifications_[map_key];
  180. base::TimeTicks now = base::TimeTicks::Now();
  181. if (!notification_info.ever_notified ||
  182. (now - notification_info.last_notification_timestamp >=
  183. kNotificationInterval)) {
  184. ShowOrUpdateNotification(battery_info);
  185. notification_info.last_notification_timestamp = base::TimeTicks::Now();
  186. notification_info.ever_notified = true;
  187. }
  188. }
  189. void PeripheralBatteryNotifier::ShowOrUpdateNotification(
  190. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  191. const std::string& map_key = battery_info.key;
  192. // Stylus battery notifications differ slightly.
  193. NotificationParams params =
  194. (battery_info.type ==
  195. PeripheralBatteryListener::BatteryInfo::PeripheralType::kStylusViaScreen)
  196. ? GetStylusNotificationParams()
  197. : GetNonStylusNotificationParams(
  198. map_key, battery_info.name, *battery_info.level,
  199. !battery_info.bluetooth_address.empty());
  200. auto delegate = base::MakeRefCounted<
  201. message_center::HandleNotificationClickDelegate>(base::BindRepeating(
  202. [](const NotificationParams& params) {
  203. Shell::Get()->system_tray_model()->client()->ShowBluetoothSettings();
  204. message_center::MessageCenter::Get()->RemoveNotification(
  205. params.id, /*by_user=*/false);
  206. },
  207. params));
  208. auto notification = CreateSystemNotification(
  209. message_center::NOTIFICATION_TYPE_SIMPLE, params.id, params.title,
  210. params.message, std::u16string(), params.url,
  211. message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
  212. params.notifier_name,
  213. NotificationCatalogName::kPeripheralBattery),
  214. message_center::RichNotificationData(), std::move(delegate), *params.icon,
  215. message_center::SystemNotificationWarningLevel::WARNING);
  216. message_center::MessageCenter::Get()->AddNotification(
  217. std::move(notification));
  218. }
  219. void PeripheralBatteryNotifier::CancelNotification(
  220. const PeripheralBatteryListener::BatteryInfo& battery_info) {
  221. const std::string& map_key = battery_info.key;
  222. const auto it = battery_notifications_.find(map_key);
  223. if (it != battery_notifications_.end()) {
  224. std::string notification_map_key =
  225. (battery_info.type == PeripheralBatteryListener::BatteryInfo::
  226. PeripheralType::kStylusViaScreen)
  227. ? kStylusNotificationId
  228. : (kPeripheralDeviceIdPrefix + map_key);
  229. message_center::MessageCenter::Get()->RemoveNotification(
  230. notification_map_key, /*by_user=*/false);
  231. // Resetting this value allows a new low battery level to post a
  232. // notification if the old one was also under the threshold.
  233. it->second.level.reset();
  234. }
  235. }
  236. } // namespace ash