Skip to content

Commit

Permalink
Implement notification view controller
Browse files Browse the repository at this point in the history
This CL adds support for adding and removing grouped
notifications. We create an extra notification to house
all group notifications. This is done by converting
the first notification view that needs to be grouped
into this container and then adding that notification as
a grouped child notification within that view. We do this
to make animating between converting grouped to non-grouped
notifications and vice-versa.

Currently added a hack to view the notifications in
NotificationViewMD for dogfood purposes.

Bug:1223697

Change-Id: I40aa97a2cdc13e316fa3df7628cac66bfd660d25
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3040300
Commit-Queue: Ahmed Mehfooz <amehfooz@chromium.org>
Reviewed-by: Yoshiki Iguchi <yoshiki@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909088}
  • Loading branch information
amehfooz32 authored and Chromium LUCI CQ committed Aug 5, 2021
1 parent 72a2dfc commit 23cef48
Show file tree
Hide file tree
Showing 14 changed files with 468 additions and 18 deletions.
25 changes: 21 additions & 4 deletions ash/system/message_center/unified_message_list_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "ash/system/message_center/unified_message_list_view.h"
#include <string>

#include "ash/constants/ash_features.h"
#include "ash/public/cpp/metrics_util.h"
#include "ash/system/message_center/message_center_style.h"
#include "ash/system/message_center/message_center_utils.h"
Expand Down Expand Up @@ -261,15 +262,17 @@ void UnifiedMessageListView::Init() {
bool is_latest = true;
for (auto* notification :
message_center_utils::GetSortedVisibleNotifications()) {
if (notification->group_child()) {
// TODO(crbug/1223697): Add grouped notifications to existing parent
// message views.
if (notification->group_child())
continue;
}

auto* view =
new MessageViewContainer(CreateMessageView(*notification), this);
view->LoadExpandedState(model_, is_latest);
AddChildViewAt(view, 0);

if (notification->group_parent())
PopulateGroupParent(notification->id());

MessageCenter::Get()->DisplayedNotification(
notification->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER);
is_latest = false;
Expand Down Expand Up @@ -404,7 +407,17 @@ UnifiedMessageListView::GetMessageViewForNotificationId(const std::string& id) {
return static_cast<MessageViewContainer*>(*it)->message_view();
}

void UnifiedMessageListView::ConvertNotificationViewToGroupedNotificationView(
const std::string& ungrouped_notification_id,
const std::string& new_grouped_notification_id) {
GetMessageViewForNotificationId(ungrouped_notification_id)
->set_notification_id(new_grouped_notification_id);
}

void UnifiedMessageListView::OnNotificationAdded(const std::string& id) {
if (ash::features::IsNotificationsRefreshEnabled())
message_center::NotificationViewController::OnNotificationAdded(id);

auto* notification = MessageCenter::Get()->FindVisibleNotificationById(id);
if (!notification)
return;
Expand Down Expand Up @@ -446,6 +459,10 @@ void UnifiedMessageListView::OnNotificationRemoved(const std::string& id,
if (ignore_notification_remove_)
return;

if (ash::features::IsNotificationsRefreshEnabled())
message_center::NotificationViewController::OnNotificationRemoved(id,
by_user);

// The corresponding MessageView may have already been deleted after being
// manually slid out.
auto* child = GetNotificationById(id);
Expand Down
3 changes: 3 additions & 0 deletions ash/system/message_center/unified_message_list_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ class ASH_EXPORT UnifiedMessageListView
// message_center::NotificationViewController:
message_center::MessageView* GetMessageViewForNotificationId(
const std::string& id) override;
void ConvertNotificationViewToGroupedNotificationView(
const std::string& ungrouped_notification_id,
const std::string& new_grouped_notification_id) override;
void OnNotificationAdded(const std::string& id) override;
void OnNotificationRemoved(const std::string& id, bool by_user) override;
void OnNotificationUpdated(const std::string& id) override;
Expand Down
5 changes: 5 additions & 0 deletions ui/message_center/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,10 @@ if (enable_message_center) {
"//ui/views:test_support",
]
}

if (is_chromeos_ash) {
sources += [ "notification_view_controller_unittest.cc" ]
deps += [ "//ash/constants" ]
}
}
}
1 change: 1 addition & 0 deletions ui/message_center/message_center.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ class MESSAGE_CENTER_EXPORT MessageCenter {
friend class MessageCenterImplTest;
friend class MessageCenterImplTestWithChangeQueue;
friend class MessageCenterImplTestWithoutChangeQueue;
friend class NotificationViewControllerTest;
friend class UiControllerTest;
friend class TrayViewControllerTest;
friend class MessagePopupCollectionTest;
Expand Down
17 changes: 17 additions & 0 deletions ui/message_center/message_center_impl_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1318,5 +1318,22 @@ TEST_F(MessageCenterImplTest, ButtonClickWithReplyOnLockScreen) {
EXPECT_FALSE(lock_screen_controller()->IsScreenLocked());
}

#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Test to ensure no grouping behavior gets accidentally enabled
// outside of chrome os.
TEST_F(MessageCenterImplTest, NoGroupingBehaviorOnNonChromeOS) {
std::vector<std::string> notification_ids{"id0", "id1", "id2"};
for (std::string id : notification_ids)
message_center()->AddNotification(
CreateSimpleNotificationWithNotifierId(id, "app0"));

for (std::string id : notification_ids) {
auto* notification = message_center()->FindNotificationById(id);
EXPECT_FALSE(notification->allow_group() || notification->group_parent() ||
notification->group_child());
}
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)

} // namespace internal
} // namespace message_center
130 changes: 123 additions & 7 deletions ui/message_center/notification_view_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

#include "ui/message_center/notification_view_controller.h"

#include "ash/constants/ash_features.h"
#include "base/time/time.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/views/message_view.h"

namespace message_center {
Expand All @@ -16,11 +20,103 @@ NotificationViewController::~NotificationViewController() {
observer_.Reset();
}

void NotificationViewController::PopulateGroupParent(
const std::string& notification_id) {
DCHECK(MessageCenter::Get()
->FindNotificationById(notification_id)
->group_parent());
MessageView* parent_view = GetMessageViewForNotificationId(notification_id);

for (auto* notification : MessageCenter::Get()->GetNotifications()) {
if (notification->notifier_id() == parent_view->notifier_id() &&
notification->id() != parent_view->notification_id()) {
child_parent_map_[notification->id()] = parent_view->notification_id();
parent_view->AddGroupNotification(*notification, /*newest_first=*/true);
}
}
}

void NotificationViewController::SetupParentNotification(
std::string* parent_id) {
Notification* parent_notification =
MessageCenter::Get()->FindNotificationById(*parent_id);
std::unique_ptr<Notification> notification_copy =
CreateCopyForParentNotification(*parent_notification);

std::string new_parent_id = notification_copy->id();
std::string old_parent_id = *parent_id;
*parent_id = new_parent_id;

parent_grouped_notification_id_set_.insert(new_parent_id);

Notification* new_parent_notification = notification_copy.get();
MessageCenter::Get()->AddNotification(std::move(notification_copy));

ConvertNotificationViewToGroupedNotificationView(
/*ungrouped_notification_id=*/old_parent_id,
/*new_grouped_notification_id=*/new_parent_id);
UpdateChildParentMap(/*new_parent_id=*/new_parent_id,
/*old_parent_id=*/old_parent_id);

// Add the old parent notification as a group child to the
// newly created parent notification which will act as a
// container for this group as long as it exists.
new_parent_notification->SetGroupParent();
parent_notification->SetGroupChild();

GetMessageViewForNotificationId(new_parent_id)
->AddGroupNotification(*parent_notification, /*newest_first=*/false);
}

std::unique_ptr<Notification>
NotificationViewController::CreateCopyForParentNotification(
const Notification& parent_notification) {
// Create a copy with a timestamp that is older than the copied notification.
// We need to set an older timestamp so that this notification will become
// the parent notification for it's notifier_id.
auto child_copy = std::make_unique<Notification>(
NotificationType::NOTIFICATION_TYPE_SIMPLE,
parent_notification.id() + kIdSuffixForGroupContainerNotification,
parent_notification.title(), parent_notification.message(), gfx::Image(),
std::u16string(), parent_notification.origin_url(),
parent_notification.notifier_id(), RichNotificationData(),
/*delegate=*/nullptr);
child_copy->set_timestamp(parent_notification.timestamp() -
base::TimeDelta::FromMilliseconds(1));
child_copy->SetGroupChild();

return child_copy;
}

void NotificationViewController::UpdateChildParentMap(
const std::string& new_parent_id,
const std::string& old_parent_id) {
// Remove entry with `new_parent_id` as a child id and replace with
// `old_parent_id` as a child id.
DCHECK(child_parent_map_.find(new_parent_id) != child_parent_map_.end());
child_parent_map_.erase(child_parent_map_.find(new_parent_id));
child_parent_map_[old_parent_id] = new_parent_id;

// Replace all occurrences of `old_parent_id` with `new_parent_id`.
std::vector<std::string> to_be_updated;
for (auto& child : child_parent_map_) {
if (child.second == old_parent_id)
to_be_updated.push_back(child.first);
}
for (auto id : to_be_updated) {
child_parent_map_.erase(child_parent_map_.find(id));
child_parent_map_[id] = new_parent_id;
}
}

void NotificationViewController::OnNotificationAdded(
const std::string& notification_id) {
Notification* notification =
MessageCenter::Get()->FindNotificationById(notification_id);

if (!notification)
return;

// We only need to process notifications that are children of an
// existing group. So do nothing otherwise.
if (!notification->group_child())
Expand All @@ -29,28 +125,48 @@ void NotificationViewController::OnNotificationAdded(
Notification* parent_notification =
MessageCenter::Get()->FindOldestNotificationByNotiferId(
notification->notifier_id());
std::string parent_id = parent_notification->id();

child_parent_map_[notification_id] = parent_notification->id();
// If we are creating a new notification group for this `notifier_id`,
// we must create a copy of the designated parent notification and
// use it to set up a container notification which will hold all
// notifications for this group.
if (!parent_grouped_notification_id_set_.count(parent_id))
SetupParentNotification(&parent_id);

MessageView* parent_view =
GetMessageViewForNotificationId(parent_notification->id());
child_parent_map_[notification_id] = parent_id;

MessageView* parent_view = GetMessageViewForNotificationId(parent_id);
if (parent_view)
parent_view->AddGroupNotification(*notification);
parent_view->AddGroupNotification(*notification, /*newest_first=*/false);
else
MessageCenter::Get()->ResetSinglePopup(parent_id);
}

void NotificationViewController::OnNotificationRemoved(
const std::string& notification_id,
bool by_user) {
auto it = child_parent_map_.find(notification_id);
if (it != child_parent_map_.end()) {
auto child_it = child_parent_map_.find(notification_id);
if (child_it != child_parent_map_.end()) {
MessageView* parent_view =
GetMessageViewForNotificationId(child_parent_map_[notification_id]);

child_parent_map_.erase(it);
child_parent_map_.erase(child_it);

if (parent_view)
parent_view->RemoveGroupNotification(notification_id);
}
auto parent = parent_grouped_notification_id_set_.find(notification_id);
if (parent != parent_grouped_notification_id_set_.end()) {
std::vector<std::string> to_be_deleted;
for (auto& child : child_parent_map_) {
if (child.second == notification_id)
to_be_deleted.push_back(child.first);
}

for (auto id : to_be_deleted)
MessageCenter::Get()->RemoveNotification(id, by_user);
}
}

} // namespace message_center
41 changes: 38 additions & 3 deletions ui/message_center/notification_view_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,54 @@ class MESSAGE_CENTER_EXPORT NotificationViewController
const NotificationViewController& other) = delete;
~NotificationViewController() override;

virtual MessageView* GetMessageViewForNotificationId(
const std::string& notification_id) = 0;

// MessageCenterObserver:
void OnNotificationAdded(const std::string& notification_id) override;
void OnNotificationRemoved(const std::string& notification_id,
bool by_user) override;

protected:
// Adds grouped child notifications that belong to a parent message
// view.
void PopulateGroupParent(const std::string& notification_id);

private:
virtual MessageView* GetMessageViewForNotificationId(
const std::string& notification_id) = 0;

// Updates the notification id associated with a message center view and
// popup if required. We do this to covert an existing message view into
// a message view that acts as a container for grouped notifications.
// Creating a new view for this would make the code simpler but we need
// to do it in place to make it easier to animate the conversion between
// grouped and non-grouped notifications.
virtual void ConvertNotificationViewToGroupedNotificationView(
const std::string& ungrouped_notification_id,
const std::string& new_grouped_notification_id) = 0;

// Sets up a parent view to hold all message views for
// a grouped notification. Does this by creating a copy of the
// parent notification and switching the notification_ids of the
// current message view associated with the parent notification.
void SetupParentNotification(std::string* parent_id);

// Creates a copy notification that will act as a parent notification
// for its group.
std::unique_ptr<Notification> CreateCopyForParentNotification(
const Notification& parent_notification);

// Replaces all instances of `old_parent_id` with `new_parent_id` in
// the `child_parent_map_`.
void UpdateChildParentMap(const std::string& new_parent_id,
const std::string& old_parent_id);

// Map for looking up the parent notification_id for any given notification
// id.
std::map<std::string, std::string> child_parent_map_;

// Set of current parent notification ids. Used to keep track of grouped
// notifications which already have a parent notification view.
std::set<std::string> parent_grouped_notification_id_set_;

base::ScopedObservation<MessageCenter, MessageCenterObserver> observer_{this};
};

Expand Down

0 comments on commit 23cef48

Please sign in to comment.