建立內容階層

Android Auto 和 Android Automotive OS (AAOS) 會呼叫應用程式的媒體瀏覽器服務,查看有哪些內容可用。如要支援這項功能,請在媒體瀏覽器服務中實作這兩種方法。

實作 onGetRoot

服務的 onGetRoot 方法會傳回內容階層根節點的相關資訊。Android Auto 和 AAOS 會使用這個根節點,透過 onLoadChildren 方法要求其他內容。以下程式碼片段顯示 onGetRoot 方法的實作方式:

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

如需此方法的詳細範例,請參閱 GitHub 上通用 Android 音樂播放器範例應用程式中的 onGetRoot

新增套件驗證

呼叫服務的 onGetRoot 方法時,呼叫套件會將身分識別資訊傳遞至您的服務。您的服務可利用此資訊判斷該套件是否能存取您的內容。

舉例來說,您可以只讓列在核准清單中的套件存取應用程式內容:

  • clientPackageName 與許可清單進行比較。
  • 驗證用於簽署套件 APK 的憑證。

如果套件沒有通過驗證,則傳回 null,拒絕該套件存取您的內容。

為了讓系統應用程式 (例如 Android Auto 和 AAOS) 存取您的內容,當這些系統應用程式呼叫 onGetRoot 方法時,您的服務必須傳回非空值的 BrowserRoot

AAOS 系統應用程式的簽章會因車輛廠牌和車型而異。請務必允許所有系統應用程式建立連線,以支援 AAOS。

下列程式碼片段說明服務如何驗證呼叫套件是系統應用程式:

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
}

以上程式碼片段摘錄自 GitHub 上 Android 通用音樂播放器範例應用程式中的 PackageValidator 類別。請參閱該類別的詳細資料範例,瞭解如何針對服務的 onGetRoot 方法實作套件驗證。

除了允許系統應用程式之外,您也必須允許 Google 助理連結至您的 MediaBrowserService。Google 助理為手機 (包括 Android Auto 和 Android Automotive OS) 分別提供不同的套件名稱

實作 onLoadChildren

收到根節點物件之後,Android Auto 和 AAOS 會透過呼叫根節點物件的 onLoadChildren,建立頂層選單,取得其後代。用戶端應用程式會使用後代節點物件呼叫相同的方法,來建構子選單。

內容階層中的每個節點都以 MediaBrowserCompat.MediaItem 物件表示。每個媒體項目都是透過一組專屬 ID 字串識別。用戶端應用程式會將這些 ID 字串視為不透明權杖。

當用戶端應用程式想要瀏覽子選單或播放媒體項目時,系統會傳遞該權杖。您的應用程式負責將權杖與適當的媒體項目建立關聯。

這個程式碼片段顯示 onLoadChildren 的實作方式

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&lt;MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check if 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 descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result&lt;List&lt;MediaBrowserCompat.MediaItem>> result) {

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

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

    // Check if 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 descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

如要查看這個方法的範例,請參閱 GitHub 上通用 Android 音樂播放器範例應用程式中的 onLoadChildren

建構根選單

Android Auto 和 Android Automotive OS 對根選單結構設有特定限制。這些限制會透過根提示傳遞至 MediaBrowserService,而您可以透過傳入 onGetRoot() 的 Bundle 引數讀取這些提示。按照這些提示操作,系統便能將根層級內容顯示為導覽分頁。如未按照這些提示操作,系統可能會捨棄某些根層級內容或使其不易偵測。

以導覽分頁的形式呈現根內容

圖 1. 以導覽分頁的形式呈現根內容。

套用這些提示後,系統會將根層級內容顯示為導覽分頁。如未套用這些提示,系統可能會捨棄某些根層級內容或降低其可偵測性。系統會傳送以下提示:

Kotlin

import androidx.media.utils.MediaConstants

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

您可以根據這些提示的值,選擇建立內容階層結構的邏輯分支,尤其是階層結構在 Android Auto 和 AAOS 以外的 MediaBrowser 整合作業中有所不同的情況。

舉例來說,假如您通常會顯示某個根層級的可播放項目,則可能會根據支援的標記提示值,將它嵌入某個根層級可瀏覽項目底下的巢狀結構。

除了根提示外,請按照下列指引以最佳方式呈現分頁:

  • 每個分頁項目的單色 (建議白色) 圖示

  • 為每個分頁項目提供簡短但有意義的標籤 (簡短的標籤可降低標籤遭截斷的機率)