Создайте шаблонное медиа-приложение

Приложения для работы с медиафайлами на основе шаблонов находятся в стадии бета-тестирования.
В настоящее время любой желающий может публиковать шаблонные медиаприложения во внутренние и закрытые тестовые среды в Google Play Store. Публикация в открытые и производственные среды будет разрешена позже.

Медиаприложения, использующие шаблоны из библиотеки автомобильных приложений, могут настраивать просмотр и воспроизведение медиафайлов, обеспечивая при этом оптимизацию для автомобильных экранов и минимизацию отвлекающих факторов во время вождения.

В этом руководстве предполагается, что у вас уже есть медиаприложение для воспроизведения аудио на телефоне, и что ваше медиаприложение соответствует архитектуре медиаприложений Android . Библиотека автомобильных приложений позволяет заменить встроенный интерфейс шаблонами вместо тех, которые созданы с использованием структуры данных MediaBrowser из библиотеки Build media apps for cars . Вам по-прежнему необходимо предоставить MediaSession для управления воспроизведением и объект MediaBrowserService или MediaLibraryService , который используется для рекомендаций и других интеллектуальных функций.

Настройте манифест вашего приложения.

В дополнение к шагам, описанным в разделе «Использование библиотеки приложений Android для автомобилей» , от шаблонных медиаприложений требуются следующие действия:

Укажите поддержку категорий в вашем манифесте.

Вашему приложению необходимо объявить категорию автомобильных приложений androidx.car.app.category.MEDIA в фильтре намерений своего CarAppService .

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

Установите минимальный уровень API для автомобильных приложений.

Медиаприложения, использующие MediaPlaybackTemplate , поддерживаются только в CAL API 8 и выше. Убедитесь, что минимальный Car App API level установлен на 8.

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

Укажите значок авторства

Обязательно добавьте значок авторства для медиаприложений, созданных с использованием библиотеки автомобильных приложений.

Объявить поддержку Android Auto

Убедитесь, что в манифест вашего приложения включено следующее:

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

Затем добавьте объявление шаблона в файл automotive_app_desc.xml в ваших XML-ресурсах. Оно должно выглядеть следующим образом:

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

Объявить о поддержке Android Automotive OS

Существует два способа распространения медиаприложения с поддержкой Car App Library на Android Automotive OS: в виде одного APK-файла или в виде двух отдельных APK-файлов. Если вы распространяете один APK-файл, он будет поддерживать автомобили, поддерживающие Android Automotive OS с хостом Car App Library, и в противном случае будет использовать приложение MediaBrowserService или MediaLibraryService , даже для более старых версий Android (Android 10 - Android 13). Если вы решите распространять два отдельных APK-файла, вы сможете проще обновлять новые дополнения к версии Car App Library, не опасаясь повлиять на версию MediaBrowserService или MediaLibraryService вашего приложения.

Распространение одного APK-файла

При распространении единого APK-файла для версий вашего приложения, включающих Car App Library и MediaBrowserService или MediaLibraryService , крайне важно установить " " to 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>

Распределение игр

Ваш APK-файл с Car App Library и MediaBrowserService или MediaLibraryService должен быть включен с более высоким кодом версии и minSdk, ориентированным на Android 14 (34).

Распространение с помощью двух APK-файлов

Для распространения двух отдельных APK-файлов, одного с использованием библиотеки Car App Library, а другого с использованием MediaBrowserService или MediaLibraryService , выполните следующие шаги, чтобы обеспечить корректное использование соответствующих возможностей автомобиля.

При создании отдельного APK-файла для версии вашего приложения из библиотеки автомобильных приложений необходимо установить параметр android.software.car.templates_host.media в значение android:required=true . Это гарантирует, что приложение будет распространяться только на сборках 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 , выполните следующие шаги, чтобы включить Android Automotive OS для запускаемого приложения Car App Library.

Распределение игр

APK-файл, использующий библиотеку автомобильных приложений, следует распространять в рамках специального направления Automotive OS.

Поддержка голосовых действий

Включите поддержку голосового управления в вашем приложении, чтобы пользователи могли выполнять распространенные действия без помощи рук. Более подробные инструкции по реализации см. в разделе «Поддержка голосовых действий для мультимедиа» . В шаблонном приложении для воспроизведения мультимедиа, если вы получаете голосовую команду, вам не нужно обновлять MediaBrowserService или MediaLibraryService результатами поиска. Вместо этого рассмотрите возможность добавления действия в ваш шаблон воспроизведения мультимедиа, чтобы пользователь мог найти больше контента на основе этого запроса воспроизведения или поиска. Поддержка голосовых команд необходима для соответствия рекомендациям по качеству VC-1 .

