Tworzenie aplikacji multimedialnej na podstawie szablonu

Aplikacje do multimediów oparte na szablonach są w wersji beta
Obecnie każdy może publikować aplikacje do multimediów oparte na szablonach na ścieżkach testów wewnętrznych i zamkniętych w Sklepie Google Play. Publikowanie na ścieżkach otwartych i produkcyjnych będzie dozwolone w późniejszym terminie.

Aplikacje do multimediów korzystające z szablonów biblioteki aplikacji do samochodu mogą dostosowywać sposób przeglądania i odtwarzania multimediów, zapewniając jednocześnie optymalizację pod kątem ekranów samochodowych i minimalizując rozpraszanie uwagi podczas jazdy.

W tym przewodniku założyliśmy, że masz już aplikację do multimediów, która odtwarza dźwięk na telefonie i że jest ona zgodna z architekturą aplikacji do multimediów na Androida. Biblioteka aplikacji do samochodu umożliwia zastąpienie szablonami funkcji w aplikacji, które zostały utworzone za pomocą struktury danych Build media apps for cars MediaBrowser. Nadal musisz udostępniać MediaSession do sterowania odtwarzaniem oraz MediaBrowserService lub MediaLibraryService, które są używane do rekomendacji i innych inteligentnych funkcji.

Konfigurowanie pliku manifestu aplikacji

Oprócz czynności opisanych w artykule Korzystanie z biblioteki aplikacji Android do samochodu, aplikacje do multimediów oparte na szablonach muszą spełniać te wymagania:

Deklarowanie obsługi kategorii w pliku manifestu

Aplikacja musi zadeklarować androidx.car.app.category.MEDIA kategorię aplikacji do samochodu w filtrze intencji usługi 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>

Aby uzyskać dostęp do MediaPlaybackTemplate, aplikacja musi też zadeklarować uprawnienie androidx.car.app.MEDIA_TEMPLATES w pliku manifestu:

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

Ustawianie minimalnego poziomu interfejsu API aplikacji do samochodu

Aplikacje do multimediów korzystające z MediaPlaybackTemplate są obsługiwane tylko w CAL API 8 i nowszych. Upewnij się, że minimalny Car App API level jest ustawiony na 8.

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

Podawanie ikony źródła

Pamiętaj, aby dodać ikonę źródła w przypadku aplikacji do multimediów utworzonych za pomocą biblioteki aplikacji do samochodu.

Deklarowanie obsługi Androida Auto

Upewnij się, że w pliku manifestu aplikacji znajdują się te elementy:

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

Następnie dodaj deklarację template do pliku automotive_app_desc.xml w zasobach XML. Powinno to wyglądać tak:

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

Deklarowanie obsługi systemu operacyjnego Android Automotive

Aplikację do multimediów korzystającą z biblioteki aplikacji do samochodu możesz rozpowszechniać w systemie operacyjnym Android Automotive na 2 sposoby: jako pojedynczy plik APK lub jako 2 osobne pliki APK. Jeśli rozpowszechniasz pojedynczy plik APK, będzie on obsługiwał pojazdy z systemem operacyjnym Android Automotive z hostem biblioteki aplikacji do samochodu i w razie potrzeby przełączy się na aplikację MediaBrowserService lub MediaLibraryService, nawet w przypadku starszych wersji Androida (Android 10–13). Jeśli zdecydujesz się rozpowszechniać 2 osobne pliki APK, możesz łatwiej aktualizować nowe dodatki do wersji biblioteki aplikacji do samochodu bez obawy o wpływ na wersję MediaBrowserService lub MediaLibraryService w aplikacji.

Rozpowszechnianie pojedynczego pliku APK

Podczas rozpowszechniania pojedynczego pliku APK dla biblioteki aplikacji do samochodu oraz MediaBrowserService lub MediaLibraryService wersji aplikacji ważne jest, aby ustawić "" na android:required="false".

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

