ساخت اپلیکیشن های رسانه ای برای ماشین ها

Android Auto و Android Automotive OS به شما کمک می‌کنند محتوای برنامه رسانه‌ای خود را به کاربرانی که در خودرویشان هستند ارائه دهید. یک برنامه رسانه برای خودروها باید یک سرویس مرورگر رسانه ارائه دهد تا Android Auto و Android Automotive OS یا برنامه دیگری با مرورگر رسانه بتواند محتوای شما را کشف و نمایش دهد.

این راهنما فرض می‌کند که شما قبلاً یک برنامه رسانه دارید که صدا را روی تلفن پخش می‌کند و برنامه رسانه شما با معماری برنامه رسانه Android مطابقت دارد.

این راهنما اجزای مورد نیاز MediaBrowserService و MediaSession را که برنامه شما برای کار بر روی Android Auto یا Android Automotive OS به آن نیاز دارد، توضیح می‌دهد. پس از تکمیل زیرساخت رسانه اصلی، می‌توانید پشتیبانی از Android Auto را اضافه کنید و پشتیبانی از Android Automotive OS را به برنامه رسانه خود اضافه کنید .

قبل از شروع
  1. مستندات Android Media API را مرور کنید.
  2. بررسی ایجاد برنامه های رسانه ای برای راهنمایی طراحی.
  3. اصطلاحات و مفاهیم کلیدی فهرست شده در این بخش را مرور کنید.
اصطلاحات و مفاهیم کلیدی
سرویس مرورگر رسانه
یک سرویس Android اجرا شده توسط برنامه رسانه شما که با MediaBrowserServiceCompat API مطابقت دارد. برنامه شما از این سرویس برای افشای محتوای خود استفاده می کند.
مرورگر رسانه
یک API که توسط برنامه های رسانه برای کشف سرویس های مرورگر رسانه و نمایش محتوای آنها استفاده می شود. Android Auto و Android Automotive OS از یک مرورگر رسانه برای یافتن سرویس مرورگر رسانه برنامه شما استفاده می کنند.
آیتم رسانه ای

مرورگر رسانه محتوای خود را در درختی از اشیاء MediaItem سازماندهی می کند. یک آیتم رسانه می‌تواند یکی یا هر دو پرچم‌های زیر را داشته باشد:

  • FLAG_PLAYABLE : نشان می دهد که مورد یک برگ روی درخت محتوا است. این آیتم یک جریان صدا را نشان می دهد، مانند یک آهنگ در یک آلبوم، یک فصل از یک کتاب صوتی، یا یک قسمت از یک پادکست.
  • FLAG_BROWSABLE : نشان می دهد که مورد یک گره در درخت محتوا است و دارای فرزندان است. به عنوان مثال، آیتم نشان دهنده یک آلبوم است و فرزندان آن آهنگ های موجود در آلبوم هستند.

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

خودرو بهینه شده است

فعالیتی برای یک برنامه سیستم عامل Android Automotive که از دستورالعمل های طراحی سیستم عامل Android Automotive پیروی می کند. رابط این فعالیت ها توسط سیستم عامل Android Automotive ترسیم نشده است، بنابراین باید مطمئن شوید که برنامه شما از دستورالعمل های طراحی پیروی می کند. به طور معمول، این شامل اهداف ضربه ای بزرگتر و اندازه فونت، پشتیبانی از حالت های روز و شب، و نسبت کنتراست بالاتر است.

رابط‌های کاربری بهینه‌سازی شده برای خودرو فقط زمانی مجاز به نمایش هستند که محدودیت‌های تجربه کاربر خودرو (CUXR) در کار نباشد، زیرا این رابط‌ها می‌توانند به توجه یا تعامل گسترده‌ای از جانب کاربر نیاز داشته باشند. CUXR ها زمانی که خودرو متوقف یا پارک شده است کارایی ندارند اما زمانی که خودرو در حال حرکت است همیشه فعال هستند.

نیازی به طراحی فعالیت‌ها برای Android Auto ندارید، زیرا Android Auto با استفاده از اطلاعات سرویس مرورگر رسانه شما، رابط بهینه‌سازی شده برای وسیله نقلیه خود را طراحی می‌کند.

فایل های مانیفست برنامه خود را پیکربندی کنید

قبل از اینکه بتوانید سرویس مرورگر رسانه خود را ایجاد کنید، باید فایل های مانیفست برنامه خود را پیکربندی کنید.

سرویس مرورگر رسانه خود را اعلام کنید

Android Auto و Android Automotive OS از طریق سرویس مرورگر رسانه شما برای مرور موارد رسانه به برنامه شما متصل می شوند. سرویس مرورگر رسانه خود را در مانیفست خود اعلام کنید تا به Android Auto و Android Automotive OS این سرویس را کشف کرده و به برنامه شما متصل شوند.

قطعه کد زیر نحوه اعلام سرویس مرورگر رسانه خود را در مانیفست نشان می دهد. این کد را در فایل مانیفست ماژول سیستم عامل Android Automotive خود و در فایل مانیفست برنامه تلفن خود قرار دهید.

<application>
    ...
    <service android:name=".MyMediaBrowserService"
             android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
    ...
</application>
نمادهای برنامه را مشخص کنید

باید نمادهای برنامه را مشخص کنید که Android Auto و Android Automotive OS می توانند برای نمایش برنامه شما در رابط کاربری سیستم استفاده کنند. دو نوع نماد مورد نیاز است:

  • نماد راه‌انداز
  • نماد اسناد
نماد راه‌انداز

نماد راه‌انداز برنامه شما را در رابط کاربری سیستم، مانند راه‌انداز و سینی نمادها نشان می‌دهد. می‌توانید مشخص کنید که می‌خواهید از نماد برنامه تلفن همراه خود برای نشان دادن برنامه رسانه خودرو خود با استفاده از مانیفست زیر استفاده کنید:

<application
    ...
    android:icon="@mipmap/ic_launcher"
    ...
/>

برای استفاده از نمادی متفاوت از نماد برنامه تلفن همراه خود، ویژگی android:icon در عنصر <service> سرویس مرورگر رسانه خود در مانیفست تنظیم کنید:

<application>
    ...
    <service
        ...
        android:icon="@mipmap/auto_launcher"
        ...
    />
</application>
نماد اسناد

شکل 1. نماد اسناد در کارت رسانه.

نماد انتساب در مکان هایی استفاده می شود که محتوای رسانه اولویت دارد، مانند کارت های رسانه. استفاده مجدد از نماد کوچک مورد استفاده برای اعلان ها را در نظر بگیرید. این نماد باید تک رنگ باشد. می‌توانید نمادی را که برای نمایش برنامه‌تان استفاده می‌شود، با استفاده از اعلان مانیفست زیر مشخص کنید:

<application>
    ...
    <meta-data
        android:name="androidx.car.app.TintableAttributionIcon"
        android:resource="@drawable/ic_status_icon" />
    ...
</application>
سرویس مرورگر رسانه خود را ایجاد کنید

شما با گسترش کلاس MediaBrowserServiceCompat یک سرویس مرورگر رسانه ایجاد می کنید. هر دو سیستم عامل Android Auto و Android Automotive می توانند از خدمات شما برای انجام کارهای زیر استفاده کنند:

  • سلسله مراتب محتوای برنامه خود را برای ارائه منو به کاربر مرور کنید.
  • برای کنترل پخش صدا، توکن شی MediaSessionCompat برنامه خود را دریافت کنید.

همچنین می‌توانید از سرویس مرورگر رسانه خود استفاده کنید تا به سایر مشتریان اجازه دهید به محتوای رسانه برنامه شما دسترسی داشته باشند. این کلاینت‌های رسانه ممکن است برنامه‌های دیگری در تلفن کاربر باشند یا می‌توانند مشتریان راه دور دیگری باشند.

گردش کار سرویس مرورگر رسانه

