Arabalar için medya uygulamaları oluşturma

Android Auto ve Android Automotive OS, medya uygulaması içeriğinizi arabalarında olan kullanıcılara sunmanıza yardımcı olur. Android Auto ve Android Automotive OS'in ya da medya tarayıcısı olan başka bir uygulamanın içeriğinizi bulup görüntüleyebilmesi için arabalar için medya uygulaması bir medya tarayıcısı hizmeti sağlamalıdır.

Bu kılavuzda, telefonda ses çalan bir medya uygulamanızın olduğu ve medya uygulamanızın Android medya uygulaması mimarisine uygun olduğu varsayılmıştır.

Bu kılavuzda, Android Auto veya Android Automotive OS'te çalışması için uygulamanızın ihtiyaç duyduğu MediaBrowserService ve MediaSession bileşenleri açıklanmaktadır. Temel medya altyapısını tamamladıktan sonra medya uygulamanıza Android Auto desteği ekleyebilir ve Android Automotive OS desteği ekleyebilirsiniz.

Başlamadan önce

  1. Android Media API belgelerini inceleyin.
  2. Tasarımla ilgili bilgi edinmek için Medya uygulamaları oluşturma başlıklı makaleyi inceleyin.
  3. Bu bölümde listelenen temel terimleri ve kavramları inceleyin.

Temel terimler ve kavramlar

Medya tarayıcı hizmeti
Medya uygulamanız tarafından uygulanan ve MediaBrowserServiceCompat API'ye uygun bir Android hizmeti. Uygulamanız, içeriğini göstermek için bu hizmeti kullanıyor.
Medya tarayıcısı
Medya uygulamaları tarafından medya tarayıcı hizmetlerini keşfetmek ve içeriklerini görüntülemek için kullanılan bir API. Android Auto ve Android Automotive OS, uygulamanızın medya tarayıcısı hizmetini bulmak için bir medya tarayıcısı kullanır.
Medya öğesi

Medya tarayıcısı, içeriğini MediaItem öğelerinden oluşan bir ağaçta düzenler. Bir medya öğesinde aşağıdaki işaretlerden biri veya her ikisi de bulunabilir:

  • FLAG_PLAYABLE: Öğenin içerik ağacında yaprak olduğunu belirtir. Öğe, albümdeki bir şarkı, sesli kitaptaki bir bölüm veya podcast bölümünün tek bir ses akışını temsil eder.
  • FLAG_BROWSABLE: Öğenin içerik ağacında bir düğüm olduğunu ve alt öğeleri olduğunu belirtir. Örneğin, öğe bir albümü temsil eder ve alt öğeleri albümdeki şarkılardır.

Hem göz atılabilir hem de oynatılabilir olan medya öğeleri oynatma listesi gibi çalışır. Öğenin kendisini seçerek tüm alt öğelerini oynatabilir veya alt öğelerine göz atabilirsiniz.

Araç için optimize edilmiş

Android Automotive OS tasarım yönergelerine uygun bir Android Automotive OS uygulaması için etkinlik. Bu etkinliklerin arayüzü Android Automotive OS tarafından çizilmediğinden, uygulamanızın tasarım yönergelerine uyduğundan emin olmanız gerekir. Genellikle bu, daha büyük dokunma hedefleri ve yazı tipi boyutları, gündüz ve gece modları desteği ve daha yüksek kontrast oranları içerir.

Araç için optimize edilmiş kullanıcı arayüzlerinin yalnızca Araç Kullanıcı Deneyimi Kısıtlamaları (CUXR'ler) geçerli olmadığında gösterilmesine izin verilir. Bu arayüzler, kullanıcının uzun süreli dikkatini veya etkileşimini gerektirebilir. CUXR'ler, araç durduğunda veya park edildiğinde geçerli değildir ancak araç hareket halindeyken her zaman geçerlidir.

Android Auto, medya tarayıcısı hizmetinizdeki bilgileri kullanarak kendi araca optimize edilmiş arayüzünü çizdiği için Android Auto için etkinlik tasarlamanıza gerek yoktur.

Uygulamanızın manifest dosyalarını yapılandırma

Medya tarayıcısı hizmetinizi oluşturmadan önce uygulamanızın manifest dosyalarını yapılandırmanız gerekir.

Medya tarayıcı hizmetinizi beyan etme

Hem Android Auto hem de Android Automotive OS, medya öğelerine göz atmak için medya tarayıcısı hizmetiniz aracılığıyla uygulamanıza bağlanır. Android Auto ve Android Automotive OS'in hizmeti keşfetmesine ve uygulamanıza bağlanmasına izin vermek için medya tarayıcısı hizmetinizi manifest dosyanızda beyan edin.

Aşağıdaki kod snippet'inde, medya tarayıcısı hizmetinizi manifest dosyanızda nasıl beyan edeceğiniz gösterilmektedir. Bu kodu Android Automotive OS modülünüzün ve telefon uygulamanızın manifest dosyasına ekleyin.

<application>
    ...
    <service android:name=".MyMediaBrowserService"
             android:exported="true">
        <intent-filter>
            <action android:name="android.media.browse.MediaBrowserService"/>
        </intent-filter>
    </service>
    ...
</application>

Uygulama simgelerini belirtin

Android Auto ve Android Automotive OS'in, uygulamanızı sistem kullanıcı arayüzünde temsil etmek için kullanabileceği uygulama simgelerini belirtmeniz gerekir. İki simge türü gereklidir:

  • Başlatıcı simgesi
  • İlişkilendirme simgesi

Başlatıcı simgesi

Başlatıcı simgesi, sistem kullanıcı arayüzünde (ör. başlatıcıda ve simge tepsisinde) uygulamanızı temsil eder. Aşağıdaki manifest beyanını kullanarak araba medya uygulamanızı temsil etmek için mobil uygulamanızdaki simgeyi kullanmak istediğinizi belirtebilirsiniz:

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

Mobil uygulamanızdan farklı bir simge kullanmak için manifest dosyasında medya tarayıcısı hizmetinizin <service> öğesinde android:icon mülkünü ayarlayın:

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

İlişkilendirme simgesi

Şekil 1. Medya kartındaki ilişkilendirme simgesi.

İlişkilendirme simgesi, medya içeriğinin öncelikli olduğu yerlerde (ör. medya kartlarında) kullanılır. Bildirimler için kullanılan küçük simgeyi yeniden kullanabilirsiniz. Bu simge tek renkli olmalıdır. Aşağıdaki manifest beyanını kullanarak uygulamanızı temsil etmek için kullanılan bir simge belirtebilirsiniz:

<application>
    ...
    <meta-data
        android:name="androidx.car.app.TintableAttributionIcon"
        android:resource="@drawable/ic_status_icon" />
    ...
</application>

Medya tarayıcı hizmetinizi oluşturma

MediaBrowserServiceCompat sınıfını genişleterek bir medya tarayıcı hizmeti oluşturursunuz. Ardından hem Android Auto hem de Android Automotive OS, aşağıdakileri yapmak için hizmetinizi kullanabilir:

  • Kullanıcıya bir menü sunmak için uygulamanızın içerik hiyerarşisine göz atın.
  • Ses oynatmayı kontrol etmek için uygulamanızın MediaSessionCompat sınıfı nesnesinin jetonunu alın.

Medya tarayıcısı hizmetinizi, diğer istemcilerin uygulamanızdaki medya içeriklerine erişmesine izin vermek için de kullanabilirsiniz. Bu medya istemcileri, kullanıcının telefonundaki diğer uygulamalar veya diğer uzak istemciler olabilir.

Medya tarayıcı hizmeti iş akışı

Bu bölümde, Android Automotive OS ve Android Auto'nun tipik bir kullanıcı iş akışı sırasında medya tarayıcısı hizmetinizle nasıl etkileşime geçtiği açıklanmaktadır.

  1. Kullanıcı, uygulamanızı Android Automotive OS veya Android Auto'da başlatır.
  2. Android Automotive OS veya Android Auto, onCreate() yöntemini kullanarak uygulamanızın medya tarayıcısı hizmetiyle iletişim kurar. onCreate() yöntemini uygularken bir MediaSessionCompat nesnesi ve geri çağırma nesnesi oluşturup kaydetmeniz gerekir.
  3. Android Automotive OS veya Android Auto, içerik hiyerarşinizdeki kök medya öğesini almak için hizmetinizin onGetRoot() yöntemini çağırır. Kök medya öğesi gösterilmez. Bunun yerine, uygulamanızdan daha fazla içerik almak için kullanılır.
  4. Android Automotive OS veya Android Auto, kök medya öğesinin alt öğelerini almak için hizmetinizin onLoadChildren() yöntemini çağırır. Android Automotive OS ve Android Auto, bu medya öğelerini içerik öğelerinin üst düzeyi olarak gösterir. Sistemin bu düzeyde ne beklediği hakkında daha fazla bilgi için bu sayfadaki Kök menüyü yapılandırma bölümüne bakın.
  5. Kullanıcı, göz atılabilir bir medya öğesi seçerse seçilen menü öğesinin alt öğelerini almak için hizmetinizin yöntemi tekrar çağrılır.onLoadChildren()
  6. Kullanıcı oynatılabilir bir medya öğesi seçerse Android Automotive OS veya Android Auto, bu işlemi gerçekleştirmek için uygun medya oturumu geri çağırma yöntemini çağırır.
  7. Uygulamanız tarafından destekleniyorsa kullanıcı içeriklerinizi de arayabilir. Bu durumda Android Automotive OS veya Android Auto, hizmetinizin onSearch() yöntemini çağırır.

İçerik hiyerarşinizi oluşturma

Android Auto ve Android Automotive OS, hangi içeriklerin mevcut olduğunu öğrenmek için uygulamanızın medya tarayıcısı hizmetini çağırır. Bunu desteklemek için medya tarayıcı hizmetinizde iki yöntem uygulamanız gerekir: onGetRoot() ve onLoadChildren()

onGetRoot işlevini uygulama

Hizmetinizin onGetRoot() yöntemi, içerik hiyerarşinizin kök düğümü hakkında bilgi döndürür. Android Auto ve Android Automotive OS, onLoadChildren() yöntemini kullanarak içeriğinizin geri kalanını istemek için bu kök düğümü kullanır.

Aşağıdaki kod snippet'inde, onGetRoot() yönteminin basit bir uygulaması gösterilmektedir:

Kotlin

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)

Java

@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);
}

Bu yöntemin daha ayrıntılı bir örneği için GitHub'daki Universal Android Music Player örnek uygulamasındaki onGetRoot() yöntemine bakın.

onGetRoot() için paket doğrulaması ekleme

Hizmetinizin onGetRoot() yöntemine bir arama yapıldığında, arayan paketi kimlik bilgilerini hizmetinize iletir. Hizmetiniz, bu paketin içeriğinize erişip erişemeyeceğine karar vermek için bu bilgileri kullanabilir. Örneğin, clientPackageName değerini izin verilenler listenizle karşılaştırarak ve paketin APK'sını imzalamak için kullanılan sertifikayı doğrulayarak uygulamanızın içeriğine erişimi onaylanmış paketlerin listesiyle kısıtlayabilirsiniz. Paket doğrulanamıyorsa içeriğinize erişimi reddetmek için null simgesini döndürün.

Android Auto ve Android Automotive OS gibi sistem uygulamalarının içeriğinize erişebilmesi için bu sistem uygulamaları onGetRoot() yöntemini çağrdığında hizmetinizin her zaman null olmayan bir BrowserRoot döndürmesi gerekir. Android Automotive OS sistem uygulamasının imzası, aracın markasına ve modeline göre değişiklik gösterebilir. Bu nedenle, Android Automotive OS'i güçlü bir şekilde desteklemek için tüm sistem uygulamalarından gelen bağlantılara izin vermeniz gerekir.

Aşağıdaki kod snippet'inde, hizmetinizin çağıran paketin sistem uygulaması olduğunu nasıl doğrulayabileceği gösterilmektedir:

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
}

Bu kod snippet'i, GitHub'daki Universal Android Music Player örnek uygulamasındaki PackageValidator sınıfından alınmıştır. Hizmetinizin onGetRoot() yöntemi için paket doğrulamasının nasıl uygulanacağına dair daha ayrıntılı bir örnek için bu sınıfa bakın.

Sistem uygulamalarına izin vermenin yanı sıra Google Asistan'ın MediaBrowserService cihazınıza bağlanmasına izin vermeniz gerekir. Google Asistan'ın, Android Auto'yu içeren telefon ve Android Automotive OS için ayrı paket adlarına sahip olduğunu unutmayın.

onLoadChildren() işlevini uygulama

Android Auto ve Android Automotive OS, kök düğüm nesnenizi aldıktan sonra, alt öğelerini almak için kök düğüm nesnesinde onLoadChildren() çağrısı yaparak üst düzey bir menü oluşturur. İstemci uygulamaları, alt düğüm nesnelerini kullanarak aynı yöntemi çağırarak alt menüler oluşturur.

İçerik hiyerarşinizdeki her düğüm bir MediaBrowserCompat.MediaItem nesnesi ile temsil edilir. Bu medya öğelerinin her biri benzersiz bir kimlik dizesiyle tanımlanır. İstemci uygulamaları bu kimlik dizelerini opak jetonlar olarak işler. Bir istemci uygulaması bir alt menüye göz atmak veya bir medya öğesini oynatmak istediğinde jetonu iletir. Jetonu uygun medya öğesiyle ilişkilendirmek uygulamanızın sorumluluğundadır.

Aşağıdaki kod snippet'inde, onLoadChildren() yönteminin basit bir uygulaması gösterilmektedir:

Kotlin

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

Java

@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);
}

Bu yöntemin tam örneği için GitHub'daki Universal Android Music Player örnek uygulamasındaki onLoadChildren() yöntemine bakın.

Kök menüyü yapılandırma

Şekil 2. Gezinme sekmeleri olarak görüntülenen kök içerik.