Następnie postępuj zgodnie z wytycznymi biblioteki aplikacji do samochodu dotyczącymi AAOS i wprowadź uruchamialną CarAppActivity (lub aktywność trampolinową). W pliku manifestu musisz ustawić aktywność na android:enabled="false". Następnie dodaj tag metadanych do deklaracji MediaBrowserService, wskazując komponent CarAppActivity jako zamiennik. Poniżej znajdziesz przykładowy plik manifestu:

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

Dystrybucja w Google Play

Plik APK z biblioteką aplikacji do samochodu oraz MediaBrowserService lub MediaLibraryService powinien mieć włączoną wyższą wersję kodu i minSdk kierowany na Androida 14 (34).

Rozpowszechnianie za pomocą 2 plików APK

Aby rozpowszechniać 2 osobne pliki APK, jeden z biblioteką aplikacji do samochodu, a drugi z MediaBrowserService lub MediaLibraryService, wykonaj te czynności, aby prawidłowo kierować reklamy na odpowiednie funkcje pojazdu.

Podczas tworzenia osobnego pliku APK dla wersji biblioteki aplikacji do samochodu musisz ustawić android.software.car.templates_host.media na android:required=true. Dzięki temu aplikacja będzie rozpowszechniana tylko w kompilacjach systemu operacyjnego Android Automotive z certyfikatem obsługi hosta biblioteki aplikacji do samochodu.

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

Oprócz użycia android.software.car.templates_host.media i ustawienia go na android:required=true wykonaj te czynności, aby włączyć system operacyjny Android Automotive w przypadku uruchamialnej aktywności biblioteki aplikacji do samochodu.

Dystrybucja w Google Play

Plik APK, który korzysta z biblioteki aplikacji do samochodu, powinien być rozpowszechniany na osobnej ścieżce Automotive OS.

Obsługa komend głosowych

Włącz obsługę komend głosowych w aplikacji, aby użytkownicy mogli wykonywać typowe czynności bez użycia rąk. Szczegółowe instrukcje implementacji znajdziesz w artykule Obsługa komend głosowych w przypadku multimediów. Jeśli w aplikacji do multimediów opartej na szablonach otrzymasz polecenie głosowe, nie musisz aktualizować MediaBrowserService ani MediaLibraryService o wyniki wyszukiwania. Zamiast tego rozważ dodanie działania w szablonie odtwarzania multimediów, które umożliwi użytkownikowi znajdowanie większej ilości treści na podstawie zapytania o odtwarzanie lub wyszukiwanie. Obsługa komend głosowych jest wymagana, aby spełnić VC-1 wytyczną dotyczącą jakości.

Tworzenie szablonu odtwarzania

The MediaPlaybackTemplate displays media playback information in your Car App Library media app. This template allows setting a header with a title and customizable actions while the media information and playback controls are populated by the host based off of the state of your app's MediaSession.

Odtwarzacz muzyki wyświetla utwór Sounds of Spring Summer Fielding z kwadratowym portretem kobiety grającej na gitarze.

Rysunek 1. MediaPlaybackTemplate z działaniem w nagłówku, które umożliwia otwarcie kolejki u góry.

Ten przykład kodu pokazuje, jak utworzyć przykładowy szablon odtwarzania, który ustawia działanie w nagłówku umożliwiające użytkownikowi przejście do ekranu z kolejką utworów.

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

Gdy używasz MediaPlaybackTemplate, zarejestruj a MediaSession token za pomocą MediaPlaybackManager w your CarAppService. Jeśli tego nie zrobisz, podczas wysyłania MediaPlaybackTemplate do hosta wyświetli się błąd.

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 jest niezbędny do udostępniania informacji o odtwarzaniu multimediów i elementów sterujących w Androidzie Auto. Jest to też ważne, aby host mógł tworzyć powiadomienia dotyczące multimediów.

W przypadku aplikacji korzystających z biblioteki Media3, które używają PlatformToken zamiast standardowego MediaSessionCompat.Token, musisz zaimplementować niestandardową SessionCommand w MediaLibrarySession.Callback, która zwraca podstawowy token platformy sesji: session.platformToken. W CarAppService wyślij to niestandardowe polecenie do sesji. Gdy otrzymasz token platformy, przekonwertuj go za pomocą MediaSessionCompat.Token.fromToken(platformToken) i przekaż ten token zgodności do biblioteki aplikacji do samochodu w .registerMediaPlaybackToken().

