Commit f0d66f2e authored by Jazz Xu's avatar Jazz Xu Committed by Commit Bot

CrOS GMC: Populate Zenith dialog.

This CL create a bubble for MediaTray and display
MediaNotificationListView which got from MediaNotificationProviderImpl.

Bug: 1122419
Change-Id: I7cb9a999f6629b63f896e9919e1692a57d168713
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2376982Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarTim Song <tengs@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Commit-Queue: Jazz Xu <jazzhsu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806321}
parent 14b94d04
......@@ -2045,6 +2045,7 @@ test("ash_unittests") {
"system/ime_menu/ime_menu_tray_unittest.cc",
"system/locale/locale_feature_pod_controller_unittest.cc",
"system/machine_learning/user_settings_event_logger_unittest.cc",
"system/media/media_tray_unittest.cc",
"system/message_center/ash_message_popup_collection_unittest.cc",
"system/message_center/inactive_user_notification_blocker_unittest.cc",
"system/message_center/message_center_ui_controller_unittest.cc",
......
......@@ -2420,6 +2420,10 @@ This file contains the strings for ash.
Control your music, videos, and more
</message>
<message name="IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE" desc="The label used as global media controls' title.">
Media Controls
</message>
<!-- Power off menu -->
<message name="IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON" desc="Text shown on power off button in power button menu.">
Power off
......
819afa95349787705b722fc6ce7176998c5a1743
\ No newline at end of file
......@@ -8,6 +8,7 @@
#include <memory>
#include "ash/public/cpp/ash_public_export.h"
#include "third_party/skia/include/core/SkColor.h"
namespace views {
class View;
......@@ -38,12 +39,17 @@ class ASH_PUBLIC_EXPORT MediaNotificationProvider {
// Returns a MediaNotificationListView populated with the correct
// MediaNotificationContainerImpls. Used to populate the dialog on the Ash
// shelf.
virtual std::unique_ptr<views::View> GetMediaNotificationListView() = 0;
virtual std::unique_ptr<views::View> GetMediaNotificationListView(
SkColor separator_color,
int separator_thickness) = 0;
// Returns a MediaNotificationContainerimplView for the active MediaSession.
// Displayed in the quick settings of the Ash shelf.
virtual std::unique_ptr<views::View> GetActiveMediaNotificationView() = 0;
// Used for ash to notify the bubble is closing.
virtual void OnBubbleClosing() = 0;
protected:
virtual ~MediaNotificationProvider() = default;
};
......
......@@ -7,18 +7,60 @@
#include "ash/public/cpp/media_notification_provider.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_popup_item_style.h"
#include "ash/system/tray/tray_utils.h"
#include "base/strings/string_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/separator.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// View that contains global media controls' title.
class GlobalMediaControlsTitleView : public views::View {
public:
GlobalMediaControlsTitleView() {
SetBorder(views::CreatePaddedBorder(
views::CreateSolidSidedBorder(
0, 0, kMenuSeparatorWidth, 0,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor)),
gfx::Insets(kMenuSeparatorVerticalPadding, 0,
kMenuSeparatorVerticalPadding - kMenuSeparatorWidth, 0)));
auto* box_layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0, 0, 0)));
box_layout->set_minimum_cross_axis_size(kTrayPopupItemMinHeight);
auto* title_label = AddChildView(std::make_unique<views::Label>());
title_label->SetText(
l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE));
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label->SetBorder(views::CreateEmptyBorder(0, 16, 0, 0));
TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::SMALL_TITLE,
true /* use_unified_theme */);
style.SetupLabel(title_label);
box_layout->SetFlexForView(title_label, 1);
}
};
} // namespace
MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
DCHECK(MediaNotificationProvider::Get());
MediaNotificationProvider::Get()->AddObserver(this);
......@@ -35,6 +77,9 @@ MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
}
MediaTray::~MediaTray() {
if (bubble_)
bubble_->GetBubbleView()->ResetDelegate();
if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->RemoveObserver(this);
}
......@@ -43,7 +88,12 @@ void MediaTray::OnNotificationListChanged() {
UpdateDisplayState();
}
void MediaTray::OnNotificationListViewSizeChanged() {}
void MediaTray::OnNotificationListViewSizeChanged() {
if (!bubble_)
return;
bubble_->GetBubbleView()->UpdateBubble();
}
base::string16 MediaTray::GetAccessibleNameForTray() {
return l10n_util::GetStringUTF16(
......@@ -60,13 +110,78 @@ void MediaTray::HandleLocaleChange() {
IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT));
}
bool MediaTray::PerformAction(const ui::Event& event) {
if (bubble_)
CloseBubble();
else
ShowBubble(event.IsMouseEvent() || event.IsGestureEvent());
return true;
}
void MediaTray::ShowBubble(bool show_by_click) {
DCHECK(MediaNotificationProvider::Get());
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
init_params.parent_window = GetBubbleWindowContainer();
init_params.anchor_view = nullptr;
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect = shelf()->GetSystemTrayAnchorRect();
init_params.insets = GetTrayBubbleInsets();
init_params.shelf_alignment = shelf()->alignment();
init_params.preferred_width = kTrayMenuWidth;
init_params.close_on_deactivate = true;
init_params.has_shadow = false;
init_params.translucent = true;
init_params.corner_radius = kTrayItemCornerRadius;
init_params.show_by_click = show_by_click;
TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
auto* title_view_ptr = bubble_view->AddChildView(
std::make_unique<GlobalMediaControlsTitleView>());
title_view_ptr->SetPaintToLayer();
title_view_ptr->layer()->SetFillsBoundsOpaquely(false);
bubble_view->AddChildView(
MediaNotificationProvider::Get()->GetMediaNotificationListView(
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor),
kMenuSeparatorWidth));
bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view,
false /*is_persistent*/);
SetIsActive(true);
}
void MediaTray::CloseBubble() {
if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->OnBubbleClosing();
SetIsActive(false);
bubble_.reset();
shelf()->UpdateAutoHideState();
}
void MediaTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
if (bubble_ && bubble_->bubble_view() == bubble_view)
CloseBubble();
}
void MediaTray::ClickedOutsideBubble() {
CloseBubble();
}
void MediaTray::UpdateDisplayState() {
if (!MediaNotificationProvider::Get())
return;
SetVisiblePreferred(
bool should_show =
MediaNotificationProvider::Get()->HasActiveNotifications() ||
MediaNotificationProvider::Get()->HasFrozenNotifications());
MediaNotificationProvider::Get()->HasFrozenNotifications();
if (!should_show && bubble_)
CloseBubble();
SetVisiblePreferred(should_show);
}
} // namespace ash
......@@ -15,6 +15,7 @@ class ImageView;
namespace ash {
class Shelf;
class TrayBubbleWrapper;
class MediaTray : public MediaNotificationProviderObserver,
public TrayBackgroundView {
......@@ -30,12 +31,22 @@ class MediaTray : public MediaNotificationProviderObserver,
base::string16 GetAccessibleNameForTray() override;
void UpdateAfterLoginStatusChange() override;
void HandleLocaleChange() override;
void HideBubbleWithView(const TrayBubbleView* bubble_view) override {}
void ClickedOutsideBubble() override {}
bool PerformAction(const ui::Event& event) override;
void ShowBubble(bool show_by_click) override;
void CloseBubble() override;
void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
void ClickedOutsideBubble() override;
TrayBubbleWrapper* tray_bubble_wrapper_for_testing() { return bubble_.get(); }
private:
friend class MediaTrayTest;
// Show/hide media tray.
void UpdateDisplayState();
std::unique_ptr<TrayBubbleWrapper> bubble_;
// Weak pointer, will be parented by TrayContainer for its lifetime.
views::ImageView* icon_;
};
......
// Copyright 2020 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/media/media_tray.h"
#include "ash/public/cpp/media_notification_provider.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 "media/base/media_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/events/event.h"
using ::testing::_;
namespace ash {
namespace {
class MockMediaNotificationProvider : public MediaNotificationProvider {
public:
MockMediaNotificationProvider() {
MediaNotificationProvider::Set(this);
ON_CALL(*this, GetMediaNotificationListView(_, _))
.WillByDefault(
[](auto, auto) { return std::make_unique<views::View>(); });
}
// Medianotificationprovider implementations.
MOCK_METHOD2(GetMediaNotificationListView,
std::unique_ptr<views::View>(SkColor, int));
MOCK_METHOD0(GetActiveMediaNotificationView, std::unique_ptr<views::View>());
MOCK_METHOD0(OnBubbleClosing, void());
void AddObserver(MediaNotificationProviderObserver* observer) override {}
void RemoveObserver(MediaNotificationProviderObserver* observer) override {}
bool HasActiveNotifications() override { return has_active_notifications_; }
bool HasFrozenNotifications() override { return has_frozen_notifications_; }
void SetHasActiveNotifications(bool has_active_notifications) {
has_active_notifications_ = has_active_notifications;
}
void SetHasFrozenNotifications(bool has_frozen_notifications) {
has_frozen_notifications_ = has_frozen_notifications;
}
private:
bool has_active_notifications_ = false;
bool has_frozen_notifications_ = false;
};
} // namespace
class MediaTrayTest : public AshTestBase {
public:
MediaTrayTest() = default;
~MediaTrayTest() override = default;
void SetUp() override {
feature_list_.InitAndEnableFeature(media::kGlobalMediaControlsForChromeOS);
provider_ = std::make_unique<MockMediaNotificationProvider>();
AshTestBase::SetUp();
media_tray_ =
StatusAreaWidgetTestHelper::GetStatusAreaWidget()->media_tray();
}
void SimulateNotificationListChanged() {
media_tray_->OnNotificationListChanged();
}
void SimulateTapOnMediaTray() {
ui::GestureEvent tap(0, 0, 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_TAP));
media_tray_->PerformAction(tap);
}
TrayBubbleWrapper* GetBubbleWrapper() {
return media_tray_->tray_bubble_wrapper_for_testing();
}
MockMediaNotificationProvider* provider() { return provider_.get(); }
MediaTray* media_tray() { return media_tray_; }
private:
std::unique_ptr<MockMediaNotificationProvider> provider_;
MediaTray* media_tray_;
base::test::ScopedFeatureList feature_list_;
};
TEST_F(MediaTrayTest, MediaTrayVisibilityTest) {
// Media tray should be invisible initially.
ASSERT_TRUE(media_tray());
EXPECT_FALSE(media_tray()->GetVisible());
// Media tray should be visible when there is active notification.
provider()->SetHasActiveNotifications(true);
SimulateNotificationListChanged();
EXPECT_TRUE(media_tray()->GetVisible());
// Media tray should hide itself when no media is playing.
provider()->SetHasActiveNotifications(false);
SimulateNotificationListChanged();
EXPECT_FALSE(media_tray()->GetVisible());
// Media tray should be visible when there is frozen notification.
provider()->SetHasFrozenNotifications(true);
SimulateNotificationListChanged();
EXPECT_TRUE(media_tray()->GetVisible());
}
TEST_F(MediaTrayTest, ShowAndHideBubbleTest) {
// Media tray should be visible when there is active notification.
provider()->SetHasActiveNotifications(true);
SimulateNotificationListChanged();
EXPECT_TRUE(media_tray()->GetVisible());
// Bubble should not exist initially, and media tray should not
// be active.
EXPECT_EQ(GetBubbleWrapper(), nullptr);
EXPECT_FALSE(media_tray()->is_active());
// Tap the media tray should show the bubble, and media tray should
// be active. GetMediaNotificationlistview also should be called for
// getting active notifications.
EXPECT_CALL(*provider(), GetMediaNotificationListView(_, _));
SimulateTapOnMediaTray();
EXPECT_NE(GetBubbleWrapper(), nullptr);
EXPECT_TRUE(media_tray()->is_active());
// Tap again should close the bubble and MediaNotificationProvider should
// be notified.
EXPECT_CALL(*provider(), OnBubbleClosing());
SimulateTapOnMediaTray();
EXPECT_EQ(GetBubbleWrapper(), nullptr);
EXPECT_FALSE(media_tray()->is_active());
}
TEST_F(MediaTrayTest, DialogCloseWhenNoActiveNotificationTest) {
// Media tray should be visible when there is active notification.
provider()->SetHasActiveNotifications(true);
SimulateNotificationListChanged();
EXPECT_TRUE(media_tray()->GetVisible());
// Bubble should not exist initially, and media tray should not
// be active.
EXPECT_EQ(GetBubbleWrapper(), nullptr);
EXPECT_FALSE(media_tray()->is_active());
// Tap and show bubble.
SimulateTapOnMediaTray();
EXPECT_NE(GetBubbleWrapper(), nullptr);
EXPECT_TRUE(media_tray()->is_active());
// Bubble should close if there's no active sessions.
EXPECT_CALL(*provider(), OnBubbleClosing());
provider()->SetHasActiveNotifications(false);
SimulateNotificationListChanged();
EXPECT_EQ(GetBubbleWrapper(), nullptr);
EXPECT_FALSE(media_tray()->GetVisible());
}
} // namespace ash
......@@ -112,6 +112,7 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
DictationButtonTray* dictation_button_tray() {
return dictation_button_tray_.get();
}
MediaTray* media_tray() { return media_tray_.get(); }
StatusAreaOverflowButtonTray* overflow_button_tray() {
return overflow_button_tray_.get();
}
......
......@@ -8,6 +8,8 @@
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service_factory.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
#include "components/session_manager/core/session_manager.h"
#include "ui/views/view.h"
......@@ -21,6 +23,9 @@ MediaNotificationProviderImpl::~MediaNotificationProviderImpl() {
if (service_)
service_->RemoveObserver(this);
for (auto containers_pair : observed_containers_)
containers_pair.second->RemoveObserver(this);
}
void MediaNotificationProviderImpl::AddObserver(
......@@ -46,8 +51,16 @@ bool MediaNotificationProviderImpl::HasFrozenNotifications() {
}
std::unique_ptr<views::View>
MediaNotificationProviderImpl::GetMediaNotificationListView() {
return std::make_unique<views::View>();
MediaNotificationProviderImpl::GetMediaNotificationListView(
SkColor separator_color,
int separator_thickness) {
DCHECK(service_);
auto notification_list_view = std::make_unique<MediaNotificationListView>(
MediaNotificationListView::SeparatorStyle(separator_color,
separator_thickness));
active_session_view_ = notification_list_view.get();
service_->SetDialogDelegate(this);
return std::move(notification_list_view);
}
std::unique_ptr<views::View>
......@@ -55,11 +68,63 @@ MediaNotificationProviderImpl::GetActiveMediaNotificationView() {
return std::make_unique<views::View>();
}
void MediaNotificationProviderImpl::OnBubbleClosing() {
service_->SetDialogDelegate(nullptr);
}
MediaNotificationContainerImpl* MediaNotificationProviderImpl::ShowMediaSession(
const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item) {
if (!active_session_view_)
return nullptr;
auto container =
std::make_unique<MediaNotificationContainerImplView>(id, item, service_);
MediaNotificationContainerImplView* container_ptr = container.get();
container_ptr->AddObserver(this);
observed_containers_[id] = container_ptr;
active_session_view_->ShowNotification(id, std::move(container));
for (auto& observer : observers_)
observer.OnNotificationListViewSizeChanged();
return container_ptr;
}
void MediaNotificationProviderImpl::HideMediaSession(const std::string& id) {
if (!active_session_view_)
return;
active_session_view_->HideNotification(id);
for (auto& observer : observers_)
observer.OnNotificationListViewSizeChanged();
}
std::unique_ptr<OverlayMediaNotification> MediaNotificationProviderImpl::PopOut(
const std::string& id,
gfx::Rect bounds) {
return active_session_view_->PopOut(id, bounds);
}
void MediaNotificationProviderImpl::OnNotificationListChanged() {
for (auto& observer : observers_)
observer.OnNotificationListChanged();
}
void MediaNotificationProviderImpl::OnContainerSizeChanged() {
for (auto& observer : observers_)
observer.OnNotificationListViewSizeChanged();
}
void MediaNotificationProviderImpl::OnContainerDestroyed(
const std::string& id) {
auto iter = observed_containers_.find(id);
DCHECK(iter != observed_containers_.end());
iter->second->RemoveObserver(this);
observed_containers_.erase(iter);
}
void MediaNotificationProviderImpl::OnUserProfileLoaded(
const AccountId& account_id) {
Profile* profile =
......
......@@ -7,14 +7,20 @@
#include "ash/public/cpp/media_notification_provider.h"
#include "base/observer_list.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_delegate.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service_observer.h"
#include "components/session_manager/core/session_manager_observer.h"
class MediaNotificationService;
class MediaNotificationListView;
class MediaNotificationContainerImplView;
class MediaNotificationProviderImpl
: public ash::MediaNotificationProvider,
public MediaDialogDelegate,
public MediaNotificationServiceObserver,
public MediaNotificationContainerObserver,
public session_manager::SessionManagerObserver {
public:
MediaNotificationProviderImpl();
......@@ -26,21 +32,51 @@ class MediaNotificationProviderImpl
ash::MediaNotificationProviderObserver* observer) override;
bool HasActiveNotifications() override;
bool HasFrozenNotifications() override;
std::unique_ptr<views::View> GetMediaNotificationListView() override;
std::unique_ptr<views::View> GetMediaNotificationListView(
SkColor separator_color,
int separator_thickness) override;
std::unique_ptr<views::View> GetActiveMediaNotificationView() override;
void OnBubbleClosing() override;
// MediaDialogDelegate implementations.
MediaNotificationContainerImpl* ShowMediaSession(
const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item) override;
void HideMediaSession(const std::string& id) override;
std::unique_ptr<OverlayMediaNotification> PopOut(const std::string& id,
gfx::Rect bounds) override;
// MediaNotificationServiceObserver implementations.
void OnNotificationListChanged() override;
void OnMediaDialogOpened() override {}
void OnMediaDialogClosed() override {}
// MediaNotificationContainerObserver implementations.
void OnContainerSizeChanged() override;
void OnContainerMetadataChanged() override {}
void OnContainerActionsChanged() override {}
void OnContainerClicked(const std::string& id) override {}
void OnContainerDismissed(const std::string& id) override {}
void OnContainerDestroyed(const std::string& id) override;
void OnContainerDraggedOut(const std::string& id, gfx::Rect bounds) override {
}
void OnAudioSinkChosen(const std::string& id,
const std::string& sink_id) override {}
// SessionManagerobserver implementation.
void OnUserProfileLoaded(const AccountId& account_id) override;
MediaNotificationService* service_for_testing() { return service_; }
private:
base::ObserverList<ash::MediaNotificationProviderObserver> observers_;
MediaNotificationListView* active_session_view_ = nullptr;
MediaNotificationService* service_ = nullptr;
std::map<const std::string, MediaNotificationContainerImplView*>
observed_containers_;
};
#endif // CHROME_BROWSER_UI_ASH_MEDIA_NOTIFICATION_PROVIDER_IMPL_H_
// Copyright 2020 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 "chrome/browser/ui/ash/media_notification_provider_impl.h"
#include "ash/public/cpp/media_notification_provider_observer.h"
#include "base/unguessable_token.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service_factory.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/session_manager/core/session_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view.h"
using media_session::mojom::AudioFocusRequestState;
using media_session::mojom::AudioFocusRequestStatePtr;
using media_session::mojom::MediaSessionInfo;
using media_session::mojom::MediaSessionInfoPtr;
namespace {
class MockMediaNotificationProviderObserver
: public ash::MediaNotificationProviderObserver {
public:
MOCK_METHOD0(OnNotificationListChanged, void());
MOCK_METHOD0(OnNotificationListViewSizeChanged, void());
};
std::unique_ptr<KeyedService> CreateMediaNotificationService(
content::BrowserContext* context) {
return std::make_unique<MediaNotificationService>(
Profile::FromBrowserContext(context), true /* show_from_all_profiles */);
}
} // namespace
class MediaNotificationProviderImplTest : public testing::Test {
protected:
MediaNotificationProviderImplTest() {}
~MediaNotificationProviderImplTest() override {}
void SetUp() override {
testing::Test::SetUp();
user_manager_->Initialize();
CHECK(testing_profile_manager_.SetUp());
AccountId account_id(AccountId::FromUserEmail("foo@test.com"));
user_manager::User* user = user_manager_->AddPublicAccountUser(account_id);
Profile* profile =
testing_profile_manager_.CreateTestingProfile("test-profile");
chromeos::ProfileHelper::Get()->SetUserToProfileMappingForTesting(user,
profile);
chromeos::ProfileHelper::Get()->SetProfileToUserMappingForTesting(user);
user_manager_->LoginUser(account_id);
DCHECK(user_manager_->GetPrimaryUser());
MediaNotificationServiceFactory::GetInstance()->SetTestingFactory(
profile, base::BindRepeating(&CreateMediaNotificationService));
provider_ = std::make_unique<MediaNotificationProviderImpl>();
mock_observer_ = std::make_unique<MockMediaNotificationProviderObserver>();
session_manager_.NotifyUserProfileLoaded(account_id);
DCHECK(provider_->service_for_testing());
provider_->AddObserver(mock_observer_.get());
}
void TearDown() override {
mock_observer_.reset();
provider_.reset();
user_manager_->Destroy();
testing::Test::TearDown();
}
void SimulateShowNotification(base::UnguessableToken id) {
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->is_controllable = true;
AudioFocusRequestStatePtr focus(AudioFocusRequestState::New());
focus->request_id = id;
focus->session_info = std::move(session_info);
provider_->service_for_testing()->OnFocusGained(std::move(focus));
provider_->service_for_testing()->ShowNotification(id.ToString());
}
void SimulateHideNotification(base::UnguessableToken id) {
provider_->service_for_testing()->HideNotification(id.ToString());
}
MockMediaNotificationProviderObserver* observer() {
return mock_observer_.get();
}
MediaNotificationProviderImpl* provider() { return provider_.get(); }
content::BrowserTaskEnvironment browser_environment;
private:
session_manager::SessionManager session_manager_;
chromeos::FakeChromeUserManager* user_manager_{
new chromeos::FakeChromeUserManager()};
TestingProfileManager testing_profile_manager_{
TestingBrowserProcess::GetGlobal()};
views::LayoutProvider layout_provider;
std::unique_ptr<MockMediaNotificationProviderObserver> mock_observer_;
std::unique_ptr<MediaNotificationProviderImpl> provider_;
};
TEST_F(MediaNotificationProviderImplTest, NotificationListTest) {
auto id_1 = base::UnguessableToken::Create();
auto id_2 = base::UnguessableToken::Create();
EXPECT_CALL(*observer(), OnNotificationListViewSizeChanged).Times(2);
SimulateShowNotification(id_1);
SimulateShowNotification(id_2);
std::unique_ptr<views::View> view =
provider()->GetMediaNotificationListView(SK_ColorBLACK, 1);
auto* notification_list_view =
static_cast<MediaNotificationListView*>(view.get());
EXPECT_EQ(notification_list_view->notifications_for_testing().size(), 2u);
EXPECT_CALL(*observer(), OnNotificationListViewSizeChanged);
SimulateHideNotification(id_1);
EXPECT_EQ(notification_list_view->notifications_for_testing().size(), 1u);
provider()->OnBubbleClosing();
}
TEST_F(MediaNotificationProviderImplTest, NotifyObserverOnListChangeTest) {
auto id = base::UnguessableToken::Create();
// Expecting 2 calls: one when MediaSessionNotificationItem is created in
// MediaNotificationService::OnFocusgained, one when
// MediaNotificationService::ShowNotification is called in
// SimulateShownotification.
EXPECT_CALL(*observer(), OnNotificationListChanged).Times(2);
SimulateShowNotification(id);
EXPECT_CALL(*observer(), OnNotificationListChanged);
SimulateHideNotification(id);
}
......@@ -18,8 +18,9 @@ constexpr int kMediaListMaxHeight = 478;
// Thickness of separator border.
constexpr int kMediaListSeparatorThickness = 2;
std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color) {
return views::CreateSolidSidedBorder(/*top=*/kMediaListSeparatorThickness,
std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color,
int thickness) {
return views::CreateSolidSidedBorder(/*top=*/thickness,
/*left=*/0,
/*bottom=*/0,
/*right=*/0, color);
......@@ -27,7 +28,18 @@ std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color) {
} // anonymous namespace
MediaNotificationListView::MediaNotificationListView() {
MediaNotificationListView::SeparatorStyle::SeparatorStyle(
SkColor separator_color,
int separator_thickness)
: separator_color(separator_color),
separator_thickness(separator_thickness) {}
MediaNotificationListView::MediaNotificationListView()
: MediaNotificationListView(base::nullopt) {}
MediaNotificationListView::MediaNotificationListView(
const base::Optional<SeparatorStyle>& separator_style)
: separator_style_(separator_style) {
SetContents(std::make_unique<views::View>());
contents()->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
......@@ -50,9 +62,16 @@ void MediaNotificationListView::ShowNotification(
// If this isn't the first notification, then create a top-sided separator
// border.
if (!notifications_.empty()) {
SkColor separator_color = GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_MenuSeparatorColor);
notification->SetBorder(CreateMediaListSeparatorBorder(separator_color));
if (separator_style_.has_value()) {
notification->SetBorder(CreateMediaListSeparatorBorder(
separator_style_->separator_color,
separator_style_->separator_thickness));
} else {
notification->SetBorder(CreateMediaListSeparatorBorder(
GetNativeTheme()->GetSystemColor(
ui::NativeTheme::kColorId_MenuSeparatorColor),
kMediaListSeparatorThickness));
}
}
notifications_[id] = contents()->AddChildView(std::move(notification));
......
......@@ -8,6 +8,7 @@
#include <map>
#include <memory>
#include "base/optional.h"
#include "ui/views/controls/scroll_view.h"
class MediaNotificationContainerImplView;
......@@ -17,6 +18,15 @@ class OverlayMediaNotification;
// sessions.
class MediaNotificationListView : public views::ScrollView {
public:
struct SeparatorStyle {
SeparatorStyle(SkColor separator_color, int separator_thickness);
const SkColor separator_color;
const int separator_thickness;
};
explicit MediaNotificationListView(
const base::Optional<SeparatorStyle>& separator_style);
MediaNotificationListView();
~MediaNotificationListView() override;
......@@ -47,6 +57,8 @@ class MediaNotificationListView : public views::ScrollView {
std::map<const std::string, MediaNotificationContainerImplView*>
notifications_;
base::Optional<SeparatorStyle> separator_style_;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationListView);
};
......
......@@ -4744,6 +4744,7 @@ test("unit_tests") {
"../browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc",
"../browser/ui/ash/launcher/shelf_context_menu_unittest.cc",
"../browser/ui/ash/media_client_impl_unittest.cc",
"../browser/ui/ash/media_notification_provider_impl_unittest.cc",
"../browser/ui/ash/multi_user/multi_profile_support_unittest.cc",
"../browser/ui/ash/multi_user/multi_user_context_menu_chromeos_unittest.cc",
"../browser/ui/ash/multi_user/multi_user_util_chromeos_unittest.cc",
......
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