Android Auto ve Android Automotive OS'in kök menünün yapısıyla ilgili belirli kısıtlamaları vardır. Bunlar, onGetRoot()'a iletilen Bundle bağımsız değişkeni aracılığıyla okunabilen kök ipuçları aracılığıyla MediaBrowserService'e iletilir. Bu ipuçları, sistemin kök içeriği gezinme sekmeleri olarak en iyi şekilde göstermesini sağlar. Bu ipuçlarına uymazsanız bazı kök içerikler kaldırılabilir veya sistem tarafından daha az bulunabilir hale getirilebilir. İki ipucu gönderilir:

Alakalı kök ipuçlarını okumak için aşağıdaki kodu kullanın:

Kotlin

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...
}

Java

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...
}

Özellikle hiyerarşiniz Android Auto ve Android Automotive OS dışındaki MediaBrowser entegrasyonları arasında değişiyorsa içerik hiyerarşinizin yapısı için mantığı bu ipuçlarının değerlerine göre dallandırmayı seçebilirsiniz. Örneğin, normalde oynatılabilir bir kök öğe gösteriyorsanız desteklenen işaretler ipucunun değeri nedeniyle bunu, kök bir göz atılabilir öğenin altına yerleştirmek isteyebilirsiniz.

Kök ipuçlarının yanı sıra, sekmelerin en iyi şekilde oluşturulmasını sağlamak için uyulması gereken birkaç ek yönerge vardır:

  • Her sekme öğesi için tek renkli, tercihen beyaz simgeler sağlayın.
  • Her sekme öğesi için kısa ancak anlamlı etiketler girin. Etiketlerin kısa tutulması, dizelerin kısaltılma olasılığını azaltır.

Medya posterini görüntüleme

Medya öğelerinin posteri, ContentResolver.SCHEME_CONTENT veya ContentResolver.SCHEME_ANDROID_RESOURCE kullanılarak yerel bir URI olarak iletilmelidir. Bu yerel URI, uygulamanın kaynaklarında bir bit eşleme veya vektör çizilebilir öğeye yönlendirmelidir. İçerik hiyerarşisindeki öğeleri temsil eden MediaDescriptionCompat nesneleri için URI'yi setIconUri() üzerinden iletin. Şu anda oynatılan öğeyi temsil eden MediaMetadataCompat nesneleri için URI'yi aşağıdaki anahtarlardan birini kullanarak putString() üzerinden iletin:

Aşağıdaki adımlarda, bir web URI'sinden posterin nasıl indirileceği ve yerel bir URI üzerinden nasıl gösterileceği açıklanmaktadır. Daha kapsamlı bir örnek için Universal Android Music Player örnek uygulamasında openFile() ve ilgili yöntemlerin uygulamasına bakın.

  1. Web URI'sine karşılık gelen bir content:// URI oluşturun. Medya tarayıcısı hizmeti ve medya oturumu bu içerik URI'sini Android Auto ve Android Automotive OS'e iletir.

    Kotlin

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

    Java

    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() uygulamanızda, ilgili URI için bir dosya olup olmadığını kontrol edin. Aksi takdirde resim dosyasını indirip önbelleğe alın. Aşağıdaki kod snippet'inde Glide kullanılmaktadır.

    Kotlin

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

    Java

    @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);
    }
    

İçerik sağlayıcılar hakkında daha fazla bilgi için İçerik sağlayıcı oluşturma başlıklı makaleyi inceleyin.

İçerik stillerini uygulama

Göz atılabilir veya oynatılabilir öğeler kullanarak içerik hiyerarşinizi oluşturduktan sonra, bu öğelerin arabada nasıl gösterileceğini belirleyen içerik stilleri uygulayabilirsiniz.

Aşağıdaki içerik stillerini kullanabilirsiniz:

Liste öğeleri

Bu içerik stilinde, resimlere kıyasla başlıklar ve meta verilere öncelik verilir.

Izgara öğeleri

Bu içerik stilinde, resimlere başlıklar ve meta verilere göre öncelik verilir.

Varsayılan içerik stillerini ayarlama

Hizmetinizin onGetRoot() yönteminin BrowserRoot ekstralar paketine belirli sabitler ekleyerek medya öğelerinizin nasıl gösterileceğiyle ilgili genel varsayılan ayarları belirleyebilirsiniz. Android Auto ve Android Automotive OS bu paketi okur ve uygun stili belirlemek için bu sabitleri arar.

Pakette anahtar olarak aşağıdaki ekstralar kullanılabilir:

Anahtarlar, bu öğelerin sunumunu etkilemek için aşağıdaki tam sayı sabit değerleriyle eşlenebilir:

Aşağıdaki kod snippet'inde, taranabilir öğeler için varsayılan içerik stilinin ızgaralar, oynatılabilir öğeler için de listeler olarak nasıl ayarlanacağı gösterilmektedir:

Kotlin

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

Java

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);
}

Öğe başına içerik stilleri ayarlama

Content Style API, tarayılabilen medya öğelerinin alt öğelerinin yanı sıra medya öğelerinin varsayılan içerik stilini geçersiz kılmanıza olanak tanır.

Göz atılabilir bir medya öğesinin alt öğelerinin varsayılan ayarını geçersiz kılmak için medya öğesinin MediaDescription bölümünde bir ekstralar paketi oluşturun ve daha önce bahsedilen ipuçlarını ekleyin. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE, söz konusu öğenin oynanabilir alt öğeleri için geçerlidir. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE ise söz konusu öğenin göz atılabilir alt öğeleri için geçerlidir.

Alt öğelerinin değil, belirli bir medya öğesinin kendisinin varsayılan ayarını geçersiz kılmak için medya öğesinin MediaDescription bölümünde bir ekstralar paketi oluşturun ve DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM anahtarını içeren bir ipucu ekleyin. Öğenin sunumunu belirtmek için daha önce açıklanan değerleri kullanın.

Aşağıdaki kod snippet'inde, hem kendi hem de alt öğelerinin varsayılan içerik stilini geçersiz kılan, göz atılabilir bir MediaItem öğesinin nasıl oluşturulacağı gösterilmektedir. Kendisini bir kategori listesi öğesi, göz atılabilir alt öğelerini liste öğesi ve oynatılabilir alt öğelerini ızgara öğesi olarak biçimlendirir:

Kotlin

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

Java

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);
}

Başlık ipuçları kullanarak öğeleri gruplandırma

İlgili medya öğelerini gruplandırmak için öğe başına ipucu kullanırsınız. Bir gruptaki her medya öğesinin MediaDescription öğesinde, DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE anahtarına ve aynı dize değerine sahip bir eşleme içeren bir ekstralar paketi tanımlaması gerekir. Grubun başlığı olarak kullanılan bu dizeyi yerelleştirin.

Aşağıdaki kod snippet'inde, "Songs" alt grubu başlığı olan bir MediaItem öğesinin nasıl oluşturulacağı gösterilmektedir:

Kotlin

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

Java

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

