Commit a1a1eb0c authored by yoshiki iguchi's avatar yoshiki iguchi Committed by Commit Bot

Sync DnD (Do-not-Disturb) status with Android

This CL adds a feature to sync the DnD status between Chrome and Android.
- When the DnD status is changed on Android side, set it to Chrome-side.
- When the DnD status is changed on Chrome side, set it to Android-side.

Bug: b/79951342
Test: Ran added tests
Change-Id: I3eba63420a91de60e7baf333dfb4dd7c1da74310
Reviewed-on: https://chromium-review.googlesource.com/1111495
Commit-Queue: Yoshiki Iguchi <yoshiki@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Reviewed-by: default avatarGreg Kerr <kerrnel@chromium.org>
Reviewed-by: default avatarEliot Courtney <edcourtney@chromium.org>
Cr-Commit-Position: refs/heads/master@{#570357}
parent e1a24b09
......@@ -14,6 +14,7 @@
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "components/arc/mojo_channel.h"
#include "ui/message_center/message_center_observer.h"
#include "ui/message_center/views/message_view_factory.h"
using arc::ConnectionHolder;
......@@ -22,6 +23,8 @@ using arc::mojom::ArcNotificationData;
using arc::mojom::ArcNotificationDataPtr;
using arc::mojom::ArcNotificationEvent;
using arc::mojom::ArcNotificationPriority;
using arc::mojom::ArcDoNotDisturbStatus;
using arc::mojom::ArcDoNotDisturbStatusPtr;
using arc::mojom::NotificationsHost;
using arc::mojom::NotificationsInstance;
using arc::mojom::NotificationsInstancePtr;
......@@ -40,6 +43,19 @@ std::unique_ptr<message_center::MessageView> CreateCustomMessageView(
return arc_delegate->CreateCustomMessageView(notification);
}
class DoNotDisturbManager : public message_center::MessageCenterObserver {
public:
DoNotDisturbManager(ArcNotificationManager* manager) : manager_(manager) {}
void OnQuietModeChanged(bool in_quiet_mode) override {
manager_->SetDoNotDisturbStatusOnAndroid(in_quiet_mode);
}
private:
ArcNotificationManager* const manager_;
DISALLOW_COPY_AND_ASSIGN(DoNotDisturbManager);
};
} // namespace
class ArcNotificationManager::InstanceOwner {
......@@ -87,14 +103,21 @@ ArcNotificationManager::ArcNotificationManager(
: delegate_(std::move(delegate)),
main_profile_id_(main_profile_id),
message_center_(message_center),
do_not_disturb_manager_(new DoNotDisturbManager(this)),
instance_owner_(std::make_unique<InstanceOwner>()) {
DCHECK(message_center_);
instance_owner_->holder()->SetHost(this);
instance_owner_->holder()->AddObserver(this);
if (!message_center::MessageViewFactory::HasCustomNotificationViewFactory())
SetCustomNotificationViewFactory();
message_center_->AddObserver(do_not_disturb_manager_.get());
}
ArcNotificationManager::~ArcNotificationManager() {
message_center_->RemoveObserver(do_not_disturb_manager_.get());
instance_owner_->holder()->RemoveObserver(this);
instance_owner_->holder()->SetHost(nullptr);
......@@ -113,8 +136,12 @@ ArcNotificationManager::GetConnectionHolderForTest() {
void ArcNotificationManager::OnConnectionReady() {
DCHECK(!ready_);
// TODO(hidehiko): Replace this by ConnectionHolder::IsConnected().
ready_ = true;
// Sync the initial quiet mode state with Android.
SetDoNotDisturbStatusOnAndroid(message_center_->IsQuietMode());
}
void ArcNotificationManager::OnConnectionClosed() {
......@@ -429,4 +456,34 @@ void ArcNotificationManager::OnGotAppId(ArcNotificationDataPtr data,
it->second->OnUpdatedFromAndroid(std::move(data), app_id);
}
void ArcNotificationManager::OnDoNotDisturbStatusUpdated(
ArcDoNotDisturbStatusPtr status) {
// Remove the observer to prevent from sending the command to Android since
// this request came from Android.
message_center_->RemoveObserver(do_not_disturb_manager_.get());
if (message_center_->IsQuietMode() != status->enabled)
message_center_->SetQuietMode(status->enabled);
// Add back the observer.
message_center_->AddObserver(do_not_disturb_manager_.get());
}
void ArcNotificationManager::SetDoNotDisturbStatusOnAndroid(bool enabled) {
auto* notifications_instance = ARC_GET_INSTANCE_FOR_METHOD(
instance_owner_->holder(), SetDoNotDisturbStatusOnAndroid);
// On shutdown, the ARC channel may quit earlier than notifications.
if (!notifications_instance) {
VLOG(2) << "Trying to send the Do Not Disturb status (" << enabled
<< "), but the ARC channel has already gone.";
return;
}
ArcDoNotDisturbStatusPtr status = ArcDoNotDisturbStatus::New();
status->enabled = enabled;
notifications_instance->SetDoNotDisturbStatusOnAndroid(std::move(status));
}
} // namespace ash
......@@ -51,6 +51,8 @@ class ArcNotificationManager
void OnNotificationUpdated(arc::mojom::ArcNotificationDataPtr data) override;
void OnNotificationRemoved(const std::string& key) override;
void OpenMessageCenter() override;
void OnDoNotDisturbStatusUpdated(
arc::mojom::ArcDoNotDisturbStatusPtr status) override;
// Methods called from ArcNotificationItem:
void SendNotificationRemovedFromChrome(const std::string& key);
......@@ -63,6 +65,7 @@ class ArcNotificationManager
void OpenNotificationSnoozeSettings(const std::string& key);
bool IsOpeningSettingsSupported() const;
void SendNotificationToggleExpansionOnChrome(const std::string& key);
void SetDoNotDisturbStatusOnAndroid(bool enabled);
private:
// Helper class to own MojoChannel and ConnectionHolder.
......@@ -77,6 +80,8 @@ class ArcNotificationManager
std::unique_ptr<ArcNotificationManagerDelegate> delegate_;
const AccountId main_profile_id_;
message_center::MessageCenter* const message_center_;
const std::unique_ptr<message_center::MessageCenterObserver>
do_not_disturb_manager_;
using ItemMap =
std::unordered_map<std::string, std::unique_ptr<ArcNotificationItem>>;
......
......@@ -19,6 +19,7 @@
#include "mojo/public/cpp/bindings/interface_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/message_center/fake_message_center.h"
#include "ui/message_center/message_center_observer.h"
namespace ash {
......@@ -52,10 +53,21 @@ class MockMessageCenter : public message_center::FakeMessageCenter {
return visible_notifications_;
}
void SetQuietMode(bool in_quiet_mode) override {
if (in_quiet_mode != in_quiet_mode_) {
in_quiet_mode_ = in_quiet_mode;
for (auto& observer : observer_list())
observer.OnQuietModeChanged(in_quiet_mode);
}
}
bool IsQuietMode() const override { return in_quiet_mode_; }
private:
message_center::NotificationList::Notifications visible_notifications_;
std::map<std::string, std::unique_ptr<message_center::Notification>>
owned_notifications_;
bool in_quiet_mode_ = false;
DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
};
......@@ -112,16 +124,7 @@ class ArcNotificationManagerTest : public testing::Test {
void FlushInstanceCall() { binding_->FlushForTesting(); }
private:
void SetUp() override {
arc_notifications_instance_ =
std::make_unique<arc::FakeNotificationsInstance>();
message_center_ = std::make_unique<MockMessageCenter>();
arc_notification_manager_ = std::make_unique<ArcNotificationManager>(
std::make_unique<FakeArcNotificationManagerDelegate>(),
EmptyAccountId(), message_center_.get());
void ConnectMojoChannel() {
binding_ =
std::make_unique<mojo::Binding<arc::mojom::NotificationsInstance>>(
arc_notifications_instance_.get());
......@@ -133,6 +136,17 @@ class ArcNotificationManagerTest : public testing::Test {
arc_notification_manager_->GetConnectionHolderForTest());
}
private:
void SetUp() override {
arc_notifications_instance_ =
std::make_unique<arc::FakeNotificationsInstance>();
message_center_ = std::make_unique<MockMessageCenter>();
arc_notification_manager_ = std::make_unique<ArcNotificationManager>(
std::make_unique<FakeArcNotificationManagerDelegate>(),
EmptyAccountId(), message_center_.get());
}
void TearDown() override {
arc_notification_manager_.reset();
message_center_.reset();
......@@ -161,10 +175,11 @@ TEST_F(ArcNotificationManagerTest, NotificationCreatedAndRemoved) {
}
TEST_F(ArcNotificationManagerTest, NotificationRemovedByChrome) {
ConnectMojoChannel();
EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size());
std::string key = CreateNotification();
EXPECT_EQ(1u, message_center()->GetVisibleNotifications().size());
{
message_center::Notification* notification =
*message_center()->GetVisibleNotifications().begin();
......@@ -181,6 +196,8 @@ TEST_F(ArcNotificationManagerTest, NotificationRemovedByChrome) {
}
TEST_F(ArcNotificationManagerTest, NotificationRemovedByConnectionClose) {
ConnectMojoChannel();
EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size());
CreateNotificationWithKey("notification1");
CreateNotificationWithKey("notification2");
......@@ -192,4 +209,96 @@ TEST_F(ArcNotificationManagerTest, NotificationRemovedByConnectionClose) {
EXPECT_EQ(0u, message_center()->GetVisibleNotifications().size());
}
TEST_F(ArcNotificationManagerTest, DoNotDisturbSetStatusByRequestFromAndroid) {
ConnectMojoChannel();
// Check the initial conditions.
EXPECT_FALSE(message_center()->IsQuietMode());
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
// Emulate the request from Android to turn on Do-Not-Distturb.
arc::mojom::ArcDoNotDisturbStatusPtr status =
arc::mojom::ArcDoNotDisturbStatus::New();
status->enabled = true;
arc_notification_manager()->OnDoNotDisturbStatusUpdated(std::move(status));
// Confirm that the Do-Not-Disturb is on.
EXPECT_TRUE(message_center()->IsQuietMode());
// Confirm that no message back to Android.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
// Emulate the request from Android to turn off Do-Not-Disturb.
status = arc::mojom::ArcDoNotDisturbStatus::New();
status->enabled = false;
arc_notification_manager()->OnDoNotDisturbStatusUpdated(std::move(status));
// Confirm that the Do-Not-Disturb is off.
EXPECT_FALSE(message_center()->IsQuietMode());
// Confirm that no message back to Android.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
}
TEST_F(ArcNotificationManagerTest, DoNotDisturbSendStatusToAndroid) {
ConnectMojoChannel();
// FlushInstanceCall();
// Check the initial conditions.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
EXPECT_FALSE(message_center()->IsQuietMode());
// Trying setting the current value (false). This should be no-op
message_center()->SetQuietMode(false);
// A request to Android should not be sent, since the status is not changed.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
// Trying turning on.
message_center()->SetQuietMode(true);
FlushInstanceCall();
// base::RunLoop().RunUntilIdle();
EXPECT_FALSE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
// A request to Android should be sent.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status()->enabled);
}
TEST_F(ArcNotificationManagerTest, DoNotDisturbSyncInitialDisabledState) {
// Check the initial conditions.
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
EXPECT_FALSE(message_center()->IsQuietMode());
// Establish the mojo connection.
ConnectMojoChannel();
FlushInstanceCall();
// The request to Android should be sent, and the status should be synced
// accordingly.
EXPECT_FALSE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
EXPECT_FALSE(
arc_notifications_instance()->latest_do_not_disturb_status()->enabled);
}
TEST_F(ArcNotificationManagerTest, DoNotDisturbSyncInitialEnabledState) {
// Set quiet mode.
message_center()->SetQuietMode(true);
// Check the initial condition.
EXPECT_TRUE(message_center()->IsQuietMode());
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
// Establish the mojo connection.
ConnectMojoChannel();
FlushInstanceCall();
// The request to Android should be sent, and the status should be synced
// accordingly.
EXPECT_FALSE(
arc_notifications_instance()->latest_do_not_disturb_status().is_null());
EXPECT_TRUE(
arc_notifications_instance()->latest_do_not_disturb_status()->enabled);
}
} // namespace ash
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Next MinVersion: 18
// Next MinVersion: 19
module arc.mojom;
......@@ -156,8 +156,17 @@ struct ArcNotificationData {
ArcNotificationFlags? flags;
};
// Next Method ID: 7
[Extensible, MinVersion=18]
struct ArcDoNotDisturbStatus {
bool enabled;
};
// Next Method ID: 8
interface NotificationsHost {
// Set the Do-Not-Disturb status on Chrome side from Android side.
[MinVersion=18]
OnDoNotDisturbStatusUpdated@7(ArcDoNotDisturbStatus status);
// Tells the Chrome that a notification is posted (created or updated) on
// Android. If you know that the notification exists, please consider to use
// OnNotificationUpdate instead.
......@@ -177,7 +186,7 @@ interface NotificationsHost {
OpenMessageCenter@6();
};
// Next Method ID: 7
// Next Method ID: 8
// TODO(lhchavez): Migrate all request/response messages to Mojo.
interface NotificationsInstance {
// DEPRECATED: Please use Init@5 instead.
......@@ -213,4 +222,8 @@ interface NotificationsInstance {
// side.
[MinVersion=17]
OpenNotificationSnoozeSettings@6(string key);
// Set the Do-Not-Disturb status on Android side from Chrome side.
[MinVersion=18]
SetDoNotDisturbStatusOnAndroid@7(ArcDoNotDisturbStatus status);
};
......@@ -30,6 +30,11 @@ void FakeNotificationsInstance::OpenNotificationSettings(
void FakeNotificationsInstance::OpenNotificationSnoozeSettings(
const std::string& key) {}
void FakeNotificationsInstance::SetDoNotDisturbStatusOnAndroid(
mojom::ArcDoNotDisturbStatusPtr status) {
latest_do_not_disturb_status_ = std::move(status);
}
void FakeNotificationsInstance::InitDeprecated(
mojom::NotificationsHostPtr host_ptr) {
Init(std::move(host_ptr), base::DoNothing());
......@@ -45,4 +50,9 @@ FakeNotificationsInstance::events() const {
return events_;
}
const mojom::ArcDoNotDisturbStatusPtr&
FakeNotificationsInstance::latest_do_not_disturb_status() const {
return latest_do_not_disturb_status_;
}
} // namespace arc
......@@ -30,12 +30,16 @@ class FakeNotificationsInstance : public mojom::NotificationsInstance {
void CloseNotificationWindow(const std::string& key) override;
void OpenNotificationSettings(const std::string& key) override;
void OpenNotificationSnoozeSettings(const std::string& key) override;
void SetDoNotDisturbStatusOnAndroid(
mojom::ArcDoNotDisturbStatusPtr status) override;
const std::vector<std::pair<std::string, mojom::ArcNotificationEvent>>&
events() const;
const mojom::ArcDoNotDisturbStatusPtr& latest_do_not_disturb_status() const;
private:
std::vector<std::pair<std::string, mojom::ArcNotificationEvent>> events_;
mojom::ArcDoNotDisturbStatusPtr latest_do_not_disturb_status_;
DISALLOW_COPY_AND_ASSIGN(FakeNotificationsInstance);
};
......
......@@ -15,9 +15,11 @@ FakeMessageCenter::~FakeMessageCenter() {
}
void FakeMessageCenter::AddObserver(MessageCenterObserver* observer) {
observer_list_.AddObserver(observer);
}
void FakeMessageCenter::RemoveObserver(MessageCenterObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void FakeMessageCenter::AddNotificationBlocker(NotificationBlocker* blocker) {
......
......@@ -74,8 +74,12 @@ class FakeMessageCenter : public MessageCenter {
protected:
void DisableTimersForTest() override;
const base::ObserverList<MessageCenterObserver>& observer_list() const {
return observer_list_;
}
private:
base::ObserverList<MessageCenterObserver> observer_list_;
const NotificationList::Notifications empty_notifications_;
bool has_message_center_view_ = true;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment