メディアカードは、タイトルやアルバムアートなどのメディア メタデータを表示し、再生、一時停止、スキップなどの再生コントロールや、サードパーティのメディアアプリが提供するカスタム アクションを表示する自己完結型の ViewGroup です。メディアカードには、再生リストなどのメディア アイテムのキューも表示できます。
図 1. メディアカードの実装例。
AAOS でメディアカードはどのように実装されていますか?
メディア情報を表示する ViewGroup は、car-media-common
ライブラリのデータモデル PlaybackViewModel
からの LiveData の更新を監視して、ViewGroup にデータを入力します。各 LiveData の更新は、変更されたメディア情報のサブセット(MediaItemMetadata
、PlaybackStateWrapper
、MediaSource
など)に対応しています。
このアプローチではコードが重複するため(各クライアント アプリが各 LiveData にオブザーバーを追加し、多くの類似ビューに更新されたデータが割り当てられます)、PlaybackCardController
が作成されました。
PlaybackCardController
メディアカードの作成を支援するために、PlaybackCardController
が car-media-common
ライブラリに追加されました。これは、ViewGroup(mView
)、PlaybackViewModel(mDataModel
)、PlaybackCardViewModel(mViewModel
)、MediaItemsRepository
インスタンス(mItemsRepository
)で構成される公開クラスです。
setupController
関数では、ViewGroup が ID で特定のビューを解析し、mView.findViewById(R.id.xxx)
で保護された View オブジェクトに割り当てられます。
private void getViewsFromWidget() {
mTitle = mView.findViewById(R.id.title);
mAlbumCover = mView.findViewById(R.id.album_art);
mDescription = mView.findViewById(R.id.album_title);
mLogo = mView.findViewById(R.id.content_format);
mAppIcon = mView.findViewById(R.id.media_widget_app_icon);
mAppName = mView.findViewById(R.id.media_widget_app_name);
// ...
}
PlaybackViewModel
からの各 LiveData 更新は、保護されたメソッドで監視され、受信したデータに関連するビューとのインタラクションを実行します。たとえば、MediaItemMetadata
のオブザーバーは mTitle
TextView
にタイトルを設定し、MediaItemMetadata.ArtworkRef
をアルバムアート ImageBinder
mAlbumArtBinder
に渡します。メタデータが null の場合、ビューは非表示になります。必要に応じて、Controller のサブクラスでこのロジックをオーバーライドできます。
mDataModel.getMetadata().observe(mViewLifecycle, this::updateMetadata);
// ...
/** Update views with {@link MediaItemMetadata} */
protected void updateMetadata(MediaItemMetadata metadata) {
if (metadata != null) {
String defaultTitle = mView.getContext().getString(
R.string.metadata_default_title);
updateTextViewAndVisibility(mTitle, metadata.getTitle(), defaultTitle);
updateTextViewAndVisibility(mSubtitle, metadata.getSubtitle());
updateMediaLink(mSubtitleLinker,metadata.getSubtitleLinkMediaId());
updateTextViewAndVisibility(mDescription, metadata.getDescription());
updateMediaLink(mDescriptionLinker, metadata.getDescriptionLinkMediaId());
updateMetadataAlbumCoverArtworkRef(metadata.getArtworkKey());
updateMetadataLogoWithUri(metadata);
} else {
ViewUtils.setVisible(mTitle, false);
ViewUtils.setVisible(mSubtitle, false);
ViewUtils.setVisible(mAlbumCover, false);
ViewUtils.setVisible(mDescription, false);
ViewUtils.setVisible(mLogo, false);
}
}
PlaybackCardController を拡張する
メディアカードを作成するクライアント アプリは、各 LiveData の更新で処理する追加機能がある場合は、PlaybackCardController
を拡張する必要があります。AAOS の既存のクライアントは、このパターンに従います。まず、MediaCardController
などの PlaybackCardController
サブクラスを作成する必要があります。次に、MediaCardController
に PlaybackCardController
の Builder クラスを拡張する静的内部クラスを追加します。
public class MediaCardController extends PlaybackCardController {
// extra fields specific to MediaCardController
/** Builder for {@link MediaCardController}. Overrides build() method to
* return NowPlayingController rather than base {@link PlaybackCardController}
*/
public static class Builder extends PlaybackCardController.Builder {
@Override
public MediaCardController build() {
MediaCardController controller = new MediaCardController(this);
controller.setupController();
return controller;
}
}
public MediaCardController(Builder builder) {
super(builder);
// any other function calls needed in constructor
// ...
}
}
PlaybackCardController またはサブクラスをインスタンス化する
LiveData オブザーバーの LifecycleOwner を設定するには、Controller クラスを Fragment または Activity からインスタンス化する必要があります。
mMediaCardController = (MediaCardController) new MediaCardController.Builder()
.setModels(mViewModel.getPlaybackViewModel(),
mViewModel,
mViewModel.getMediaItemsRepository())
.setViewGroup((ViewGroup) view)
.build();
mViewModel
は PlaybackCardViewModel
のインスタンス(またはサブクラス)です。
PlaybackCardViewModel で状態を保存する
PlaybackCardViewModel
は、Fragment または Activity に関連付けられた状態保存 ViewModel です。設定が変更された場合(ユーザーがトンネルを走行するときにライトモードからダークモードに切り替えるなど)に、メディアカードのコンテンツを再構築するために使用する必要があります。デフォルトの PlaybackCardViewModel
は、再生用の MediaModel
のインスタンスの保存を処理します。このインスタンスから PlaybackViewModel
と MediaItemsRepository
を取得できます。PlaybackCardViewModel
を使用して、提供されたゲッターとセッターを使用してキュー、履歴、オーバーフロー メニューの状態を追跡します。
public class PlaybackCardViewModel extends AndroidViewModel {
private MediaModels mModels;
private boolean mNeedsInitialization = true;
private boolean mQueueVisible = false;
private boolean mHistoryVisible = false;
private boolean mOverflowExpanded = false;
public PlaybackCardViewModel(@NonNull Application application) {
super(application);
}
/** Initialize the PlaybackCardViewModel */
public void init(MediaModels models) {
mModels = models;
mNeedsInitialization = false;
}
/**
* Returns whether the ViewModel needs to be initialized. The ViewModel may
* need re-initialization if a config change occurs or if the system kills
* the Fragment.
*/
public boolean needsInitialization() {
return mNeedsInitialization;
}
public MediaItemsRepository getMediaItemsRepository() {
return mModels.getMediaItemsRepository();
}
public PlaybackViewModel getPlaybackViewModel() {
return mModels.getPlaybackViewModel();
}
public MediaSourceViewModel getMediaSourceViewModel() {
return mModels.getMediaSourceViewModel();
}
public void setQueueVisible(boolean visible) {
mQueueVisible = visible;
}
public boolean getQueueVisible() {
return mQueueVisible;
}
public void setHistoryVisible(boolean visible) {
mHistoryVisible = visible;
}
public boolean getHistoryVisible() {
return mHistoryVisible;
}
public void setOverflowExpanded(boolean expanded) {
mOverflowExpanded = expanded;
}
public boolean getOverflowExpanded() {
return mOverflowExpanded;
}
}
追加の状態を追跡する必要がある場合は、このクラスを拡張できます。
メディアカードにキューを表示する
PlaybackViewModel
は、MediaSource がキューをサポートしているかどうかを検出し、キュー内の MediaItemMetadata
オブジェクトのリストを取得するための LiveData API を提供します。これらの API を直接使用してキュー情報で RecyclerView
オブジェクトを入力することもできますが、このプロセスを効率化するために、car-media-common
ライブラリに PlaybackQueueController
クラスが追加されています。CarUiRecyclerView
の各アイテムのレイアウトは、オプションのヘッダー レイアウトと同様にクライアント アプリによって指定されます。クライアント アプリは、カスタム UXR 制限を使用して、ドライブ中のキューに表示されるアイテムの数を制限することもできます。
PlaybackQueueController
のコンストラクタとセッターは次のサンプルに示されています。queueResource
レイアウト リソースと headerResource
レイアウト リソースは、前者の場合はコンテナに id queue_list
を含む CarUiRecyclerView
がすでに含まれている場合、後者の場合はキューにヘッダーがない場合に、Resources.ID_NULL
として渡すことができます。
/**
* Construct a PlaybackQueueController. If clients don't have a separate
* layout for the queue, where the queue is already inflated within the
* container, they should pass {@link Resources.ID_NULL} as the LayoutRes
* resource. If clients don't require a UxrContentLimiter, they should pass
* null for uxrContentLimiter and the int passed for uxrConfigurationId will
* be ignored.
*/
public PlaybackQueueController(
ViewGroup container,
@LayoutRes int queueResource,
@LayoutRes int queueItemResource,
@LayoutRes int headerResource,
LifecycleOwner lifecycleOwner,
PlaybackViewModel playbackViewModel,
MediaItemsRepository itemsRepository,
@Nullable LifeCycleObserverUxrContentLimiter uxrContentLimiter,
int uxrConfigurationId) {
// ...
}
public void setShowTimeForActiveQueueItem(boolean show) {
mShowTimeForActiveQueueItem = show;
}
public void setShowIconForActiveQueueItem(boolean show) {
mShowIconForActiveQueueItem = show;
}
public void setShowThumbnailForQueueItem(boolean show) {
mShowThumbnailForQueueItem = show;
}
public void setShowSubtitleForQueueItem(boolean show) {
mShowSubtitleForQueueItem = show;
}
/** Calls {@link RecyclerView#setVerticalFadingEdgeEnabled(boolean)} */
public void setVerticalFadingEdgeLengthEnabled(boolean enabled) {
mQueue.setVerticalFadingEdgeEnabled(enabled);
}
public void setCallback(PlaybackQueueCallback callback) {
mPlaybackQueueCallback = callback;
}
各キューアイテムのレイアウトには、QueueViewHolder
内部クラスで使用されているビューに対応する、表示するビューの ID を含める必要があります。
QueueViewHolder(View itemView) {
super(itemView);
mView = itemView;
mThumbnailContainer = itemView.findViewById(R.id.thumbnail_container);
mThumbnail = itemView.findViewById(R.id.thumbnail);
mSpacer = itemView.findViewById(R.id.spacer);
mTitle = itemView.findViewById(R.id.queue_list_item_title);
mSubtitle = itemView.findViewById(R.id.queue_list_item_subtitle);
mCurrentTime = itemView.findViewById(R.id.current_time);
mMaxTime = itemView.findViewById(R.id.max_time);
mTimeSeparator = itemView.findViewById(R.id.separator);
mActiveIcon = itemView.findViewById(R.id.now_playing_icon);
// ...
}
PlaybackCardController
(またはサブクラス)で作成されたメディアカードにキューを表示するには、PlaybackViewModel
インスタンスと MediaItemsRepository
インスタンスにそれぞれ mDataModel
と mItemsRepository
を使用して、PlaybackCardController
コンストラクタで PlaybackQueueController
を作成します。
以前に再生された MediaSource の履歴を表示する
このセクションでは、以前に再生したメディアソースの履歴を表示する方法について説明します。
PlaybackCardViewModel API を使用して履歴リストを取得する
PlaybackCardViewModel
は、メディア履歴リストを取得する getHistoryList()
という LiveData API を提供します。以前に再生された MediaSource のリストを含む LiveData を返します。このデータは、CarUiRecyclerView
オブジェクトに入力するために使用できます。PlaybackQueueController
と同様に、プロセスを効率化するために、car-media-common
ライブラリに PlaybackHistoryController
というクラスが追加されました。
public class PlaybackCardViewModel extends AndroidViewModel {
public PlaybackCardViewModel(@NonNull Application application) {
}
/** Initialize the PlaybackCardViewModel */
public void init(MediaModels models) {
}
public LiveData<List<MediaSource>> getHistoryList() {
return mHistoryListData;
}
}
PlaybackHistoryController による履歴 UI の表示
新しい PlaybackHistoryController
を使用して、履歴データを CarUiRecyclerView
に入力します。このクラスのコンストラクタと主な関数は次のとおりです。クライアント アプリから渡されるコンテナには、ID が history_list
の CarUiRecyclerView
が含まれている必要があります。CarUiRecyclerView
には、リストアイテムとオプションのヘッダーが表示されます。リストアイテムとヘッダーの両方のレイアウトをクライアント アプリから渡すことができます。headerResource として Resources.ID_NULL
が設定されている場合、ヘッダーは表示されません。PlaybackCardViewModel
がコントローラに渡されると、コントローラは playbackCardViewModel.getHistoryList()
から取得した LiveData<List<MediaSource>>
をモニタリングします。
public class PlaybackHistoryController {
public PlaybackHistoryController(
LifecycleOwner lifecycleOwner,
PlaybackCardViewModel playbackCardViewModel,
ViewGroup container,
@LayoutRes int itemResource,
@LayoutRes int headerResource,
int uxrConfigurationId) {
}
/**
* Renders the view.
*/
public void setupView() {
}
}
各アイテムのレイアウトには、ViewHolder
内部クラスで使用されているビューの ID を指定します。
HistoryItemViewHolder(View itemView) {
super(itemView);
mContext = itemView.getContext();
mActiveView = itemView.findViewById(R.id.history_card_container_active);
mInactiveView = itemView.findViewById(R.id.history_card_container_inactive);
mMetadataTitleView = itemView.findViewById(R.id.history_card_title_active);
mAdditionalInfo = itemView.findViewById(R.id.history_card_subtitle_active);
mAppIcon = itemView.findViewById(R.id.history_card_app_thumbnail);
mAlbumArt = itemView.findViewById(R.id.history_card_album_art);
mAppTitleInactive = itemView.findViewById(R.id.history_card_app_title_inactive);
mAppIconInactive = itemView.findViewById(R.id.history_item_app_icon_inactive);
// ...
}
PlaybackCardController
(またはサブクラス)で作成されたメディアカードに履歴リストを表示するには、PlaybackHistoryController
を PlaybackCardController
のコンストラクタで作成します。