Arabalar için medya uygulamaları oluşturma

Android Auto ve Android Automotive OS, medya uygulaması içeriğinizi arabadaki 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 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 çocuklarına 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 özel 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. Böylece hem Android Auto hem de Android Automotive OS, hizmetinizi kullanarak aşağıdakileri yapabilir:

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

Diğer istemcilerin uygulamanızdaki medya içeriğine erişmesini sağlamak için medya tarayıcısı hizmetinizi de kullanabilirsiniz. Bu medya istemcileri, bir kullanıcının telefonundaki başka uygulamalar veya başka uzak istemciler olabilir.

Medya tarayıcısı 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 destekliyorsa kullanıcı da içeriğinizi 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. Medya tarayıcısı hizmetinizde bunu desteklemek için iki yöntem uygulamanız gerekir: onGetRoot() ve onLoadChildren()

onGetRoot'u 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ında yer alan onGetRoot() yöntemine bakın.

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

Hizmetinizin onGetRoot() yöntemiyle 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 öğesini 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ış paketler listesine kısıtlayabilirsiniz. Paket doğrulanamıyorsa içeriğinize erişimi reddetmek için null değerini döndürün.

Android Auto ve Android Automotive OS gibi sistem uygulamalarının içeriğinize erişmesini sağlamak için bu sistem uygulamaları onGetRoot() yöntemini çağırdığında hizmetiniz her zaman boş olmayan bir BrowserRoot değeri döndürmelidir. 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'i, hizmetinizin çağrı paketinin bir sistem uygulaması olduğunu nasıl doğrulayabileceğini gösterir:

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'i, onLoadChildren() yönteminin basit bir uygulamasını gösterir:

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, kök menünün yapısıyla ilgili belirli kısıtlamalara sahiptir. Bunlar MediaBrowserService ürününe kök ipuçları aracılığıyla iletilir. Bu ipuçları, onGetRoot() ürününe iletilen Bundle bağımsız değişkeni aracılığıyla okunabilir. Bu ipuçlarının uygulanması, sistemin kök içeriği gezinme sekmeleri olarak en iyi şekilde görüntüleyebilmesini 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 kök oynatılabilir bir öğe gösteriyorsanız desteklenen flag'ler ipucundan dolayı bunun yerine kök göz atılabilir bir öğenin altına iç içe 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 ve tercihen beyaz simgeler sağlayın.
  • Her sekme öğesi için kısa ancak anlamlı etiketler girin. Etiketleri kısa tutmak, dizelerin kesilme olasılığını azaltır.

Medya posterini göster

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'i Glide'ı kullanı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öntemindeki BrowserRoot ekstra paketine belirli sabit değerleri ekleyerek medya öğelerinizin nasıl görüntüleneceği için 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:

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM: İlgili öğeler liste öğeleri olarak sunulur.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM: İlgili öğeler ızgara öğeleri olarak sunulur.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM: İlgili öğeler "kategori" liste öğeleri olarak sunulur. Bunlar sıradan liste öğeleriyle aynıdır. Tek fark, simgeler küçük olduğunda daha iyi göründükleri için kenar boşluklarının öğelerin simgelerinin etrafına uygulanmasıdır. Simgelerin tonlanabilir vektör çizimleri olmalıdır. Bu ipucunun yalnızca göz atılabilir öğeler için sağlanması beklenir.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM: İlgili öğeler "kategori" ızgara öğeleri olarak sunulur. Bunlar, simgeler küçükken daha iyi göründüğü için simgelerin etrafına kenar boşlukları uygulanması dışında normal tablo öğeleriyle aynıdır. Simgeler, boyanabilir drawable vektörler olmalıdır. Bu ipucunun yalnızca göz atılabilir öğeler için sağlanması beklenir.

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 stillerini ayarlayın

Content Style API, herhangi bir medya öğesinin kendisi ve herhangi bir göz atılabilir medya öğesinin alt öğeleri için varsayılan içerik stilini geçersiz kılmanıza olanak tanır.

Göz atılabilir medya öğesinin alt öğeleri için varsayılanı geçersiz kılmak için medya öğesinin MediaDescription öğesinde ekstra 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 taranabilir alt öğeleri için geçerlidir.

Belirli bir medya öğesinin alt öğeleri için değil, kendisi için varsayılanı geçersiz kılmak isterseniz medya öğesinin MediaDescription öğesinde bir ekstra paketi oluşturun ve DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM anahtarıyla 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ını kullanarak öğeleri gruplandırma

