建構媒體應用程式範本

範本媒體應用程式目前為 Beta 版
目前,任何人都可以將範本媒體應用程式發布至 Google Play 商店的內部測試和封閉測試群組。日後將允許發布至公開測試群組和正式版群組。

使用 Car App Library 範本的媒體應用程式,可以自訂媒體瀏覽和播放體驗,同時確保體驗針對車輛螢幕進行最佳化,並盡可能減少駕駛時的干擾。

本指南假設您已有媒體應用程式,可在手機上播放音訊,且符合 Android 媒體應用程式架構。您可以使用 Car App Library,以範本取代應用程式內體驗,而非使用「建構車用媒體應用程式」MediaBrowser 資料結構建構的體驗。您仍須為播放控制項提供 MediaSession,並提供 MediaBrowserService 或 MediaLibraryService,用於推薦和其他智慧體驗。

Park-to-drive 體驗現已推出 Beta 版
範本媒體應用程式現在可在 Android Automotive OS 上,新增停車到行車體驗。這樣一來,應用程式就能在停車時加入自動調整的原生活動,並在車輛行駛時順暢切換至範本式媒體播放體驗。

設定應用程式的資訊清單

除了「使用車輛專用 Android 應用程式庫」一文所述步驟外,範本媒體應用程式還須符合下列規定:

在資訊清單中宣告類別支援

您的應用程式必須在 CarAppService 的意圖篩選器中宣告 androidx.car.app.category.MEDIA 車用應用程式類別:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.MEDIA"/>
      </intent-filter>
    </service>
    ...
<application>

如要存取 MediaPlaybackTemplate,應用程式也必須在資訊清單檔案中宣告 androidx.car.app.MEDIA_TEMPLATES 權限:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.MEDIA_TEMPLATES"/>
  ...
</manifest>

設定最低 Car App API 級別

使用 MediaPlaybackTemplate 的媒體應用程式僅支援 CAL API 8 以上級別,請務必將最低 Car App API level 設為 8。

<application ...>
  ...
  <meta-data
    android:name="androidx.car.app.minCarApiLevel"
    android:value="8"/>
  ...
</application>

提供出處圖示

請務必為使用 Car App Library 建構的媒體應用程式新增出處圖示。

宣告 Android Auto 支援

請確認應用程式資訊清單中包含下列內容:

<application>
  ...
  <meta-data android:name="com.google.android.gms.car.application"
      android:resource="@xml/automotive_app_desc"/>
  ...
</application>

接著,在 XML 資源中將 template 宣告新增至 automotive_app_desc.xml。如下所示:

<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
 <uses name="media"/>
 <uses name="template"/>
</automotiveApp>

宣告 Android Automotive OS 支援

在 Android Automotive OS 上,您可以透過兩種方式發布已啟用 Car App Library 的媒體應用程式:單一 APK 或兩個獨立 APK。如果您發布單一 APK,該 APK 將支援搭載 Android Automotive OS 且具備 Car App Library 主機的車輛,否則會改用 MediaBrowserService 或 MediaLibraryService 應用程式,即使是舊版 Android (Android 10 - Android 13) 也是如此。如果您選擇發布兩個獨立的 APK,則可更新 Car App Library 版本的新增內容,而不必擔心會影響應用程式的 MediaBrowserService 或 MediaLibraryService 版本。

發行單一 APK

為 Car App Library 和 MediaBrowserService 或 MediaLibraryService 版本的應用程式發布單一 APK 時,請務必將 android.software.car.templates_host.media 設為 android:required="false"。

<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>

接著,請按照 AAOS 的車輛應用程式庫指南,導入可啟動的 CarAppActivity (或跳板活動)。您必須在資訊清單中將活動設為 android:enabled="false"。接著,在 MediaBrowserService 宣告中加入中繼資料標記,指出 CarAppActivity 元件為替代元件。請參閱以下資訊清單範例:

<service android:name=".media.MyMediaService"
    android:exported="true"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
    </intent-filter>

    <!-- Link to Car App Library Activity -->
    <meta-data
        android:name="androidx.car.app.media.CalMediaActivityComponent"
        android:value="com.example.mediaapp.LaunchableTrampoline"/>
</service>

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false"> <!-- Set to false -->

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

Play 發行