این بخش نحوه تعامل سیستم عامل Android Automotive و Android Auto با سرویس مرورگر رسانه شما را در طول یک گردش کار معمولی کاربر توضیح می دهد.

  1. کاربر برنامه شما را در سیستم عامل Android Automotive یا Android Auto راه اندازی می کند.
  2. سیستم عامل Android Automotive یا Android Auto با استفاده از روش onCreate() با سرویس مرورگر رسانه برنامه شما تماس می گیرد. در پیاده سازی متد onCreate() باید یک شی MediaSessionCompat و آبجکت برگشت آن را ایجاد و ثبت کنید.
  3. سیستم عامل Android Automotive یا Android Auto روش onGetRoot() سرویس شما را فراخوانی می کند تا آیتم رسانه ریشه در سلسله مراتب محتوای شما را دریافت کند. مورد رسانه ریشه نمایش داده نمی شود. در عوض، برای بازیابی محتوای بیشتر از برنامه شما استفاده می شود.
  4. سیستم عامل Android Automotive یا Android Auto روش onLoadChildren() سرویس شما را فراخوانی می کند تا فرزندان آیتم رسانه ریشه را دریافت کند. سیستم‌عامل Android Automotive و Android Auto این موارد رسانه‌ای را به عنوان سطح بالای آیتم‌های محتوا نمایش می‌دهند. برای اطلاعات بیشتر در مورد آنچه سیستم در این سطح انتظار دارد، به ساختار منوی ریشه در این صفحه مراجعه کنید.
  5. اگر کاربر یک مورد رسانه قابل مرور را انتخاب کند، متد onLoadChildren() سرویس شما دوباره فراخوانی می شود تا فرزندان آیتم منوی انتخاب شده را بازیابی کند.
  6. اگر کاربر یک مورد رسانه قابل پخش را انتخاب کند، سیستم عامل Android Automotive یا Android Auto روش بازگشت تماس جلسه رسانه مناسب را برای انجام آن عمل فراخوانی می کند.
  7. اگر توسط برنامه شما پشتیبانی می شود، کاربر می تواند محتوای شما را نیز جستجو کند. در این حالت، سیستم عامل Android Automotive یا Android Auto روش onSearch() سرویس شما را فراخوانی می کند.
سلسله مراتب محتوای خود را بسازید

Android Auto و Android Automotive OS با سرویس مرورگر رسانه برنامه شما تماس می گیرند تا بفهمند چه محتوایی در دسترس است. برای پشتیبانی از آن باید دو روش را در سرویس مرورگر رسانه خود پیاده سازی کنید: onGetRoot() و onLoadChildren()

پیاده سازی onGetRoot

متد onGetRoot() سرویس شما اطلاعات مربوط به گره ریشه سلسله مراتب محتوای شما را برمی گرداند. Android Auto و Android Automotive OS از این گره ریشه برای درخواست بقیه محتوای شما با استفاده از روش onLoadChildren() استفاده می کنند.

قطعه کد زیر یک پیاده سازی ساده از متد onGetRoot() را نشان می دهد:

کاتلین
override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? =
    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        null
    } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)
جاوا
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        return null;
    }

    return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
}

برای مثال دقیق‌تر از این روش، متد onGetRoot() را در برنامه نمونه Universal Android Music Player در GitHub ببینید.

افزودن اعتبار سنجی بسته برای onGetRoot()

هنگامی که با متد onGetRoot() سرویس شما تماس برقرار می شود، بسته فراخوانی اطلاعات شناسایی را به سرویس شما ارسال می کند. سرویس شما می تواند از این اطلاعات برای تصمیم گیری در مورد اینکه آیا آن بسته می تواند به محتوای شما دسترسی داشته باشد استفاده کند. برای مثال، می‌توانید با مقایسه clientPackageName با لیست مجاز خود و تأیید گواهی استفاده شده برای امضای APK بسته، دسترسی به محتوای برنامه خود را به فهرستی از بسته‌های تأییدشده محدود کنید. اگر بسته قابل تأیید نیست، برای جلوگیری از دسترسی به محتوای خود، null برگردانید.

برای ارائه برنامه‌های سیستمی، مانند Android Auto و Android Automotive OS، با دسترسی به محتوای شما، سرویس شما باید همیشه یک BrowserRoot غیر تهی را زمانی که این برنامه‌های سیستم متد onGetRoot() را فراخوانی می‌کنند، برگرداند. امضای برنامه سیستم سیستم عامل Android Automotive بسته به نوع و مدل خودرو می تواند متفاوت باشد، بنابراین باید به اتصالات همه برنامه های سیستم اجازه دهید تا از سیستم عامل Android Automotive به شدت پشتیبانی کنند.

قطعه کد زیر نشان می دهد که چگونه سرویس شما می تواند تأیید کند که بسته تماس یک برنامه سیستمی است:

fun isKnownCaller(
    callingPackage: String,
    callingUid: Int
): Boolean {
    ...
    val isCallerKnown = when {
       // If the system is making the call, allow it.
       callingUid == Process.SYSTEM_UID -> true
       // If the app was signed by the same certificate as the platform
       // itself, also allow it.
       callerSignature == platformSignature -> true
       // ... more cases
    }
    return isCallerKnown
}

این قطعه کد گزیده ای از کلاس PackageValidator در برنامه نمونه برنامه پخش موسیقی جهانی اندروید در GitHub است. برای مثال دقیق تر از نحوه پیاده سازی اعتبار سنجی بسته برای متد onGetRoot() سرویس خود، آن کلاس را ببینید.

علاوه بر اجازه دادن به برنامه‌های سیستم، باید به دستیار Google اجازه دهید به MediaBrowserService شما متصل شود. توجه داشته باشید که Google Assistant دارای نام‌های بسته جداگانه برای تلفن است که شامل Android Auto و سیستم عامل Android Automotive است.

پیاده سازی onLoadChildren()

پس از دریافت شی root node، Android Auto و Android Automotive OS با فراخوانی onLoadChildren() روی شی گره ریشه، یک منوی سطح بالایی ایجاد می کنند تا فرزندان آن را دریافت کنند. برنامه های سرویس گیرنده با فراخوانی همین روش با استفاده از اشیاء گره فرزند، زیر منوها را می سازند.

هر گره در سلسله مراتب محتوای شما توسط یک شی MediaBrowserCompat.MediaItem نشان داده می شود. هر یک از این موارد رسانه ای با یک رشته ID منحصر به فرد شناسایی می شوند. برنامه های سرویس گیرنده با این رشته های شناسه به عنوان توکن های غیر شفاف برخورد می کنند. هنگامی که یک برنامه مشتری می خواهد به یک منوی فرعی مرور کند یا یک آیتم رسانه را پخش کند، رمز را ارسال می کند. برنامه شما مسئول مرتبط کردن توکن با آیتم رسانه ای مناسب است.

قطعه کد زیر یک پیاده سازی ساده از متد onLoadChildren() را نشان می دهد:

کاتلین
override fun onLoadChildren(
    parentMediaId: String,
    result: Result<List<MediaBrowserCompat.MediaItem>>
) {
    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check whether this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the children of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}
جاوا
@Override
public void onLoadChildren(final String parentMediaId,
    final Result<List<MediaBrowserCompat.MediaItem>> result) {

    // Assume for example that the music catalog is already loaded/cached.

    List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

    // Check whether this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the children of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

برای مثال کاملی از این روش، متد onLoadChildren() را در برنامه نمونه Universal Android Music Player در GitHub ببینید.

ساختار منوی ریشه

شکل 2. محتوای ریشه به عنوان برگه های ناوبری نمایش داده می شود.

Android Auto و Android Automotive OS محدودیت های خاصی در مورد ساختار منوی ریشه دارند. اینها از طریق راهنمایی ریشه به MediaBrowserService منتقل می شوند، که می توانند از طریق آرگومان Bundle که به onGetRoot() ارسال می شود، خوانده شوند. پیروی از این نکات به سیستم اجازه می دهد تا محتوای ریشه را به صورت بهینه به عنوان برگه های ناوبری نمایش دهد. اگر این نکات را دنبال نکنید، ممکن است برخی از محتوای ریشه حذف شود یا توسط سیستم کمتر قابل شناسایی باشد. دو نکته ارسال می شود:

از کد زیر برای خواندن نکات ریشه مربوطه استفاده کنید:

کاتلین
import androidx.media.utils.MediaConstants

// Later, in your MediaBrowserServiceCompat.
override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}
جاوا
import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

می‌توانید منطق ساختار سلسله مراتب محتوای خود را بر اساس مقادیر این نکات منشعب کنید، به خصوص اگر سلسله مراتب شما در میان ادغام‌های MediaBrowser خارج از Android Auto و Android Automotive OS متفاوت باشد. به عنوان مثال، اگر به طور معمول یک آیتم قابل پخش ریشه را نشان می دهید، ممکن است بخواهید به جای آن، به دلیل ارزش اشاره پرچم های پشتیبانی شده، آن را زیر یک آیتم قابل مرور ریشه قرار دهید.

جدا از نکات ریشه، چند دستورالعمل اضافی وجود دارد که باید رعایت کنید تا اطمینان حاصل شود که برگه ها بهینه ارائه می شوند:

  • برای هر مورد برگه، نمادهای تک رنگ، ترجیحاً سفید را ارائه دهید.
  • برای هر مورد برگه برچسب های کوتاه اما معنی دار ارائه کنید. کوتاه نگه داشتن برچسب ها احتمال کوتاه شدن رشته ها را کاهش می دهد.
نمایش آثار هنری رسانه ای

