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.
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 MediaModel
s 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.