İlgili medya öğelerini birlikte 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 grup başlıklı 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" adlı 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 A medya öğesi
  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 medya öğesi C
  4. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") ile medya öğesi D
  5. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren E medya öğesi

"Şarkılar" grubu ve "Albümler" grubunun medya öğeleri 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 A medya öğesi
  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 medya öğesi D
  4. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren B medya öğesi
  5. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums") içeren E medya öğesi

Ek meta veri göstergelerini göster

Medya tarayıcısı ağacındaki ve oynatma sırasındaki içerik için bir bakışta bilgi sağlamak üzere 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'i, %70 tamamlanmış uygunsuz bir medya öğesi için göstergelerin nasıl görüntüleneceğini gösterir:

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'i, oynatma görünümündeki mevcut şarkının uygunsuz ve indirilmiş olduğunu nasıl belirteceğinizi gösterir:

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üncelleyin

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ğu olması için aşağıdaki koşulların karşılanması gerekir:

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

5. Şekil. 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ılavuzdaki Sesli işlemleri destekle bölümüne bakın.

Göz atılabilir arama sonuçlarını görüntülemek için hizmetinizin onGetRoot() yöntemindeki ekstralar paketine BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED sabit anahtarını dahil edin ve true boole değeriyle eşleyin.

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

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ı" olanağı çağrıldığında kullanıcının arama terimlerini bu yönteme yönlendirir.

Arama sonuçlarını, daha göz atılabilir hale getirmek için başlık öğelerini kullanarak hizmetinizin onSearch() yönteminden düzenleyebilirsiniz. Örneğin, uygulamanızda 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 Bir Özel Göz Atma İşlemi

Özel Göz Atma İşlemleri, arabanın medya uygulamasındaki MediaItem nesnelerine özel simgeler ve etiketler eklemenize ve bu işlemlerle kullanıcı 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ü.

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

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 bir Özel Göz Atma İşlemi şunlarla 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 (Tonlama uygulanabilen bir çekilebilir 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 bir göz atma işlemiyle etkileşime geçtiğinde 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. "Radyoyu Ç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 nasıl uygulanır?

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 belirtir.
  3. Özel Göz Atma İşlemlerinin küresel 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. Genel listeyi BrowseRoot cihazınıza ekleyin:
  5. MediaItem nesnelerinize işlem ekleyin:
    • DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST anahtarını kullanarak MediaDescriptionCompat ekstralarındaki işlem kimliklerinin listesini dahil ederek bağımsız MediaItem nesnelerine işlem ekleyebilirsiniz. Bu liste, BrowseRoot içinde tanımladığınız genel 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 üzerinde yapabileceğiniz değişikliklerden bazılarını aşağıda bulabilirsiniz.

BrowserServiceCompat'i geçersiz kılma

MediaBrowserServiceCompat ürününde aşağıdaki yöntemleri geçersiz kılmanız gerekiyor.

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 İş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 İşlemi Bundle nesnelerini bir ArrayList öğesine 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;
}

Göz atma köküne Özel Göz Atma İşlemi listesi ekleyin

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 değerini ayrıştırın:
    @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);
    }
  • Asenkron 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öz Atma Düğümünü Güncelle
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
  • Hata oluşursa result.sendError(resultBundle). numaralı telefonu arayın
  • İlerleme durumu güncellenirse result.sendProgressUpdate(resultBundle) numaralı telefonu arayın.
  • result.sendResult(resultBundle)'ü arayarak sonlandırın.

İşlem Durumunu Güncelle

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 ilerleme ve yaptığı işlemin sonucu hakkında anlık geri bildirim verebilirsiniz.

Örnek: İndirme İşlemi

Aşağıda, üç durumu olan bir indirme işlemini uygulamak için bu özelliği nasıl kullanabileceğinize dair bir örnek 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 başarı mesajı göstermek için EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE tuşunu kullanabilirsiniz.

