Arabalar için medya uygulamaları oluşturma

Android Auto ve Android Automotive OS, medya uygulaması içeriğinizi kullanıcılara araçlarında sunmanıza yardımcı olur. Android Auto ve Android Automotive OS'in veya medya tarayıcısı olan başka bir uygulamanın içeriğinizi keşfedip görüntüleyebilmesi için arabalara yönelik bir medya uygulamasının bir medya tarayıcı hizmeti sağlaması gerekir.

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ılmaktadır.

Bu kılavuzda, uygulamanızın Android Auto veya Android Automotive OS'te çalışması için gereken MediaBrowserService ve MediaSession bileşenleri açıklanmaktadır. Temel medya altyapısını tamamladıktan sonra medya uygulamanıza Android Auto için destek ve Android Automotive işletim sistemi için destek ekleyebilirsiniz.

Başlamadan önce

  1. Android media API dokümanlarını inceleyin.
  2. Tasarım konusunda rehberlik için Medya uygulamaları oluşturma konusunu inceleyin.
  3. Bu bölümde listelenen temel terimleri ve kavramları gözden geçirin.

Anahtar terimler ve kavramlar

Medya tarayıcı hizmeti
Medya uygulamanız tarafından uygulanan ve MediaBrowserServiceCompat API'siyle uyumlu 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 nesnelerinden oluşan bir ağaçta düzenler. Bir medya öğesi, aşağıdaki işaretlerin birine veya her ikisine birden sahip olabilir:

  • FLAG_PLAYABLE: Öğenin içerik ağacında bir yaprak olduğunu belirtir. Bu öğe; albümdeki bir şarkı, sesli kitaptaki bir bölüm veya podcast'in bir bölümü gibi 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 bunun alt öğeleri de albümdeki şarkılardır.

Hem göz atılabilen hem de oynatılabilen bir medya öğesi, oynatma listesi gibi çalışır. Öğenin kendisini seçerek tüm alt öğelerini oynayabilir veya alt öğelerine göz atabilirsiniz.

Araç için optimize edilmiş

Android Automotive OS tasarım yönergelerine uygun bir Android Automotive OS uygulaması etkinliği. Bu işlemlerin arayüzü Android Automotive OS tarafından çizilmez. Bu nedenle, uygulamanızın tasarım yönergelerine uyduğundan emin olmanız gerekir. Buna tipik olarak, dokunma hedefleri ve yazı tipi boyutlarının büyük olması, gündüz ve gece modları için destek ve daha yüksek kontrast oranları dahildir.