آثار هنری برای موارد رسانه باید به عنوان یک URI محلی با استفاده از ContentResolver.SCHEME_CONTENT یا ContentResolver.SCHEME_ANDROID_RESOURCE ارسال شوند. این URI محلی باید به یک بیت مپ یا یک بردار قابل ترسیم در منابع برنامه حل شود. برای اشیاء MediaDescriptionCompat که مواردی را در سلسله مراتب محتوا نشان می‌دهند، URI را از طریق setIconUri() عبور دهید. برای اشیاء MediaMetadataCompat که آیتم در حال پخش را نشان می‌دهند، با استفاده از هر یک از کلیدهای زیر، URI را از طریق putString() عبور دهید:

مراحل زیر نحوه بارگیری هنر از یک URI وب و نمایش آن از طریق یک URI محلی را توضیح می دهد. برای مثال کامل‌تر، پیاده‌سازی openFile() و متدهای اطراف آن را در برنامه نمونه برنامه پخش موسیقی جهانی اندروید ببینید.

  1. یک content:// URI مطابق با URI وب بسازید. سرویس مرورگر رسانه و جلسه رسانه این URI محتوا را به Android Auto و Android Automotive OS منتقل می کند.

    کاتلین
    fun Uri.asAlbumArtContentURI(): Uri {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(this.getPath()) // Make sure you trust the URI
        .build()
    }
    جاوا
    public static Uri asAlbumArtContentURI(Uri webUri) {
      return new Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(webUri.getPath()) // Make sure you trust the URI!
        .build();
    }
  2. در اجرای ContentProvider.openFile() ، بررسی کنید که آیا فایلی برای URI مربوطه وجود دارد یا خیر. اگر نه، فایل تصویر را دانلود و کش کنید. قطعه کد زیر از Glide استفاده می کند.

    کاتلین
    override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
      val context = this.context ?: return null
      val file = File(context.cacheDir, uri.path)
      if (!file.exists()) {
        val remoteUri = Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.path)
            .build()
        val cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
    
        cacheFile.renameTo(file)
        file = cacheFile
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
    }
    جاوا
    @Nullable
    @Override
    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
        throws FileNotFoundException {
      Context context = this.getContext();
      File file = new File(context.getCacheDir(), uri.getPath());
      if (!file.exists()) {
        Uri remoteUri = new Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.getPath())
            .build();
        File cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    
        cacheFile.renameTo(file);
        file = cacheFile;
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    }

برای جزئیات بیشتر در مورد ارائه دهندگان محتوا، به ایجاد یک ارائه دهنده محتوا مراجعه کنید.

اعمال سبک های محتوا

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

می توانید از سبک های محتوای زیر استفاده کنید:

فهرست موارد

این سبک محتوا عناوین و ابرداده ها را بر تصاویر اولویت می دهد.

آیتم های شبکه

این سبک محتوا، تصاویر را بر عناوین و ابرداده ها در اولویت قرار می دهد.

سبک های محتوای پیش فرض را تنظیم کنید

می‌توانید پیش‌فرض‌های جهانی را برای نحوه نمایش آیتم‌های رسانه خود با گنجاندن ثابت‌های خاصی در بسته اضافی BrowserRoot متد onGetRoot() سرویس خود تنظیم کنید. Android Auto و Android Automotive OS این بسته را مطالعه کرده و برای تعیین سبک مناسب به دنبال آن ثابت‌ها بگردید.

موارد اضافی زیر را می توان به عنوان کلید در بسته استفاده کرد:

کلیدها می توانند به مقادیر ثابت اعداد صحیح زیر نگاشت شوند تا بر نمایش آن موارد تأثیر بگذارند:

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM : موارد مربوطه به عنوان موارد فهرست ارائه می‌شوند.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM : موارد مربوطه به عنوان موارد شبکه ارائه می شوند.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM : موارد مربوطه به عنوان موارد فهرست "رده" ارائه می شوند. این موارد مانند آیتم های لیست معمولی هستند با این تفاوت که حاشیه ها در اطراف نمادهای آیتم ها اعمال می شوند، زیرا نمادها وقتی کوچک هستند بهتر به نظر می رسند. نمادها باید بردار قابل ترسیم باشند. انتظار می رود این راهنمایی فقط برای موارد قابل مرور ارائه شود.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM : موارد مربوطه به عنوان آیتم‌های شبکه «رده» ارائه می‌شوند. این موارد مانند آیتم‌های شبکه‌ای معمولی هستند، با این تفاوت که حاشیه‌ها در اطراف نمادهای آیتم‌ها اعمال می‌شوند، زیرا آیکون‌ها وقتی کوچک هستند بهتر به نظر می‌رسند. نمادها باید بردار قابل ترسیم باشند. انتظار می رود این راهنمایی فقط برای موارد قابل مرور ارائه شود.

قطعه کد زیر نحوه تنظیم سبک محتوای پیش‌فرض برای موارد قابل مرور به شبکه و موارد قابل پخش در لیست را نشان می‌دهد:

کاتلین
import androidx.media.utils.MediaConstants

@Nullable
override fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    return BrowserRoot(ROOT_ID, extras)
}
جاوا
import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    return new BrowserRoot(ROOT_ID, extras);
}
سبک محتوای هر مورد را تنظیم کنید

Content Style API به شما امکان می‌دهد سبک محتوای پیش‌فرض را برای فرزندان هر آیتم رسانه قابل مرور و همچنین هر مورد رسانه‌ای لغو کنید.

برای لغو پیش‌فرض برای فرزندان یک مورد رسانه قابل مرور، یک بسته اضافی در MediaDescription مورد رسانه ایجاد کنید و همان نکات ذکر شده قبلی را اضافه کنید. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE برای کودکان قابل بازی آن مورد اعمال می شود، در حالی که DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE برای کودکان قابل مرور آن مورد اعمال می شود.

برای لغو پیش‌فرض برای خود یک مورد رسانه خاص، نه فرزندان آن، یک بسته اضافی در MediaDescription مورد رسانه ایجاد کنید و یک راهنمایی با کلید DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM اضافه کنید. از همان مقادیری که قبلا توضیح داده شد برای مشخص کردن ارائه آن مورد استفاده کنید.

قطعه کد زیر نحوه ایجاد یک MediaItem قابل مرور را نشان می دهد که سبک محتوای پیش فرض را هم برای خودش و هم برای فرزندانش لغو می کند. خود را به‌عنوان آیتم فهرست دسته‌بندی، فرزندان قابل مرور آن به عنوان آیتم‌های فهرست، و کودکان قابل پخش آن به عنوان آیتم‌های شبکه‌ای استایل می‌کند:

کاتلین
import androidx.media.utils.MediaConstants

private fun createBrowsableMediaItem(
    mediaId: String,
    folderName: String,
    iconUri: Uri
): MediaBrowser.MediaItem {
    val mediaDescriptionBuilder = MediaDescription.Builder()
    mediaDescriptionBuilder.setMediaId(mediaId)
    mediaDescriptionBuilder.setTitle(folderName)
    mediaDescriptionBuilder.setIconUri(iconUri)
    val extras = Bundle()
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)
}
جاوا
import androidx.media.utils.MediaConstants;

private MediaBrowser.MediaItem createBrowsableMediaItem(
    String mediaId,
    String folderName,
    Uri iconUri) {
    MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
    mediaDescriptionBuilder.setMediaId(mediaId);
    mediaDescriptionBuilder.setTitle(folderName);
    mediaDescriptionBuilder.setIconUri(iconUri);
    Bundle extras = new Bundle();
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    mediaDescriptionBuilder.setExtras(extras);
    return new MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
موارد را با استفاده از نکات عنوان گروه بندی کنید

برای گروه بندی آیتم های رسانه ای مرتبط با هم، از یک اشاره برای هر مورد استفاده می کنید. هر مورد رسانه ای در یک گروه باید یک بسته اضافی را در MediaDescription خود اعلام کند که شامل یک نقشه با کلید DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE و یک مقدار رشته یکسان است. این رشته را که به عنوان عنوان گروه استفاده می شود، محلی کنید.

قطعه کد زیر نحوه ایجاد یک MediaItem با عنوان زیرگروه "Songs" را نشان می دهد:

کاتلین
import androidx.media.utils.MediaConstants

private fun createMediaItem(
    mediaId: String,
    folderName: String,
    iconUri: Uri
): MediaBrowser.MediaItem {
    val mediaDescriptionBuilder = MediaDescription.Builder()
    mediaDescriptionBuilder.setMediaId(mediaId)
    mediaDescriptionBuilder.setTitle(folderName)
    mediaDescriptionBuilder.setIconUri(iconUri)
    val extras = Bundle()
    extras.putString(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
        "Songs")
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), /* playable or browsable flag*/)
}
جاوا
import androidx.media.utils.MediaConstants;