Создайте свой шаблон воспроизведения

Шаблон MediaPlaybackTemplate отображает информацию о воспроизведении мультимедиа в медиатеке вашего автомобильного приложения. Этот шаблон позволяет установить заголовок с названием и настраиваемыми действиями, в то время как информация о мультимедиа и элементы управления воспроизведением заполняются хостом на основе состояния MediaSession вашего приложения.

На музыкальном проигрывателе воспроизводится альбом «Звуки весны» Саммер Филдинг, а на экране — квадратный портрет женщины, играющей на гитаре.

Рисунок 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 зарегистрируйте токен MediaSession с помощью MediaPlaybackManager в вашем CarAppService . В противном случае при отправке 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 , вам потребуется реализовать пользовательскую SessionCommand в вашем MediaLibrarySession.Callback , которая возвращает базовый токен платформы сессии: session.platformToken . В вашем CarAppService отправьте эту пользовательскую команду в сессию. После получения токена платформы преобразуйте его с помощью MediaSessionCompat.Token.fromToken(platformToken) и передайте этот токен совместимости в библиотеку Car App Library в .registerMediaPlaybackToken() .

Организуйте медиафайлы с помощью шаблонов.

Для организации медиафайлов для просмотра, таких как песни или альбомы, мы рекомендуем использовать SectionedItemTemplate , который позволяет использовать GridSection и RowSection вместе для создания макетов, сочетающих списки изображений и текстовых элементов.

В интерфейсе музыкального приложения отображаются недавно воспроизведенные песни и альбомы, включая два вертикальных ряда и три горизонтальных портрета обложек альбомов.

Рисунок 2: Шаблон SectionedItemTemplate , содержащий RowSection , за которым следует GridSection

Использование SectionedItemTemplate внутри TabTemplate

Один из удобных способов классификации медиафайлов в вашем приложении — использование SectionedItemTemplate внутри TabTemplate .

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

Компоненты и функции библиотеки автомобильных приложений версии 1.9

В версии 1.9 API библиотеки автомобильных приложений представлены настраиваемые компоненты для уникальных возможностей просмотра, такие как индикаторы , индикаторы выполнения , сокращенные элементы , интерактивный и развернутый заголовок , разделы «В центре внимания» и баннеры .

В интерфейсе музыкального приложения отображаются недавно воспроизведенные песни и альбомы, включая два вертикальных ряда и три горизонтальных портрета обложек альбомов.

Рисунок 3: Шаблон SectionedItemTemplate , содержащий Chips , Condensed Items , Interactive Header , Grid Items и Minimized Control Panel

В интерфейсе музыкального приложения отображаются недавно воспроизведенные песни и альбомы, включая два вертикальных ряда и три горизонтальных портрета обложек альбомов.

Рисунок 4: Два экрана просмотра медиафайлов с Expanded Header , Spotlight Sections и Progress Bars

Более подробную информацию о том, как разработать пользовательский интерфейс вашего медиаприложения с помощью этих шаблонов, см. в разделе «Медиаприложения» .

При просмотре медиафайлов важно, чтобы пользователь мог быстро перейти к шаблону MediaPlaybackTemplate с минимальным отвлечением внимания. Для соответствия требованиям качества MFT-1 ваше приложение должно иметь возможность доступа к MediaPlaybackTemplate со всех экранов просмотра медиафайлов.

Если вы используете SectionedItemTemplate этого можно добиться, добавив кнопку действия, которая перенаправляет на экран воспроизведения мультимедиа. Используйте стандартное действие Car App Library Action.MEDIA_PLAYBACK . Приложение для воспроизведения мультимедиа отобразит это действие в виде свернутой панели управления , что необходимо для соответствия требованиям качества MFT-1 , если вы используете Car App Library API 1.9 или выше. Для других шаблонов это можно сделать с помощью действия в заголовке.

Обработка намерений воспроизведения системных мультимедиа

Необходимо перенаправлять пользователя на MediaPlaybackTemplate при запуске приложения с системного носителя мультимедиа, например, карты памяти. Мы требуем, чтобы медиаприложения обрабатывали это Intent Action для обеспечения бесперебойной работы для пользователей.

Добавьте действие androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK в intent-filter компонента библиотеки автомобильных приложений (либо CarAppActivity , либо вашей Activity трамплин).

Убедитесь, что ваша активность использует launchMode со значением singleTask или singleTop , чтобы вызывался 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() для анализа входящего запроса Intent. Если действие входящего запроса совпадает с 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));
    }
}

Если вы используете активность типа «батут», проверьте действие Intent внутри onCreate() . Передайте это действие в Intent создания 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();
    }
}