یک کارت رسانه ای را در AAOS پیاده سازی کنید

کارت رسانه یک ViewGroup مستقل است که ابرداده رسانه مانند عنوان، آلبوم هنری و موارد دیگر را نمایش می‌دهد و کنترل‌های پخش سطحی مانند پخش و مکث ، پرش و حتی اقدامات سفارشی ارائه شده توسط برنامه رسانه شخص ثالث را نمایش می‌دهد. یک کارت رسانه همچنین می‌تواند صفی از آیتم‌های رسانه مانند فهرست پخش را نشان دهد.

کارت رسانه

کارت رسانه

کارت رسانه

شکل 1. اجرای نمونه کارت رسانه.

کارت های رسانه ای چگونه در AAOS پیاده سازی می شوند؟

ViewGroup هایی که اطلاعات رسانه را نشان می دهند، به روز رسانی های LiveData را از مدل داده های کتابخانه car-media-common ، PlaybackViewModel ، مشاهده می کنند تا ViewGroup را پر کنند. هر به‌روزرسانی LiveData مربوط به زیرمجموعه‌ای از اطلاعات رسانه‌ای است که تغییر کرده است، مانند MediaItemMetadata ، PlaybackStateWrapper ، و MediaSource .

از آنجایی که این رویکرد منجر به کدهای مکرر می شود (هر برنامه مشتری روی هر قسمت از LiveData Observers اضافه می کند و به بسیاری از View های مشابه داده های به روز شده اختصاص داده می شود)، ما PlaybackCardController را ایجاد کردیم.

PlaybackCardController

PlaybackCardController به کتابخانه car-media-common اضافه شده است تا به ایجاد کارت رسانه کمک کند. این یک کلاس عمومی است که با یک ViewGroup ( mView )، PlaybackViewModel ( mDataModel )، PlaybackCardViewModel ( mViewModel ) و نمونه MediaItemsRepository ( mItemsRepository ) ساخته شده است.

در تابع setupController ، ViewGroup برای نماهای خاص با ID، با 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);

         // ...
}

هر به‌روزرسانی LiveData از PlaybackViewModel در یک روش محافظت شده مشاهده می‌شود و با Views مربوط به داده‌های دریافتی تعامل دارد. به عنوان مثال، یک ناظر در MediaItemMetadata عنوان را روی mTitle TextView تنظیم می کند و MediaItemMetadata.ArtworkRef را به آلبوم هنری ImageBinder mAlbumArtBinder ارسال می کند. اگر ابرداده خالی باشد، View ها پنهان می شوند. در صورت لزوم، زیر کلاس های Controller می توانند این منطق را لغو کنند.

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 یا یک کلاس فرعی را نمونه سازی کنید

کلاس Controller باید از یک Fragment یا Activity نمونه برداری شود تا یک LifecycleOwner برای ناظران LiveData داشته باشد.

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

mViewModel یک نمونه از PlaybackCardViewModel (یا زیر کلاس) است.

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 API های LiveData را برای تشخیص اینکه آیا MediaSource از یک صف پشتیبانی می کند و برای بازیابی لیست اشیاء MediaItemMetadata در صف ارائه می دهد. اگرچه این APIها می توانند مستقیماً برای پر کردن یک شی RecyclerView با اطلاعات صف مورد استفاده قرار گیرند، یک کلاس PlaybackQueueController به کتابخانه car-media-common اضافه شده است تا این فرآیند را ساده کند. طرح بندی برای هر آیتم در CarUiRecyclerView توسط برنامه مشتری و همچنین یک طرح سرصفحه اختیاری مشخص شده است. برنامه مشتری همچنین می تواند تعداد موارد نشان داده شده در صف را در طول وضعیت درایو با محدودیت های UXR سفارشی محدود کند.

سازنده و تنظیم کننده PlaybackQueueController در نمونه زیر نشان داده شده است. منابع طرح‌بندی queueResource و headerResource را می‌توان به‌عنوان Resources.ID_NULL ارسال کرد، اگر در مورد اول، کانتینر قبلاً حاوی CarUiRecyclerView با id queue_list باشد و در مورد دوم، صف دارای سرصفحه نباشد.

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

طرح‌بندی هر آیتم صف باید حاوی شناسه‌هایی برای View‌هایی باشد که می‌خواهد نشان دهد که مطابق با موارد استفاده شده در کلاس داخلی 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 با استفاده از mDataModel و mItemsRepository برای نمونه های PlaybackViewModel و MediaItemsRepository به ترتیب ساخت.

نمایش تاریخچه رسانه‌هایی که قبلاً پخش شده‌اند

در این بخش، نحوه نمایش و نمایش تاریخچه منابع رسانه‌ای که قبلاً پخش شده‌اند را می‌آموزید.

لیست تاریخچه را با PlaybackCardViewModel API دریافت کنید

PlaybackCardViewModel یک API LiveData به نام getHistoryList() برای بازیابی لیست سابقه رسانه ارائه می دهد. یک LiveData حاوی لیستی از منابع رسانه ای که قبلاً پخش شده اند را برمی گرداند. از این داده ها می توان برای پر کردن یک شی CarUiRecyclerView استفاده کرد. مشابه PlaybackQueueController ، کلاسی به نام PlaybackHistoryController به کتابخانه car-media-common اضافه شده است تا فرآیند را ساده کند.

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

از PlaybackHistoryController جدید برای کمک به پر کردن داده های سابقه در CarUiRecyclerView استفاده کنید. سازنده ها و توابع اصلی این کلاس به شرح زیر است. ظرف ارسال شده از برنامه مشتری باید حاوی CarUiRecyclerView با شناسه history_list باشد. CarUiRecyclerView موارد لیست و یک سرصفحه اختیاری را نمایش می دهد. هر دو طرح بندی مورد لیست و هدر را می توان از برنامه مشتری منتقل کرد. اگر Resources.ID_NULL به عنوان headerResource تنظیم شده باشد، هدر نشان داده نمی شود. پس از اینکه PlaybackCardViewModel به کنترلر منتقل شد، LiveData<List<MediaSource>> بازیابی شده از playbackCardViewModel.getHistoryList() نظارت می کند.

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() {
    }
}

طرح‌بندی هر آیتم باید شامل شناسه‌هایی برای View‌هایی باشد که می‌خواهد نشان دهد که مطابق با موارد استفاده شده در کلاس داخلی 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 ساخت.