Commit fedac800 authored by wutao's avatar wutao Committed by Commit Bot

ambient: Add mask layer to media string view

This patch adds a mask layer to generate gradient fade effect on the media
string.

Bug: b/167236615
Test: added new tests
Change-Id: I4c812c678031d0680c039c2c629aca082550ca9c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2414670
Commit-Queue: Tao Wu <wutao@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810934}
parent 27a0d652
......@@ -52,6 +52,8 @@ constexpr int kMarginLeftOfRelatedImageDip = 8;
// Media string related.
constexpr int kMediaStringMaxWidthDip = 280;
constexpr int kMediaStringGradientWidthDip = 20;
} // namespace ash
#endif // ASH_AMBIENT_AMBIENT_CONSTANTS_H_
......@@ -21,8 +21,10 @@
#include "services/media_session/public/mojom/media_session_service.mojom.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/shadow_value.h"
#include "ui/gfx/skia_paint_util.h"
#include "ui/gfx/text_constants.h"
#include "ui/gfx/transform.h"
#include "ui/views/border.h"
......@@ -35,6 +37,63 @@ namespace ash {
namespace {
// A layer delegate used for mask layer, with left and right gradient fading out
// zones.
class FadeoutLayerDelegate : public ui::LayerDelegate {
public:
explicit FadeoutLayerDelegate() : layer_(ui::LAYER_TEXTURED) {
layer_.set_delegate(this);
layer_.SetFillsBoundsOpaquely(false);
}
~FadeoutLayerDelegate() override { layer_.set_delegate(nullptr); }
ui::Layer* layer() { return &layer_; }
private:
// ui::LayerDelegate:
void OnPaintLayer(const ui::PaintContext& context) override {
const gfx::Size& size = layer()->size();
gfx::Rect left_rect(0, 0, kMediaStringGradientWidthDip, size.height());
gfx::Rect right_rect(size.width() - kMediaStringGradientWidthDip, 0,
kMediaStringGradientWidthDip, size.height());
views::PaintInfo paint_info =
views::PaintInfo::CreateRootPaintInfo(context, size);
const auto& prs = paint_info.paint_recording_size();
// Pass the scale factor when constructing PaintRecorder so the MaskLayer
// size is not incorrectly rounded (see https://crbug.com/921274).
ui::PaintRecorder recorder(context, paint_info.paint_recording_size(),
static_cast<float>(prs.width()) / size.width(),
static_cast<float>(prs.height()) / size.height(),
nullptr);
gfx::Canvas* canvas = recorder.canvas();
// Clear the canvas.
canvas->DrawColor(SK_ColorBLACK, SkBlendMode::kSrc);
// Draw left gradient zone.
cc::PaintFlags flags;
flags.setBlendMode(SkBlendMode::kSrc);
flags.setAntiAlias(false);
flags.setShader(gfx::CreateGradientShader(
gfx::Point(), gfx::Point(kMediaStringGradientWidthDip, 0),
SK_ColorTRANSPARENT, SK_ColorBLACK));
canvas->DrawRect(left_rect, flags);
// Draw right gradient zone.
flags.setShader(gfx::CreateGradientShader(
gfx::Point(size.width() - kMediaStringGradientWidthDip, 0),
gfx::Point(size.width(), 0), SK_ColorBLACK, SK_ColorTRANSPARENT));
canvas->DrawRect(right_rect, flags);
}
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override {}
ui::Layer layer_;
};
// Typography.
constexpr char kMiddleDotSeparator[] = " \u00B7 ";
constexpr char kPreceedingEighthNoteSymbol[] = "\u266A ";
......@@ -68,6 +127,10 @@ void MediaStringView::VisibilityChanged(View* starting_from, bool is_visible) {
media_text_->layer()->GetAnimator()->StopAnimating();
}
void MediaStringView::OnViewBoundsChanged(views::View* observed_view) {
UpdateMaskLayer();
}
void MediaStringView::MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) {
if (ambient::util::IsShowing(LockScreen::ScreenType::kLock) &&
......@@ -153,6 +216,7 @@ void MediaStringView::InitLayout() {
text_layout->SetOrientation(views::LayoutOrientation::kHorizontal);
text_layout->SetMainAxisAlignment(views::LayoutAlignment::kStart);
text_layout->SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
observed_view_.Add(media_text_container_);
media_text_ =
media_text_container_->AddChildView(std::make_unique<views::Label>());
......@@ -198,6 +262,21 @@ void MediaStringView::BindMediaControllerObserver() {
observer_receiver_.BindNewPipeAndPassRemote());
}
void MediaStringView::UpdateMaskLayer() {
if (!NeedToAnimate()) {
media_text_container_->layer()->SetMaskLayer(nullptr);
return;
}
if (!fadeout_layer_delegate_) {
fadeout_layer_delegate_ = std::make_unique<FadeoutLayerDelegate>();
fadeout_layer_delegate_->layer()->SetBounds(
media_text_container_->layer()->bounds());
}
media_text_container_->layer()->SetMaskLayer(
fadeout_layer_delegate_->layer());
}
bool MediaStringView::NeedToAnimate() const {
return media_text_->GetPreferredSize().width() >
media_text_container_->GetPreferredSize().width();
......
......@@ -5,11 +5,15 @@
#ifndef ASH_AMBIENT_UI_MEDIA_STRING_VIEW_H_
#define ASH_AMBIENT_UI_MEDIA_STRING_VIEW_H_
#include <memory>
#include "base/scoped_observer.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/views/view.h"
#include "ui/views/view_observer.h"
namespace views {
class Label;
......@@ -17,10 +21,15 @@ class Label;
namespace ash {
namespace {
class FadeoutLayerDelegate;
}
// Container for displaying ongoing media information, including the name of the
// media and the artist, formatted with a proceding music note symbol and a
// middle dot separator.
class MediaStringView : public views::View,
public views::ViewObserver,
public media_session::mojom::MediaControllerObserver,
public ui::ImplicitAnimationObserver {
public:
......@@ -29,10 +38,13 @@ class MediaStringView : public views::View,
MediaStringView& operator=(const MediaStringView&) = delete;
~MediaStringView() override;
// views::Label:
// views::View:
const char* GetClassName() const override;
void VisibilityChanged(View* starting_from, bool is_visible) override;
// views::ViewObserver:
void OnViewBoundsChanged(views::View* observed_view) override;
// media_session::mojom::MediaControllerObserver:
void MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) override;
......@@ -56,6 +68,8 @@ class MediaStringView : public views::View,
void BindMediaControllerObserver();
void UpdateMaskLayer();
bool NeedToAnimate() const;
// Get the transform of |media_text_| for scrolling animation.
......@@ -78,11 +92,15 @@ class MediaStringView : public views::View,
// With an extra copy of media info text for scrolling animation.
views::Label* media_text_ = nullptr;
std::unique_ptr<FadeoutLayerDelegate> fadeout_layer_delegate_;
// Used to receive updates to the active media controller.
mojo::Remote<media_session::mojom::MediaController> media_controller_remote_;
mojo::Receiver<media_session::mojom::MediaControllerObserver>
observer_receiver_{this};
ScopedObserver<views::View, views::ViewObserver> observed_view_{this};
base::WeakPtrFactory<MediaStringView> weak_factory_{this};
};
......
......@@ -251,6 +251,81 @@ TEST_F(MediaStringViewTest, PauseMediaWillStopAnimationWithLongText) {
GetMediaStringViewTextLabel()->layer()->GetAnimator()->is_animating());
}
TEST_F(MediaStringViewTest, HasNoMaskLayerWithShortText) {
ShowAmbientScreen();
// Sets metadata for current session.
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
SimulateMediaMetadataChanged(metadata);
// Wait for layout.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_LT(GetMediaStringViewTextLabel()->GetPreferredSize().width(),
kMediaStringMaxWidthDip);
EXPECT_FALSE(GetMediaStringViewTextContainer()->layer()->layer_mask_layer());
}
TEST_F(MediaStringViewTest, HasMaskLayerWithLongText) {
ShowAmbientScreen();
// Sets metadata for current session.
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("A super duper long title");
metadata.artist = base::ASCIIToUTF16("A super duper long artist name");
SimulateMediaMetadataChanged(metadata);
// Wait for layout.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_GT(GetMediaStringViewTextLabel()->GetPreferredSize().width(),
kMediaStringMaxWidthDip);
EXPECT_TRUE(GetMediaStringViewTextContainer()->layer()->layer_mask_layer());
}
TEST_F(MediaStringViewTest, MaskLayerShouldUpdate) {
ShowAmbientScreen();
// Sets metadata for current session.
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
SimulateMediaMetadataChanged(metadata);
// Wait for layout.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_LT(GetMediaStringViewTextLabel()->GetPreferredSize().width(),
kMediaStringMaxWidthDip);
EXPECT_FALSE(GetMediaStringViewTextContainer()->layer()->layer_mask_layer());
// Change to long text.
metadata.title = base::ASCIIToUTF16("A super duper long title");
metadata.artist = base::ASCIIToUTF16("A super duper long artist name");
SimulateMediaMetadataChanged(metadata);
// Wait for layout.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_GT(GetMediaStringViewTextLabel()->GetPreferredSize().width(),
kMediaStringMaxWidthDip);
EXPECT_TRUE(GetMediaStringViewTextContainer()->layer()->layer_mask_layer());
// Change to short text.
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
SimulateMediaMetadataChanged(metadata);
// Wait for layout.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(100));
EXPECT_LT(GetMediaStringViewTextLabel()->GetPreferredSize().width(),
kMediaStringMaxWidthDip);
EXPECT_FALSE(GetMediaStringViewTextContainer()->layer()->layer_mask_layer());
}
TEST_F(MediaStringViewTest, ShowWhenMediaIsPlaying) {
ShowAmbientScreen();
EXPECT_FALSE(GetMediaStringView()->GetVisible());
......
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