含有 Car App Library 和 MediaBrowserService 或 MediaLibraryService 的 APK 應啟用,並指定較高的版本代碼和 minSdk,以 Android 14 (34) 為目標。

使用兩個 APK 發行

如要發布兩個獨立的 APK,一個使用 Car App Library,另一個使用 MediaBrowserService 或 MediaLibraryService,請按照下列步驟操作,確保正確指定車輛功能。

為 Car App Library 版本的應用程式建立獨立 APK 時,您必須將 android.software.car.templates_host.media 設為 android:required=true。這樣一來,應用程式只會發布到支援 Car App Library 主機的 Android Automotive OS 版本。

<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>

除了使用 android.software.car.templates_host.media 並將其設為 android:required=true 之外,請按照下列步驟,為可啟動的 Car App Library 活動啟用 Android Automotive OS。

Play 發行

使用 Car App Library 的 APK 應在 Automotive OS 專屬測試群組中發布。

支援語音指令

為應用程式啟用語音功能,讓使用者不必動手就能完成常見操作。如需更詳細的實作操作說明,請參閱「支援媒體語音指令」。如果使用範本媒體應用程式,當您收到語音指令時,不需要使用搜尋結果更新 MediaBrowserService 或 MediaLibraryService。建議您在媒體播放範本中加入動作,讓使用者根據播放或搜尋查詢,尋找更多內容。您必須支援語音指令,才能符合 VC-1 品質指南。

建立播放範本

MediaPlaybackTemplate 會在車輛應用程式庫媒體應用程式中顯示媒體播放資訊。這個範本可讓您設定標題和可自訂動作的標頭,而媒體資訊和播放控制項則由主機根據應用程式 MediaSession 的狀態填入。

音樂播放器顯示 Summer Fielding 的《Sounds of Spring》,並附上女性彈吉他的正方形肖像。

圖 1: MediaPlaybackTemplate 頂端有標頭動作,可開啟佇列。

以下程式碼範例說明如何建構範例播放範本,設定標頭動作,讓使用者前往含有歌曲佇列的畫面。

val playbackTemplate = MediaPlaybackTemplate.Builder()
      .setHeader(
        Header.Builder()
          .setStartHeaderAction(Action.BACK)
          .addEndHeaderAction(
                Action.Builder()
                  .setTitle(model.context.getString(R.string.queue_button_title))
                  .setIcon(
                    CarIcon.Builder(
                        IconCompat.createWithResource(
                          model.context,
                          R.drawable.gs_queue_music_vd_theme_24,
                        ))
                      .build())
                  .setOnClickListener(showQueueScreen())
                  .build())
          .setTitle(model.context.getString(R.string.media_playback_view_title))
          .build())
      .build()

使用 MediaPlaybackTemplate 時,請在 CarAppService 中使用 MediaPlaybackManager 註冊 MediaSession 權杖。否則,當 MediaPlaybackTemplate 傳送至主機時,系統會顯示錯誤。

import androidx.car.app.media.MediaPlaybackManager
…

override fun onCreateSession(sessionInfo: SessionInfo): Session {
    return object : Session() {
        …

        init {
          lifecycle.addObserver(
            LifecycleEventObserver { _, event ->
              if (event == ON_CREATE) {
                val token = ... // MediaSessionCompat.Token
                (carContext.getCarService(CarContext.MEDIA_PLAYBACK_SERVICE) as MediaPlaybackManager)
                  .registerMediaPlaybackToken(token)
              }
              ...
            }
          )
        }
    }
}

.registerMediaPlaybackToken 是向 Android Auto 公開媒體播放資訊和控制項的必要項目。這對於主機建立媒體專屬通知也很重要。

如果應用程式使用 Media3 程式庫,且使用 PlatformToken 而非標準 MediaSessionCompat.Token,您必須在 MediaLibrarySession.Callback 中實作自訂 SessionCommand,傳回工作階段的基礎平台權杖:session.platformToken。在 CarAppService 中,將這項自訂指令傳送至工作階段。收到平台權杖後,請使用 MediaSessionCompat.Token.fromToken(platformToken) 轉換,並將這個相容權杖傳遞至 .registerMediaPlaybackToken() 中的 Car App Library。

使用範本整理媒體