private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) {
   MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
   mediaDescriptionBuilder.setMediaId(mediaId);
   mediaDescriptionBuilder.setTitle(folderName);
   mediaDescriptionBuilder.setIconUri(iconUri);
   Bundle extras = new Bundle();
   extras.putString(
       MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
       "Songs");
   mediaDescriptionBuilder.setExtras(extras);
   return new MediaBrowser.MediaItem(
       mediaDescriptionBuilder.build(), /* playable or browsable flag*/);
}

برنامه شما باید همه آیتم های رسانه ای را که می خواهید با هم به عنوان یک بلوک پیوسته گروه بندی کنید، ارسال کند. برای مثال، فرض کنید که می‌خواهید دو گروه از آیتم‌های رسانه، «آهنگ‌ها» و «آلبوم‌ها» را به ترتیب نمایش دهید و برنامه شما در پنج آیتم رسانه به ترتیب زیر ارسال شود:

  1. مورد رسانه A با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  2. مورد رسانه B با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums" )
  3. مورد رسانه C با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  4. مورد رسانه D با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  5. مورد رسانه E با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums" )

از آنجایی که آیتم‌های رسانه گروه «آهنگ‌ها» و گروه «آلبوم‌ها» با هم در بلوک‌های پیوسته نگهداری نمی‌شوند، Android Auto و Android Automotive OS این را به چهار گروه زیر تعبیر می‌کنند:

  • گروه 1 به نام "ترانه ها" حاوی آیتم رسانه ای A
  • گروه 2 به نام "آلبوم" حاوی آیتم رسانه ای B
  • گروه 3 به نام "آهنگ ها" حاوی آیتم های رسانه ای C و D
  • گروه 4 به نام "آلبوم" حاوی آیتم رسانه ای E

برای نمایش این موارد در دو گروه، برنامه شما باید موارد رسانه را به ترتیب زیر ارسال کند:

  1. مورد رسانه A با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  2. مورد رسانه C با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  3. مورد رسانه D با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs" )
  4. مورد رسانه B با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums" )
  5. مورد رسانه E با extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums" )
نشانگرهای فراداده اضافی را نمایش دهید

می توانید شاخص های فراداده اضافی را برای ارائه اطلاعات در یک نگاه برای محتوا در درخت مرورگر رسانه و در حین پخش اضافه کنید. در درخت مرور، Android Auto و Android Automotive OS موارد اضافی مرتبط با یک مورد را می‌خوانند و به دنبال ثابت‌های خاصی می‌گردند تا مشخص کنند کدام نشانگرها باید نمایش داده شوند. در حین پخش رسانه، Android Auto و Android Automotive OS فراداده‌های جلسه رسانه را می‌خوانند و به دنبال ثابت‌های خاصی می‌گردند تا نشانگرهایی را برای نمایش تعیین کنند.

شکل 3. نمای پخش با ابرداده شناسایی آهنگ و هنرمند و همچنین نمادی که محتوای صریح را نشان می دهد.

شکل 4. نمای را با نقطه برای محتوای پخش نشده در مورد اول و نوار پیشرفت برای محتوای نیمه پخش شده در مورد دوم مرور کنید.

ثابت‌های زیر را می‌توان هم در توضیحات اضافی MediaItem و هم در موارد اضافی MediaMetadata استفاده کرد:

  • EXTRA_DOWNLOAD_STATUS : وضعیت دانلود یک مورد را نشان می دهد. از این ثابت به عنوان کلید استفاده کنید. ثابت های طولانی زیر مقادیر ممکن هستند:
  • METADATA_KEY_IS_EXPLICIT : نشان می دهد که آیا مورد حاوی محتوای صریح است یا خیر. برای نشان دادن صریح بودن یک مورد، از این ثابت به عنوان کلید و METADATA_VALUE_ATTRIBUTE_PRESENT طولانی به عنوان مقدار استفاده کنید.

ثابت های زیر را فقط می توان در توضیحات اضافی MediaItem استفاده کرد:

برای نمایش نشانگرهایی که در هنگام مرور درخت مرور رسانه ظاهر می شوند، یک بسته اضافی ایجاد کنید که شامل یک یا چند مورد از این ثابت ها باشد و آن بسته را به متد MediaDescription.Builder.setExtras() منتقل کنید.

قطعه کد زیر نحوه نمایش نشانگرها را برای یک مورد رسانه صریح که 70٪ کامل است نشان می دهد:

کاتلین
import androidx.media.utils.MediaConstants

val extras = Bundle()
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED)
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)
جاوا
import androidx.media.utils.MediaConstants;

Bundle extras = new Bundle();
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);

برای نمایش نشانگرهای مورد رسانه ای که در حال پخش است، می توانید مقادیر Long را برای METADATA_KEY_IS_EXPLICIT یا EXTRA_DOWNLOAD_STATUS در MediaMetadataCompat mediaSession خود اعلام کنید. شما نمی توانید نشانگرهای DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS یا DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE در نمای پخش نمایش دهید.

قطعه کد زیر نشان می دهد که چگونه می توان نشان داد که آهنگ فعلی در نمای پخش صریح و دانلود شده است:

کاتلین
import androidx.media.utils.MediaConstants

mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build())
جاوا
import androidx.media.utils.MediaConstants;

mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build());
هنگام پخش محتوا، نوار پیشرفت را در نمای مرور به روز کنید

همانطور که قبلا ذکر شد، می‌توانید از DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE اضافی برای نمایش نوار پیشرفت برای محتوای نیمه‌بازی‌شده در نمای مرور استفاده کنید. با این حال، اگر کاربر به پخش محتوای نیمه‌بازی‌شده از Android Auto یا Android Automotive OS ادامه دهد، با گذشت زمان این نشانگر نادرست می‌شود.

برای اینکه Android Auto و Android Automotive OS نوار پیشرفت را به روز نگه دارند، می توانید اطلاعات بیشتری را در MediaMetadataCompat و PlaybackStateCompat ارائه دهید تا محتوای در حال انجام را به موارد رسانه در نمای مرور پیوند دهید. برای اینکه آیتم رسانه نوار پیشرفت به‌روزرسانی خودکار داشته باشد، باید شرایط زیر رعایت شود:

قطعه کد زیر نشان می دهد که چگونه می توان نشان داد که آیتم در حال پخش به یک آیتم در نمای مرور مرتبط است:

کاتلین
import androidx.media.utils.MediaConstants

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
val mediaItemExtras = Bundle()
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build())

val playbackStateExtras = Bundle()
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id")
mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build())
جاوا
import androidx.media.utils.MediaConstants;

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
Bundle mediaItemExtras = new Bundle();
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build();
return MediaBrowserCompat.MediaItem(description, /* flags */);

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build());

Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id");
mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build());
نمایش نتایج جستجوی قابل مرور

شکل 5. نمای پخش با گزینه "نتایج جستجو" برای مشاهده موارد رسانه ای مربوط به جستجوی صوتی کاربر.

برنامه شما می‌تواند نتایج جستجوی متنی را ارائه کند که هنگام شروع یک عبارت جستجو برای کاربران نمایش داده شود. Android Auto و Android Automotive OS این نتایج را از طریق رابط‌های درخواست جستجو یا از طریق مقرون به صرفه‌هایی که بر روی عبارت‌های قبلی در جلسه انجام شده است، نشان می‌دهند. برای کسب اطلاعات بیشتر، به بخش پشتیبانی از اقدامات صوتی در این راهنما مراجعه کنید.

برای نمایش نتایج جستجوی قابل مرور، کلید ثابت BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED را در بسته اضافی متد onGetRoot() سرویس خود قرار دهید، با نگاشت true بولی.

قطعه کد زیر نحوه فعال کردن پشتیبانی را در متد onGetRoot() نشان می دهد:

کاتلین
import androidx.media.utils.MediaConstants

@Nullable
fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true)
    return BrowserRoot(ROOT_ID, extras)
}
جاوا
import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putBoolean(
        MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true);
    return new BrowserRoot(ROOT_ID, extras);
}

برای شروع ارائه نتایج جستجو، روش onSearch() را در سرویس مرورگر رسانه خود لغو کنید. Android Auto و Android Automotive OS هر زمان که کاربر یک رابط جستجو یا "نتایج جستجو" را فراخوانی کند، عبارات جستجوی کاربر را به این روش ارسال می کند.

می توانید نتایج جستجو را از روش onSearch() سرویس خود با استفاده از آیتم های عنوان سازماندهی کنید تا آنها را قابل مرور کنید. به عنوان مثال، اگر برنامه شما موسیقی پخش می کند، ممکن است نتایج جستجو را بر اساس آلبوم، هنرمند و آهنگ ها سازماندهی کنید.

قطعه کد زیر پیاده سازی ساده ای از متد onSearch() را نشان می دهد:

