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") { ...@@ -2045,6 +2045,7 @@ test("ash_unittests") {
"system/ime_menu/ime_menu_tray_unittest.cc", "system/ime_menu/ime_menu_tray_unittest.cc",
"system/locale/locale_feature_pod_controller_unittest.cc", "system/locale/locale_feature_pod_controller_unittest.cc",
"system/machine_learning/user_settings_event_logger_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/ash_message_popup_collection_unittest.cc",
"system/message_center/inactive_user_notification_blocker_unittest.cc", "system/message_center/inactive_user_notification_blocker_unittest.cc",
"system/message_center/message_center_ui_controller_unittest.cc", "system/message_center/message_center_ui_controller_unittest.cc",
......
...@@ -2420,6 +2420,10 @@ This file contains the strings for ash. ...@@ -2420,6 +2420,10 @@ This file contains the strings for ash.
Control your music, videos, and more Control your music, videos, and more
</message> </message>
<message name="IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE" desc="The label used as global media controls' title.">
Media Controls
</message>
<!-- Power off menu --> <!-- Power off menu -->
<message name="IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON" desc="Text shown on power off button in power button menu."> <message name="IDS_ASH_POWER_BUTTON_MENU_POWER_OFF_BUTTON" desc="Text shown on power off button in power button menu.">
Power off Power off
......
819afa95349787705b722fc6ce7176998c5a1743
\ No newline at end of file
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include "ash/public/cpp/ash_public_export.h" #include "ash/public/cpp/ash_public_export.h"
#include "third_party/skia/include/core/SkColor.h"
namespace views { namespace views {
class View; class View;
...@@ -38,12 +39,17 @@ class ASH_PUBLIC_EXPORT MediaNotificationProvider { ...@@ -38,12 +39,17 @@ class ASH_PUBLIC_EXPORT MediaNotificationProvider {
// Returns a MediaNotificationListView populated with the correct // Returns a MediaNotificationListView populated with the correct
// MediaNotificationContainerImpls. Used to populate the dialog on the Ash // MediaNotificationContainerImpls. Used to populate the dialog on the Ash
// shelf. // 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. // Returns a MediaNotificationContainerimplView for the active MediaSession.
// Displayed in the quick settings of the Ash shelf. // Displayed in the quick settings of the Ash shelf.
virtual std::unique_ptr<views::View> GetActiveMediaNotificationView() = 0; virtual std::unique_ptr<views::View> GetActiveMediaNotificationView() = 0;
// Used for ash to notify the bubble is closing.
virtual void OnBubbleClosing() = 0;
protected: protected:
virtual ~MediaNotificationProvider() = default; virtual ~MediaNotificationProvider() = default;
}; };
......
...@@ -7,18 +7,60 @@ ...@@ -7,18 +7,60 @@
#include "ash/public/cpp/media_notification_provider.h" #include "ash/public/cpp/media_notification_provider.h"
#include "ash/resources/vector_icons/vector_icons.h" #include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h" #include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/strings/grit/ash_strings.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_constants.h"
#include "ash/system/tray/tray_container.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 "ash/system/tray/tray_utils.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h" #include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.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 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) { MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
DCHECK(MediaNotificationProvider::Get()); DCHECK(MediaNotificationProvider::Get());
MediaNotificationProvider::Get()->AddObserver(this); MediaNotificationProvider::Get()->AddObserver(this);
...@@ -35,6 +77,9 @@ MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) { ...@@ -35,6 +77,9 @@ MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
} }
MediaTray::~MediaTray() { MediaTray::~MediaTray() {
if (bubble_)
bubble_->GetBubbleView()->ResetDelegate();
if (MediaNotificationProvider::Get()) if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->RemoveObserver(this); MediaNotificationProvider::Get()->RemoveObserver(this);
} }
...@@ -43,7 +88,12 @@ void MediaTray::OnNotificationListChanged() { ...@@ -43,7 +88,12 @@ void MediaTray::OnNotificationListChanged() {
UpdateDisplayState(); UpdateDisplayState();
} }
void MediaTray::OnNotificationListViewSizeChanged() {} void MediaTray::OnNotificationListViewSizeChanged() {
if (!bubble_)
return;
bubble_->GetBubbleView()->UpdateBubble();
}
base::string16 MediaTray::GetAccessibleNameForTray() { base::string16 MediaTray::GetAccessibleNameForTray() {
return l10n_util::GetStringUTF16( return l10n_util::GetStringUTF16(
...@@ -60,13 +110,78 @@ void MediaTray::HandleLocaleChange() { ...@@ -60,13 +110,78 @@ void MediaTray::HandleLocaleChange() {
IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT)); 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() { void MediaTray::UpdateDisplayState() {
if (!MediaNotificationProvider::Get()) if (!MediaNotificationProvider::Get())
return; return;
SetVisiblePreferred( bool should_show =
MediaNotificationProvider::Get()->HasActiveNotifications() || MediaNotificationProvider::Get()->HasActiveNotifications() ||
MediaNotificationProvider::Get()->HasFrozenNotifications()); MediaNotificationProvider::Get()->HasFrozenNotifications();
if (!should_show && bubble_)
CloseBubble();
SetVisiblePreferred(should_show);
} }
} // namespace ash } // namespace ash
...@@ -15,6 +15,7 @@ class ImageView; ...@@ -15,6 +15,7 @@ class ImageView;
namespace ash { namespace ash {
class Shelf; class Shelf;
class TrayBubbleWrapper;
class MediaTray : public MediaNotificationProviderObserver, class MediaTray : public MediaNotificationProviderObserver,
public TrayBackgroundView { public TrayBackgroundView {
...@@ -30,12 +31,22 @@ class MediaTray : public MediaNotificationProviderObserver, ...@@ -30,12 +31,22 @@ class MediaTray : public MediaNotificationProviderObserver,
base::string16 GetAccessibleNameForTray() override; base::string16 GetAccessibleNameForTray() override;
void UpdateAfterLoginStatusChange() override; void UpdateAfterLoginStatusChange() override;
void HandleLocaleChange() override; void HandleLocaleChange() override;
void HideBubbleWithView(const TrayBubbleView* bubble_view) override {} bool PerformAction(const ui::Event& event) override;
void ClickedOutsideBubble() 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: private:
friend class MediaTrayTest;
// Show/hide media tray.
void UpdateDisplayState(); void UpdateDisplayState();
std::unique_ptr<TrayBubbleWrapper> bubble_;
// Weak pointer, will be parented by TrayContainer for its lifetime. // Weak pointer, will be parented by TrayContainer for its lifetime.
views::ImageView* icon_; 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, ...@@ -112,6 +112,7 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
DictationButtonTray* dictation_button_tray() { DictationButtonTray* dictation_button_tray() {
return dictation_button_tray_.get(); return dictation_button_tray_.get();
} }
MediaTray* media_tray() { return media_tray_.get(); }
StatusAreaOverflowButtonTray* overflow_button_tray() { StatusAreaOverflowButtonTray* overflow_button_tray() {
return overflow_button_tray_.get(); return overflow_button_tray_.get();
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "chrome/browser/chromeos/profiles/profile_helper.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.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service_factory.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 "components/session_manager/core/session_manager.h"
#include "ui/views/view.h" #include "ui/views/view.h"
...@@ -21,6 +23,9 @@ MediaNotificationProviderImpl::~MediaNotificationProviderImpl() { ...@@ -21,6 +23,9 @@ MediaNotificationProviderImpl::~MediaNotificationProviderImpl() {
if (service_) if (service_)
service_->RemoveObserver(this); service_->RemoveObserver(this);
for (auto containers_pair : observed_containers_)
containers_pair.second->RemoveObserver(this);
} }
void MediaNotificationProviderImpl::AddObserver( void MediaNotificationProviderImpl::AddObserver(
...@@ -46,8 +51,16 @@ bool MediaNotificationProviderImpl::HasFrozenNotifications() { ...@@ -46,8 +51,16 @@ bool MediaNotificationProviderImpl::HasFrozenNotifications() {
} }
std::unique_ptr<views::View> std::unique_ptr<views::View>
MediaNotificationProviderImpl::GetMediaNotificationListView() { MediaNotificationProviderImpl::GetMediaNotificationListView(
return std::make_unique<views::View>(); 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> std::unique_ptr<views::View>
...@@ -55,11 +68,63 @@ MediaNotificationProviderImpl::GetActiveMediaNotificationView() { ...@@ -55,11 +68,63 @@ MediaNotificationProviderImpl::GetActiveMediaNotificationView() {
return std::make_unique<views::View>(); 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() { void MediaNotificationProviderImpl::OnNotificationListChanged() {
for (auto& observer : observers_) for (auto& observer : observers_)
observer.OnNotificationListChanged(); 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( void MediaNotificationProviderImpl::OnUserProfileLoaded(
const AccountId& account_id) { const AccountId& account_id) {
Profile* profile = Profile* profile =
......
...@@ -7,14 +7,20 @@ ...@@ -7,14 +7,20 @@
#include "ash/public/cpp/media_notification_provider.h" #include "ash/public/cpp/media_notification_provider.h"
#include "base/observer_list.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 "chrome/browser/ui/global_media_controls/media_notification_service_observer.h"
#include "components/session_manager/core/session_manager_observer.h" #include "components/session_manager/core/session_manager_observer.h"
class MediaNotificationService; class MediaNotificationService;
class MediaNotificationListView;
class MediaNotificationContainerImplView;
class MediaNotificationProviderImpl class MediaNotificationProviderImpl
: public ash::MediaNotificationProvider, : public ash::MediaNotificationProvider,
public MediaDialogDelegate,
public MediaNotificationServiceObserver, public MediaNotificationServiceObserver,
public MediaNotificationContainerObserver,
public session_manager::SessionManagerObserver { public session_manager::SessionManagerObserver {
public: public:
MediaNotificationProviderImpl(); MediaNotificationProviderImpl();
...@@ -26,21 +32,51 @@ class MediaNotificationProviderImpl ...@@ -26,21 +32,51 @@ class MediaNotificationProviderImpl
ash::MediaNotificationProviderObserver* observer) override; ash::MediaNotificationProviderObserver* observer) override;
bool HasActiveNotifications() override; bool HasActiveNotifications() override;
bool HasFrozenNotifications() 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; 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. // MediaNotificationServiceObserver implementations.
void OnNotificationListChanged() override; void OnNotificationListChanged() override;
void OnMediaDialogOpened() override {} void OnMediaDialogOpened() override {}
void OnMediaDialogClosed() 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. // SessionManagerobserver implementation.
void OnUserProfileLoaded(const AccountId& account_id) override; void OnUserProfileLoaded(const AccountId& account_id) override;
MediaNotificationService* service_for_testing() { return service_; }
private: private:
base::ObserverList<ash::MediaNotificationProviderObserver> observers_; base::ObserverList<ash::MediaNotificationProviderObserver> observers_;
MediaNotificationListView* active_session_view_ = nullptr;
MediaNotificationService* service_ = nullptr; MediaNotificationService* service_ = nullptr;
std::map<const std::string, MediaNotificationContainerImplView*>
observed_containers_;
}; };
#endif // CHROME_BROWSER_UI_ASH_MEDIA_NOTIFICATION_PROVIDER_IMPL_H_ #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; ...@@ -18,8 +18,9 @@ constexpr int kMediaListMaxHeight = 478;
// Thickness of separator border. // Thickness of separator border.
constexpr int kMediaListSeparatorThickness = 2; constexpr int kMediaListSeparatorThickness = 2;
std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color) { std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color,
return views::CreateSolidSidedBorder(/*top=*/kMediaListSeparatorThickness, int thickness) {
return views::CreateSolidSidedBorder(/*top=*/thickness,
/*left=*/0, /*left=*/0,
/*bottom=*/0, /*bottom=*/0,
/*right=*/0, color); /*right=*/0, color);
...@@ -27,7 +28,18 @@ std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color) { ...@@ -27,7 +28,18 @@ std::unique_ptr<views::Border> CreateMediaListSeparatorBorder(SkColor color) {
} // anonymous namespace } // 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>()); SetContents(std::make_unique<views::View>());
contents()->SetLayoutManager(std::make_unique<views::BoxLayout>( contents()->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical)); views::BoxLayout::Orientation::kVertical));
...@@ -50,9 +62,16 @@ void MediaNotificationListView::ShowNotification( ...@@ -50,9 +62,16 @@ void MediaNotificationListView::ShowNotification(
// If this isn't the first notification, then create a top-sided separator // If this isn't the first notification, then create a top-sided separator
// border. // border.
if (!notifications_.empty()) { if (!notifications_.empty()) {
SkColor separator_color = GetNativeTheme()->GetSystemColor( if (separator_style_.has_value()) {
ui::NativeTheme::kColorId_MenuSeparatorColor); notification->SetBorder(CreateMediaListSeparatorBorder(
notification->SetBorder(CreateMediaListSeparatorBorder(separator_color)); 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)); notifications_[id] = contents()->AddChildView(std::move(notification));
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include "base/optional.h"
#include "ui/views/controls/scroll_view.h" #include "ui/views/controls/scroll_view.h"
class MediaNotificationContainerImplView; class MediaNotificationContainerImplView;
...@@ -17,6 +18,15 @@ class OverlayMediaNotification; ...@@ -17,6 +18,15 @@ class OverlayMediaNotification;
// sessions. // sessions.
class MediaNotificationListView : public views::ScrollView { class MediaNotificationListView : public views::ScrollView {
public: 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();
~MediaNotificationListView() override; ~MediaNotificationListView() override;
...@@ -47,6 +57,8 @@ class MediaNotificationListView : public views::ScrollView { ...@@ -47,6 +57,8 @@ class MediaNotificationListView : public views::ScrollView {
std::map<const std::string, MediaNotificationContainerImplView*> std::map<const std::string, MediaNotificationContainerImplView*>
notifications_; notifications_;
base::Optional<SeparatorStyle> separator_style_;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationListView); DISALLOW_COPY_AND_ASSIGN(MediaNotificationListView);
}; };
......
...@@ -4744,6 +4744,7 @@ test("unit_tests") { ...@@ -4744,6 +4744,7 @@ test("unit_tests") {
"../browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc", "../browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc",
"../browser/ui/ash/launcher/shelf_context_menu_unittest.cc", "../browser/ui/ash/launcher/shelf_context_menu_unittest.cc",
"../browser/ui/ash/media_client_impl_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_profile_support_unittest.cc",
"../browser/ui/ash/multi_user/multi_user_context_menu_chromeos_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", "../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