0

Add Lockscreen notification

This CL adds a pinned, system priority notification
if there are any notifications not being shown because
of the screen being locked.
The notification is also updated if notifications are
added or removed while the screen is locked.

Bug: b:264925225
Change-Id: I19cada727000df93b8547d383b18dcc7c24bda31
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4198461
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Commit-Queue: Ahmed Mehfooz <amehfooz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1097727}
This commit is contained in:
Ahmed Mehfooz
2023-01-27 01:13:45 +00:00
committed by Chromium LUCI CQ
parent dca0c7202a
commit efc44188f6
13 changed files with 357 additions and 20 deletions

@ -1366,6 +1366,8 @@ component("ash") {
"system/locale/locale_update_controller_impl.h",
"system/locale/unified_locale_detailed_view_controller.cc",
"system/locale/unified_locale_detailed_view_controller.h",
"system/lock_screen_notification_controller.cc",
"system/lock_screen_notification_controller.h",
"system/machine_learning/user_settings_event_logger.cc",
"system/machine_learning/user_settings_event_logger.h",
"system/media/media_notification_provider.cc",
@ -3085,6 +3087,7 @@ test("ash_unittests") {
"system/keyboard_brightness/keyboard_backlight_color_nudge_controller_unittest.cc",
"system/locale/locale_detailed_view_unittest.cc",
"system/locale/locale_feature_pod_controller_unittest.cc",
"system/lock_screen_notification_controller_unittest.cc",
"system/machine_learning/user_settings_event_logger_unittest.cc",
"system/media/media_tray_unittest.cc",
"system/media/unified_media_controls_container_unittest.cc",

@ -6179,6 +6179,14 @@ New install
Turn off
</message>
<!-- Lock screen notification -->
<message name="IDS_ASH_LOCKSCREEN_NOTIFICATION_TITLE" desc="Label used for the notification that shows up when there are hidden notifications on the lock screen.">
Notifications are hidden
</message>
<message name="IDS_ASH_LOCKSCREEN_NOTIFICATION_DESCRIPTION" desc="Text description used for the notification that show up when there are hidden notifications on the lock screen.">
Please unlock to view notifications
</message>
<!-- security curtain -->
<message name="IDS_ASH_CURTAIN_TITLE" desc="Title text displayed to inform the user a remote admin has taken control of the ChromeOs device.">
Your administrator is controlling your device

@ -0,0 +1 @@
3944cecb9baa6747654d4bc53a8f115a17ed13f0

@ -0,0 +1 @@
3944cecb9baa6747654d4bc53a8f115a17ed13f0

@ -178,7 +178,8 @@ enum class NotificationCatalogName {
kDicationOnlyPumpkinDownloaded = 163,
kDictationOnlySodaDownloaded = 164,
kIPHGoogleOneOffer = 165,
kMaxValue = kIPHGoogleOneOffer
kLockScreen = 166,
kMaxValue = kLockScreen
};
// A living catalog that registers system nudges.

@ -0,0 +1,112 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/lock_screen_notification_controller.h"
#include <string>
#include "ash/constants/notifier_catalogs.h"
#include "ash/public/cpp/notification_utils.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/message_center/message_center_constants.h"
#include "ash/system/message_center/message_center_utils.h"
#include "base/observer_list_types.h"
#include "components/session_manager/session_manager_types.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_types.h"
#include "ui/message_center/public/cpp/notifier_id.h"
namespace ash {
LockScreenNotificationController::LockScreenNotificationController() {
Shell::Get()->session_controller()->AddObserver(this);
}
LockScreenNotificationController::~LockScreenNotificationController() {
Shell::Get()->session_controller()->RemoveObserver(this);
}
// static
const char LockScreenNotificationController::kLockScreenNotificationId[] =
"lock_screen";
std::unique_ptr<message_center::Notification>
LockScreenNotificationController::CreateNotification() {
message_center::RichNotificationData optional_fields;
optional_fields.pinned = true;
optional_fields.priority =
message_center::NotificationPriority::SYSTEM_PRIORITY;
return ash::CreateSystemNotificationPtr(
message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE,
kLockScreenNotificationId,
l10n_util::GetStringUTF16(IDS_ASH_LOCKSCREEN_NOTIFICATION_TITLE),
l10n_util::GetStringUTF16(IDS_ASH_LOCKSCREEN_NOTIFICATION_DESCRIPTION),
/*display_source=*/std::u16string(), /*origin_url=*/GURL(),
message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
kLockScreenNotifierId,
NotificationCatalogName::kLockScreen),
optional_fields,
/*delegate=*/nullptr, vector_icons::kLockIcon,
message_center::SystemNotificationWarningLevel::NORMAL);
}
void LockScreenNotificationController::OnNotificationAdded(
const std::string& id) {
auto* message_center = message_center::MessageCenter::Get();
if (message_center->FindNotificationById(kLockScreenNotificationId)) {
return;
}
if (message_center_utils::AreNotificationsHiddenOnLockscreen()) {
message_center->AddNotification(CreateNotification());
}
}
void LockScreenNotificationController::OnNotificationRemoved(
const std::string& id,
bool by_user) {
if (message_center_utils::AreNotificationsHiddenOnLockscreen()) {
return;
}
auto* message_center = message_center::MessageCenter::Get();
if (message_center->FindNotificationById(kLockScreenNotificationId)) {
message_center->RemoveNotification(kLockScreenNotificationId,
/*by_user=*/false);
}
}
void LockScreenNotificationController::OnSessionStateChanged(
session_manager::SessionState state) {
is_screen_locked_ = state == session_manager::SessionState::LOCKED;
auto* message_center = message_center::MessageCenter::Get();
// Observe the `MessageCenter` for notification changes while the screen is
// locked.
if (is_screen_locked_) {
message_center_observation_.Observe(message_center);
} else {
message_center_observation_.Reset();
}
if (is_screen_locked_ &&
message_center_utils::AreNotificationsHiddenOnLockscreen()) {
message_center->AddNotification(CreateNotification());
return;
}
if (!is_screen_locked_ &&
message_center->FindNotificationById(kLockScreenNotificationId)) {
message_center->RemoveNotification(kLockScreenNotificationId,
/*by_user=*/false);
}
}
} // namespace ash

@ -0,0 +1,59 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_SYSTEM_LOCK_SCREEN_NOTIFICATION_CONTROLLER_H_
#define ASH_SYSTEM_LOCK_SCREEN_NOTIFICATION_CONTROLLER_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "ash/public/cpp/session/session_observer.h"
#include "base/scoped_observation.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_observer.h"
namespace message_center {
class Notification;
} // namespace message_center
namespace ash {
// Controller class to manage the "Lock screen" notification. This class only
// exists when `IsQsRevampEnabled` is true.
class ASH_EXPORT LockScreenNotificationController
: public message_center::MessageCenterObserver,
public SessionObserver {
public:
LockScreenNotificationController();
LockScreenNotificationController(const LockScreenNotificationController&) =
delete;
LockScreenNotificationController& operator=(
const LockScreenNotificationController&) = delete;
~LockScreenNotificationController() override;
static const char kLockScreenNotificationId[];
// message_center::MessageCenterObserver:
void OnNotificationAdded(const std::string& id) override;
void OnNotificationRemoved(const std::string& id, bool by_user) override;
// SessionObserver:
void OnSessionStateChanged(session_manager::SessionState state) override;
private:
std::unique_ptr<message_center::Notification> CreateNotification();
bool is_screen_locked_ = false;
base::ScopedObservation<message_center::MessageCenter,
message_center::MessageCenterObserver>
message_center_observation_{this};
};
} // namespace ash
#endif // ASH_SYSTEM_LOCK_SCREEN_NOTIFICATION_CONTROLLER_H_

@ -0,0 +1,114 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/lock_screen_notification_controller.h"
#include "ash/constants/ash_features.h"
#include "ash/system/notification_center/notification_center_test_api.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "ui/message_center/message_center.h"
namespace ash {
class LockScreenNotificationControllerTest
: public AshTestBase,
public testing::WithParamInterface<bool> {
public:
LockScreenNotificationControllerTest() = default;
LockScreenNotificationControllerTest(
const LockScreenNotificationControllerTest&) = delete;
LockScreenNotificationControllerTest& operator=(
const LockScreenNotificationControllerTest&) = delete;
~LockScreenNotificationControllerTest() override = default;
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(features::kQsRevamp);
AshTestBase::SetUp();
test_api_ = std::make_unique<NotificationCenterTestApi>(
StatusAreaWidgetTestHelper::GetStatusAreaWidget()
->notification_center_tray());
}
bool IsLockScreenNotificationPresent() {
return message_center::MessageCenter::Get()->FindNotificationById(
LockScreenNotificationController::kLockScreenNotificationId);
}
NotificationCenterTestApi* test_api() { return test_api_.get(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<NotificationCenterTestApi> test_api_;
};
// Tests that locking/unlocking the screen adds/removes the lockscreen
// notification if notifications are hidden.
TEST_F(LockScreenNotificationControllerTest,
NoLockScreenNotificationWithoutHiddenNotifications) {
// The notification should not exist on an unlocked screen.
ASSERT_FALSE(IsLockScreenNotificationPresent());
// The notification should not exist on the lockscreen either because there is
// no hidden notification.
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_FALSE(IsLockScreenNotificationPresent());
}
TEST_F(LockScreenNotificationControllerTest,
LockScreenNotificationWithHiddenNotification) {
// Locking the screen with a hidden notification should cause the lockscreen
// notification to show up.
test_api()->AddNotification();
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_TRUE(IsLockScreenNotificationPresent());
// Unlocking the screen should result in the lockscreen notification being
// removed.
UnblockUserSession();
EXPECT_FALSE(IsLockScreenNotificationPresent());
}
TEST_F(LockScreenNotificationControllerTest,
NotificationNotShownWithSystemNotifications) {
// A system notification should not trigger the lockscreen notification since
// it's not hidden.
test_api()->AddSystemNotification();
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_FALSE(IsLockScreenNotificationPresent());
}
TEST_F(LockScreenNotificationControllerTest, NotificationsAddedOnLockScreen) {
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_FALSE(IsLockScreenNotificationPresent());
// Adding a system notification on the lockscreen should have no effect.
test_api()->AddSystemNotification();
EXPECT_FALSE(IsLockScreenNotificationPresent());
// Adding a normal notification should trigger the lock screen notification.
test_api()->AddNotification();
EXPECT_TRUE(IsLockScreenNotificationPresent());
}
TEST_F(LockScreenNotificationControllerTest, NotificationsRemovedOnLockScreen) {
std::string id, system_id;
system_id = test_api()->AddSystemNotification();
id = test_api()->AddNotification();
BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
EXPECT_TRUE(IsLockScreenNotificationPresent());
// Removing `id` should remove the lock screen notification since there are no
// hidden notifications left.
test_api()->RemoveNotification(id);
EXPECT_FALSE(IsLockScreenNotificationPresent());
}
} // namespace ash

@ -124,9 +124,12 @@ constexpr int kInlineSettingsExpandAndCollapseAnimationDuration = 200;
constexpr int kGeneralExpandAnimationDuration = 300;
constexpr int kGeneralCollapseAnimationDuration = 200;
// Animation durations for adding / removing grouped child views
// Animation durations for adding / removing grouped child views.
constexpr int kSlideOutGroupedNotificationAnimationDurationMs = 200;
// System notification notifier ids.
const char kLockScreenNotifierId[] = "ash.lockscreen_notification_controller";
} // namespace ash
#endif // ASH_SYSTEM_MESSAGE_CENTER_MESSAGE_CENTER_CONSTANTS_H_

@ -9,6 +9,7 @@
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/message_center/message_center_constants.h"
#include "ash/system/message_center/message_center_controller.h"
#include "ash/system/message_center/notification_grouping_controller.h"
#include "ash/system/message_center/session_state_notification_blocker.h"
@ -66,8 +67,6 @@ std::vector<message_center::Notification*> GetSortedNotificationsWithOwnView() {
}
size_t GetNotificationCount() {
size_t count = 0;
// We need to ignore the `session_state_notification_blocker` when getting the
// notification count on the lock screen. This is because we want the counter
// to show the total number of available notifications including notifications
@ -79,25 +78,51 @@ size_t GetNotificationCount() {
->session_state_notification_blocker()
: nullptr;
for (message_center::Notification* notification :
message_center::MessageCenter::Get()
->GetVisibleNotificationsWithoutBlocker(blocker_to_ignore)) {
const std::string& notifier = notification->notifier_id().id;
// Don't count these notifications since we have `CameraMicTrayItemView` to
// show indicators on the systray.
if (notifier == kVmCameraMicNotifierId) {
continue;
}
return base::ranges::count_if(
message_center::MessageCenter::Get()
->GetVisibleNotificationsWithoutBlocker(blocker_to_ignore),
[](auto* notification) {
const std::string& notifier = notification->notifier_id().id;
// Don't count group child notifications since they're contained in a single
// parent view.
if (notification->group_child()) {
continue;
}
// Don't count these notifications since we have `CameraMicTrayItemView`
// to show indicators on the systray.
if (notifier == kVmCameraMicNotifierId) {
return false;
}
++count;
// The lockscreen notification is used to signify that there are
// notifications hidden. It should not effect the number of
// notifications.
if (notifier == kLockScreenNotifierId) {
return false;
}
// Don't count group child notifications since they're contained in a
// single parent view.
if (notification->group_child()) {
return false;
}
return true;
});
}
bool AreNotificationsHiddenOnLockscreen() {
DCHECK(Shell::Get()->session_controller()->IsScreenLocked());
// Return true if the `session_state_notification_blocker` is hiding any
// notifications.
auto* message_center = message_center::MessageCenter::Get();
if (message_center->GetVisibleNotifications().size() !=
message_center
->GetVisibleNotificationsWithoutBlocker(
Shell::Get()
->message_center_controller()
->session_state_notification_blocker())
.size()) {
return true;
}
return count;
return false;
}
message_center::NotificationViewController*

@ -41,6 +41,10 @@ std::vector<message_center::Notification*> GetSortedNotificationsWithOwnView();
// grouped notifications only need to be counted as one.
size_t ASH_EXPORT GetNotificationCount();
// Returns true if there are any notifications hidden because we're on the
// lockscreen. Should be only called if the screen is locked.
bool AreNotificationsHiddenOnLockscreen();
// Get the notification view controller associated to a certain display.
message_center::NotificationViewController*
GetActiveNotificationViewControllerForDisplay(int64_t display_id);

@ -9,6 +9,7 @@
#include "ash/system/cast/cast_notification_controller.h"
#include "ash/system/do_not_disturb_notification_controller.h"
#include "ash/system/gesture_education/gesture_education_notification_controller.h"
#include "ash/system/lock_screen_notification_controller.h"
#include "ash/system/network/auto_connect_notifier.h"
#include "ash/system/network/cellular_setup_notifier.h"
#include "ash/system/network/managed_sim_lock_notifier.h"
@ -45,6 +46,9 @@ SystemNotificationController::SystemNotificationController()
: nullptr),
gesture_education_(
std::make_unique<GestureEducationNotificationController>()),
lock_screen_(features::IsQsRevampEnabled()
? std::make_unique<LockScreenNotificationController>()
: nullptr),
power_(std::make_unique<PowerNotificationController>(
message_center::MessageCenter::Get())),
power_sounds_(MaybeCreatePowerSoundsController()),

@ -16,6 +16,7 @@ class GestureEducationNotificationController;
class CastNotificationController;
class CellularSetupNotifier;
class DoNotDisturbNotificationController;
class LockScreenNotificationController;
class ManagedSimLockNotifier;
class PowerNotificationController;
class PowerSoundsController;
@ -55,6 +56,7 @@ class SystemNotificationController {
const std::unique_ptr<DoNotDisturbNotificationController> do_not_disturb_;
const std::unique_ptr<GestureEducationNotificationController>
gesture_education_;
const std::unique_ptr<LockScreenNotificationController> lock_screen_;
// TODO(b/228093904): Make |managed_sim_lock_notifier_| const during cleanup.
std::unique_ptr<ManagedSimLockNotifier> managed_sim_lock_notifier_;
const std::unique_ptr<PowerNotificationController> power_;