Как создать мультимедийное приложение на основе шаблона

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

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

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

Бета-версия функции "Парковка"
Теперь в мультимедийные приложения на основе шаблонов можно добавлять функции, которые будут доступны во время движения на Android Automotive OS. Это позволит вашему приложению добавлять адаптивные нативные действия, когда автомобиль припаркован, и плавно переходить к воспроизведению медиаконтента по шаблону, когда автомобиль находится в движении.

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

Помимо шагов, описанных в разделе Использование библиотеки Android for Cars App Library, для мультимедийных приложений на основе шаблонов требуется следующее:

Как объявить поддержку категории в манифесте

В фильтре интентов элемента 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>

Затем добавьте объявление template в 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

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

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

Если вы распространяете один APK-файл для версий приложения, предназначенных для библиотеки автомобильных приложений и MediaBrowserService или MediaLibraryService, важно установить для 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>

Распространение в Google Play

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

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

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

При создании отдельного APK-файла для версии приложения, использующей библиотеку приложений Car App Library, необходимо задать для 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.

Распространение в Google Play

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

Поддержка голосовых команд

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

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

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

В музыкальном проигрывателе показан трек Sounds of Spring исполнителя Summer Fielding с квадратным портретом женщины, играющей на гитаре.

Рисунок 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, в которой вместо стандартного MediaSessionCompat.Token применяется PlatformToken, вам нужно будет реализовать пользовательский 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

В Car App Library API версии 1.9 появились настраиваемые компоненты для уникальных возможностей просмотра, такие как чипы, индикаторы выполнения, сжатые элементы, интерактивный и расширенный заголовок, разделы Spotlight и баннеры.

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

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

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

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

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

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

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

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

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

Добавьте действие androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK в фильтр интентов компонента библиотеки приложений Car App Library (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(), чтобы проанализировать входящее намерение. Если действие входящего намерения совпадает с 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(). Передайте это действие в намерение создания 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();
    }
}