Araç için optimize edilmiş kullanıcı arayüzlerinin görüntülenmesine yalnızca Araba Kullanıcı Deneyimi Kısıtlamaları (CUXR'ler) geçerli değilken izin verilir, çünkü bu arayüzler kullanıcının daha uzun süre dikkat etmesini veya etkileşimde bulunmasını gerektirebilir. CUXR'ler araç durduğunda veya park edildiğinde etkin olmaz, ancak araç hareket halindeyken her zaman etkindir.

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

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

Medya tarayıcı hizmetinizi oluşturabilmek için önce uygulamanızın manifest dosyalarını yapılandırmanız gerekir.

Medya tarayıcı hizmetinizi tanımlama

Hem Android Auto hem de Android Automotive OS, medya öğelerine göz atmak için medya tarayıcı hizmetiniz üzerinden 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 manifest dosyanızda medya tarayıcısı hizmetinizi tanımlayın.

Aşağıdaki kod snippet'i, manifest dosyanızda medya tarayıcısı hizmetinizi nasıl tanımlayacağınızı gösterir. Bu kodu, Android Automotive OS modülünüzün manifest dosyasına 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, başlatıcıda ve simgeler tepsisi gibi sistem kullanıcı arayüzünde 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ızın simgesinden farklı bir simge kullanmak için manifest dosyasında medya tarayıcısı hizmetinizin <service> öğesinde android:icon özelliğini 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ı) kullanılır. Bildirimler için kullanılan küçük simgeyi yeniden kullanmayı düşünün. Bu simge tek renkli olmalıdır. Aşağıdaki manifest beyanını kullanarak uygulamanızı temsil etmek için kullanılacak bir simge belirtebilirsiniz:

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

Medya tarayıcı hizmetinizi oluşturun

MediaBrowserServiceCompat sınıfını genişleterek bir medya tarayıcısı hizmeti oluşturursunuz. Böylece hem Android Auto hem de Android Automotive OS, hizmetinizi kullanarak şunları yapabilir:

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

Medya tarayıcısı hizmetinizi, diğer istemcilerin uygulamanızdaki medya içeriğine erişebilmesini sağlamak için de kullanabilirsiniz. Bu medya istemcileri, 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şimde bulunduğu açıklanmaktadır.

  1. Kullanıcı, Android Automotive OS veya Android Auto'da uygulamanızı başlatır.
  2. Android Automotive OS veya Android Auto, onCreate() yöntemini kullanarak uygulamanızın medya tarayıcı hizmetiyle iletişim kurar. onCreate() yöntemini uygularken bir MediaSessionCompat nesnesi ve bunun 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örüntülenmez; 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 en üst düzeyi olarak gösterir. Sistemin bu düzeyde ne beklediği hakkında daha fazla bilgi edinmek 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 hizmetinizin onLoadChildren() yöntemi, seçilen menü öğesinin alt öğelerini almak için tekrar çağrılır.
  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 kullanır.
  7. Uygulamanız tarafından destekleniyorsa kullanıcılar, içeriğinizde de arama yapabilir. 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çeriğin kullanılabilir olduğunu öğrenmek için uygulamanızın medya tarayıcı hizmetini arar. Bunu desteklemek için medya tarayıcısı hizmetinizde iki yöntem uygulamanız gerekir: onGetRoot() ve onLoadChildren()

onGetRoot 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, içeriğinizin geri kalanını onLoadChildren() yöntemini kullanarak istemek için bu kök düğümü kullanır.

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

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öntemine çağrı yapıldığında çağrı paketi, tanımlayıcı bilgileri hizmetinize iletir. Hizmetiniz bu paketin içeriğinize erişip erişemeyeceğine karar vermek için bu bilgileri kullanabilir. Örneğin, clientPackageName özelliğini 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 kodunu 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 hizmetinizin her zaman boş olmayan bir BrowserRoot döndürmesi gerekir. Android Automotive OS sistem uygulamasının imzası, arabanın markasına ve modeline bağlı olarak değişebilir. Bu nedenle, tüm sistem uygulamalarındaki bağlantıların Android Automotive OS'i güçlü bir şekilde desteklemesine 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 bir alıntıdır. Hizmetinizin onGetRoot() yöntemi için paket doğrulamanın nasıl uygulanacağına ilişkin daha ayrıntılı bir örnek görmek üzere bu sınıfa bakın.

Google Asistan'ın sistem uygulamalarına izin vermenin yanı sıra MediaBrowserService cihazınıza bağlanmasına da izin vermeniz gerekir. Google Asistan'ın, Android Auto ve Android Automotive OS dahil olmak üzere telefon için ayrı paket adları olduğunu unutmayın.

onLoadChildren() işlevini uygulama

Kök düğüm nesnenizi aldıktan sonra Android Auto ve Android Automotive OS, alt düğüm nesnesini almak için kök düğüm nesnesinde onLoadChildren() yöntemini çağırarak ü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 nesnesiyle 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 aktarır. Jetonu uygun medya öğesiyle ilişkilendirmekten uygulamanız sorumludur.

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

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 eksiksiz bir örneği için GitHub'daki Universal Android Music Player örnek uygulamasında yer alan onLoadChildren() yöntemine bakın.

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

2. Şekil. Kök içerik, gezinme sekmeleri olarak görüntülenir.

Android Auto ve Android Automotive OS'te, kök menüsünün yapısıyla ilgili belirli kısıtlamalar vardır. Bunlar MediaBrowserService öğesine kök ipuçları aracılığıyla iletilir. Bu ipuçları, onGetRoot()'a geçirilen Bundle bağımsız değişkeni aracılığıyla okunabilir. Bu ipuçlarının uygulanması, sistemin kök içeriği en uygun şekilde gezinme sekmeleri şeklinde görüntülemesini sağlar. Bu ipuçlarını uygulamazsanız bazı kök içerikler kaldırılabilir veya sistem tarafından keşfedilebilirliği daha düşük olabilir. İki ipucu gönderilir:

İlgili 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 de hiyerarşiniz Android Auto ve Android Automotive OS dışındaki MediaBrowser entegrasyonları arasında farklılık gösteriyorsa içerik hiyerarşinizin mantığını bu ipuçlarının değerlerine göre kollara ayırabilirsiniz. Örneğin, normalde root oynatılabilir bir öğe gösteriyorsanız bunu, desteklenen flag'ler ipucunun değeri nedeniyle kök göz atılabilir bir öğenin altına yerleştirmek isteyebilirsiniz.

Sekmelerin en iyi şekilde oluşturulmasını sağlamak için kök ipuçlarının yanı sıra uygulanması 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 ama anlamlı etiketler sağlayın. Etiketleri kısa tutmak, dizelerin kısaltılma olasılığını azaltır.

Medya posterini göster

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

Aşağıdaki adımlarda, bir web URI'sinden resmin nasıl indirileceği ve yerel URI aracılığıyla nasıl gösterileceği açıklanmaktadır. Daha eksiksiz bir örnek için openFile() uygulamasının uygulamasına ve Universal Android Music Player örnek uygulamasındaki ilgili yöntemlere bakın.

  1. Web URI'sine karşılık gelen bir content:// URI'sı oluşturun. Medya tarayıcı hizmeti ve medya oturumu, bu içerik URI'sını 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, karşılık gelen URI için bir dosyanın mevcut olup olmadığını kontrol edin. Aksi takdirde resim dosyasını indirip önbelleğe alın. Aşağıdaki kod snippet'i Glide'ı kullanmaktadı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 bölümüne bakın.

İçerik stillerini uygula

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

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

Liste öğeleri

Bu içerik stili, başlıklara ve meta verilere resimlerden daha fazla öncelik verir.

Izgara öğeleri

Bu içerik stili, başlıklara ve meta verilere göre resimlere öncelik verir.

Varsayılan içerik stillerini ayarlama

Medya öğelerinizin görüntülenme şekli için genel varsayılanları, hizmetinizin onGetRoot() yönteminin BrowserRoot ekstra paketine belirli sabit değerleri ekleyerek ayarlayabilirsiniz. Android Auto ve Android Automotive OS, bu paketi okur ve uygun stili belirlemek için bu sabit değerleri 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 olduklarında daha iyi görünmesi nedeniyle öğelerin simgelerinin etrafına kenar boşluklarının uygulanmasıdır. Simgeler, renklenebilir vektör çekilebilir öğeleri 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 normal ızgara öğeleriyle aynıdır ancak simgeler küçük olduklarında daha iyi göründüğü için öğelerin simgelerinin etrafına kenar boşlukları uygulanır. Simgeler, renklenebilir vektör çekilebilir öğeleri olmalıdır. Bu ipucunun yalnızca göz atılabilir öğeler için sağlanması beklenir.

Aşağıdaki kod snippet'i, göz atılabilir öğeler için varsayılan içerik stilinin ızgaralara, oynatılabilir öğeler için ise listelere nasıl ayarlanacağını gösterir:

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 ayarla

Content Style API, hem göz atılabilir medya öğelerinin alt öğeleri için hem de medya öğelerinin varsayılan içerik stilini geçersiz kılmanıza olanak tanır.

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

Varsayılan değeri belirli bir medya öğesinin alt öğeleri yerine kendisi için geçersiz kılmak istiyorsanız medya öğesinin MediaDescription bölümünde ekstralar paketi oluşturun ve DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM anahtarıyla bir ipucu ekleyin. İlgili öğenin sunumunu belirtmek için daha önce açıklanan değerleri kullanın.

Aşağıdaki kod snippet'i, hem kendisi hem de alt öğeleri için varsayılan içerik stilini geçersiz kılan bir göz atılabilir MediaItem öğesinin nasıl oluşturulacağını göstermektedir. Kendisini bir kategori listesi öğesi, gözatılabilir alt öğelerini liste öğeleri ve oynanabilir alt öğelerini ızgara öğeleri 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 bir ipucu kullanırsınız. Bir gruptaki her medya öğesinin, MediaDescription özelliğinde DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE anahtarıyla bir eşleme ve aynı dize değeri içeren bir ekstra paket tanımlaması gerekir. Grubun başlığı olarak kullanılan bu dizeyi yerelleştirin.

Aşağıdaki kod snippet'i, "Songs" alt grup başlığıyla bir MediaItem öğesini nasıl oluşturacağınızı göstermektedir:

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, bitişik blok olarak gruplandırmak istediğiniz tüm medya öğelerini geçirmelidir. Ö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 gönderdiğ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 medya öğesi C
  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 medya öğesi E

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

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

Bu öğeleri iki grup halinde 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 medya öğesi C
  3. extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") içeren medya öğesi D
  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 medya öğesi E

Ek meta veri göstergelerini göster

Medya tarayıcı ağacındaki ve oynatma sırasında içerikle ilgili bir bakışta bilgi sağlamak için ek meta veri göstergeleri ekleyebilirsiniz. Göz atma ağacında, Android Auto ve Android Automotive OS bir öğeyle ilişkilendirilen ekstraları okur ve hangi göstergelerin gösterileceğini belirlemek için belirli sabit değerleri arar. Medya oynatma sırasında, Android Auto ve Android Automotive OS medya oturumu için meta verileri okur ve görüntülenecek göstergeleri belirlemek için belirli sabit değerleri arar.

3. Şekil. Ş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ü.

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

Aşağıdaki sabit değerler, hem MediaItem açıklama ekstralarında hem de MediaMetadata ekstrada kullanılabilir:

Aşağıdaki sabit değerler yalnızca MediaItem açıklama ekstrasında kullanılabilir:

Kullanıcı medyaya göz atma ağacına göz atarken görünen göstergeleri görüntülemek için bu sabit değerlerden birini veya daha fazlasını içeren bir ekstra paket oluşturun ve bu paketi MediaDescription.Builder.setExtras() yöntemine geçirin.

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

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

Oynatılmakta olan bir medya öğesine ilişkin göstergeleri görüntülemek amacıyla mediaSession uygulamanızın MediaMetadataCompat bölümünde METADATA_KEY_IS_EXPLICIT veya EXTRA_DOWNLOAD_STATUS için Long değerleri bildirebilirsiniz. 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ğunun nasıl belirtileceğini 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üncelle

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

Android Auto ve Android Automotive OS için ilerleme çubuğunu güncel tutmak amacıyla MediaMetadataCompat ve PlaybackStateCompat içinde ek bilgiler sağlayarak devam eden içeriği göz atma görünümündeki medya öğelerine bağlayabilirsiniz. Medya öğesinin otomatik olarak güncellenen bir ilerleme çubuğuna sahip olması için aşağıdaki gereksinimlerin karşılanması gerekir:

Aşağıdaki kod snippet'i, geçerli olarak oynatılan öğenin göz atma görünümünde bir öğeye bağlı olduğunun nasıl belirtileceğini gösterir:

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ğiyle oynatma görünümü.

Uygulamanız, kullanıcılara arama sorgusu başlattıklarında gösterilen bağlamsal 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 belirlenen fırsatlar aracılığıyla gösterir. Daha fazla bilgi edinmek için bu kılavuzdaki Sesli işlemleri destekleme bölümüne bakın.

Göz atılabilir arama sonuçlarını görüntülemek için hizmetinizin onGetRoot() yönteminde true boole ile eşlenen ekstralar paketine BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED sabit anahtarını ekleyin.

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ı sağlamaya başlamak için medya tarayıcısı hizmetinizde onSearch() yöntemini geçersiz kılın. Android Auto ve Android Automotive OS, bir kullanıcı arama sorgusu arayüzünü veya "Arama sonuçları" olanağını çağırdığında, kullanıcının arama terimlerini bu yönteme yönlendirir.

Hizmetinizin onSearch() yönteminden elde edilen arama sonuçlarını, başlık öğelerini kullanarak düzenleyerek daha göz atılabilir hale getirebilirsiniz. Örneğin, uygulamanızda müzik çalarsa arama sonuçlarını albüm, sanatçı ve şarkılara göre düzenleyebilirsiniz.

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

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.

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

Özel Göz Atma İşlemleri, arabanın medya uygulamasında uygulamanızın MediaItem nesnelerine özel simgeler ve etiketler eklemenize ve bu işlemlerle kullanıcı etkileşimlerini yönetmenize olanak tanır. Bu, Medya Uygulamasının işlevselliğini "İndir", "Sıraya Ekle", "Radyoyu Çal", "Favori" veya "Kaldır" işlemleri eklemek gibi çeşitli şekillerde genişletmenize olanak tanır.

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

7. Şekil. Ö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

Her Özel Göz Atma İşlemi şununla tanımlanır:

  • İşlem Kimliği (benzersiz dize tanımlayıcısı)
  • İşlem Etiketi (kullanıcıya gösterilen metin)
  • İşlem Simgesi URI'sı (renk tonu eklenebilen bir vektör çekilebilir öğesi)

BrowseRoot kapsamında genel olarak bir Özel Tarama İşlemleri listesi tanımlarsınız. Ardından, bu işlemlerin bir alt kümesini tek tek MediaItem.

Kullanıcı bir Özel Göz Atma İşlemi ile etkileşime geçtiğinde uygulamanız onCustomAction() üzerinden bir geri arama alır. Ardından, işlemi gerçekleştirebilir ve gerekirse MediaItem ile ilgili işlem listesini güncelleyebilirsiniz. Bu, "En Beğendiklerim" ve "İndir" gibi durum bilgili işlemler için yararlıdır. "Radyo Çal" gibi güncelleme gerektirmeyen işlemler için işlem listesini güncellemeniz gerekmez.

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

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

Ayrıca, bir göz atma düğümü köküne Özel Göz Atma İşlemleri ekleyebilirsiniz. Bu işlemler, ana araç çubuğunun altındaki ikincil bir araç çubuğunda görüntülenir.

Özel Göz Atma İşlemleri nasıl uygulanır?

Projenize Özel Göz Atma İşlemleri eklemek için şu adımları uygulayın:

  1. MediaBrowserServiceCompat uygulamanızda iki yöntemi geçersiz kılın:
  2. Çalışma zamanında işlem sınırlarını ayrıştırın:
    • onGetRoot() ürününde, rootHints Bundle içinde BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT anahtarını kullanarak her bir 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 İşlemlerinin genel listesini oluşturun:
    • Her işlem için aşağıdaki anahtarlarla 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'sı * Tüm Bundle işlemi nesnelerini bir listeye ekleyin.
  4. Global listeyi BrowseRoot cihazınıza ekleyin:
  5. MediaItem nesnenize işlemler ekleyin:
    • DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST anahtarını kullanarak MediaDescriptionCompat ekstralarına 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 işleme ve döndürme ilerlemesi veya sonuçları döndürme:

Özel Göz Atma İşlemleri'ni kullanmaya başlamak için BrowserServiceCompat üzerinde yapabileceğiniz bazı değişiklikleri aşağıda bulabilirsiniz.

TarayıcıServiceCompat'ı Geçersiz Kıl

MediaBrowserServiceCompat içinde 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şlemi 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 Göz Atma İşlemi Oluşturma

Her işlemin ayrı bir Bundle içinde 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 için Özel Göz Atma İşlemleri ekleyin

Tüm Özel Tarama İş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 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 öğesine işlem ekleyin

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

Derleme onCustomAction sonucu

  • MediaId'yi Bundle extras öğesinden ayrıştır:
    @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
    • mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
      adlı kullanıcıya mesaj
    • Öğ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ç
      //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 varsa result.sendError(resultBundle). numaralı telefonu arayın
  • İlerleme durumu güncellenirse result.sendProgressUpdate(resultBundle) numaralı telefonu arayın.
  • result.sendResult(resultBundle) numaralı telefonu arayarak işlemi tamamlayın.

İşlem Durumunu Güncelle

result.sendProgressUpdate(resultBundle) yöntemini EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM anahtarıyla kullanarak MediaItem özelliğini, işlemin yeni durumunu yansıtacak şekilde güncelleyebilirsiniz. Bu sayede kullanıcıya işlemin ilerlemesi ve sonucu hakkında gerçek zamanlı geri bildirim sunabilirsiniz.

Örnek: İndirme İşlemi

Aşağıda, üç durumda bir indirme işlemi uygulamak için bu özelliği nasıl kullanabileceğinize ilişkin bir örnek verilmiştir:

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

Bu yaklaşım, kullanıcıya indirme işlemi ve mevcut durumu hakkında net geri bildirim sağlamanıza olanak tanır. %25, %50, %75 indirme durumlarını göstermek için simgelerle daha fazla ayrıntı ekleyebilirsiniz.

Örnek: Favori İşlem

Başka bir örnek de iki durumu olan favori bir işlemdir:

  1. Favori: Bu işlem, kullanıcının favoriler listesinde olmayan öğeler için görüntülenir. Kullanıcı bu işlemi seçtiğinde, işlemi "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 öğesini ç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, işlemi "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 numarasını çağırabilirsiniz.

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

Bu örnekler, Özel Göz Atma İşlemlerinin 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 bunları nasıl kullanabileceğinizi göstermektedir.

Bu özelliğin tam bir örneği için TestMediaApp projesine bakabilirsiniz.

Oynatma kontrolünü etkinleştir

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

Medya oturumu kaydetme

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

Aşağıdaki kod snippet'i, bir medya oturumunun nasıl oluşturulacağını ve kaydedileceğini gösterir:

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 uygulanmasını sağlayarak oluşturursunuz. Bir sonraki bölümde, bu nesnenin nasıl uygulanacağı açıklanmaktadır.

Oynatma komutlarını uygulama

Bir kullanıcı, uygulamanızdaki bir medya öğesinin oynatılmasını istediğinde Android Automotive OS ve Android Auto, uygulamanızın medya tarayıcısı hizmetinden edindikleri, uygulamanızın MediaSessionCompat nesnesine ait MediaSessionCompat.Callback sınıfını kullanır. Bir kullanıcı, oynatmayı duraklatmak veya sonraki parçaya atlamak gibi içerik oynatmayı kontrol etmek istediğinde, Android Auto ve Android Automotive OS, geri çağırma nesnesinin yöntemlerinden birini çağırır.

İçerik oynatma işlemini işlemek için uygulamanızın, soyut MediaSessionCompat.Callback sınıfını genişletmesi ve uygulamanızın desteklediği yöntemleri uygulaması gerekir.

Uygulamanızın sunduğu içerik türü için anlamlı 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, başlatıldıktan hemen sonra da bu yöntemi ç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ıdır. Oynatma onPause() ile duraklatıldıysa uygulamanız oynatmaya devam eder.

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

onPlayFromMediaId()
Kullanıcı, belirli bir öğeyi oynatmayı seçtiğinde çağrılır. Yöntem, medya tarayıcı hizmetinizin içerik hiyerarşinizdeki medya öğesine atadığı ID'yi geçirir.
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. İşleviniz uygulamanız tarafından desteklenmeyen bir yöntemi uygulamanıza gerek yoktur. Örneğin, uygulamanız spor yayını gibi canlı bir yayın yayınlıyorsa onSkipToNext() yöntemini uygulamanız gerekmez. Bunun yerine, varsayılan onSkipToNext() 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, içeriği kullanıcının telefon hoparlörlerinden veya kulaklığından çaldığı gibi çalabilir. Android Auto ve Android Automotive OS, ses içeriğini aracın hoparlörlerinden çalmak için otomatik olarak araba sistemine gönderir.

Ses içeriği çalma hakkında daha fazla bilgi için bkz. MediaPlayer'a genel bakış, Ses uygulamasına genel bakış ve ExoPlayer genel bakış.

Standart oynatma işlemlerini ayarlama

Android Auto ve Android Automotive OS, oynatma kontrollerini PlaybackStateCompat nesnesinde etkinleştirilen işlemlere dayalı olarak gösterir.

Varsayılan olarak, uygulamanız aşağıdaki işlemleri desteklemelidir:

Uygulamanız, içeriğiyle alakalı olmaları durumunda aşağıdaki işlemleri de destekleyebilir:

Buna ek olarak, kullanıcı için gösterilebilecek bir oynatma sırası da 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ğırmayı onSkipToQueueItem() tanımlayın.

Ayrıca, şu anda neyin çaldığını belirten Şimdi oynatılıyor simgesi için destek ekleyin. Bunu yapmak için setActiveQueueItemId() yöntemini çağırın ve sıradaki oynatılan öğenin kimliğini iletin. Bir sıra değişikliği olduğunda setActiveQueueItemId() uygulamasını güncellemeniz gerekir.

Android Auto ve Android Automotive OS, etkinleştirilen her işlem için ve oynatma sırası için düğmeler görüntüler. Düğmeler tıklandığında sistem, ilgili geri çağırmayı MediaSessionCompat.Callback üzerinden başlatır.

Kullanılmayan alan ayırın

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 bu alanları, uygulamanız ilgili işlevi desteklemediğinde Android Auto ve Android Automotive OS'in alanı boş bırakması için ayırabilirsiniz. Bunu yapmak için setExtras() yöntemini, ayrılmış işlevlere karşılık gelen sabit değerler içeren bir ekstra paketle çağırın. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT ACTION_SKIP_TO_NEXT değerine, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV ACTION_SKIP_TO_PREVIOUS değerine karşılık gelir. Bu sabit değerleri pakette anahtar olarak ve değerleri için true boole'sini kullanın.

İlk Oynatma Durumunu ayarla

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ı hizmetinize bağlandığında uygulamanız otomatik olarak müzik çalmaya başlamamalıdır. Bunun yerine, arabanın durumuna veya kullanıcı işlemlerine göre oynatmayı devam ettirmek veya başlatmak için Android Auto ve Android Automotive OS'e güvenin.

Bunu gerçekleştirmek 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. Sürücüler arasında sorunsuz bir deneyim sunmak için kullanıcının önceki oturum durumunu takip edin. Böylece medya uygulaması bir devam ettirme isteği aldığında kullanıcı, kaldığı yerden (ör. en son oynatılan medya öğesi, PlaybackStateCompat ve sıra) otomatik olarak devam edebilir.

Özel oynatma işlemleri ekle

Medya uygulamanızın desteklediği diğer işlemleri görüntülemek için özel oynatma işlemleri ekleyebilirsiniz. Alan uygunsa (ve ayrılmamışsa) Android, özel işlemleri aktarım denetimlerine ekler. Aksi takdirde, özel işlemler taşma menüsünde görüntülenir. Özel işlemler, PlaybackStateCompat öğesine eklendikleri sırayla görüntülenir.

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

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

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

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ında yer alan 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şlat" 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 bir simge kaynağı gerektirir. Arabalardaki uygulamalar pek çok farklı ekran boyutunda ve yoğunlukta çalışabilir. Bu nedenle, sağladığınız simgeler vektör çekilebilir öğeleri olmalıdır. Vektör çekilebilir özelliği, öğeleri ayrıntıyı kaybetmeden ö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 durum bilgiliyse (örneğin, bir oynatma ayarını açıp kapatıyorsa) farklı durumlar için farklı simgeler sağlar. Böylece kullanıcılar işlemi seçerken bir değişiklik görebilir.

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

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 sahibi olmayan özel işlem simgesi örnekleri.

Ses biçimini belirtin

Oynatılan medyanın özel bir ses biçimi kullandığını belirtmek için bu özelliği destekleyen arabalarda oluşturulacak simgeleri belirtebilirsiniz. Şu anda oynatılan medya öğesinin (MediaSession.setMetadata() bölümüne iletilen) ekstralar paketindeki KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI ve KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI özelliklerini ayarlayabilirsiniz. Farklı düzenleri barındıracak şekilde bu ekstraların ikisini de ayarladığınızdan emin olun.

Ayrıca, araç OEM'lerine sesin etkileyici bir görüntü oluşturduğunu ve sürükleyici içeriği etkileyebilecek ses efektleri uygulayıp uygulamayacağına karar verirken çok dikkatli olmaları gerektiğini belirtmek için KEY_IMMERSIVE_AUDIO ekstrasını ayarlayabilirsiniz.

Oynatılan medya öğesini, alt başlığı, açıklaması veya her ikisi de diğer medya öğelerine bağlantı olacak şekilde yapılandırabilirsiniz. Bu, kullanıcının ilgili öğelere (ör. aynı sanatçının diğer şarkılarına, podcast'in diğer bölümlerine vb.) atlamasını sağlar. Araba 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 belgelerine bakın.

Sesli işlemleri destekleyin

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

Android Auto veya Android Automotive OS bir sesli işlem algılayıp yorumladığında, bu sesli işlem onPlayFromSearch() üzerinden uygulamaya iletilir. Bu geri çağırmayı aldığında uygulama, query dizesiyle eşleşen içeriği bulur ve oynatmayı başlatır.

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

Kullanıcı arama terimleri belirtmezse Android Auto veya Android Automotive OS tarafından gönderilebilen boş bir query dizesini hesaba katın. Örneğin, kullanıcı "Müzik çal" diyorsa. Bu durumda, uygulamanız yakın zamanda çalınan veya yeni önerilen bir parçayı başlatmayı seçebilir.

Arama hızlıca işlenemiyorsa onPlayFromSearch() içinde engelleme yapmayın. Bunun yerine, oynatma durumunu STATE_CONNECTING olarak ayarlayın ve aramayı eşzamansız bir iş parçacığında gerçekleştirin.

Oynatma başladıktan sonra medya oturumu sırasını ilgili içerikle doldurmayı düşünün. Örneğin, kullanıcı bir albümün çalınmasını isterse uygulamanız bu 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, "oynat" sorgularına ek olarak "müziği duraklat" ve "sonraki şarkı" gibi çalma işlemlerini kontrol etmek için sesli sorguları tanır ve bu komutları onPause() ve onSkipToNext() gibi uygun medya oturumu geri aramalarıyla eşleştirir.

Uygulamanızda sesle etkinleştirilen oynatma işlemlerini nasıl uygulayacağınızla ilgili ayrıntılı bir örnek için Google Asistan ve medya uygulamaları bölümüne bakın.

Dikkat dağıtıcı önlemler uygulayın

Android Auto kullanılırken kullanıcının telefonu arabası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ı bastırın

Android Auto medya uygulamaları, kullanıcı örneğin bir oynat düğmesine basarak oynatmaya başlamadığı sürece sesleri araç hoparlörlerinden çalmaya başlamamalıdır. Medya uygulamanızdan kullanıcı tarafından planlanmış bir alarm bile araç hoparlörlerinden müzik çalmaya başlamamalıdır.

Uygulamanız bu gereksinimi karşılamak için herhangi bir ses çalmadan önce sinyal olarak CarConnection kullanabilir. Uygulamanız, araba bağlantı türü için LiveData gözlemleyerek ve CONNECTION_TYPE_PROJECTION ile aynı olup olmadığını kontrol ederek telefonun bir araba ekranına projeksiyon yapıp yapmadığını kontrol edebilir.

Kullanıcının telefonu projeksiyon yapıyorsa alarmları destekleyen medya uygulamalarının aşağıdakilerden birini yapması gerekir:

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

Medya reklamlarını yönetme

Varsayılan olarak bir ses çalma oturumu sırasında medya meta verileri değiştiğinde Android Auto bir bildirim gösterir. Bir medya uygulaması müzik çalma modundan reklam yayınlamaya geçiş yaptığında, kullanıcıya bildirim göstermesi dikkat dağıtıcı olur. 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ı ele alma

Uygulama bir hatayla karşılaştığında, 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 listesi için PlaybackStateCompat sayfasına göz atın. Hata mesajları kullanıcıya yönelik olmalı ve kullanıcının mevcut yerel ayarıyla yerelleştirilmelidir. Ardından Android Auto ve Android Automotive OS, kullanıcıya hata mesajını görüntüleyebilir.

Örneğin, içerik kullanıcının geçerli bölgesinde yoksa 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 bölümüne bakın.

Bir Android Auto kullanıcısının bir hatayı çözmek için telefon uygulamanızı açması gerekiyorsa mesajınızda bu kullanıcıya bu bilgiyi sağlayın. Örneğin, hata mesajınızda "Lütfen oturum açın" yerine "[Uygulamanızın adı] uygulamasında oturum açın" ifadesini görebilirsiniz.

Diğer kaynaklar