کاتلین
fun onSearch(query: String, extras: Bundle) {
  // Detach from results to unblock the caller (if a search is expensive).
  result.detach()
  object:AsyncTask() {
    internal var searchResponse:ArrayList
    internal var succeeded = false
    protected fun doInBackground(vararg params:Void):Void {
      searchResponse = ArrayList()
      if (doSearch(query, extras, searchResponse))
      {
        succeeded = true
      }
      return null
    }
    protected fun onPostExecute(param:Void) {
      if (succeeded)
      {
        // Sending an empty List informs the caller that there were no results.
        result.sendResult(searchResponse)
      }
      else
      {
        // This invokes onError() on the search callback.
        result.sendResult(null)
      }
      return null
    }
  }.execute()
}
// Populates resultsToFill with search results. Returns true on success or false on error.
private fun doSearch(
    query: String,
    extras: Bundle,
    resultsToFill: ArrayList
): Boolean {
  // Implement this method.
}
جاوا
@Override
public void onSearch(final String query, final Bundle extras,
                        Result<List<MediaItem>> result) {

  // Detach from results to unblock the caller (if a search is expensive).
  result.detach();

  new AsyncTask<Void, Void, Void>() {
    List<MediaItem> searchResponse;
    boolean succeeded = false;
    @Override
    protected Void doInBackground(Void... params) {
      searchResponse = new ArrayList<MediaItem>();
      if (doSearch(query, extras, searchResponse)) {
        succeeded = true;
      }
      return null;
    }

    @Override
    protected void onPostExecute(Void param) {
      if (succeeded) {
       // Sending an empty List informs the caller that there were no results.
       result.sendResult(searchResponse);
      } else {
        // This invokes onError() on the search callback.
        result.sendResult(null);
      }
    }
  }.execute()
}

/** Populates resultsToFill with search results. Returns true on success or false on error. */
private boolean doSearch(String query, Bundle extras, ArrayList<MediaItem> resultsToFill) {
    // Implement this method.
}
اقدامات مرور سفارشی
یک اقدام مرور سفارشی.

شکل 6. اقدام مرور سفارشی منفرد

اعمال مرور سفارشی به شما این امکان را می دهد که نمادها و برچسب های سفارشی را به اشیاء MediaItem برنامه خود در برنامه رسانه خودرو اضافه کنید و تعاملات کاربر را با این کنش ها مدیریت کنید. این به شما امکان می‌دهد عملکرد برنامه رسانه را به روش‌های مختلفی گسترش دهید، مانند افزودن عملکردهای «دانلود»، «افزودن به صف»، «رادیو پخش»، «مورد علاقه» یا «حذف».

منوی سرریز اقدامات مرور سفارشی.

شکل 7. سرریز اقدام مرور سفارشی

اگر اقدامات سفارشی بیشتر از آنچه OEM اجازه نمایش داده است وجود داشته باشد، یک منوی سرریز به کاربر ارائه می شود.

چگونه کار می کنند؟

هر اقدام مرور سفارشی با موارد زیر تعریف می شود:

  • شناسه اقدام (شناسه رشته منحصر به فرد)
  • یک برچسب اقدام (متن نمایش داده شده به کاربر)
  • URI یک Action Icon (یک بردار قابل ترسیم که می تواند رنگ آمیزی شود)

شما فهرستی از اقدامات مرور سفارشی را در سطح جهانی به عنوان بخشی از BrowseRoot خود تعریف می کنید. سپس می توانید زیرمجموعه ای از این اقدامات را به MediaItem.

هنگامی که یک کاربر با یک اقدام مرور سفارشی تعامل می کند، برنامه شما در onCustomAction() یک تماس پاسخ دریافت می کند. سپس می‌توانید این عمل را انجام دهید و در صورت لزوم فهرست اقدامات MediaItem را به‌روزرسانی کنید. این برای اقدامات حالتی مانند "مورد علاقه" و "دانلود" مفید است. برای کنش‌هایی که نیازی به به‌روزرسانی ندارند، مانند «Play Radio»، نیازی نیست فهرست کنش‌ها را به‌روزرسانی کنید.

اقدامات مرور سفارشی در ریشه گره مرور.

شکل 8. نوار ابزار اقدام مرور سفارشی

همچنین می‌توانید اقدام‌های مرور سفارشی را به ریشه گره مرور متصل کنید. این اقدامات در یک نوار ابزار ثانویه در زیر نوار ابزار اصلی نمایش داده می شود.

نحوه پیاده سازی اقدامات مرور سفارشی

در اینجا مراحل افزودن اعمال مرور سفارشی به پروژه شما آمده است:

  1. دو روش را در اجرای MediaBrowserServiceCompat خود لغو کنید:
  2. محدودیت های عمل را در زمان اجرا تجزیه کنید:
    • در onGetRoot() با استفاده از کلید BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT در rootHints Bundle حداکثر تعداد اعمال مجاز را برای هر MediaItem دریافت کنید. محدودیت 0 نشان می دهد که این ویژگی توسط سیستم پشتیبانی نمی شود.
  3. فهرست جهانی اقدامات مرور سفارشی را بسازید:
    • برای هر کنش، یک شی Bundle با کلیدهای زیر ایجاد کنید: * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID : شناسه کنش * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL : برچسب کنش * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI تمام Bundle را اضافه کنید به یک لیست
  4. لیست جهانی را به BrowseRoot خود اضافه کنید:
  5. اعمالی را به اشیاء MediaItem خود اضافه کنید:
    • می‌توانید با استفاده از کلید DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST ، کنش‌ها را با گنجاندن فهرست شناسه‌های کنش در موارد اضافی MediaDescriptionCompat به اشیاء جداگانه MediaItem اضافه کنید. این لیست باید زیرمجموعه ای از فهرست جهانی اقداماتی باشد که در BrowseRoot تعریف کرده اید.
  6. مدیریت اقدامات و بازگشت پیشرفت یا نتایج:
    • در onCustomAction ، عملکرد را بر اساس شناسه اقدام و هر داده دیگری که نیاز دارید مدیریت کنید. با استفاده از کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID می‌توانید شناسه MediaItem را که این عمل را آغاز کرده است، از موارد اضافی دریافت کنید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID .
    • با قرار دادن کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM در بسته پیشرفت یا نتیجه، می‌توانید فهرست اقدامات یک MediaItem را به‌روزرسانی کنید.

در اینجا تغییراتی وجود دارد که می توانید در BrowserServiceCompat خود ایجاد کنید تا با اعمال مرور سفارشی شروع کنید.

نادیده گرفتن BrowserServiceCompat

باید روش های زیر را در MediaBrowserServiceCompat لغو کنید.

public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)

public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)
محدودیت کنش‌ها را تجزیه کنید

باید بررسی کنید تا ببینید چه تعداد از عملکردهای مرور سفارشی پشتیبانی می شوند.

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
    rootHints.getInt(
            MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0)
}

یک اکشن مرور سفارشی بسازید

هر عمل باید در یک Bundle جداگانه بسته بندی شود.

  • شناسه اقدام
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
  • برچسب اقدام
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
  • Action Icon URI
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")
اعمال مرور سفارشی را به Parceable ArrayList اضافه کنید

تمام اشیاء Custom Browse Action Bundle را به یک ArrayList اضافه کنید.

private ArrayList<Bundle> createCustomActionsList(
                                        CustomBrowseAction browseActions) {
    ArrayList<Bundle> browseActionsBundle = new ArrayList<>();
    for (CustomBrowseAction browseAction : browseActions) {
        Bundle action = new Bundle();
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                browseAction.mId);
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                getString(browseAction.mLabelResId));
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                browseAction.mIcon);
        browseActionsBundle.add(action);
    }
    return browseActionsBundle;
}

فهرست اقدام مرور سفارشی را به ریشه مرور اضافه کنید
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {
    Bundle browserRootExtras = new Bundle();
    browserRootExtras.putParcelableArrayList(
            BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST,
            createCustomActionsList()));
    mRoot = new BrowserRoot(ROOT_ID, browserRootExtras);
    return mRoot;
}
اعمال را به MediaItem اضافه کنید
MediaDescriptionCompat buildDescription (long id, String title, String subtitle,
                String description, Uri iconUri, Uri mediaUri,
                ArrayList<String> browseActionIds) {

    MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
    bob.setMediaId(id);
    bob.setTitle(title);
    bob.setSubtitle(subtitle);
    bob.setDescription(description);
    bob.setIconUri(iconUri);
    bob.setMediaUri(mediaUri);

    Bundle extras = new Bundle();
    extras.putStringArrayList(
          DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST,
          browseActionIds);

    bob.setExtras(extras);
    return bob.build();
}
MediaItem mediaItem = new MediaItem(buildDescription(...), flags);
بر روی نتیجه onCustomAction بسازید
  • تجزیه mediaId از Bundle extras :
    @Override
    public void onCustomAction(
              @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){
      String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID);
    }
  • برای نتایج ناهمزمان، نتیجه را جدا کنید. result.detach()
  • بسته نتیجه را بسازید
    • پیام به کاربر
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
    • به‌روزرسانی مورد (برای به‌روزرسانی اقدامات در یک مورد استفاده کنید)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
    • نمای پخش را باز کنید
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
    • گره مرور را به روز کنید
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
  • در صورت بروز خطا، result.sendError(resultBundle).
  • در صورت به‌روزرسانی، با result.sendProgressUpdate(resultBundle) تماس بگیرید.
  • با فراخوانی result.sendResult(resultBundle) به پایان برسانید.