如要整理歌曲或專輯等媒體供使用者瀏覽,建議使用 SectionedItemTemplate,這樣就能同時使用 GridSection 和 RowSection,建立混合圖片和文字項目清單的版面配置。

音樂應用程式介面顯示最近播放的歌曲和專輯,包括兩列垂直和三張水平專輯封面。

圖 2:SectionedItemTemplate 包含 RowSection,後面接著 GridSection

在 TabTemplate 中使用 SectionedItemTemplate

如要將應用程式中的媒體分類,其中一個便利的方法是在 TabTemplate 中使用 SectionedItemTemplate。

val template =
      SectionedItemTemplate.Builder()...build();
val tabTemplate =
      TabTemplate.Builder(tabCallback)
          .setTabContents(TabContents.Builder(template).build)
          .setHeaderAction(Action.APP_ICON)
          …
          .build();

Car App Library 1.9 元件和功能

Car App Library API 1.9 版推出自訂元件,提供獨特的瀏覽功能,例如方塊、進度列、精簡項目、互動式和展開式標頭、精選區段和橫幅。

音樂應用程式介面顯示最近播放的歌曲和專輯,包括兩列垂直和三張水平專輯封面。

圖 3:包含 Chips、Condensed Items、Interactive Header、Grid Items 和 Minimized Control Panel 的 SectionedItemTemplate

音樂應用程式介面顯示最近播放的歌曲和專輯,包括兩列垂直和三張水平專輯封面。

圖 4:兩個媒體瀏覽畫面,分別顯示 Expanded Header、Spotlight Sections 和 Progress Bars

如要進一步瞭解如何運用這些範本設計媒體應用程式的使用者介面,請參閱「媒體應用程式」。

瀏覽媒體時,使用者必須能快速前往MediaPlaybackTemplate,且盡可能不受干擾。為符合MFT-1品質規定,應用程式必須提供從所有媒體瀏覽畫面存取MediaPlaybackTemplate的方法。

如果您使用 SectionedItemTemplate,可以新增動作按鈕,前往媒體播放畫面,請使用標準 Car App Library Action.MEDIA_PLAYBACK 動作。媒體應用程式會將這項動作顯示為最小化控制面板,如果您使用 Car 應用程式庫 API 1.9 以上版本,則必須符合 MFT-1 品質規定。如果是其他範本,則可透過標頭動作達到這個目的。

處理系統媒體播放意圖

從系統播放媒體介面 (例如媒體資訊卡) 啟動應用程式時,必須將使用者導向 MediaPlaybackTemplate。媒體應用程式必須處理這個 Intent Action,才能為使用者提供流暢的體驗。

將 androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK 動作新增至 Car App Library 元件 (CarAppActivity 或跳板 Activity) 的意圖篩選器。

請確認活動使用 singleTask 或 singleTop 的 launchMode,以便叫用 onNewIntent()。

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false">

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

在 Session 類別中,覆寫 onNewIntent() 以剖析傳入的意圖。如果傳入的意圖動作與 SHOW_MEDIA_PLAYBACK 相符,請將使用者導向「聽聲辨曲」畫面。

@Override
public void onNewIntent(@NonNull Intent intent) {
    super.onNewIntent(intent);
    if (SHOW_MEDIA_PLAYBACK.equals(intent.getAction())) {
        ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
        // Avoid redundant navigation if already on the playing screen
        if (screenManager.getTop() instanceof MyMediaPlayScreen) {
            return;
        }
        screenManager.push(MyMediaPlayScreen.createScreenFromPlaying(
                getCarContext(), mMediaSessionController));
    }
}

如果您使用跳床活動,請檢查 onCreate() 中的意圖動作。請先將這個動作傳遞至 CarAppActivity 建立意圖,再呼叫 finish()。

public class LaunchableTrampoline extends AppCompatActivity {
    private static final String SHOW_MEDIA_PLAYBACK = "androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent receivedIntent = getIntent();
        String action;

        if (SHOW_MEDIA_PLAYBACK.equals(receivedIntent.getAction())) {
            action = SHOW_MEDIA_PLAYBACK;
        } else {
            action = Intent.ACTION_MAIN;
        }

        Intent intent = new Intent(action);
        intent.setClassName(getPackageName(), "androidx.car.app.activity.CarAppActivity");
        startActivity(intent);
        finish();
    }
}