AAOS में मीडिया कार्ड को लागू करना

मीडिया कार्ड एक ViewGroup होता है. इसमें मीडिया का मेटाडेटा दिखता है. जैसे, टाइटल, एल्बम आर्ट वगैरह. साथ ही, इसमें प्लेबैक कंट्रोल भी दिखते हैं. जैसे, चलाएं और रोकें, छोड़ें. इसमें तीसरे पक्ष के मीडिया ऐप्लिकेशन की ओर से उपलब्ध कराई गई कस्टम कार्रवाइयां भी दिखती हैं. मीडिया कार्ड में, मीडिया आइटम की एक सूची भी दिख सकती है. जैसे, प्लेलिस्ट.

मीडिया कार्ड

मीडिया कार्ड

मीडिया कार्ड

पहली इमेज. मीडिया कार्ड को लागू करने के उदाहरण.

AAOS में मीडिया कार्ड कैसे लागू किए जाते हैं?

मीडिया की जानकारी दिखाने वाले ViewGroups, car-media-common लाइब्रेरी के डेटा मॉडल PlaybackViewModel से LiveData अपडेट को मॉनिटर करते हैं, ताकि ViewGroup को पॉप्युलेट किया जा सके. हर LiveData अपडेट, मीडिया की जानकारी के उस सबसेट से जुड़ा होता है जिसमें बदलाव हुआ है. जैसे, MediaItemMetadata, PlaybackStateWrapper, और MediaSource.

इस तरीके से, कोड को बार-बार इस्तेमाल करना पड़ता है. ऐसा इसलिए, क्योंकि हर क्लाइंट ऐप्लिकेशन, LiveData के हर हिस्से में ऑब्ज़र्वर जोड़ता है. साथ ही, कई मिलते-जुलते व्यू को अपडेट किया गया डेटा असाइन किया जाता है. इसलिए, हमने PlaybackCardController बनाया है.

PlaybackCardController

मीडिया कार्ड बनाने में मदद करने के लिए, PlaybackCardController को car-media-common लाइब्रेरी में जोड़ दिया गया है. यह एक सार्वजनिक क्लास है, जिसे ViewGroup (mView), PlaybackViewModel (mDataModel), PlaybackCardViewModel (mViewModel), और MediaItemsRepository इंस्टेंस (mItemsRepository) के साथ बनाया गया है.

setupController फ़ंक्शन में, ViewGroup को आईडी के हिसाब से कुछ व्यू के लिए पार्स किया जाता है. इसके बाद, 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 पर पास करता है. अगर मेटाडेटा शून्य है, तो व्यू छिपे होते हैं. अगर ज़रूरत हो, तो कंट्रोलर की सबक्लास इस लॉजिक को बदल सकती हैं.

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 को बढ़ाना

जिन क्लाइंट ऐप्लिकेशन को मीडिया कार्ड बनाना है उन्हें PlaybackCardController को बढ़ाना चाहिए. ऐसा तब करना चाहिए, जब उनके पास कोई ऐसी अतिरिक्त सुविधा हो जिसे वे LiveData के हर अपडेट में हैंडल करना चाहते हैं. 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 क्लास को किसी फ़्रैगमेंट या ऐक्टिविटी से इंस्टैंशिएट किया जाना चाहिए.

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

mViewModel, PlaybackCardViewModel (या सबक्लास) का एक उदाहरण है.

PlaybackCardViewModel को सेव स्टेट में

PlaybackCardViewModel एक ViewModel है, जो किसी फ़्रैगमेंट या ऐक्टिविटी से जुड़ा होता है. इसका इस्तेमाल, कॉन्फ़िगरेशन में बदलाव होने पर मीडिया कार्ड के कॉन्टेंट को फिर से बनाने के लिए किया जाना चाहिए. जैसे, जब कोई उपयोगकर्ता किसी सुरंग से गाड़ी चलाता है, तो लाइट थीम से डार्क थीम पर स्विच करना. डिफ़ॉल्ट 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, LiveData API उपलब्ध कराता है. इनकी मदद से यह पता लगाया जा सकता है कि MediaSource किसी सूची के साथ काम करता है या नहीं. साथ ही, सूची में मौजूद MediaItemMetadata ऑब्जेक्ट की सूची को वापस पाया जा सकता है. इन एपीआई का इस्तेमाल सीधे तौर पर, RecyclerView ऑब्जेक्ट में कतार की जानकारी भरने के लिए किया जा सकता है. हालांकि, इस प्रोसेस को आसान बनाने के लिए, car-media-common लाइब्रेरी में PlaybackQueueController क्लास जोड़ी गई है. CarUiRecyclerView में मौजूद हर आइटम के लेआउट के बारे में क्लाइंट ऐप्लिकेशन बताता है. साथ ही, हेडर के लेआउट के बारे में भी बताता है. हालांकि, यह ज़रूरी नहीं है. क्लाइंट ऐप्लिकेशन के पास, ड्राइव करते समय कस्टम यूएक्सआर की पाबंदियों के साथ, कतार में दिखाए जाने वाले आइटम की संख्या को सीमित करने का विकल्प भी होता है.

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 (या सबक्लास) की मदद से बनाए गए मीडिया कार्ड में, कतार दिखाने के लिए, PlaybackQueueController को PlaybackCardController कंस्ट्रक्टर में बनाया जा सकता है. इसके लिए, PlaybackViewModel और MediaItemsRepository इंस्टेंस के लिए, mDataModel और mItemsRepository का इस्तेमाल किया जा सकता है.

पहले चलाए गए MediaSources का इतिहास दिखाएं

इस सेक्शन में, पहले चलाए गए मीडिया सोर्स का इतिहास दिखाने और उसे फिर से चलाने का तरीका बताया गया है.

PlaybackCardViewModel API की मदद से, इतिहास की सूची पाना

PlaybackCardViewModel, मीडिया इतिहास की सूची पाने के लिए getHistoryList() नाम का LiveData API उपलब्ध कराता है. यह एक LiveData ऑब्जेक्ट दिखाता है. इसमें पहले चलाए गए MediaSource ऑब्जेक्ट की सूची होती है. इस डेटा का इस्तेमाल, 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;
    }
}

PlaybackHistoryController के साथ Surface का इतिहास दिखाने वाला यूज़र इंटरफ़ेस (यूआई)

CarUiRecyclerView में इतिहास का डेटा अपने-आप भरने के लिए, नए PlaybackHistoryController का इस्तेमाल करें. इस क्लास के कंस्ट्रक्टर और मुख्य फ़ंक्शन यहां दिए गए हैं. क्लाइंट ऐप्लिकेशन से पास किए गए कंटेनर में, आईडी history_list वाला CarUiRecyclerView होना चाहिए. CarUiRecyclerView में सूची के आइटम और एक हेडर दिखता है. हालांकि, हेडर जोड़ना ज़रूरी नहीं है. सूची आइटम और हेडर, दोनों के लेआउट को क्लाइंट ऐप्लिकेशन से पास किया जा सकता है. अगर Resources.ID_NULL को headerResource के तौर पर सेट किया जाता है, तो हेडर नहीं दिखता है. 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 (या कोई सबक्लास) का इस्तेमाल करके बनाए गए मीडिया कार्ड में, इतिहास की सूची दिखाने के लिए, PlaybackHistoryController को PlaybackCardController के कंस्ट्रक्टर में बनाया जा सकता है.