Bu yaklaşım, kullanıcıya indirme işlemi ve mevcut durumu hakkında net geri bildirim sağlamanıza olanak tanır. İndirme durumlarını (%25, %50, %75) simgelerle göstererek 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. Favorilere eklendi: Bu işlem, kullanıcının favoriler listesindeki öğeler için görüntülenir. Kullanıcı bu işlemi seçtiğinde, kullanıcı arayüzünü güncellemek için işlemi "Favori" ile değiştirebilir ve 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 İşlemleri'nin esnekliğini ve arabanın medya uygulamasında gelişmiş bir kullanıcı deneyimi için gerçek zamanlı geri bildirimle çeşitli işlevleri uygulamak üzere nasıl kullanabileceğ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şturduğunuzda, oynatma kontrol 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. Sonraki bölümde bu nesnenin nasıl uygulanacağı açıklanmaktadı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 nesnesinden 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.

İçerik oynatmayı işlemek için uygulamanızın 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 çalmayı 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ı bir ö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 arabanı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ış sayfalarına göz atın.

Standart oynatma işlemlerini ayarlama

Android Auto ve Android Automotive OS, PlaybackStateCompat nesnesinde etkinleştirilen işlemlere dayalı oynatma kontrollerini 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.

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

Kullanılmayan alanı ayır

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 değeri ACTION_SKIP_TO_NEXT, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV ise ACTION_SKIP_TO_PREVIOUS öğesine karşılık gelir. Bu sabit değerleri, pakette anahtar olarak ve değerleri için true boole değeri kullanın.

İlk PlaybackState değerini ayarlama

Android Auto ve Android Automotive OS, medya tarayıcı hizmetinizle iletişim kurarken medya oturumunuz, içerik oynatma durumunu PlaybackStateCompat kullanarak bildirir. 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ği için 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 ekleyin

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'ye 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

stateBuilder.addCustomAction(
    PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon
    ).run {
        setExtras(customActionExtras)
        build()
    }
)

Java

stateBuilder.addCustomAction(
    new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon)
    .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'i, uygulamanızın "Radyo kanalı başlatma" işlemine nasıl tepki verebileceğini gösterir:

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ında yer alan onCustomAction yöntemine bakın.

Özel işlemler için simgeler

Oluşturduğunuz her özel işlem için bir simge kaynağı 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 çekilebilir özelliği, 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

Bir özel işlem, geçerli bağlam için kullanılamadığında özel işlem simgesini, işlemin devre dışı bırakıldığını gösteren alternatif bir simgeyle değiştirin.

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

Ses biçimini belirtin

Oynatılan medyanın özel bir ses biçimini 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ıdan bağlantı oluşturmak için) veya KEY_DESCRIPTION_LINK_MEDIA_ID (açıklamadan bağlantı vermek 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ızda bir medya öğesi çalıyorsa kullanıcı, "[şarkı adını]çal" diyerek uygulamanızdan arabanın ekranına bakmadan veya ekrana dokunmadan farklı bir şarkı çalmasını söyleyebilir. 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ı eşzamansız bir ileti dizisinde gerçekleştirin.

Oynatma işlemi başladıktan sonra, medya oturumunun sırasını ilgili içerikle doldurmayı düşünebilirsiniz. Ö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ının sorgusuyla eşleşen farklı bir parça seçebilmesi için göz atılabilir arama sonuçları desteği uygulamayı da düşünebilirsiniz.

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şlemlerini nasıl uygulayacağınızla ilgili 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

Kullanıcı çalmaya başlamadığı sürece (örneğin, oynat düğmesine basarak) Android Auto medya uygulamaları, araç hoparlörlerinden ses ç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.

Uygulamanız bu şartı karşılamak için ses çalmadan önce sinyal olarak CarConnection'i kullanabilir. Uygulamanız, araba bağlantı türü için LiveData değerini gözlemleyip CONNECTION_TYPE_PROJECTION değerine eşit olup olmadığını kontrol ederek telefonun bir araç 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 bir bildirim göstermek dikkat dağıtıcıdır. Bu durumda Android Auto'nun bildirim görüntülemesini ö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ı gösterin. Hata mesajını ayarlarken kullanabileceğiniz hata kodlarının listesi için PlaybackStateCompat adresine göz atın. Hata mesajları kullanıcıya yönelik olmalı ve kullanıcının mevcut yerel ayarlarına göre yerelleştirilmelidir. Böylece 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 bir hatayı çözmek için telefon uygulamanızı açması gerekirse mesajda bu bilgiyi kullanıcıya sağlayın. Örneğin, hata mesajınızda "Lütfen oturum açın" yerine "[Uygulamanızın adı] uygulamasında oturum açın" yazıyor olabilir.

Diğer kaynaklar