মিডিয়া কার্ড হলো একটি স্বয়ংসম্পূর্ণ ভিউগ্রুপ যা মিডিয়ার মেটাডেটা, যেমন শিরোনাম, অ্যালবাম আর্ট ইত্যাদি প্রদর্শন করে এবং প্লেব্যাক কন্ট্রোল, যেমন প্লে ও পজ , স্কিপ, এমনকি থার্ড-পার্টি মিডিয়া অ্যাপের দেওয়া কাস্টম অ্যাকশনগুলোও দেখায়। একটি মিডিয়া কার্ড মিডিয়া আইটেমের একটি কিউ, যেমন একটি প্লেলিস্টও দেখাতে পারে।



চিত্র ১. মিডিয়া কার্ডের নমুনা বাস্তবায়ন।
AAOS-এ মিডিয়া কার্ডগুলো কীভাবে প্রয়োগ করা হয়?
যে ViewGroup-গুলো মিডিয়া তথ্য প্রদর্শন করে, সেগুলো নিজেদের ডেটা দিয়ে পূর্ণ করার জন্য car-media-common লাইব্রেরির ডেটা মডেল, PlaybackViewModel থেকে LiveData আপডেট গ্রহণ করে। প্রতিটি LiveData আপডেট মিডিয়া তথ্যের পরিবর্তিত কোনো একটি উপসেটের সাথে সম্পর্কিত, যেমন MediaItemMetadata , PlaybackStateWrapper , এবং MediaSource ।
যেহেতু এই পদ্ধতিতে কোডের পুনরাবৃত্তি ঘটে (প্রতিটি ক্লায়েন্ট অ্যাপ LiveData-এর প্রতিটি অংশে Observer যোগ করে এবং অনেকগুলো একই ধরনের View-কে আপডেট করা ডেটা অ্যাসাইন করা হয়), তাই আমরা PlaybackCardController তৈরি করেছি।
প্লেব্যাককার্ডকন্ট্রোলার
মিডিয়া কার্ড তৈরিতে সহায়তা করার জন্য car-media-common লাইব্রেরিতে PlaybackCardController যোগ করা হয়েছে। এটি একটি পাবলিক ক্লাস যা একটি ViewGroup ( mView ), PlaybackViewModel ( mDataModel ), PlaybackCardViewModel ( mViewModel ), এবং MediaItemsRepository ইনস্ট্যান্স ( mItemsRepository ) দিয়ে গঠিত হয়।
setupController ফাংশনে, mView.findViewById(R.id.xxx) ব্যবহার করে ID দ্বারা ViewGroup থেকে নির্দিষ্ট ভিউগুলো পার্স করা হয় এবং protected 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 আপডেট একটি protected মেথডে পর্যবেক্ষণ করা হয় এবং প্রাপ্ত ডেটার সাথে প্রাসঙ্গিক ভিউগুলোর সাথে ইন্টারঅ্যাকশন সম্পাদন করে। উদাহরণস্বরূপ, MediaItemMetadata এর উপর থাকা একটি অবজারভার mTitle TextView তে টাইটেল সেট করে এবং MediaItemMetadata.ArtworkRef কে অ্যালবাম আর্ট ImageBinder mAlbumArtBinder এ পাস করে। যদি মেটাডেটা null হয়, তাহলে ভিউগুলো হাইড হয়ে যায়। প্রয়োজনে কন্ট্রোলারের সাবক্লাসগুলো এই লজিকটি ওভাররাইড করতে পারে।
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);
}
}
প্লেব্যাককার্ডকন্ট্রোলার প্রসারিত করুন
যেসব ক্লায়েন্ট অ্যাপ একটি মিডিয়া কার্ড তৈরি করতে চায়, তাদের প্রতিটি LiveData আপডেটে অতিরিক্ত কোনো সক্ষমতা পরিচালনা করার প্রয়োজন হলে PlaybackCardController কে এক্সটেন্ড করা উচিত। AAOS-এর বিদ্যমান ক্লায়েন্টরা এই পদ্ধতি অনুসরণ করে। প্রথমে, একটি PlaybackCardController সাবক্লাস তৈরি করতে হবে, যেমন MediaCardController । এরপর, MediaCardController মধ্যে একটি স্ট্যাটিক ইনার Builder ক্লাস যুক্ত করতে হবে, যা 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 বা এর একটি সাবক্লাস ইনস্ট্যানশিয়েট করুন
LiveData অবজারভারদের জন্য LifecycleOwner পেতে হলে, Controller ক্লাসটিকে অবশ্যই একটি Fragment বা Activity থেকে ইনস্ট্যানশিয়েট করতে হবে।
mMediaCardController = (MediaCardController) new MediaCardController.Builder()
.setModels(mViewModel.getPlaybackViewModel(),
mViewModel,
mViewModel.getMediaItemsRepository())
.setViewGroup((ViewGroup) view)
.build();
mViewModel হলো PlaybackCardViewModel (বা এর সাবক্লাস)-এর একটি ইনস্ট্যান্স।
অবস্থা সংরক্ষণ করতে প্লেব্যাককার্ডভিউমডেল
PlaybackCardViewModel হলো একটি স্টেট-সেভিং ViewModel যা একটি Fragment বা Activity-র সাথে যুক্ত থাকে এবং কোনো কনফিগারেশন পরিবর্তন ঘটলে (যেমন, কোনো ব্যবহারকারী টানেলের মধ্যে দিয়ে যাওয়ার সময় লাইট থিম থেকে ডার্ক থিমে পরিবর্তিত হলে) মিডিয়া কার্ডের বিষয়বস্তু পুনর্গঠন করতে এটি ব্যবহৃত হয়। ডিফল্ট 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 এর প্রতিটি আইটেমের লেআউট ক্লায়েন্ট অ্যাপ এবং একটি ঐচ্ছিক Header লেআউট দ্বারা নির্দিষ্ট করা হয়। ক্লায়েন্ট অ্যাপ কাস্টম UXR বিধিনিষেধের মাধ্যমে ড্রাইভ অবস্থায় কিউ-তে প্রদর্শিত আইটেমের সংখ্যা সীমিত করার বিকল্পও বেছে নিতে পারে।
PlaybackQueueController কনস্ট্রাক্টর এবং সেটারগুলো নিম্নলিখিত নমুনায় দেখানো হয়েছে। queueResource এবং headerResource লেআউট রিসোর্সগুলোকে Resources.ID_NULL হিসেবে পাস করা যেতে পারে, যদি প্রথম ক্ষেত্রে কন্টেইনারটিতে ইতিমধ্যেই id queue_list সহ একটি CarUiRecyclerView থাকে এবং দ্বিতীয় ক্ষেত্রে কিউটিতে কোনো হেডার না থাকে।
/**
* 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 ইনার ক্লাসে ব্যবহৃত আইডিগুলোর সাথে সামঞ্জস্যপূর্ণ এবং যা এটি প্রদর্শন করতে চায়।
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 (বা এর কোনো সাবক্লাস) দিয়ে তৈরি মিডিয়া কার্ডে একটি কিউ দেখানোর জন্য, PlaybackCardController কনস্ট্রাক্টরে PlaybackViewModel এবং MediaItemsRepository ইনস্ট্যান্সগুলোর জন্য যথাক্রমে mDataModel এবং mItemsRepository ব্যবহার করে PlaybackQueueController টি কনস্ট্রাক্ট করা যেতে পারে।
পূর্বে প্লে করা মিডিয়া উৎসগুলির ইতিহাস দেখান
এই অংশে, আপনি শিখবেন কীভাবে পূর্বে চালানো মিডিয়া উৎসগুলোর ইতিহাস প্রদর্শন ও তুলে ধরতে হয়।
PlaybackCardViewModel API ব্যবহার করে হিস্ট্রি লিস্ট পান
PlaybackCardViewModel মিডিয়া হিস্ট্রি লিস্ট পুনরুদ্ধার করার জন্য getHistoryList() নামে একটি LiveData API প্রদান করে। এটি পূর্বে প্লে করা MediaSources-এর একটি তালিকা সম্বলিত একটি 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;
}
}
প্লেব্যাকহিস্টোরিকন্ট্রোলার সহ সারফেস হিস্ট্রি UI
একটি CarUiRecyclerView তে হিস্ট্রি ডেটা পপুলেট করতে নতুন PlaybackHistoryController ব্যবহার করুন। এই ক্লাসের কনস্ট্রাক্টর এবং মেইন ফাংশনগুলো নিম্নরূপ। ক্লায়েন্ট অ্যাপ থেকে পাঠানো কন্টেইনারটিতে 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 ইনার ক্লাসে ব্যবহৃত আইডিগুলোর সাথে সঙ্গতিপূর্ণ।
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 (বা এর কোনো সাবক্লাস) দিয়ে তৈরি মিডিয়া কার্ডে হিস্ট্রি লিস্ট দেখানোর জন্য, PlaybackCardController এর কনস্ট্রাক্টরের মধ্যে PlaybackHistoryController টি কনস্ট্রাক্ট করা যেতে পারে।