Uygulamanız, birlikte gruplandırmak istediğiniz tüm medya öğelerini bitişik bir blok olarak iletmelidir. Örneğin, "Şarkılar" ve "Albümler" olmak üzere iki medya öğesi grubunu bu sırayla görüntülemek istediğinizi ve uygulamanızın beş medya öğesini aşağıdaki sırayla ilettiğini varsayalım:

  1. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren medya öğesi A
  2. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren medya öğesi B
  3. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren C medya öğesi
  4. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren medya öğesi D
  5. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren E medya öğesi

"Şarkılar" ve "Albümler" gruplarına ait medya öğeleri birbirine bitişik bloklar halinde tutulmadığından Android Auto ve Android Automotive OS bunu aşağıdaki dört grup olarak yorumlar:

  • A medya öğesini içeren "Şarkılar" adlı 1. grup
  • B medya öğesini içeren "Albümler" adlı 2. grup
  • C ve D medya öğelerini içeren "Şarkılar" adlı 3. grup
  • E medya öğesini içeren "Albümler" adlı 4. grup

Bu öğeleri iki grupta görüntülemek için uygulamanızın medya öğelerini aşağıdaki sırayla iletmesi gerekir:

  1. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren medya öğesi A
  2. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren C medya öğesi
  3. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") ile D medya öğesi
  4. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren medya öğesi B
  5. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren E medya öğesi

Ek meta veri göstergelerini görüntüleme

Medya tarayıcısı ağacındaki ve oynatma sırasındaki içerikle ilgili bir bakışta bilgi sağlamak için ek meta veri göstergeleri ekleyebilirsiniz. Android Auto ve Android Automotive OS, göz atma ağacında bir öğeyle ilişkili ekstraları okur ve hangi göstergelerin gösterileceğini belirlemek için belirli sabitleri arar. Android Auto ve Android Automotive OS, medya oynatma sırasında medya oturumunun meta verilerini okur ve gösterilecek göstergeleri belirlemek için belirli sabitler arar.

Şekil 3. Şarkıyı ve sanatçıyı tanımlayan meta verilerin yanı sıra uygunsuz içeriği belirten bir simgenin yer aldığı oynatma görünümü.

Şekil 4. İlk öğede oynatılmayan içerik için nokta ve ikinci öğede kısmen oynatılan içerik için ilerleme çubuğu içeren göz atma görünümü.

Aşağıdaki sabitler MediaItem açıklama ekstralarında ve MediaMetadata ekstralarında hem kullanılabilir:

Aşağıdaki sabitler yalnızca MediaItem açıklama ekstralarında kullanılabilir:

Kullanıcı medya göz atma ağacına göz atarken görünen göstergeleri görüntülemek için bu sabitlerden en az birini içeren bir ekstralar paketi oluşturun ve bu paketi MediaDescription.Builder.setExtras() yöntemine iletin.

Aşağıdaki kod snippet'inde, %70'i tamamlanmış uygunsuz bir medya öğesi için göstergelerin nasıl gösterileceği gösterilmektedir:

Kotlin

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 */)

Java

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

Şu anda oynatılan bir medya öğesinin göstergelerini görüntülemek için mediaSession öğenizin MediaMetadataCompat alanında METADATA_KEY_IS_EXPLICIT veya EXTRA_DOWNLOAD_STATUS için Long değerlerini belirtebilirsiniz. Oynatma görünümünde DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS veya DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE göstergelerini görüntüleyemezsiniz.

Aşağıdaki kod snippet'inde, oynatma görünümündeki mevcut şarkının uygunsuz ve indirilmiş olduğunun nasıl belirtileceği gösterilmektedir:

Kotlin

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

Java

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());

İçerik oynatılırken göz atma görünümündeki ilerleme çubuğunu güncelleme

Daha önce de belirtildiği gibi, göz atma görünümünde kısmen oynatılan içerik için bir ilerleme çubuğu göstermek üzere DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE ek öğesini kullanabilirsiniz. Ancak kullanıcı, kısmen oynatılan içeriği Android Auto veya Android Automotive OS'ten oynatmaya devam ederse bu gösterge zaman geçtikçe yanlış olur.

Android Auto ve Android Automotive OS'in ilerleme çubuğunu güncel tutması için devam eden içeriği, göz atma görünümündeki medya öğelerine bağlamak üzere MediaMetadataCompat ve PlaybackStateCompat öğelerinde ek bilgiler sağlayabilirsiniz. Medya öğesinin otomatik olarak güncellenen bir ilerleme çubuğuna sahip olması için aşağıdaki koşullar karşılanmalıdır:

Aşağıdaki kod snippet'inde, şu anda oynatılan öğenin göz atma görünümündeki bir öğeye bağlı olduğunun nasıl belirtileceği gösterilmektedir:

Kotlin

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

Java

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());

Şekil 5. Kullanıcının sesli aramasıyla ilgili medya öğelerini görüntülemek için "Arama sonuçları" seçeneği içeren oynatma görünümü.

Uygulamanız, kullanıcılar arama sorgusu başlattığında onlara gösterilecek bağlama dayalı arama sonuçları sağlayabilir. Android Auto ve Android Automotive OS bu sonuçları arama sorgusu arayüzleri veya oturumda daha önce yapılan sorgulara göre değişen olanaklar aracılığıyla gösterir. Daha fazla bilgi edinmek için bu kılavuzun Sesli işlemleri destekleme bölümüne bakın.

Göz atılabilir arama sonuçları görüntülemek için sabit anahtar BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED değerini, hizmetinizin onGetRoot() yönteminin ekstralar paketine true mantıksal değeriyle eşlenecek şekilde ekleyin.

Aşağıdaki kod snippet'inde, onGetRoot() yönteminde desteğin nasıl etkinleştirileceği gösterilmektedir:

Kotlin

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

Java

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);
}

Arama sonuçları sunmaya başlamak için medya tarayıcısı hizmetinizde onSearch() yöntemini geçersiz kılın. Android Auto ve Android Automotive OS, kullanıcı bir arama sorgusu arayüzü veya "Arama sonuçları" özelliğini çağrdığında kullanıcının arama terimlerini bu yönteme iletir.

Hizmetinizin onSearch() yönteminden gelen arama sonuçlarını daha kolay göz atılabilir hale getirmek için başlık öğelerini kullanarak düzenleyebilirsiniz. Örneğin, uygulamanız müzik çalıyorsa arama sonuçlarını albüme, sanatçıya ve şarkılara göre düzenleyebilirsiniz.

Aşağıdaki kod snippet'inde, onSearch() yönteminin basit bir uygulaması gösterilmektedir:

Kotlin

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.
}

Java

@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.
}

Özel Göz Atma İşlemleri

Tek bir özel göz atma işlemi.

Şekil 6. Tek Özel Göz Atma İşlemi

Özel Göz Atma İşlemleri, aracın medya uygulamasında uygulamanızın MediaItem nesnelerine özel simgeler ve etiketler eklemenize ve kullanıcıların bu işlemlerle olan etkileşimlerini yönetmenize olanak tanır. Bu sayede, "İndir", "Sıraya ekle", "Radyo oynat", "Favori" veya "Kaldır" gibi işlemler ekleyerek medya uygulamasının işlevini çeşitli şekillerde genişletebilirsiniz.

