blob: bbec73390fe64757b51a954fdd793495ab794191 [file] [log] [blame]
// Copyright 2018 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 "components/media_message_center/media_notification_item.h"
#include "base/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "components/media_message_center/media_notification_constants.h"
#include "components/media_message_center/media_notification_controller.h"
#include "components/media_message_center/media_notification_view.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "ui/gfx/image/image.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
namespace media_message_center {
using media_session::mojom::MediaSessionAction;
namespace {
constexpr base::TimeDelta kDefaultSeekTime =
base::TimeDelta::FromSeconds(media_session::mojom::kDefaultSeekTimeSeconds);
MediaNotificationItem::Source GetSource(const std::string& name) {
if (name == "web")
return MediaNotificationItem::Source::kWeb;
if (name == "arc")
return MediaNotificationItem::Source::kArc;
if (name == "assistant")
return MediaNotificationItem::Source::kAssistant;
return MediaNotificationItem::Source::kUnknown;
}
} // namespace
// static
const char MediaNotificationItem::kSourceHistogramName[] =
"Media.Notification.Source";
// static
const char MediaNotificationItem::kUserActionHistogramName[] =
"Media.Notification.UserAction";
MediaNotificationItem::MediaNotificationItem(
MediaNotificationController* notification_controller,
const std::string& request_id,
const std::string& source_name,
media_session::mojom::MediaControllerPtr controller,
media_session::mojom::MediaSessionInfoPtr session_info)
: controller_(notification_controller),
request_id_(request_id),
source_(GetSource(source_name)),
media_controller_ptr_(std::move(controller)),
session_info_(std::move(session_info)) {
DCHECK(controller_);
if (media_controller_ptr_.is_bound()) {
// Bind an observer to the associated media controller.
media_session::mojom::MediaControllerObserverPtr media_controller_observer;
observer_binding_.Bind(mojo::MakeRequest(&media_controller_observer));
media_controller_ptr_->AddObserver(std::move(media_controller_observer));
// TODO(https://crbug.com/931397): Use dip to calculate the size.
// Bind an observer to be notified when the artwork changes.
media_session::mojom::MediaControllerImageObserverPtr artwork_observer;
artwork_observer_binding_.Bind(mojo::MakeRequest(&artwork_observer));
media_controller_ptr_->ObserveImages(
media_session::mojom::MediaSessionImageType::kArtwork,
kMediaSessionNotificationArtworkMinSize,
kMediaSessionNotificationArtworkDesiredSize,
std::move(artwork_observer));
media_session::mojom::MediaControllerImageObserverPtr icon_observer;
icon_observer_binding_.Bind(mojo::MakeRequest(&icon_observer));
media_controller_ptr_->ObserveImages(
media_session::mojom::MediaSessionImageType::kSourceIcon,
message_center::kSmallImageSizeMD, message_center::kSmallImageSizeMD,
std::move(icon_observer));
}
MaybeHideOrShowNotification();
}
MediaNotificationItem::~MediaNotificationItem() {
controller_->HideNotification(request_id_);
}
void MediaNotificationItem::MediaSessionInfoChanged(
media_session::mojom::MediaSessionInfoPtr session_info) {
session_info_ = std::move(session_info);
MaybeHideOrShowNotification();
if (view_)
view_->UpdateWithMediaSessionInfo(session_info_);
}
void MediaNotificationItem::MediaSessionMetadataChanged(
const base::Optional<media_session::MediaMetadata>& metadata) {
session_metadata_ = metadata.value_or(media_session::MediaMetadata());
view_needs_metadata_update_ = true;
MaybeHideOrShowNotification();
// |MaybeHideOrShowNotification()| can synchronously create a
// MediaNotificationView that calls |SetView()|. If that happens, then we
// don't want to call |view_->UpdateWithMediaMetadata()| below since |view_|
// will have already received the metadata when calling |SetView()|.
// |view_needs_metadata_update_| is set to false in |SetView()|.
if (view_ && view_needs_metadata_update_)
view_->UpdateWithMediaMetadata(session_metadata_);
view_needs_metadata_update_ = false;
}
void MediaNotificationItem::MediaSessionActionsChanged(
const std::vector<media_session::mojom::MediaSessionAction>& actions) {
session_actions_ = std::set<media_session::mojom::MediaSessionAction>(
actions.begin(), actions.end());
if (view_)
view_->UpdateWithMediaActions(session_actions_);
}
void MediaNotificationItem::MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType type,
const SkBitmap& bitmap) {
switch (type) {
case media_session::mojom::MediaSessionImageType::kArtwork:
session_artwork_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
if (view_)
view_->UpdateWithMediaArtwork(*session_artwork_);
break;
case media_session::mojom::MediaSessionImageType::kSourceIcon:
session_icon_ = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
if (view_)
view_->UpdateWithMediaIcon(session_icon_);
break;
}
}
void MediaNotificationItem::SetView(MediaNotificationView* view) {
DCHECK(view_ || view);
view_ = view;
if (view) {
view_needs_metadata_update_ = false;
view_->UpdateWithMediaSessionInfo(session_info_);
view_->UpdateWithMediaMetadata(session_metadata_);
view_->UpdateWithMediaActions(session_actions_);
if (session_artwork_.has_value())
view_->UpdateWithMediaArtwork(*session_artwork_);
}
}
void MediaNotificationItem::FlushForTesting() {
media_controller_ptr_.FlushForTesting();
}
void MediaNotificationItem::MaybeHideOrShowNotification() {
// If the |is_controllable| bit is set in MediaSessionInfo then we should show
// a media notification.
if (!session_info_ || !session_info_->is_controllable) {
controller_->HideNotification(request_id_);
return;
}
// If we do not have a title and an artist then we should hide the
// notification.
if (session_metadata_.title.empty() || session_metadata_.artist.empty()) {
controller_->HideNotification(request_id_);
return;
}
// If we have an existing view, then we don't need to create a new one.
if (view_)
return;
controller_->ShowNotification(request_id_);
UMA_HISTOGRAM_ENUMERATION(kSourceHistogramName, source_);
}
void MediaNotificationItem::OnMediaSessionActionButtonPressed(
MediaSessionAction action) {
UMA_HISTOGRAM_ENUMERATION(kUserActionHistogramName, action);
switch (action) {
case MediaSessionAction::kPreviousTrack:
media_controller_ptr_->PreviousTrack();
break;
case MediaSessionAction::kSeekBackward:
media_controller_ptr_->Seek(kDefaultSeekTime * -1);
break;
case MediaSessionAction::kPlay:
media_controller_ptr_->Resume();
break;
case MediaSessionAction::kPause:
media_controller_ptr_->Suspend();
break;
case MediaSessionAction::kSeekForward:
media_controller_ptr_->Seek(kDefaultSeekTime);
break;
case MediaSessionAction::kNextTrack:
media_controller_ptr_->NextTrack();
break;
case MediaSessionAction::kStop:
media_controller_ptr_->Stop();
break;
case MediaSessionAction::kSkipAd:
break;
}
}
} // namespace media_message_center