Medya kartı, başlık ve albüm resmi gibi medya meta verilerini görüntüleyen ve Oynat, Duraklat, Atla gibi oynatma kontrollerinin yanı sıra üçüncü taraf medya uygulaması tarafından sağlanan özel işlemleri de gösteren bağımsız bir ViewGroup'tur. Medya kartı, oynatma listesi gibi medya öğelerinin sırasını da gösterebilir.
Şekil 1. Medya kartı örnek uygulamaları.
Medya kartları AAOS'te nasıl uygulanır?
Medya bilgilerini gösteren ViewGroups, ViewGroup'u doldurmak için car-media-common kitaplığındaki data modelinden (PlaybackViewModel) gelen LiveData güncellemelerini izler. Her LiveData güncellemesi, değişen medya bilgilerinin bir alt kümesine karşılık gelir. Örneğin, MediaItemMetadata, PlaybackStateWrapper ve MediaSource.
Bu yaklaşım, tekrarlanan koda yol açtığı için (her istemci uygulaması, her LiveData parçasına Gözlemciler ekler ve birçok benzer Görünüme güncellenmiş veriler atanır) PlaybackCardController oluşturduk.
PlaybackCardController
PlaybackCardController, medya kartı oluşturmaya yardımcı olmak için car-media-common kitaplığına eklendi. Bu, ViewGroup (mView), PlaybackViewModel (mDataModel), PlaybackCardViewModel (mViewModel) ve MediaItemsRepository örneği (mItemsRepository) ile oluşturulmuş herkese açık bir sınıftır.
setupController işlevinde, ViewGroup, mView.findViewById(R.id.xxx) ile belirli görünümler için kimliğe göre ayrıştırılır ve korumalı View nesnelerine atanır.
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 kaynağından gelen her LiveData güncellemesi, korumalı bir yöntemde gözlemlenir ve alınan verilerle ilgili Görünümlerle etkileşimde bulunur. Örneğin, MediaItemMetadata üzerindeki bir gözlemci, mTitle TextView üzerindeki başlığı ayarlar ve MediaItemMetadata.ArtworkRef öğesini albüm kapak resmine ImageBinder mAlbumArtBinder iletir. Meta veriler null ise görüntülemeler gizlenir. Denetleyicinin alt sınıfları gerekirse bu mantığı geçersiz kılabilir.
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'ı genişletin.
Medya kartı oluşturmak isteyen istemci uygulamaları, her LiveData güncellemesinde işlemek istedikleri ek özelliklere sahipse PlaybackCardController öğesini genişletmelidir. AAOS'taki mevcut istemciler bu kalıbı izler.
Öncelikle PlaybackCardController gibi bir alt sınıf oluşturulmalıdır.
MediaCardController. Ardından, MediaCardController, PlaybackCardController sınıfını genişleten statik bir iç Builder sınıfı eklemelidir.
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'ı veya bir alt sınıfı başlatın.
Denetleyici sınıfı, LiveData gözlemcileri için bir LifecycleOwner'a sahip olmak üzere bir Parçadan veya Etkinlikten başlatılmalıdır.
mMediaCardController = (MediaCardController) new MediaCardController.Builder()
.setModels(mViewModel.getPlaybackViewModel(),
mViewModel,
mViewModel.getMediaItemsRepository())
.setViewGroup((ViewGroup) view)
.build();
mViewModel, PlaybackCardViewModel (veya alt sınıf) örneğidir.
Durumu Kaydetmek İçin PlaybackCardViewModel
PlaybackCardViewModel, bir Fragment veya Activity'ye bağlı, durumu kaydeden bir ViewModel'dir. Yapılandırma değişikliği (ör. kullanıcı tünelden geçerken açık temadan koyu temaya geçiş) meydana gelirse medya kartının içeriğini yeniden oluşturmak için kullanılmalıdır. Varsayılan PlaybackCardViewModel, oynatma için MediaModel örneklerinin depolanmasını sağlar. Bu örneklerden PlaybackViewModel ve MediaItemsRepository alınabilir. Kuyruğun durumunu, geçmişini ve taşma menüsünü sağlanan getter'lar ve setter'lar aracılığıyla izlemek için PlaybackCardViewModel kullanın.
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;
}
}
Ek eyaletlerin izlenmesi gerekiyorsa bu sınıf genişletilebilir.
Medya kartında sırayı gösterme
PlaybackViewModel, MediaSource'un bir kuyruğu destekleyip desteklemediğini algılamak ve kuyruktaki MediaItemMetadata nesnelerinin listesini almak için LiveData API'leri sağlar. Bu API'ler, RecyclerView nesnesini sıra bilgileriyle doldurmak için doğrudan kullanılabilse de bu süreci kolaylaştırmak amacıyla car-media-common kitaplığına bir PlaybackQueueController sınıfı eklenmiştir. CarUiRecyclerView içindeki her öğenin düzeni, istemci uygulaması ve isteğe bağlı bir başlık düzeni tarafından belirtilir. İstemci uygulaması, sürüş durumunda kuyrukta gösterilen öğe sayısını özel UXR kısıtlamalarıyla sınırlamayı da seçebilir.
PlaybackQueueController oluşturucusu ve ayarlayıcıları aşağıdaki örnekte gösterilmektedir. queueResource ve headerResource düzen kaynakları, Resources.ID_NULL olarak iletilebilir. İlk durumda kapsayıcıda zaten id queue_list ile CarUiRecyclerView varsa, ikinci durumda ise kuyrukta başlık yoksa bu kaynaklar Resources.ID_NULL olarak iletilebilir.
/**
* 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;
}
Her kuyruk öğesinin düzeni, QueueViewHolder iç sınıfında kullanılanlarla eşleşen ve göstermek istediği Görünümlerin kimliklerini içermelidir.
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 ile oluşturulan bir medya kartında sıra göstermek için PlaybackCardController, PlaybackViewModel ve MediaItemsRepository örnekleri için sırasıyla mDataModel ve mItemsRepository kullanılarak PlaybackCardController oluşturucusunda oluşturulabilir.PlaybackQueueController
Daha önce oynatılan MediaSource'ların geçmişini gösterme
Bu bölümde, daha önce oynatılan medya kaynaklarının geçmişini nasıl göstereceğinizi ve ortaya çıkaracağınızı öğreneceksiniz.
PlaybackCardViewModel API ile geçmiş listesini alma
PlaybackCardViewModel, medya geçmişi listesini almak için getHistoryList() adlı bir LiveData API'si sağlar. Daha önce oynatılmış MediaSource'ların listesini içeren bir LiveData döndürür. Bu veriler, CarUiRecyclerView nesnesini doldurmak için kullanılabilir. PlaybackQueueController'ya benzer şekilde, işlemi kolaylaştırmak için car-media-common kitaplığına PlaybackHistoryController adlı bir sınıf eklendi.
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 ile Surface geçmişi kullanıcı arayüzü
Geçmiş verilerini CarUiRecyclerView ile doldurmak için yeni PlaybackHistoryController özelliğini kullanın. Bu sınıfın oluşturucuları ve ana işlevleri aşağıda verilmiştir. İstemci uygulamasından geçirilen kapsayıcı, kimliği history_list olan bir CarUiRecyclerView içermelidir. CarUiRecyclerView
liste öğelerini ve isteğe bağlı bir üstbilgiyi gösterir. Hem liste öğesi hem de başlık düzenleri istemci uygulamasından geçirilebilir. Resources.ID_NULL, headerResource olarak ayarlanırsa başlık gösterilmez. PlaybackCardViewModel denetleyiciye iletildikten sonra playbackCardViewModel.getHistoryList()'dan alınan LiveData<List<MediaSource>> izlenir.
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() {
}
}
Her öğenin düzeni, ViewHolder iç sınıfında kullanılanlarla eşleşen ve göstermek istediği Görünümlerin kimliklerini içermelidir.
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 (veya bir alt sınıf) ile oluşturulan bir medya kartında geçmiş listesi göstermek için PlaybackHistoryController, PlaybackCardController oluşturucusunda oluşturulabilir.