به روز رسانی وضعیت اقدام

با استفاده از روش result.sendProgressUpdate(resultBundle) با کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM ، می توانید MediaItem را به روز کنید تا وضعیت جدید عملکرد را منعکس کند. این به شما امکان می‌دهد در زمان واقعی به کاربر در مورد پیشرفت و نتیجه عملکرد او بازخورد ارائه دهید.

مثال: دانلود اکشن

در اینجا مثالی از نحوه استفاده از این ویژگی برای اجرای یک اقدام دانلود با سه حالت آورده شده است:

  1. دانلود: این حالت اولیه اکشن است. وقتی کاربر این عمل را انتخاب کرد، می‌توانید آن را با «Downloading» تغییر دهید و sendProgressUpdate تماس بگیرید تا رابط کاربری به‌روزرسانی شود.
  2. دانلود: این حالت نشان می دهد که دانلود در حال انجام است. می توانید از این حالت برای نشان دادن نوار پیشرفت یا نشانگر دیگری به کاربر استفاده کنید.
  3. دانلود شده: این حالت نشان می دهد که دانلود کامل شده است. وقتی بارگیری تمام شد، می‌توانید «دانلود» را با «دانلود شده» تعویض کنید و sendResult با کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM فراخوانی کنید تا نشان دهید که مورد باید بازخوانی شود. علاوه بر این، می توانید از کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE برای نمایش پیام موفقیت آمیز به کاربر استفاده کنید.

این رویکرد به شما امکان می دهد بازخورد واضحی در مورد فرآیند دانلود و وضعیت فعلی آن به کاربر ارائه دهید. می توانید جزئیات بیشتری را با نمادها اضافه کنید تا وضعیت های دانلود 25٪، 50٪، 75٪ را نشان دهید.

مثال: اکشن مورد علاقه

مثال دیگر یک عمل مورد علاقه با دو حالت است:

  1. Favorite: این عمل برای مواردی که در لیست علاقه مندی های کاربر نیستند نمایش داده می شود. وقتی کاربر این عملکرد را انتخاب کرد، می‌توانید آن را با «مورد علاقه» عوض کنید و sendResult با کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM برای به‌روزرسانی رابط کاربری تماس بگیرید.
  2. Favorited: این عمل برای مواردی که در لیست علاقه مندی های کاربر قرار دارند نمایش داده می شود. وقتی کاربر این عملکرد را انتخاب کرد، می‌توانید آن را با «مورد علاقه» عوض کنید و sendResult با کلید EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM برای به‌روزرسانی رابط کاربری تماس بگیرید.

این رویکرد راهی واضح و ثابت را برای کاربران فراهم می کند تا آیتم های مورد علاقه خود را مدیریت کنند.

این مثال‌ها انعطاف‌پذیری «عملکردهای مرور سفارشی» و نحوه استفاده از آن‌ها را برای پیاده‌سازی انواع عملکردها با بازخورد بلادرنگ برای یک تجربه کاربری پیشرفته در برنامه رسانه خودرو نشان می‌دهند.

برای اجرای نمونه کامل این قابلیت می توانید به پروژه TestMediaApp مراجعه کنید.

کنترل پخش را فعال کنید

Android Auto و Android Automotive OS دستورات کنترل پخش را از طریق MediaSessionCompat سرویس شما ارسال می کنند. شما باید یک جلسه را ثبت کنید و روش های مربوط به تماس را پیاده سازی کنید.

ثبت یک جلسه رسانه ای

در روش onCreate() سرویس مرورگر رسانه خود، یک MediaSessionCompat ایجاد کنید، سپس با فراخوانی setSessionToken() جلسه رسانه را ثبت کنید.

قطعه کد زیر نحوه ایجاد و ثبت یک جلسه رسانه را نشان می دهد:

کاتلین
override fun onCreate() {
    super.onCreate()
    ...
    // Start a new MediaSession.
    val session = MediaSessionCompat(this, "session tag").apply {
        // Set a callback object that implements MediaSession.Callback
        // to handle play control requests.
        setCallback(MyMediaSessionCallback())
    }
    sessionToken = session.sessionToken
    ...
}
جاوا
public void onCreate() {
    super.onCreate();
    ...
    // Start a new MediaSession.
    MediaSessionCompat session = new MediaSessionCompat(this, "session tag");
    setSessionToken(session.getSessionToken());

    // Set a callback object that implements MediaSession.Callback
    // to handle play control requests.
    session.setCallback(new MyMediaSessionCallback());
    ...
}

هنگامی که شیء جلسه رسانه را ایجاد می کنید، یک شیء پاسخ به تماس را تنظیم می کنید که برای رسیدگی به درخواست های کنترل پخش استفاده می شود. شما با ارائه یک پیاده‌سازی از کلاس MediaSessionCompat.Callback برای برنامه خود، این شئ callback را ایجاد می‌کنید. بخش بعدی نحوه پیاده سازی این شی را مورد بحث قرار می دهد.

اجرای دستورات بازی

هنگامی که کاربری از برنامه شما درخواست پخش برای یک مورد رسانه ای می کند، سیستم عامل Android Automotive و Android Auto از کلاس MediaSessionCompat.Callback از شی MediaSessionCompat برنامه شما که از سرویس مرورگر رسانه برنامه شما دریافت کرده اند استفاده می کنند. وقتی کاربر می‌خواهد بازپخش محتوا را کنترل کند، مانند توقف پخش یا پرش به آهنگ بعدی، Android Auto و Android Automotive OS یکی از روش‌های شی پاسخ تماس را فراخوانی می‌کنند.

برای مدیریت بازپخش محتوا، برنامه شما باید کلاس MediaSessionCompat.Callback انتزاعی را گسترش دهد و روش هایی را که برنامه شما پشتیبانی می کند پیاده سازی کند.

همه روش‌های پاسخ به تماس زیر را که برای نوع محتوایی که برنامه شما ارائه می‌دهد منطقی است، اجرا کنید:

onPrepare()
هنگام تغییر منبع رسانه فراخوانی می شود. سیستم عامل Android Automotive نیز بلافاصله پس از راه اندازی این روش را فراخوانی می کند. برنامه رسانه شما باید این روش را اجرا کند.
onPlay()
در صورتی که کاربر بازی را بدون انتخاب آیتم خاصی انتخاب کند، فراخوانی می شود. برنامه شما باید محتوای پیش فرض خود را پخش کند یا اگر پخش با onPause() متوقف شد، برنامه شما پخش را از سر می گیرد.

توجه: وقتی سیستم عامل Android Automotive یا Android Auto به سرویس مرورگر رسانه شما متصل می شود، برنامه شما نباید به طور خودکار شروع به پخش موسیقی کند. برای اطلاعات بیشتر، به بخش مربوط به تنظیم وضعیت پخش اولیه مراجعه کنید.

onPlayFromMediaId()
هنگامی که کاربر انتخاب می کند یک آیتم خاص را پخش کند فراخوانی می شود. این روش به شناسه‌ای منتقل می‌شود که سرویس مرورگر رسانه شما به آیتم رسانه در سلسله مراتب محتوای شما اختصاص داده است.
onPlayFromSearch()
هنگامی که کاربر بازی را از یک عبارت جستجو انتخاب می کند، فراخوانی می شود. برنامه باید بر اساس رشته جستجوی ارسال شده انتخاب مناسبی داشته باشد.
onPause()
زمانی که کاربر توقف پخش را انتخاب می کند، فراخوانی می شود.
onSkipToNext()
هنگامی که کاربر انتخاب می کند به مورد بعدی پرش کند، فراخوانی می شود.
onSkipToPrevious()
هنگامی که کاربر انتخاب می کند به مورد قبلی پرش کند، فراخوانی می شود.
onStop()
زمانی فراخوانی می شود که کاربر تصمیم می گیرد پخش را متوقف کند.

برای ارائه هر گونه عملکرد دلخواه، این روش ها را در برنامه خود نادیده بگیرید. اگر روشی توسط برنامه شما پشتیبانی نمی شود، نیازی به پیاده سازی آن ندارید. به عنوان مثال، اگر برنامه شما یک پخش زنده مانند پخش ورزشی پخش می کند، نیازی به پیاده سازی متد onSkipToNext() ندارید. به جای آن می توانید از پیاده سازی پیش فرض onSkipToNext() استفاده کنید.

