Medienkarte in AAOS implementieren

Eine Mediakarte ist eine eigenständige ViewGroup, die unter anderem Medienmetadaten wie den Titel und das Albumcover anzeigt. Außerdem werden Wiedergabesteuerungen wie Wiedergabe, Pause und Überspringen sowie benutzerdefinierte Aktionen angezeigt, die von der Drittanbieter-Media-App bereitgestellt werden. Eine Mediakarte kann auch eine Warteschlange mit Medienelementen wie einer Playlist enthalten.

Medienkarte

Medienkarte

Medienkarte

Abbildung 1. Beispielimplementierungen für Medienkarte

Wie werden Mediacards in AAOS implementiert?

ViewGroups, die Medieninformationen anzeigen, erfassen LiveData-Aktualisierungen der Das data-Modell der car-media-common-Bibliothek, das PlaybackViewModel, zum Ausfüllen des ViewGroup hinzu. Jedes LiveData-Update entspricht einer Teilmenge der geänderten Medieninformationen, z. B. MediaItemMetadata, PlaybackStateWrapper und MediaSource.

Da dieser Ansatz zu wiederholtem Code führt (jede Client-App fügt allen LiveData-Elementen und vielen ähnlichen Aufrufen die aktualisierten Daten zugeordnet werden. hat PlaybackCardController erstellt.

Wiedergabekarten-Controller

PlaybackCardController wurde der car-media-common-Bibliothek hinzugefügt zu eine Medienkarte zu erstellen. Dies ist eine öffentliche Klasse, die mit eine ViewGroup (mView), PlaybackViewModel (mDataModel), WiedergabeCardViewModel (mViewModel) und MediaItemsRepository Instanz (mItemsRepository).

In der Funktion setupController wird die ViewGroup anhand der ID für bestimmte Ansichten mit mView.findViewById(R.id.xxx) geparst und geschützten View-Objekten zugewiesen.

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);

         // ...
}

Jede LiveData-Aktualisierung vom PlaybackViewModel wird in einem geschützten und führt Interaktionen mit den für die Daten relevanten Datenansichten durch. erhalten haben. Ein Beobachter auf MediaItemMetadata legt beispielsweise den Titel auf dem mTitle TextView fest und übergibt den MediaItemMetadata.ArtworkRef an das Albumcover ImageBinder mAlbumArtBinder. Wenn die Metadaten null sind, werden die Ansichten ausgeblendet. Abgeleitete Klassen des Controllers können diese Logik bei Bedarf überschreiben.

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);
        }
    }

WiedergabeCardController erweitern

Client-Apps, die eine Medienkarte erstellen möchten, sollten die PlaybackCardController, wenn sie über zusätzliche Funktionen verfügen bei jeder LiveData-Aktualisierung verwendet. Bestehende Clients in AAOS folgen diesem Muster. Zuerst muss eine PlaybackCardController-Unterklasse erstellt werden, z. B. die MediaCardController. Als Nächstes sollte die MediaCardController eine statische innere Builder-Klasse hinzufügen, die die der PlaybackCardController erweitert.

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
    // ...

  }
}

WiedergabeCardController oder Unterklasse instanziieren

Die Controller-Klasse sollte über ein Fragment oder eine Aktivität instanziiert werden, um einen LifecycleOwner für die LiveData-Beobachter zu haben.

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

mViewModel ist eine Instanz der PlaybackCardViewModel (oder einer abgeleiteten Klasse).

WiedergabekarteViewModel zum Speichern des Status

PlaybackCardViewModel ist ein zustandssparendes ViewModel, das mit einem Fragment oder Aktivität, die zur Rekonstruktion des Inhalts der Medienkarte verwendet werden soll, wenn ein eine Konfigurationsänderung stattfindet, z. B. ein Wechsel vom hellen zum dunklen Design, wenn ein Nutzer durch einen Tunnel fährt). Die Standard-PlaybackCardViewModel verarbeitet die Speichern von Instanzen der MediaModels für die Wiedergabe, von denen der PlaybackViewModel und MediaItemsRepository können abgerufen werden. Mit PlaybackCardViewModel können Sie den Status der Warteschlange, des Verlaufs und des Überlaufmenüs über die bereitgestellten Getter und Setter verfolgen.

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;
    }
}