Özel göz atma işlemleri taşma menüsü.

Şekil 7. Özel Göz Atma İşlemi taşması

OEM'nin gösterilmesine izin verdiğinden daha fazla özel işlem varsa kullanıcıya bir taşma menüsü sunulur.

İşleyiş şekli nasıldır?

Her özel göz atma işlemi aşağıdakilerle tanımlanır:

  • İşlem kimliği (benzersiz bir dize tanımlayıcısı)
  • İşlem etiketi (kullanıcıya gösterilen metin)
  • İşlem simgesi URI'si (tonlandırılabilen bir drawable vektör)

BrowseRoot öğenizin bir parçası olarak özel göz atma işlemlerinin listesini global olarak tanımlarsınız. Ardından, bu işlemlerin bir alt kümesini ayrı ayrı öğelere ekleyebilirsiniz. MediaItem.

Bir kullanıcı özel göz atma işlemiyle etkileşim kurduğunda uygulamanız onCustomAction() içinde geri çağırma alır. Ardından işlemi gerçekleştirebilir ve gerekirse MediaItem için işlem listesini güncelleyebilirsiniz. Bu, "Favori" ve "İndir" gibi durum bilgisine sahip işlemler için kullanışlıdır. "Radyo çal" gibi güncellenmesi gerekmeyen işlemler için işlem listesini güncellemeniz gerekmez.

Göz atma düğümü kökündeki özel göz atma işlemleri.

Şekil 8. Özel Göz Atma İşlemi araç çubuğu

Özel göz atma işlemlerini bir göz atma düğümü köküne de ekleyebilirsiniz. Bu işlemler, ana araç çubuğunun altındaki ikincil araç çubuğunda gösterilir.

Özel Göz Atma İşlemleri'ni uygulama

Projenize özel göz atma işlemleri eklemek için aşağıdaki adımları uygulayın:

  1. MediaBrowserServiceCompat uygulamanızda iki yöntemi geçersiz kılın:
  2. İşlem sınırlarını çalışma zamanında ayrıştırın:
    • onGetRoot() içinde, rootHints Bundle içindeki BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT anahtarını kullanarak her MediaItem için izin verilen maksimum işlem sayısını alın. 0 sınırı, özelliğin sistem tarafından desteklenmediğini gösterir.
  3. Özel Göz Atma İşlemleri'nin dünya çapındaki listesini oluşturun:
    • Her işlem için aşağıdaki anahtarlara sahip bir Bundle nesnesi oluşturun: * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID: İşlem kimliği * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL: İşlem etiketi * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI: İşlem simgesi URI'si * Tüm işlem Bundle nesnelerini bir listeye ekleyin.
  4. Global listeyi BrowseRoot'inize ekleyin:
  5. MediaItem nesnelerinize işlem ekleyin:
    • DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST anahtarını kullanarak işlem kimliklerinin listesini MediaDescriptionCompat ekstralarına ekleyerek ayrı MediaItem nesnelerine işlem ekleyebilirsiniz. Bu liste, BrowseRoot içinde tanımladığınız global işlem listesinin bir alt kümesi olmalıdır.
  6. İşlemleri yönetme ve ilerleme durumunu veya sonuçları döndürme:

Özel Göz Atma İşlemleri'ni kullanmaya başlamak için BrowserServiceCompat hesabınızda yapabileceğiniz bazı değişiklikler aşağıda verilmiştir.

BrowserServiceCompat'i geçersiz kılma

MediaBrowserServiceCompat dosyasında aşağıdaki yöntemleri geçersiz kılmanız gerekir.

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

public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)

Ayrıştırma işlemleri sınırı

Kaç tane özel göz atma işleminin desteklendiğini kontrol etmeniz gerekir.

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

Özel bir göz atma işlemi oluşturma

Her işlemin ayrı bir Bundle içine paketlenmesi gerekir.

  • İşlem kimliği
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
    
  • İşlem etiketi
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
    
  • İşlem simgesi URI'si
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")
    

Parceable ArrayList'e Özel Göz Atma İşlemleri Ekleme

Tüm özel göz atma işlemi Bundle nesnelerini bir ArrayList içine ekleyin.

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

Özel Göz Atma İşlemi listesini göz atma köküne ekleme

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'ye işlem ekleme

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 sonucunu derle

  • Bundle extras kaynağından mediaId'yi ayrıştırma:
    @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);
    }
    
  • Eşzamansız sonuçlar için sonucu ayırın. result.detach()
  • Derleme sonucu paketi
    • Kullanıcıya mesaj
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
    • Öğeyi güncelle(bir öğedeki işlemleri güncellemek için kullanın)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
      
    • Oynatma görünümünü açma
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
      
    • Güncelleme Düğümüne Göz At
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  • Hata varsa result.sendError(resultBundle). numaralı telefonu arayın
  • İlerleme güncellemesi varsa result.sendProgressUpdate(resultBundle) numaralı telefonu arayın.
  • result.sendResult(resultBundle)'ü arayarak sonlandırın.

İşlem durumunu güncelleme

EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM anahtarıyla result.sendProgressUpdate(resultBundle) yöntemini kullanarak MediaItem öğesini işlemin yeni durumunu yansıtacak şekilde güncelleyebilirsiniz. Bu sayede, kullanıcıya işleminin ilerleme durumu ve sonucu hakkında anlık geri bildirim verebilirsiniz.

Örnek: İndirme İşlemi

Üç durumu olan bir indirme işlemini uygulamak için bu özelliği nasıl kullanabileceğinize dair bir örnek aşağıda verilmiştir:

  1. İndir: İşlemin ilk durumudur. Kullanıcı bu işlemi seçtiğinde "İndiriliyor" ile değiştirebilir ve kullanıcı arayüzünü güncellemek için sendProgressUpdate işlevini çağırabilirsiniz.
  2. İndiriliyor: Bu durum, indirme işleminin devam ettiğini gösterir. Kullanıcıya bir ilerleme çubuğu veya başka bir gösterge göstermek için bu durumu kullanabilirsiniz.
  3. İndirilen: Bu durum, indirme işleminin tamamlandığını gösterir. İndirme işlemi tamamlandığında, "İndiriliyor"u "İndirildi" ile değiştirebilir ve öğenin yenilenmesi gerektiğini belirtmek için EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM tuşuyla sendResult'ü çağırabilirsiniz. Ayrıca, kullanıcıya bir başarı mesajı göstermek için EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE anahtarını kullanabilirsiniz.

Bu yaklaşım, kullanıcıya indirme işlemi ve mevcut durumu hakkında net geri bildirim vermenizi sağlar. İndirme durumunu %25, %50 ve% 75 olarak gösteren simgelerle daha da fazla ayrıntı ekleyebilirsiniz.

Örnek: Favori İşlem