برنامه شما برای پخش محتوا از طریق بلندگوهای خودرو نیازی به منطق خاصی ندارد. هنگامی که برنامه شما درخواست پخش محتوا را دریافت می کند، می تواند صدا را به همان روشی که محتوا را از طریق بلندگوها یا هدفون های تلفن کاربر پخش می کند پخش کند. Android Auto و Android Automotive OS به طور خودکار محتوای صوتی را به سیستم خودرو ارسال می کنند تا از طریق بلندگوهای خودرو پخش شود.

برای اطلاعات بیشتر درباره پخش محتوای صوتی، به نمای کلی MediaPlayer ، نمای کلی برنامه صوتی و نمای کلی ExoPlayer مراجعه کنید.

عملکردهای پخش استاندارد را تنظیم کنید

Android Auto و Android Automotive OS کنترل‌های پخش را بر اساس اقداماتی که در شی PlaybackStateCompat فعال می‌شوند، نمایش می‌دهند.

به طور پیش فرض، برنامه شما باید از اقدامات زیر پشتیبانی کند:

اگر برنامه شما با محتوای برنامه مرتبط باشد، می‌تواند از اقدامات زیر نیز پشتیبانی کند:

علاوه بر این، شما می توانید یک صف پخش ایجاد کنید که می تواند برای کاربر نمایش داده شود، اما نیازی به آن نیست. برای انجام این کار، متدهای setQueue() و setQueueTitle() را فراخوانی کنید، عمل ACTION_SKIP_TO_QUEUE_ITEM را فعال کنید، و بازگشت به تماس را onSkipToQueueItem() تعریف کنید.

همچنین، پشتیبانی از نماد Now playing را اضافه کنید، که نشانگر چیزی است که در حال پخش است. برای انجام این کار، متد setActiveQueueItemId() را فراخوانی کنید و شناسه آیتم در حال پخش را در صف ارسال کنید. هر زمان که صف تغییر کرد باید setActiveQueueItemId() به روز کنید.

دکمه‌های نمایش Android Auto و Android Automotive OS برای هر عملکرد فعال و همچنین صف پخش. هنگامی که دکمه ها کلیک می شوند، سیستم پاسخ تماس مربوطه خود را از MediaSessionCompat.Callback فراخوانی می کند.

فضای بلا استفاده را رزرو کنید

Android Auto و Android Automotive OS فضایی را در UI برای کنش‌های ACTION_SKIP_TO_PREVIOUS و ACTION_SKIP_TO_NEXT رزرو می‌کنند. اگر برنامه شما یکی از این عملکردها را پشتیبانی نمی‌کند، Android Auto و Android Automotive OS از این فضا برای نمایش هر اقدام سفارشی که ایجاد می‌کنید استفاده می‌کنند.

اگر نمی‌خواهید آن فضاها را با اقدامات سفارشی پر کنید، می‌توانید آن‌ها را رزرو کنید تا هر زمان که برنامه شما از عملکرد مربوطه پشتیبانی نمی‌کند، Android Auto و Android Automotive OS فضای خالی را خالی بگذارند. برای انجام این کار، متد setExtras() را با یک بسته اضافی که حاوی ثابت هایی است که با توابع رزرو شده مطابقت دارد، فراخوانی کنید. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT با ACTION_SKIP_TO_NEXT و SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV با ACTION_SKIP_TO_PREVIOUS مطابقت دارد. از این ثابت‌ها به‌عنوان کلید در بسته‌بندی استفاده کنید و برای مقادیر آنها از Boolean true استفاده کنید.

PlaybackState اولیه را تنظیم کنید

از آنجایی که Android Auto و Android Automotive OS با سرویس مرورگر رسانه شما ارتباط برقرار می کنند، جلسه رسانه شما با استفاده از PlaybackStateCompat وضعیت پخش محتوا را به اطلاع می رساند. وقتی سیستم عامل Android Automotive یا Android Auto به سرویس مرورگر رسانه شما متصل می شود، برنامه شما نباید به طور خودکار شروع به پخش موسیقی کند. در عوض، برای ازسرگیری یا شروع پخش بر اساس وضعیت خودرو یا عملکرد کاربر، به Android Auto و Android Automotive OS تکیه کنید.

برای انجام این کار، PlaybackStateCompat اولیه جلسه رسانه خود را روی STATE_STOPPED ، STATE_PAUSED ، STATE_NONE ، یا STATE_ERROR تنظیم کنید.

جلسات رسانه در Android Auto و Android Automotive OS فقط برای مدت زمان درایو دوام می‌آورند، بنابراین کاربران اغلب این جلسات را شروع و متوقف می‌کنند. برای ارتقای یک تجربه یکپارچه بین درایوها، وضعیت جلسه قبلی کاربر را پیگیری کنید، به طوری که وقتی برنامه رسانه درخواست رزومه دریافت می‌کند، کاربر بتواند به طور خودکار از جایی که کار را متوقف کرده است ادامه دهد - به عنوان مثال، آخرین آیتم رسانه پخش شده، PlaybackStateCompat ، و صف.

اقدامات پخش سفارشی را اضافه کنید

می‌توانید کنش‌های پخش سفارشی را برای نمایش اقدامات اضافی که برنامه رسانه شما پشتیبانی می‌کند، اضافه کنید. اگر فضا اجازه می دهد (و رزرو نمی شود) ، Android اقدامات سفارشی را به کنترل های حمل و نقل اضافه می کند. در غیر این صورت، اقدامات سفارشی در منوی سرریز نمایش داده می شود. اقدامات سفارشی به ترتیبی که به PlaybackStateCompat اضافه می شوند نمایش داده می شوند.

از اقدامات سفارشی برای ارائه رفتار متمایز از اقدامات استاندارد استفاده کنید. از آنها برای جایگزینی یا تکرار اقدامات استاندارد استفاده نکنید.

می‌توانید با استفاده از متد addCustomAction() در کلاس PlaybackStateCompat.Builder اکشن‌های سفارشی اضافه کنید.

قطعه کد زیر نحوه افزودن یک اقدام سفارشی "شروع یک کانال رادیویی" را نشان می دهد:

کاتلین
val customActionExtras = Bundle()
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO)

stateBuilder.addCustomAction(
    PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon // or R.drawable.media3_icon_radio
    ).run {
        setExtras(customActionExtras)
        build()
    }
)
جاوا
Bundle customActionExtras = new Bundle();
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO);

stateBuilder.addCustomAction(
    new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon) // or R.drawable.media3_icon_radio
    .setExtras(customActionExtras)
    .build());

برای مثال دقیق تر از این روش، متد setCustomAction() را در برنامه نمونه برنامه پخش موسیقی جهانی اندروید در GitHub ببینید.

پس از ایجاد اکشن سفارشی شما، جلسه رسانه شما می تواند با نادیده گرفتن متد onCustomAction() به عمل پاسخ دهد.

قطعه کد زیر نشان می دهد که برنامه شما چگونه ممکن است به یک اقدام «شروع یک کانال رادیویی» پاسخ دهد:

کاتلین
override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> {
            ...
        }
    }
}
جاوا
@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) {
        ...
    }
}

برای مثال دقیق تر از این روش، روش onCustomAction را در برنامه نمونه برنامه پخش موسیقی جهانی اندروید در GitHub ببینید.

نمادهایی برای اقدامات سفارشی

هر اقدام سفارشی که ایجاد می کنید به یک نماد نیاز دارد.

اگر توضیحات آن نماد با یکی از CommandButton.ICON_ مطابقت دارد، باید آن عدد صحیح را برای کلید EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT از موارد اضافی اقدام سفارشی تنظیم کنید. در سیستم‌های پشتیبانی‌شده، این منبع نماد ارسال شده به CustomAction.Builder را لغو می‌کند و به اجزای سیستم اجازه می‌دهد تا اکشن شما و سایر اقدامات پخش را به سبکی ثابت ارائه کنند.

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

اگر یک کنش سفارشی حالتی باشد - برای مثال، یک تنظیم پخش را روشن یا خاموش می‌کند - نمادهای مختلفی برای حالت‌های مختلف ارائه می‌کند، بنابراین کاربران می‌توانند هنگام انتخاب کنش، تغییری را ببینند.

سبک‌های نماد جایگزین را برای اقدامات غیرفعال ارائه کنید

هنگامی که یک کنش سفارشی برای شرایط فعلی در دسترس نیست، نماد اقدام سفارشی را با نماد جایگزینی که نشان می‌دهد این عمل غیرفعال شده است، تعویض کنید.

شکل 6. نمونه هایی از آیکون های اقدام سفارشی غیر سبک.

فرمت صوتی را مشخص کنید