Diese Klasse kann erweitert werden, wenn zusätzliche Zustände verfolgt werden müssen.

Warteschlange auf einer Medienkarte anzeigen

PlaybackViewModel stellt LiveData APIs bereit, um zu erkennen, ob die MediaSource unterstützt eine Warteschlange und zum Abrufen der Liste der MediaItemMetadata-Objekte in der in die Warteschlange stellen. Diese APIs können zwar direkt verwendet werden, um ein RecyclerView-Objekt mit den Warteschlangeninformationen zu füllen, aber der car-media-common-Bibliothek wurde eine PlaybackQueueController-Klasse hinzugefügt, um diesen Vorgang zu optimieren. Das Layout für jedes Element im CarUiRecyclerView auch von der Client-App angegeben wird als optionales Header-Layout. Die Client-App kann auch die Anzahl der Elemente begrenzen, die im Drive-Status in der Warteschlange angezeigt werden, mit benutzerdefinierten UXR-Einschränkungen.

Der PlaybackQueueController-Konstruktor und die Setter werden im Folgenden dargestellt: Stichprobe. Die Layoutressourcen queueResource und headerResource können übergeben werden. als Resources.ID_NULL, wenn der Container im ersten Fall bereits ein CarUiRecyclerView mit id queue_list und im letzteren Fall der Warteschlange hat keinen Header.

   /**
    * 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;
    }

Das Layout für jedes Warteschlangenelement sollte die IDs für die Ansichten enthalten, die es angezeigt werden sollen, die denen in der inneren QueueViewHolder-Klasse entsprechen.

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);

            // ...
}

Wenn du eine Warteschlange in einer Medienkarte anzeigen möchtest, die mit der PlaybackCardController (oder einer Unterklasse) erstellt wurde, kannst du die PlaybackQueueController im Konstruktor der PlaybackCardController mit mDataModel und mItemsRepository für die PlaybackViewModel- und MediaItemsRepository-Instanzen erstellen.

Verlauf der zuvor wiedergegebenen MediaSources anzeigen

In diesem Abschnitt erfahren Sie, wie Sie den Verlauf der zuvor abgespielten Medienquellen anzeigen und präsentieren.

Verlaufsliste mit der WiedergabeCardViewModel API abrufen

PlaybackCardViewModel bietet eine LiveData API mit dem Namen getHistoryList(), mit der Sie um die Liste mit dem Medienverlauf abzurufen. Sie gibt ein LiveData-Objekt zurück, das eine Liste MediaSources, die bereits wiedergegeben wurden. Diese Daten können verwendet werden, Ein CarUiRecyclerView-Objekt. Ähnlich wie PlaybackQueueController, ein Kurs mit dem Namen PlaybackHistoryController wurde car-media-common hinzugefügt um den Prozess zu vereinfachen.

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;
    }
}

Oberflächenverlaufs-UI mit WiedergabeverlaufController

Verwenden Sie das neue PlaybackHistoryController, um die Verlaufsdaten auszufüllen. zu einem CarUiRecyclerView. Die Konstruktoren und Hauptfunktionen dieser Klasse sind wie im Folgenden beschrieben. Der von der Client-App übergebene Container sollte Folgendes enthalten: CarUiRecyclerView mit der ID history_list. Das CarUiRecyclerView zeigt die Listenelemente und eine optionale Kopfzeile an. Beide Layouts für das Listenelement und der Header kann von der Client-App übergeben werden. Wenn Resources.ID_NULL festgelegt ist als headerResource ein, wird der Header nicht angezeigt. Nachdem PlaybackCardViewModel an den Controller übergeben wurde, überwacht er die aus playbackCardViewModel.getHistoryList() abgerufenen 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() {
    }
}

Das Layout für jedes Element sollte die IDs für die anzuzeigenden Ansichten enthalten, die denen in der inneren ViewHolder-Klasse entsprechen.

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);
// ...
}

Um eine Verlaufsliste auf einer Medienkarte anzuzeigen, die mit der PlaybackCardController (oder eine abgeleitete Klasse), kann der PlaybackHistoryController im Konstruktor von PlaybackCardController erstellt.