AAOS でメディアカードを実装する

メディアカードは、タイトルやアルバムアートなどのメディア メタデータを表示し、再生一時停止スキップなどの再生コントロールや、サードパーティのメディアアプリが提供するカスタム アクションを表示する自己完結型の ViewGroup です。メディアカードには、再生リストなどのメディア アイテムのキューも表示できます。

メディアカード

メディアカード

メディアカード

図 1. メディアカードの実装例

AAOS でメディアカードはどのように実装されていますか?

メディア情報を表示する ViewGroup は、 car-media-common ライブラリのデータモデル(PlaybackViewModel)を使用して、 ViewGroup。各 LiveData の更新は、変更されたメディア情報のサブセット(MediaItemMetadataPlaybackStateWrapperMediaSource など)に対応しています。

このアプローチではコードが重複するため(各クライアント アプリが各 LiveData にオブザーバーを追加し、多くの類似ビューに更新されたデータが割り当てられます)、PlaybackCardController が作成されました。

PlaybackCardController

メディアカードの作成を支援するために、PlaybackCardControllercar-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 の既存のクライアントは、このパターンに従います。まず、PlaybackCardController サブクラス( MediaCardController。次に、MediaCardController が静的な内部 URL を PlaybackCardController を拡張するビルダークラス。

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 またはサブクラスをインスタンス化する

コントローラ クラスは、Fragment または Activity から順番にインスタンス化する必要があります。 LiveData オブザーバーの LifecycleOwner が設定されている必要があります。

mMediaCardController = (MediaCardController) new MediaCardController.Builder()
                    .setModels(mViewModel.getPlaybackViewModel(),
                            mViewModel,
                            mViewModel.getMediaItemsRepository())
                    .setViewGroup((ViewGroup) view)
                    .build();

mViewModelPlaybackCardViewModel(またはサブクラス)のインスタンスです。

PlaybackCardViewModel で状態を保存する

PlaybackCardViewModel は、Fragment または Activity に関連付けられた状態保存 ViewModel です。設定が変更された場合(ユーザーがトンネルを走行するときにライトモードからダークモードに切り替えるなど)に、メディアカードのコンテンツを再構築するために使用する必要があります。デフォルトの PlaybackCardViewModel は、 再生用の MediaModel のインスタンスを格納します。このインスタンスはこの PlaybackViewModelMediaItemsRepository を取得できます。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 に値を入力できます。 キュー情報を含むオブジェクトには、PlaybackQueueController クラスが このプロセスを効率化するために car-media-common ライブラリに追加されました。レイアウト 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;
    }

各キューアイテムのレイアウトには、キューに追加するビューの ID を含める必要があります。 QueueViewHolder 内部クラスで使用されているものに対応するものです。

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 インスタンスにそれぞれ mDataModelmItemsRepository を使用して、PlaybackCardController コンストラクタで PlaybackQueueController を作成します。

以前に再生された MediaSource の履歴を表示する

このセクションでは、以前に再生したメディアソースの履歴を表示する方法について説明します。

PlaybackCardViewModel API を使用して履歴リストを取得する

PlaybackCardViewModel は、メディア履歴リストを取得する getHistoryList() という LiveData API を提供します。以前に再生された MediaSource のリストを含む LiveData を返します。このデータを使用して CarUiRecyclerView オブジェクト。PlaybackQueueController と同様に、クラスは 「PlaybackHistoryController」という名前を「car-media-common」に追加しました ライブラリを使用してプロセスを合理化します。

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_listCarUiRecyclerView が含まれている必要があります。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(またはサブクラス)で作成されたメディアカードに履歴リストを表示するには、PlaybackHistoryControllerPlaybackCardController のコンストラクタで作成します。