برای نشان دادن اینکه رسانه در حال پخش از فرمت صوتی خاصی استفاده می کند، می توانید نمادهایی را مشخص کنید که در خودروهایی که از این ویژگی پشتیبانی می کنند ارائه می شوند. می‌توانید KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI و KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI را در بسته‌های اضافی آیتم رسانه در حال پخش تنظیم کنید (به MediaSession.setMetadata() ) منتقل شد. مطمئن شوید که هر دوی این موارد اضافی را تنظیم کنید تا طرح‌بندی‌های مختلف را در خود جای دهید.

علاوه بر این، می‌توانید KEY_IMMERSIVE_AUDIO اضافی را تنظیم کنید تا به OEM‌های خودرو بگوید که این صدای همهجانبه است، و آنها باید هنگام تصمیم‌گیری در مورد اعمال جلوه‌های صوتی که ممکن است با محتوای فراگیر تداخل ایجاد کنند، بسیار مراقب باشند.

پیوندهایی را از مورد در حال پخش اضافه کنید

می توانید آیتم رسانه ای را که در حال پخش است پیکربندی کنید تا زیرنویس، توضیحات یا هر دو پیوندی به سایر موارد رسانه باشد. که به کاربر اجازه می دهد سریع به موارد مرتبط بپرد. برای مثال، ممکن است به آهنگ‌های دیگری از همان هنرمند، قسمت‌های دیگر آن پادکست، و غیره بپرند. اگر خودرو از این ویژگی پشتیبانی می‌کند، کاربران می‌توانند برای مرور آن محتوا روی پیوند ضربه بزنند.

برای افزودن پیوندها، فراداده KEY_SUBTITLE_LINK_MEDIA_ID (برای پیوند از زیرنویس) یا KEY_DESCRIPTION_LINK_MEDIA_ID (برای پیوند از توضیحات) پیکربندی کنید. برای جزئیات، به مستندات مرجع برای آن فیلدهای فراداده مراجعه کنید.

از اقدامات صوتی پشتیبانی کنید

برنامه رسانه شما باید از کنش‌های صوتی پشتیبانی کند تا به رانندگان کمک کند تجربه‌ای امن و راحت داشته باشند که حواس‌پرتی را به حداقل برساند. به عنوان مثال، اگر برنامه شما در حال پخش یک آیتم رسانه ای است، کاربر می تواند با گفتن « پخش [عنوان آهنگ] » به برنامه شما بگوید که آهنگ دیگری را بدون نگاه کردن یا لمس کردن نمایشگر خودرو پخش کند. کاربران می توانند با کلیک کردن روی دکمه های مربوطه، درخواست ها را آغاز کنند. روی فرمان آنها یا با کلمات کلیدی " OK Google " صحبت می کنند.

وقتی Android Auto یا Android Automotive OS یک کنش صوتی را شناسایی و تفسیر می‌کند، آن عملکرد صوتی از طریق onPlayFromSearch() به برنامه تحویل داده می‌شود. با دریافت این تماس، برنامه محتوای مطابق با رشته query را پیدا می کند و پخش را شروع می کند.

کاربران می‌توانند دسته‌های مختلفی از اصطلاحات را در جست‌وجوی خود مشخص کنند: ژانر، هنرمند، آلبوم، نام آهنگ، ایستگاه رادیویی یا لیست پخش و غیره. هنگام ایجاد پشتیبانی برای جستجو، تمام دسته بندی هایی را که برای برنامه شما منطقی هستند در نظر بگیرید. اگر Android Auto یا Android Automotive OS تشخیص دهد که یک جستجوی داده شده در دسته‌های خاصی قرار می‌گیرد، موارد اضافی را به پارامتر extras اضافه می‌کند. موارد اضافی زیر قابل ارسال است:

یک رشته query خالی را حساب کنید، که اگر کاربر عبارات جستجو را مشخص نکرده باشد، می تواند توسط Android Auto یا Android Automotive OS ارسال شود. به عنوان مثال، اگر کاربر بگوید " Play some music ." در این صورت، برنامه شما ممکن است انتخاب کند که آهنگی را که اخیراً پخش شده یا پیشنهاد شده است شروع کند.

اگر جستجو را نمی توان به سرعت پردازش کرد، در onPlayFromSearch() را مسدود نکنید. درعوض، وضعیت پخش را روی STATE_CONNECTING تنظیم کنید و جستجو را در یک رشته ناهمگام انجام دهید.

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

علاوه بر جستارهای « play »، Android Auto و Android Automotive OS برای کنترل بازپخش مانند « مکث موسیقی » و « آهنگ بعدی » پرس‌وجوهای صوتی را تشخیص می‌دهند و این دستورات را با تماس‌های مربوط به جلسه رسانه، مانند onPause() و onSkipToNext() مطابقت می‌دهند.

برای مثالی دقیق در مورد نحوه اجرای عملکردهای پخش صوتی فعال در برنامه خود، به «دستیار Google» و برنامه‌های رسانه مراجعه کنید.

اقدامات حفاظتی حواس پرتی را اجرا کنید

از آنجایی که تلفن کاربر در حین استفاده از Android Auto به بلندگوهای خودروی آنها متصل است، باید اقدامات احتیاطی بیشتری برای جلوگیری از حواس پرتی راننده انجام دهید.

آلارم های خودرو را خاموش کنید

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

برای برآورده کردن این الزام، برنامه شما می‌تواند از CarConnection به عنوان سیگنال قبل از پخش هر صوتی استفاده کند. برنامه شما می‌تواند با مشاهده LiveData برای نوع اتصال خودرو و بررسی اینکه آیا این تلفن برابر با CONNECTION_TYPE_PROJECTION است یا خیر، بررسی کند که آیا تلفن به صفحه ماشین نمایش داده می‌شود یا خیر.

اگر تلفن کاربر در حال پخش است، برنامه های رسانه ای که از زنگ هشدار پشتیبانی می کنند باید یکی از کارهای زیر را انجام دهند:

  • زنگ هشدار را غیرفعال کنید.
  • زنگ را از طریق STREAM_ALARM پخش کنید و برای غیرفعال کردن زنگ هشدار، یک رابط کاربری روی صفحه تلفن ارائه دهید.
رسیدگی به تبلیغات رسانه ای

به‌طور پیش‌فرض، Android Auto هنگامی که فراداده رسانه در طول جلسه پخش صدا تغییر می‌کند، اعلانی را نشان می‌دهد. هنگامی که یک برنامه رسانه ای از پخش موسیقی به اجرای یک تبلیغ تغییر می کند، نمایش یک اعلان به کاربر حواس پرت می شود. برای جلوگیری از نمایش اعلان Android Auto در این مورد، باید کلید فراداده رسانه METADATA_KEY_IS_ADVERTISEMENT روی METADATA_VALUE_ATTRIBUTE_PRESENT تنظیم کنید، همانطور که در قطعه کد زیر نشان داده شده است:

کاتلین
import androidx.media.utils.MediaConstants

override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) {
    MediaMetadataCompat.Builder().apply {
        if (isAd(mediaId)) {
            putLong(
                MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
                MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        }
        // ...add any other properties you normally would.
        mediaSession.setMetadata(build())
    }
}
جاوا
import androidx.media.utils.MediaConstants;

@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
    MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
    if (isAd(mediaId)) {
        builder.putLong(
            MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
    }
    // ...add any other properties you normally would.
    mediaSession.setMetadata(builder.build());
}
خطاهای عمومی را مدیریت کنید

وقتی برنامه با خطا مواجه شد، وضعیت پخش را روی STATE_ERROR تنظیم کنید و با استفاده از متد setErrorMessage() پیغام خطا ارائه دهید. برای لیستی از کدهای خطا که می توانید هنگام تنظیم پیام خطا از آنها استفاده کنید، به PlaybackStateCompat مراجعه کنید. پیام های خطا باید رو به روی کاربر باشد و با محلی فعلی کاربر بومی سازی شود. سپس Android Auto و Android Automotive OS می توانند پیام خطا را به کاربر نمایش دهند.

به عنوان مثال، اگر محتوا در منطقه فعلی کاربر در دسترس نیست، می توانید هنگام تنظیم پیام خطا از کد خطا ERROR_CODE_NOT_AVAILABLE_IN_REGION استفاده کنید.

کاتلین
mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build())
جاوا
mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build());

برای اطلاعات بیشتر در مورد وضعیت های خطا، به استفاده از جلسه رسانه: وضعیت ها و خطاها مراجعه کنید.

اگر یک کاربر Android Auto برای رفع خطا باید برنامه تلفن شما را باز کند، آن اطلاعات را در پیام خود در اختیار کاربر قرار دهید. به عنوان مثال، ممکن است پیام خطای شما به جای «لطفا وارد شوید»، عبارت «Sign in to [your app name]» باشد.

منابع دیگر