0

[Adaptive-Charging] Add notifications for Adaptive Charging

Adds a controller that show notification for adaptive charging.
The actually use of this controller will be added in following CL(s).

Screenshots of the strings added in this CL are here:
go/strpic/2a8b80e5f34c93fd91001855358d84f2a16802d4
go/strpic/d08545ab42b37cbf26202964438abbd2435b188f


BUG=b:216035329

Change-Id: I38cc9381d73579f744f88e594404b0b71a8bab90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3613887
Reviewed-by: Michael Martis <martis@chromium.org>
Reviewed-by: Evan Stade <estade@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Commit-Queue: Thanh Nguyen <thanhdng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#998266}
This commit is contained in:
Thanh Nguyen
2022-05-02 07:28:29 +00:00
committed by Chromium LUCI CQ
parent af15c0e008
commit 3a6f413371
11 changed files with 325 additions and 0 deletions

@ -1454,6 +1454,8 @@ component("ash") {
"system/phonehub/ui_constants.h",
"system/power/adaptive_charging_controller.cc",
"system/power/adaptive_charging_controller.h",
"system/power/adaptive_charging_notification_controller.cc",
"system/power/adaptive_charging_notification_controller.h",
"system/power/adaptive_charging_nudge.cc",
"system/power/adaptive_charging_nudge.h",
"system/power/adaptive_charging_nudge_controller.cc",
@ -2755,6 +2757,7 @@ test("ash_unittests") {
"system/phonehub/silence_phone_quick_action_controller_unittest.cc",
"system/phonehub/task_continuation_view_unittest.cc",
"system/power/adaptive_charging_controller_unittest.cc",
"system/power/adaptive_charging_notification_controller_unittest.cc",
"system/power/adaptive_charging_nudge_controller_unittest.cc",
"system/power/backlights_forced_off_setter_unittest.cc",
"system/power/lock_on_leave_controller_unittest.cc",

@ -4871,6 +4871,19 @@ New install
<message name="IDS_ASH_ADAPTIVE_CHARGING_EDUCATIONAL_NUDGE_TEXT" desc="The body text explaining to the user that adaptive charging is on.">
Adaptive charging is on. Your battery will stay at 80% to extend battery life, and fully charge when you need it.
</message>
<message name="IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_TITLE" desc="Title of adaptive charging notification explaining adpative charging is on.">
Adaptive charging is on
</message>
<message name="IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_MESSAGE_INDEFINITE" desc="Notification message explaining the battery is kept at 80%.">
Your battery is kept at 80% while plugged in to extend battery life.
</message>
<message name="IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_MESSAGE_TEMPORARY" desc="Notification message explaining the battery is temporarily at 80%. The message includes the predicted time at which charging will resume.">
Your battery is temporarily at 80% while plugged in to extend battery life. Your battery will fully charge by <ph name="FULLY_CHARGE_TIME">$1<ex>7:30 AM</ex></ph>.
</message>
<message name="IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_FULLY_CHARGE_NOW_BUTTON_TEXT" desc="Text in notification button to fully charge the device.">
Fully charge now
</message>
</messages>
</release>
</grit>

@ -0,0 +1 @@
2a8b80e5f34c93fd91001855358d84f2a16802d4

@ -0,0 +1 @@
d08545ab42b37cbf26202964438abbd2435b188f

@ -0,0 +1 @@
2a8b80e5f34c93fd91001855358d84f2a16802d4

@ -0,0 +1 @@
2a8b80e5f34c93fd91001855358d84f2a16802d4

@ -12,6 +12,7 @@ aggregate_vector_icons("ash_vector_icons") {
icon_directory = "."
sources = [
"adaptive_charging_battery.icon",
"adaptive_charging_nudge_dark.icon",
"adaptive_charging_nudge_light.icon",
"add_cellular_network.icon",

@ -0,0 +1,41 @@
// Copyright 2022 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.
CANVAS_DIMENSIONS, 20,
R_MOVE_TO, 9.31f, 15.67f,
R_LINE_TO, 2.76f, -5.52f,
H_LINE_TO, 10.69f,
V_LINE_TO, 6,
R_LINE_TO, -2.76f, 5.52f,
R_H_LINE_TO, 1.38f,
R_V_LINE_TO, 4.14f,
CLOSE,
NEW_PATH,
MOVE_TO, 12, 4.5f,
R_H_LINE_TO, 1,
R_CUBIC_TO, 0.5f, 0, 1, 0.5f, 1, 1,
V_LINE_TO, 16,
R_CUBIC_TO, 0, 0.5f, -0.5f, 1, -1, 1,
H_LINE_TO, 7,
R_CUBIC_TO, -0.5f, 0, -1, -0.5f, -1, -1,
V_LINE_TO, 5.5f,
R_CUBIC_TO, 0, -0.5f, 0.5f, -1, 1, -1,
R_H_LINE_TO, 1,
V_LINE_TO, 3,
R_H_LINE_TO, 4,
R_V_LINE_TO, 1.5f,
CLOSE,
R_MOVE_TO, -4.75f, 7.45f,
R_ARC_TO, 0.8f, 0.8f, 0, 0, 1, -0.04f, -0.78f,
R_LINE_TO, 2.76f, -5.52f,
ARC_TO, 0.8f, 0.8f, 0, 0, 1, 11.49f, 6,
R_V_LINE_TO, 3.34f,
R_H_LINE_TO, 0.58f,
R_ARC_TO, 0.8f, 0.8f, 0, 0, 1, 0.72f, 1.16f,
R_LINE_TO, -2.76f, 5.52f,
R_ARC_TO, 0.8f, 0.8f, 0, 0, 1, -1.51f, -0.36f,
R_V_LINE_TO, -3.34f,
R_H_LINE_TO, -0.58f,
R_ARC_TO, 0.8f, 0.8f, 0, 0, 1, -0.68f, -0.38f,
CLOSE

@ -0,0 +1,93 @@
// Copyright 2022 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/adaptive_charging_notification_controller.h"
#include <string>
#include "ash/constants/ash_pref_names.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/i18n/time_formatting.h"
#include "components/prefs/pref_service.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
namespace ash {
namespace {
constexpr char kNotifierId[] = "adaptive-charging-notify";
constexpr char kInfoNotificationId[] = "adaptive-charging-notify-info";
} // namespace
AdaptiveChargingNotificationController::
AdaptiveChargingNotificationController() = default;
AdaptiveChargingNotificationController::
~AdaptiveChargingNotificationController() = default;
void AdaptiveChargingNotificationController::ShowAdaptiveChargingNotification(
absl::optional<int> hours_to_full) {
if (!ShouldShowNotification())
return;
std::u16string notification_message;
if (hours_to_full.has_value()) {
DCHECK_GE(hours_to_full.value(), 0);
notification_message = l10n_util::GetStringFUTF16(
IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_MESSAGE_TEMPORARY,
base::TimeFormatTimeOfDayWithHourClockType(
base::Time::Now() + base::Hours(hours_to_full.value()),
base::GetHourClockType(), base::kKeepAmPm));
} else {
notification_message = l10n_util::GetStringUTF16(
IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_MESSAGE_INDEFINITE);
}
message_center::RichNotificationData notification_data;
notification_data.buttons.push_back(
message_center::ButtonInfo(l10n_util::GetStringUTF16(
IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_FULLY_CHARGE_NOW_BUTTON_TEXT)));
auto notification = CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE, kInfoNotificationId,
l10n_util::GetStringUTF16(IDS_ASH_ADAPTIVE_CHARGING_NOTIFICATION_TITLE),
notification_message,
/*display_source=*/std::u16string(), /*origin_url=*/GURL(),
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
kNotifierId),
notification_data,
base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
weak_ptr_factory_.GetWeakPtr()),
kAdaptiveChargingBatteryIcon,
message_center::SystemNotificationWarningLevel::NORMAL);
if (hours_to_full.has_value())
notification->set_priority(message_center::SYSTEM_PRIORITY);
message_center::MessageCenter::Get()->AddNotification(
std::move(notification));
}
bool AdaptiveChargingNotificationController::ShouldShowNotification() {
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
return pref_service &&
pref_service->GetBoolean(ash::prefs::kPowerAdaptiveChargingEnabled);
}
void AdaptiveChargingNotificationController::Click(
const absl::optional<int>& button_index,
const absl::optional<std::u16string>& reply) {
// TODO(b/216035329): Add logic to this function when "Charge now" function is
// available.
}
} // namespace ash

@ -0,0 +1,50 @@
// Copyright 2022 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.
#ifndef ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_NOTIFICATION_CONTROLLER_H_
#define ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_NOTIFICATION_CONTROLLER_H_
#include <string>
#include "ash/ash_export.h"
#include "base/memory/weak_ptr.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
namespace ash {
class ASH_EXPORT AdaptiveChargingNotificationController
: public message_center::NotificationObserver {
public:
AdaptiveChargingNotificationController();
AdaptiveChargingNotificationController(
const AdaptiveChargingNotificationController&) = delete;
AdaptiveChargingNotificationController& operator=(
const AdaptiveChargingNotificationController&) = delete;
virtual ~AdaptiveChargingNotificationController();
// Show the adaptive charging notification. There are two possible
// scenarios:
// 1. When the battery is kept at 80% indefinitely (i.e. no value of
// |hours_to_full| is provided), the notification is a
// normal notification.
// 2. When the battery is temporary kept at 80% (i.e. |hours_to_full| has
// a value), the notification will have higher priority (SYSTEM_PRIORITY).
void ShowAdaptiveChargingNotification(
absl::optional<int> hours_to_full = absl::nullopt);
bool ShouldShowNotification();
// message_center::NotificationObserver:
void Click(const absl::optional<int>& button_index,
const absl::optional<std::u16string>& reply) override;
private:
base::WeakPtrFactory<AdaptiveChargingNotificationController>
weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_POWER_ADAPTIVE_CHARGING_NOTIFICATION_CONTROLLER_H_

@ -0,0 +1,120 @@
// Copyright 2022 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/adaptive_charging_notification_controller.h"
#include <string>
#include "ash/constants/ash_pref_names.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/i18n/rtl.h"
#include "base/run_loop.h"
#include "base/test/icu_test_util.h"
#include "base/time/time.h"
#include "base/time/time_override.h"
#include "ui/message_center/message_center.h"
namespace ash {
namespace {
const base::Time::Exploded kTestDateTimeExploded = {
2022, 4, 5, 29, // Fri, Apr 29, 2022
2, 42, 7, 0 // 2:42:07.000 in UTC = 12:42:07 in Australia AEST.
};
// Enables or disables the user pref for the entire feature.
void SetAdaptiveChargingPref(bool enabled) {
Shell::Get()->session_controller()->GetActivePrefService()->SetBoolean(
prefs::kPowerAdaptiveChargingEnabled, enabled);
base::RunLoop().RunUntilIdle();
}
// Returns the number of (popup or non-popup) notifications that are currently
// visible in the message center queue.
size_t VisibleNotificationCount() {
return message_center::MessageCenter::Get()->GetVisibleNotifications().size();
}
} // namespace
class AdaptiveChargingNotificationControllerTest : public AshTestBase {
public:
AdaptiveChargingNotificationControllerTest() = default;
AdaptiveChargingNotificationControllerTest(
const AdaptiveChargingNotificationControllerTest&) = delete;
AdaptiveChargingNotificationControllerTest& operator=(
const AdaptiveChargingNotificationControllerTest&) = delete;
~AdaptiveChargingNotificationControllerTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
controller_ = std::make_unique<AdaptiveChargingNotificationController>();
}
AdaptiveChargingNotificationController* GetController() {
return controller_.get();
}
private:
std::unique_ptr<AdaptiveChargingNotificationController> controller_;
};
TEST_F(AdaptiveChargingNotificationControllerTest, ShouldntShowNotification) {
SetAdaptiveChargingPref(false);
GetController()->ShowAdaptiveChargingNotification();
GetController()->ShowAdaptiveChargingNotification(5);
EXPECT_EQ(VisibleNotificationCount(), 0u);
}
TEST_F(AdaptiveChargingNotificationControllerTest, ShowNotificationWithHour) {
SetAdaptiveChargingPref(true);
GetController()->ShowAdaptiveChargingNotification(5);
EXPECT_EQ(VisibleNotificationCount(), 1u);
}
TEST_F(AdaptiveChargingNotificationControllerTest,
ShowNotificationWithoutHour) {
SetAdaptiveChargingPref(true);
GetController()->ShowAdaptiveChargingNotification();
EXPECT_EQ(VisibleNotificationCount(), 1u);
}
TEST_F(AdaptiveChargingNotificationControllerTest, HaveTimeInNotification) {
// Set default locale.
base::test::ScopedRestoreICUDefaultLocale restore_locale;
base::i18n::SetICUDefaultLocale("en_AU");
base::test::ScopedRestoreDefaultTimezone sydney_time("Australia/Sydney");
// Override time for testing.
base::subtle::ScopedTimeClockOverrides time_override(
[]() {
base::Time time;
EXPECT_TRUE(base::Time::FromUTCExploded(kTestDateTimeExploded, &time));
return time;
},
/*time_ticks_override=*/nullptr,
/*thread_ticks_override=*/nullptr);
SetAdaptiveChargingPref(true);
GetController()->ShowAdaptiveChargingNotification(5);
const message_center::Notification* notification =
message_center::MessageCenter::Get()->FindPopupNotificationById(
"adaptive-charging-notify-info");
ASSERT_TRUE(notification);
// Current local time is 12:42 pm, so 5 hours after should be 5:42 pm.
EXPECT_NE(notification->message().find(u"5:42 pm"), std::u16string::npos);
}
} // namespace ash