İki durumu olan bir favori işlem de örnek verilebilir:

  1. Favori: Bu işlem, kullanıcının favoriler listesinde bulunmayan öğeler için gösterilir. Kullanıcı bu işlemi seçtiğinde "Favorilere eklendi" ile değiştirebilir ve kullanıcı arayüzünü güncellemek için EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM tuşuyla sendResult işlevini çağırabilirsiniz.
  2. Favori: Bu işlem, kullanıcının favoriler listesinde bulunan öğeler için gösterilir. Kullanıcı bu işlemi seçtiğinde "Favori" ile değiştirebilir ve kullanıcı arayüzünü güncellemek için EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM tuşuyla sendResult öğesini çağırabilirsiniz.

Bu yaklaşım, kullanıcıların favori öğelerini yönetmeleri için net ve tutarlı bir yol sunar.

Bu örnekler, özel göz atma işlemlerinin esnekliğini ve bunları kullanarak aracın medya uygulamasında gelişmiş bir kullanıcı deneyimi için anlık geri bildirimlerle çeşitli işlevler uygulamayı nasıl yapabileceğinizi gösterir.

Bu özelliğin tam bir örnek uygulaması için TestMediaApp projesine göz atabilirsiniz.

Oynatma kontrolünü etkinleştirme

Android Auto ve Android Automotive OS, oynatma kontrol komutlarını hizmetinizin MediaSessionCompat üzerinden gönderir. Bir oturum kaydetmeniz ve ilişkili geri arama yöntemlerini uygulamanız gerekir.

Medya oturumu kaydetme

Medya tarayıcısı hizmetinizin onCreate() yönteminde bir MediaSessionCompat oluşturun, ardından setSessionToken()'i çağırarak medya oturumunu kaydedin.

Aşağıdaki kod snippet'inde, medya oturumunun nasıl oluşturulacağı ve kaydedileceği gösterilmektedir:

Kotlin

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
    ...
}

Java

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());
    ...
}

Medya oturumu nesnesini oluştururken oynatma denetimi isteklerini işlemek için kullanılan bir geri çağırma nesnesi ayarlarsınız. Bu geri çağırma nesnesini, uygulamanız için MediaSessionCompat.Callback sınıfının bir uygulamasını sağlayarak oluşturursunuz. Bu nesnenin nasıl uygulanacağı sonraki bölümde açıklanmıştır.

Oynatma komutlarını uygulama

Bir kullanıcı uygulamanızdan bir medya öğesinin oynatılmasını istediğinde Android Automotive OS ve Android Auto, uygulamanızın medya tarayıcısı hizmetinden elde ettiği uygulamanızın MediaSessionCompat sınıfındaki MediaSessionCompat.Callback sınıfını kullanır. Kullanıcı, oynatma işlemini duraklatma veya sonraki parçaya atlama gibi içerik oynatma işlemlerini kontrol etmek istediğinde Android Auto ve Android Automotive OS, geri çağırma nesnesinin yöntemlerinden birini çağırır.

Uygulamanızın içerik oynatmayı işleyebilmesi için soyut MediaSessionCompat.Callback sınıfını genişletmesi ve desteklediği yöntemleri uygulaması gerekir.

Uygulamanızın sunduğu içerik türü için uygun olan aşağıdaki geri çağırma yöntemlerinin tümünü uygulayın:

onPrepare()
Medya kaynağı değiştirildiğinde çağrılır. Android Automotive OS de bu yöntemi önyüklemeden hemen sonra çağırır. Medya uygulamanız bu yöntemi uygulamalıdır.
onPlay()
Kullanıcı belirli bir öğe seçmeden oynamayı seçerse çağrılır. Uygulamanız varsayılan içeriğini oynatmalı veya oynatma onPause() ile duraklatıldıysa oynatmayı devam ettirmelidir.

Not: Android Automotive OS veya Android Auto, medya tarayıcınız hizmetine bağlandığında uygulamanız otomatik olarak müzik çalmaya başlamamalıdır. Daha fazla bilgi için ilk oynatma durumunu ayarlama ile ilgili bölüme bakın.

onPlayFromMediaId()
Kullanıcı belirli bir öğeyi oynatmayı seçtiğinde çağrılır. Yöntem, medya tarayıcısı hizmetinizin içerik hiyerarşinizdeki medya öğesine atadığı kimliği alır.
onPlayFromSearch()
Kullanıcı bir arama sorgusundan oynatmayı seçtiğinde çağrılır. Uygulama, iletilen arama dizesine göre uygun bir seçim yapmalıdır.
onPause()
Kullanıcı oynatmayı duraklatmayı seçtiğinde çağrılır.
onSkipToNext()
Kullanıcı bir sonraki öğeye atlamayı seçtiğinde çağrılır.
onSkipToPrevious()
Kullanıcı önceki öğeye atlamayı seçtiğinde çağrılır.
onStop()
Kullanıcı oynatmayı durdurmayı seçtiğinde çağrılır.

İstediğiniz işlevi sağlamak için uygulamanızda bu yöntemleri geçersiz kılın. İşlevselliği uygulamanız tarafından desteklenmeyen bir yöntemi uygulamanız gerekmez. Örneğin, uygulamanızda spor yayını gibi canlı yayın oynatılıyorsa onSkipToNext() yöntemini uygulamanız gerekmez. Bunun yerine onSkipToNext()'ün varsayılan uygulamasını kullanabilirsiniz.

Uygulamanızın, içeriği aracın hoparlörlerinden çalmak için özel bir mantığa ihtiyacı yoktur. Uygulamanız içerik oynatma isteği aldığında, sesi kullanıcının telefon hoparlörleri veya kulaklıkları üzerinden oynattığı şekilde oynatabilir. Android Auto ve Android Automotive OS, ses içeriğini otomatik olarak aracın sistemine göndererek aracın hoparlörlerinden çalar.

Ses içeriği oynatma hakkında daha fazla bilgi için MediaPlayer'a genel bakış, Ses uygulamasına genel bakış ve ExoPlayer'a genel bakış bölümlerine bakın.

Standart oynatma işlemlerini ayarlama

Android Auto ve Android Automotive OS, PlaybackStateCompat nesnesinde etkinleştirilen işlemlere göre oynatma denetimlerini gösterir.

Uygulamanız varsayılan olarak aşağıdaki işlemleri desteklemelidir:

Uygulamanızın içeriğiyle alakalı olduğu takdirde uygulamanız aşağıdaki işlemleri de destekleyebilir:

Ayrıca, kullanıcıya gösterilebilecek bir oynatma listesi oluşturabilirsiniz ancak bu zorunlu değildir. Bunu yapmak için setQueue() ve setQueueTitle() yöntemlerini çağırın, ACTION_SKIP_TO_QUEUE_ITEM işlemini etkinleştirin ve geri çağırma işlevini onSkipToQueueItem() tanımlayın.

Ayrıca, şu anda çalan içeriği gösteren Şu anda çalan simgesine de destek ekleyin. Bunu yapmak için setActiveQueueItemId() yöntemini çağırın ve sırada oynatılan öğenin kimliğini iletin. Sırayla ilgili bir değişiklik olduğunda setActiveQueueItemId() değerini güncellemeniz gerekir.