Porządkowanie multimediów za pomocą szablonów

Aby uporządkować multimedia do przeglądania, np. utwory lub albumy, zalecamy używanie SectionedItemTemplate, który umożliwia używanie razem GridSection i RowSection do tworzenia układów łączących listy obrazów i elementów tekstowych.

Interfejs aplikacji muzycznej wyświetla ostatnio odtwarzane utwory i albumy, w tym dwa pionowe rzędy i trzy poziome okładki albumów.

Rysunek 2. SectionedItemTemplate zawierający RowSection i GridSection

Używanie SectionedItemTemplate w TabTemplate

Wygodnym sposobem na kategoryzowanie multimediów w aplikacji jest używanie SectionedItemTemplate w TabTemplate.

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

Komponenty i funkcje biblioteki aplikacji do samochodu w wersji 1.9

W wersji 1.9 biblioteki aplikacji do samochodu wprowadziliśmy dostosowane komponenty do unikalnych funkcji przeglądania, takie jak Chips, Progress Bars, Condensed Items, Interactive and Expanded Header, Spotlight Sections i Banners.

Interfejs aplikacji muzycznej wyświetla ostatnio odtwarzane utwory i albumy, w tym dwa pionowe rzędy i trzy poziome okładki albumów.

Rysunek 3. SectionedItemTemplate zawierający Chips, Condensed Items, Interactive Header, Grid Items i Minimized Control Panel

Interfejs aplikacji muzycznej wyświetla ostatnio odtwarzane utwory i albumy, w tym dwa pionowe rzędy i trzy poziome okładki albumów.

Rysunek 4. 2 ekrany przeglądania multimediów z Expanded Header, Spotlight Sections i Progress Bars

Więcej informacji o projektowaniu interfejsu aplikacji do multimediów za pomocą tych szablonów znajdziesz w artykule Aplikacje do multimediów.

Podczas przeglądania multimediów ważne jest, aby użytkownik mógł szybko przejść do MediaPlaybackTemplate przy minimalnym rozpraszaniu uwagi.Aby spełnić wymaganie dotyczące jakości MFT-1, aplikacja musi mieć możliwość uzyskania dostępu do MediaPlaybackTemplate ze wszystkich ekranów przeglądania multimediów.

Jeśli używasz SectionedItemTemplate, możesz to zrobić, dodając przycisk polecenia, który przenosi użytkownika do ekranu odtwarzania multimediów. Użyj standardowego działania biblioteki aplikacji do samochodu Action.MEDIA_PLAYBACK. Aplikacja do multimediów wyświetli to działanie jako zminimalizowany panel sterowania, który jest wymagany do spełnienia wymagania dotyczącego jakości MFT-1, jeśli używasz biblioteki aplikacji do samochodu w wersji 1.9 lub nowszej. W przypadku innych szablonów można to zrobić za pomocą działania w nagłówku.

Obsługa intencji systemowego odtwarzania multimediów

Gdy aplikacja jest uruchamiana z powierzchni systemowej odtwarzającej multimedia, np. z karty multimediów, użytkownik musi zostać przekierowany do MediaPlaybackTemplate. Wymagamy, aby aplikacje do multimediów obsługiwały to Intent Action, aby zapewnić użytkownikom płynne działanie.

Dodaj działanie androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK do filtra intencji komponentu biblioteki aplikacji do samochodu (albo CarAppActivity lub aktywności trampolinowej Activity).

Upewnij się, że aktywność używa launchMode o wartości singleTask lub singleTop, aby wywoływać 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>

W klasie Session zastąp onNewIntent(), aby przeanalizować przychodzącą intencję. Jeśli przychodzące działanie intencji pasuje do SHOW_MEDIA_PLAYBACK, przekieruj użytkownika do ekranu odtwarzania.

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

Jeśli używasz aktywności trampolinowej, sprawdź działanie intencji w onCreate(). Przed wywołaniem finish() przekaż to działanie do intencji tworzenia CarAppActivity.

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