Commit bfb7ac21 authored by Abigail Klein's avatar Abigail Klein Committed by Commit Bot

[Live Caption] Introduce a Live Caption toggle button to the global

media controls dialog.

Live Caption is a Chrome-wide feature that is currently enabled through
settings. To provide a more accessible entrypoint to the Live Caption
feature, we are adding a button to the global media controls dialog.
This button is located below the media notification container in a
footer.

Screenshots:
Live Caption enabled: https://screenshot.googleplex.com/5PpHAbLDzA7jPbe
Live Caption disabled: https://screenshot.googleplex.com/4abNrFcTiiFmTdc

UX Mock: https://docs.google.com/presentation/d/13BT8O7Gggi41sAUlIVRiwJnE73cKYmSUDniZgV_gQxE/edit#slide=id.g88757e649e_0_10

Bug: 1055150
Change-Id: If195f291da92a8b81cc070b72ac072327a2ebd65
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2463705Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Commit-Queue: Abigail Klein <abigailbklein@google.com>
Cr-Commit-Position: refs/heads/master@{#816367}
parent c2f3aa63
......@@ -15,5 +15,8 @@
</message>
<message name="IDS_GLOBAL_MEDIA_CONTROLS_STOP_CASTING_BUTTON_LABEL" desc="Label for a button that stops the current cast session.">
Stop casting
</message>
<message name="IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION" desc="Label for the toggle that enables the Live Caption feature. Live Caption only works for English captions.">
Live Caption (English only)
</message>
</grit-part>
41b4f4aa6df1fecc6b9632615aeecf08260130c4
\ No newline at end of file
......@@ -65,6 +65,7 @@ aggregate_vector_icons2("chrome_vector_icons") {
"keyboard_arrow_right.icon",
"keyboard_arrow_up.icon",
"laptop.icon",
"live_caption.icon",
"media_toolbar_button.icon",
"media_toolbar_button_touch.icon",
"mixed_content.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.
CANVAS_DIMENSIONS, 20,
MOVE_TO, 2, 5,
CUBIC_TO, 2, 4.45f, 2.45f, 4, 3, 4,
H_LINE_TO, 17,
CUBIC_TO, 17.55f, 4, 18, 4.45f, 18, 5,
V_LINE_TO, 15,
CUBIC_TO, 18, 15.55f, 17.55f, 16, 17, 16,
H_LINE_TO, 3,
CUBIC_TO, 2.45f, 16, 2, 15.55f, 2, 15,
V_LINE_TO, 5,
CLOSE,
MOVE_TO, 4, 6,
H_LINE_TO, 16,
V_LINE_TO, 14,
H_LINE_TO, 4,
V_LINE_TO, 6,
CLOSE,
MOVE_TO, 5, 8,
H_LINE_TO, 7,
V_LINE_TO, 10,
H_LINE_TO, 5,
V_LINE_TO, 8,
CLOSE,
MOVE_TO, 13, 11,
H_LINE_TO, 15,
V_LINE_TO, 13,
H_LINE_TO, 13,
V_LINE_TO, 11,
CLOSE,
MOVE_TO, 15, 8,
H_LINE_TO, 8,
V_LINE_TO, 10,
H_LINE_TO, 15,
V_LINE_TO, 8,
CLOSE,
MOVE_TO, 5, 11,
H_LINE_TO, 12,
V_LINE_TO, 13,
H_LINE_TO, 5,
V_LINE_TO, 11,
CLOSE
......@@ -4,22 +4,47 @@
#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
#include "chrome/browser/ui/global_media_controls/overlay_media_notification.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/global_media_controls/media_dialog_view_observer.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 "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/background.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/toggle_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
using media_session::mojom::MediaSessionAction;
namespace {
static constexpr int kLiveCaptionBetweenChildSpacing = 4;
static constexpr int kLiveCaptionHorizontalMarginDip = 10;
static constexpr int kLiveCaptionImageWidthDip = 20;
static constexpr int kLiveCaptionVerticalMarginDip = 16;
} // namespace
// static
MediaDialogView* MediaDialogView::instance_ = nullptr;
......@@ -28,10 +53,11 @@ bool MediaDialogView::has_been_opened_ = false;
// static
views::Widget* MediaDialogView::ShowDialog(views::View* anchor_view,
MediaNotificationService* service) {
MediaNotificationService* service,
Profile* profile) {
DCHECK(!instance_);
DCHECK(service);
instance_ = new MediaDialogView(anchor_view, service);
instance_ = new MediaDialogView(anchor_view, service, profile);
views::Widget* widget =
views::BubbleDialogDelegateView::CreateBubble(instance_);
......@@ -72,7 +98,7 @@ MediaNotificationContainerImpl* MediaDialogView::ShowMediaSession(
observed_containers_[id] = container_ptr;
active_sessions_view_->ShowNotification(id, std::move(container));
SizeToContents();
UpdateBubbleSize();
for (auto& observer : observers_)
observer.OnMediaSessionShown();
......@@ -86,7 +112,7 @@ void MediaDialogView::HideMediaSession(const std::string& id) {
if (active_sessions_view_->empty())
HideDialog();
else
SizeToContents();
UpdateBubbleSize();
for (auto& observer : observers_)
observer.OnMediaSessionHidden();
......@@ -113,6 +139,8 @@ void MediaDialogView::AddedToWidget() {
SetPaintToLayer();
layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(corner_radius));
if (IsLiveCaptionEnabled())
layer()->SetFillsBoundsOpaquely(false);
service_->SetDialogDelegate(this);
}
......@@ -128,8 +156,18 @@ gfx::Size MediaDialogView::CalculatePreferredSize() const {
return gfx::Size(width, 1);
}
void MediaDialogView::OnContainerSizeChanged() {
void MediaDialogView::UpdateBubbleSize() {
SizeToContents();
if (!IsLiveCaptionEnabled())
return;
const int width = GetPreferredSize().width();
const int height = live_caption_container_->GetPreferredSize().height();
live_caption_container_->SetPreferredSize(gfx::Size(width, height));
}
void MediaDialogView::OnContainerSizeChanged() {
UpdateBubbleSize();
}
void MediaDialogView::OnContainerMetadataChanged() {
......@@ -168,10 +206,16 @@ const MediaNotificationListView* MediaDialogView::GetListViewForTesting()
return active_sessions_view_;
}
views::Button* MediaDialogView::GetLiveCaptionButtonForTesting() {
return live_caption_button_;
}
MediaDialogView::MediaDialogView(views::View* anchor_view,
MediaNotificationService* service)
MediaNotificationService* service,
Profile* profile)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
service_(service),
profile_(profile),
active_sessions_view_(
AddChildView(std::make_unique<MediaNotificationListView>())) {
SetButtons(ui::DIALOG_BUTTON_NONE);
......@@ -186,8 +230,49 @@ MediaDialogView::~MediaDialogView() {
void MediaDialogView::Init() {
// Remove margins.
set_margins(gfx::Insets());
SetLayoutManager(std::make_unique<views::FillLayout>());
if (!IsLiveCaptionEnabled()) {
SetLayoutManager(std::make_unique<views::FillLayout>());
return;
}
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical))
->set_cross_axis_alignment(views::BoxLayout::CrossAxisAlignment::kStart);
views::View* live_caption_container = new views::View();
auto* live_caption_container_layout =
live_caption_container->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(kLiveCaptionHorizontalMarginDip,
kLiveCaptionVerticalMarginDip),
kLiveCaptionBetweenChildSpacing));
views::ImageView* live_caption_image = new views::ImageView();
live_caption_image->SetImage(
gfx::CreateVectorIcon(kLiveCaptionIcon, kLiveCaptionImageWidthDip,
SkColor(gfx::kGoogleGrey700)));
views::Label* live_caption_title = new views::Label(
l10n_util::GetStringUTF16(IDS_GLOBAL_MEDIA_CONTROLS_LIVE_CAPTION));
live_caption_title->SetHorizontalAlignment(
gfx::HorizontalAlignment::ALIGN_LEFT);
views::ToggleButton* live_caption_button =
new views::ToggleButton(base::BindRepeating(
&MediaDialogView::ToggleLiveCaption, base::Unretained(this)));
live_caption_button->SetIsOn(
profile_->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
live_caption_button->SetAccessibleName(live_caption_title->GetText());
live_caption_button->SetThumbOnColor(SkColor(gfx::kGoogleBlue600));
live_caption_button->SetTrackOnColor(SkColorSetA(gfx::kGoogleBlue600, 128));
live_caption_button->SetThumbOffColor(SK_ColorWHITE);
live_caption_button->SetTrackOffColor(SkColor(gfx::kGoogleGrey400));
live_caption_container->AddChildView(std::move(live_caption_image));
live_caption_container_layout->SetFlexForView(
live_caption_container->AddChildView(std::move(live_caption_title)), 1);
live_caption_button_ =
live_caption_container->AddChildView(std::move(live_caption_button));
live_caption_container_ = AddChildView(std::move(live_caption_container));
}
void MediaDialogView::WindowClosing() {
......@@ -196,3 +281,13 @@ void MediaDialogView::WindowClosing() {
service_->SetDialogDelegate(nullptr);
}
}
void MediaDialogView::ToggleLiveCaption(const ui::Event& event) {
bool enabled = !profile_->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled);
profile_->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, enabled);
live_caption_button_->SetIsOn(enabled);
}
bool MediaDialogView::IsLiveCaptionEnabled() {
return base::FeatureList::IsEnabled(media::kLiveCaption);
}
......@@ -5,6 +5,10 @@
#ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
#include <map>
#include <memory>
#include <string>
#include "base/observer_list.h"
#include "base/optional.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_delegate.h"
......@@ -15,6 +19,11 @@ class MediaDialogViewObserver;
class MediaNotificationContainerImplView;
class MediaNotificationListView;
class MediaNotificationService;
class Profile;
namespace views {
class ToggleButton;
}
// Dialog that shows media controls that control the active media session.
class MediaDialogView : public views::BubbleDialogDelegateView,
......@@ -22,7 +31,8 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
public MediaNotificationContainerObserver {
public:
static views::Widget* ShowDialog(views::View* anchor_view,
MediaNotificationService* service);
MediaNotificationService* service,
Profile* profile);
static void HideDialog();
static bool IsShowing();
......@@ -61,9 +71,12 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
const MediaNotificationListView* GetListViewForTesting() const;
views::Button* GetLiveCaptionButtonForTesting();
private:
explicit MediaDialogView(views::View* anchor_view,
MediaNotificationService* service);
MediaNotificationService* service,
Profile* profile);
~MediaDialogView() override;
static MediaDialogView* instance_;
......@@ -75,8 +88,16 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
void Init() override;
void WindowClosing() override;
// views::Button::PressedCallback
void ToggleLiveCaption(const ui::Event& event);
void UpdateBubbleSize();
bool IsLiveCaptionEnabled();
MediaNotificationService* const service_;
Profile* const profile_;
MediaNotificationListView* const active_sessions_view_;
base::ObserverList<MediaDialogViewObserver> observers_;
......@@ -85,6 +106,10 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
std::map<const std::string, MediaNotificationContainerImplView*>
observed_containers_;
views::View* live_caption_container_ = nullptr;
views::ToggleButton* live_caption_button_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(MediaDialogView);
};
......
......@@ -16,6 +16,7 @@
#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
#include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
#include "chrome/browser/ui/views/toolbar/toolbar_view.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
......@@ -307,7 +308,9 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest {
void SetUp() override {
feature_list_.InitWithFeatures(
{media::kGlobalMediaControls, media::kGlobalMediaControlsForCast}, {});
{media::kGlobalMediaControls, media::kGlobalMediaControlsForCast,
media::kLiveCaption},
{});
presentation_manager_ =
std::make_unique<TestWebContentsPresentationManager>();
......@@ -464,6 +467,14 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest {
ClickButton(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
}
void ClickEnableLiveCaptionOnDialog() {
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(MediaDialogView::IsShowing());
auto* live_caption_button = MediaDialogView::GetDialogViewForTesting()
->GetLiveCaptionButtonForTesting();
ClickButton(live_caption_button);
}
void ClickNotificationByTitle(const base::string16& title) {
ASSERT_TRUE(MediaDialogView::IsShowing());
MediaNotificationContainerImplView* notification =
......@@ -841,3 +852,37 @@ IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
EXPECT_TRUE(IsPlayingSessionDisplayedFirst());
}
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, LiveCaption) {
// Open a tab and play media.
OpenTestURL();
StartPlayback();
WaitForStart();
// Open the media dialog.
WaitForVisibleToolbarIcon();
ClickToolbarIcon();
WaitForDialogOpened();
EXPECT_TRUE(IsDialogVisible());
ClickEnableLiveCaptionOnDialog();
EXPECT_TRUE(
browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
ClickEnableLiveCaptionOnDialog();
EXPECT_FALSE(
browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
// Close dialog and enable live caption preference. Reopen dialog.
ClickToolbarIcon();
EXPECT_FALSE(IsDialogVisible());
browser()->profile()->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled,
true);
ClickToolbarIcon();
WaitForDialogOpened();
EXPECT_TRUE(IsDialogVisible());
ClickEnableLiveCaptionOnDialog();
EXPECT_FALSE(
browser()->profile()->GetPrefs()->GetBoolean(prefs::kLiveCaptionEnabled));
}
......@@ -115,7 +115,7 @@ void MediaToolbarButtonView::ButtonPressed() {
if (MediaDialogView::IsShowing()) {
MediaDialogView::HideDialog();
} else {
MediaDialogView::ShowDialog(this, service_);
MediaDialogView::ShowDialog(this, service_, browser_->profile());
feature_promo_controller_->CloseBubble(
feature_engagement::kIPHLiveCaptionFeature);
......
......@@ -64,7 +64,7 @@ bool MediaRouterDialogControllerViews::ShowMediaRouterDialogForPresentation(
// just doing the same thing here doesn't work. I suspect that approach
// will work, though, once the issue causing the blue border is fixed.
scoped_widget_observer_.Add(
MediaDialogView::ShowDialog(media_button, service));
MediaDialogView::ShowDialog(media_button, service, profile));
return true;
} else {
// Delegate to the base class, which will show the Cast dialog.
......
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