Android Auto ve Android Automotive OS, etkinleştirilen her işlem için düğmelerin yanı sıra oynatma sırasını gösterir. Düğmeler tıklandığında sistem, MediaSessionCompat.Callback kaynağından ilgili geri çağırma işlevini çağırır.

Kullanılmayan alanı ayırma

Android Auto ve Android Automotive OS, kullanıcı arayüzünde ACTION_SKIP_TO_PREVIOUS ve ACTION_SKIP_TO_NEXT işlemleri için yer ayırır. Uygulamanız bu işlevlerden birini desteklemiyorsa Android Auto ve Android Automotive OS, oluşturduğunuz özel işlemleri görüntülemek için bu alanı kullanır.

Bu alanları özel işlemlerle doldurmak istemiyorsanız Android Auto ve Android Automotive OS'in, uygulamanız ilgili işlevi desteklemediğinde alanı boş bırakması için bu alanları ayırabilirsiniz. Bunu yapmak için, ayrılmış işlevlere karşılık gelen sabitleri içeren bir ekstralar paketiyle setExtras() yöntemini çağırın. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, ACTION_SKIP_TO_NEXT'a, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV ise ACTION_SKIP_TO_PREVIOUS'a karşılık gelir. Bu sabitleri pakette anahtar olarak kullanın ve değerleri için boole true değerini kullanın.

İlk PlaybackState değerini ayarlama

Android Auto ve Android Automotive OS, medya tarayıcısı hizmetinizle iletişim kurarken medya oturumunuz PlaybackStateCompat kullanarak içerik oynatma durumunu iletir. Android Automotive OS veya Android Auto, medya tarayıcısı hizmetinize bağlandığında uygulamanız otomatik olarak müzik çalmamalıdır. Bunun yerine, oynatmayı arabanın durumuna veya kullanıcı işlemlerine göre devam ettirmek ya da başlatmak için Android Auto ve Android Automotive OS'ten yararlanın.

Bunu yapmak için medya oturumunuzun ilk PlaybackStateCompat değerini STATE_STOPPED, STATE_PAUSED, STATE_NONE veya STATE_ERROR olarak ayarlayın.

Android Auto ve Android Automotive OS'teki medya oturumları yalnızca sürüş süresi boyunca devam ettiğinden kullanıcılar bu oturumları sık sık başlatıp durdurur. Kullanıcıların sürücüler arasında sorunsuz bir deneyim yaşamasını sağlamak için kullanıcının önceki oturum durumunu takip edin. Böylece, medya uygulaması devam etme isteği aldığında kullanıcı otomatik olarak kaldığı yerden devam edebilir (ör. son oynatılan medya öğesi, PlaybackStateCompat ve sıra).

Özel oynatma işlemleri ekleme

Medya uygulamanızın desteklediği ek işlemleri görüntülemek için özel oynatma işlemleri ekleyebilirsiniz. Alan izin veriyorsa (ve ayrılmış değilse) Android, özel işlemleri taşıma kontrollerine ekler. Aksi takdirde özel işlemler taşma menüsünde gösterilir. Özel işlemler, PlaybackStateCompat'e eklendikleri sırayla gösterilir.

Standart işlemlerden farklı bir davranış sağlamak için özel işlemleri kullanın. Standart işlemleri değiştirmek veya kopyalamak için bunları kullanmayın.

PlaybackStateCompat.Builder sınıfındaki addCustomAction() yöntemini kullanarak özel işlemler ekleyebilirsiniz.

Aşağıdaki kod snippet'inde, özel bir "Radyo kanalı başlat" işleminin nasıl ekleneceği gösterilmektedir:

Kotlin

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

Java

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());

Bu yöntemin daha ayrıntılı bir örneği için GitHub'daki Universal Android Music Player örnek uygulamasındaki setCustomAction() yöntemine bakın.

Özel işleminizi oluşturduktan sonra medya oturumunuz, onCustomAction() yöntemini geçersiz kılarak işleme yanıt verebilir.

Aşağıdaki kod snippet'inde, uygulamanızın "Radyo kanalı başlat" işlemine nasıl yanıt verebileceği gösterilmektedir:

Kotlin

override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> {
            ...
        }
    }
}

Java

@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) {
        ...
    }
}

Bu yöntemin daha ayrıntılı bir örneği için GitHub'daki Universal Android Music Player örnek uygulamasındaki onCustomAction yöntemine bakın.

Özel işlemler için simgeler

Oluşturduğunuz her özel işlem için bir simge gerekir.

Bu simgenin açıklaması CommandButton.ICON_ sabitlerinden biriyle eşleşiyorsa bu tam sayı değerini, özel işlemin ekstralarının EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT anahtarı için ayarlamanız gerekir. Desteklenen sistemlerde bu, CustomAction.Builder öğesine iletilen simge kaynağını geçersiz kılar. Böylece sistem bileşenleri, işleminizi ve diğer oynatma işlemlerini tutarlı bir tarzda oluşturabilir.

Ayrıca bir simge kaynağı da belirtmeniz gerekir. Arabalardaki uygulamalar birçok farklı ekran boyutunda ve yoğunluğunda çalışabileceğinden, sağladığınız simgeler vektör çizilebilir olmalıdır. Vektör çizilebilir öğeler, ayrıntıları kaybetmeden öğeleri ölçeklendirmenize olanak tanır. Vektör çizilebilir öğeler, daha düşük çözünürlüklerde kenar ve köşeleri piksel sınırlarıyla hizalamayı da kolaylaştırır.

Özel bir işlem duruma bağlıysa (ör. oynatma ayarını açar veya kapatır) kullanıcıların işlemi seçtiğinde bir değişiklik görebilmesi için farklı durumlar için farklı simgeler sağlayın.

Devre dışı bırakılan işlemler için alternatif simge stilleri sağlama

Geçerli bağlamda kullanılamayan özel işlem simgelerini, işlemin devre dışı olduğunu gösteren alternatif simgelerle değiştirin.

Şekil 6. Stil dışı özel işlem simgesi örnekleri.

Ses biçimini belirtin

Şu anda oynatılan medyanın özel bir ses biçimi kullandığını belirtmek için bu özelliği destekleyen araçlarda oluşturulan simgeleri belirtebilirsiniz. KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI ve KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI öğelerini, şu anda oynatılan medya öğesinin ekstralar paketinde ayarlayabilirsiniz (MediaSession.setMetadata() öğesine iletilir). Farklı düzenlere uyum sağlamak için bu ekstraların ikisini de ayarladığınızdan emin olun.

Ayrıca, KEY_IMMERSIVE_AUDIO ekstrasını ayarlayarak otomobil OEM'lerine bunun etkileyici ses olduğunu ve etkileyici içeriği etkileyebilecek ses efektleri uygulayıp uygulamayacakları konusunda çok dikkatli olmaları gerektiğini bildirebilirsiniz.

Şu anda oynatılan medya öğesini, altyazı, açıklama veya her ikisi de diğer medya öğelerine yönlendiren bağlantılar olacak şekilde yapılandırabilirsiniz. Bu sayede kullanıcılar ilgili öğelere hızlıca atlayabilir. Örneğin, aynı sanatçının diğer şarkılarına veya podcast'in diğer bölümlerine atlayabilirler. Araç bu özelliği destekliyorsa kullanıcılar bağlantıya dokunarak ilgili içeriğe göz atabilir.

Bağlantı eklemek için KEY_SUBTITLE_LINK_MEDIA_ID meta verilerini (altyazıda bağlantı oluşturmak için) veya KEY_DESCRIPTION_LINK_MEDIA_ID meta verilerini (açıklamadan bağlantı oluşturmak için) yapılandırın. Ayrıntılar için bu meta veri alanlarının referans dokümanlarına bakın.

Sesli işlemleri destekleme

Medya uygulamanız, sürücülere dikkat dağıtıcı unsurları en aza indiren güvenli ve kullanışlı bir deneyim sunmak için sesli işlemleri desteklemelidir. Örneğin, uygulamanız bir medya öğesi çalıyorsa kullanıcı, arabanın ekranına bakmadan veya ekrana dokunmadan uygulamanıza farklı bir şarkıyı çalmasını söylemek için"[şarkı adı]çal" diyebilir. Kullanıcılar, direksiyonlarındaki uygun düğmeleri tıklayarak veya "Ok Google" anahtar kelimelerini söyleyerek sorgu başlatabilir.

Android Auto veya Android Automotive OS bir sesli işlemi algılayıp yorumladığında bu sesli işlem onPlayFromSearch() üzerinden uygulamaya gönderilir. Uygulama bu geri aramayı aldığında query dizesine eşleşen içeriği bulur ve oynatmaya başlar.

Kullanıcılar sorgularında tür, sanatçı, albüm, şarkı adı, radyo istasyonu veya şarkı listesi gibi farklı terim kategorileri belirtebilir. Arama desteği oluştururken uygulamanız için anlamlı olan tüm kategorileri göz önünde bulundurun. Android Auto veya Android Automotive OS, belirli bir sorgunun belirli kategorilere uyduğunu algılarsa extras parametresine ek bilgiler ekler. Aşağıdaki ekstralar gönderilebilir:

Kullanıcı arama terimlerini belirtmezse Android Auto veya Android Automotive OS tarafından gönderilebilecek boş bir query dizesi için yer ayırın. Örneğin, kullanıcı "Müzik çal" derse. Bu durumda uygulamanız, son çalınan veya yeni önerilen bir parçayı başlatmayı seçebilir.

Bir arama hızlı bir şekilde işlenemiyorsa onPlayFromSearch()'te engellemeyin. Bunun yerine, oynatma durumunu STATE_CONNECTING olarak ayarlayın ve aramayı ayarsız bir ileti dizisinde gerçekleştirin.

Oynatma işlemi başladıktan sonra medya oturumunun sırasını ilgili içerikle doldurabilirsiniz. Örneğin, kullanıcı bir albümün çalınmasını isterse uygulamanız, sırayı albümün parça listesiyle doldurabilir. Ayrıca, kullanıcıların sorgusuyla eşleşen farklı bir parça seçebilmesi için gezilebilir arama sonuçları desteği de sunabilirsiniz.

Android Auto ve Android Automotive OS, "çal" sorgularına ek olarak "müziği duraklat" ve "sonraki şarkı" gibi oynatmayı kontrol etmek için sesli sorguları tanır ve bu komutları onPause() ve onSkipToNext() gibi uygun medya oturumu geri çağırmalarıyla eşleştirir.

Uygulamanızda sesli oynatma işlemlerinin nasıl uygulanacağına dair ayrıntılı bir örnek için Google Asistan ve medya uygulamaları başlıklı makaleyi inceleyin.

Dikkat dağıtıcı unsurlara karşı önlemler alın

Android Auto kullanılırken kullanıcının telefonu aracının hoparlörlerine bağlı olduğundan sürücünün dikkatinin dağılmasını önlemek için ek önlemler almanız gerekir.

Arabadaki alarmları engelleme

Android Auto medya uygulamaları, kullanıcı oynatmaya (ör. oynatma düğmesine basarak) başlamadığı sürece sesleri araba hoparlörlerinden çalmaya başlamamalıdır. Medya uygulamanızdan kullanıcı tarafından planlanan alarmlar bile araç hoparlörlerinden müzik çalmaya başlamamalıdır.

Bu koşulu karşılamak için uygulamanız, ses çalmadan önce CarConnection simgesini sinyal olarak kullanabilir. Uygulamanız, araba bağlantısı türü için LiveData değerini gözlemleyip CONNECTION_TYPE_PROJECTION değerine eşit olup olmadığını kontrol ederek telefonun bir araba ekranına yansıtılıp yansıtılmadığını kontrol edebilir.

Kullanıcının telefonu yansıtıyorsa alarmları destekleyen medya uygulamaları aşağıdakilerden birini yapmalıdır:

  • Alarmı devre dışı bırakın.
  • Alarmı STREAM_ALARM üzerinden çalın ve alarmı devre dışı bırakmak için telefon ekranında bir kullanıcı arayüzü sağlayın.

Medya reklamlarını işleme

Android Auto, ses oynatma oturumu sırasında medya meta verileri değiştiğinde varsayılan olarak bir bildirim gösterir. Bir medya uygulaması müzik çalmaktan reklam yayınlamaya geçtiğinde kullanıcıya bildirim gösterilmesi dikkat dağıtıcıdır. Android Auto'nun bu durumda bildirim göstermesini önlemek için medya meta veri anahtarını METADATA_KEY_IS_ADVERTISEMENT, aşağıdaki kod snippet'inde gösterildiği gibi METADATA_VALUE_ATTRIBUTE_PRESENT olarak ayarlamanız gerekir:

Kotlin

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

Java

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

Genel hataları giderme

Uygulamada hata oluştuğunda oynatma durumunu STATE_ERROR olarak ayarlayın ve setErrorMessage() yöntemini kullanarak bir hata mesajı sağlayın. Hata mesajını ayarlarken kullanabileceğiniz hata kodlarının listesini PlaybackStateCompat sayfasında bulabilirsiniz. Hata mesajları kullanıcıya yönelik olmalı ve kullanıcının mevcut yerel ayarlarına göre yerelleştirilmelidir. Android Auto ve Android Automotive OS, hata mesajını kullanıcıya gösterebilir.

Örneğin, içerik kullanıcının mevcut bölgesinde kullanılamıyorsa hata mesajını ayarlarken ERROR_CODE_NOT_AVAILABLE_IN_REGION hata kodunu kullanabilirsiniz.

Kotlin

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

Java

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());

Hata durumları hakkında daha fazla bilgi için Medya oturumu kullanma: Durumlar ve hatalar başlıklı makaleyi inceleyin.

Bir Android Auto kullanıcısının hatayı düzeltmek için telefon uygulamanızı açması gerekiyorsa bu bilgiyi mesajınızda kullanıcıya iletin. Örneğin, hata mesajınızda "Lütfen oturum açın" yerine "[Uygulamanızın adı] uygulamasında oturum açın" yazabilir.

Diğer kaynaklar