자동차 앱 라이브러리 템플릿을 사용하는 미디어 앱은 미디어 탐색 및 재생 환경을 맞춤설정하면서도 환경이 자동차 화면에 최적화되고 운전 중 주의 산만을 최소화하도록 할 수 있습니다.
이 가이드에서는 개발자에게 이미 휴대전화에서 오디오를 재생하는 미디어 앱이 있고 미디어 앱이 Android 미디어 앱 아키텍처를 준수한다고 가정합니다. 자동차 앱 라이브러리를 사용하면 자동차용 미디어 앱 빌드
MediaBrowser 데이터 구조를 사용하여 빌드된 앱 대신 템플릿으로 인앱 환경을 대체할 수 있습니다. 재생 제어를 위한 MediaSession와 추천 및 기타 스마트 환경에 사용되는 MediaBrowserService 또는 MediaLibraryService는 계속 제공해야 합니다.
앱의 매니페스트 구성
자동차용 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>
최소 자동차 앱 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>
그런 다음 xml 리소스의 automotive_app_desc.xml에 template 선언을 추가합니다. 다음과 같이 표시됩니다.
<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로 배포하는 방법의 두 가지가 있습니다. 단일 APK를 배포하면 자동차 앱 라이브러리 호스트로 Android Automotive OS가 지원되는 차량을 지원하고, 지원되지 않는 경우 이전 Android 버전 (Android 10~Android 13)에서도 MediaBrowserService 또는 MediaLibraryService 애플리케이션으로 대체됩니다. 두 개의 별도 APK를 배포하면 앱의 MediaBrowserService 또는 MediaLibraryService 버전에 영향을 미치지 않고 자동차 앱 라이브러리 버전에 새로 추가된 항목을 더 쉽게 업데이트할 수 있습니다.
단일 APK 배포
자동차 앱 라이브러리 및 MediaBrowserService 또는 MediaLibraryService 버전의 앱에 단일 APK를 배포할 때는 android:required="false"에
<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>
다음으로 AAOS용 자동차 앱 라이브러리 가이드라인을 따르고 실행 가능한 CarAppActivity (또는 트램펄린 활동)을 도입합니다. 매니페스트에서 활동을 android:enabled="false"로 설정해야 합니다. 다음으로 CarAppActivity 구성요소를 대체로 나타내는 메타데이터 태그를 MediaBrowserService 선언에 추가합니다. 아래 매니페스트 예를 참고하세요.
<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 배포
자동차 앱 라이브러리와 MediaBrowserService 또는 MediaLibraryService이 있는 APK는 버전 코드가 더 높고 Android 14 (34)를 타겟팅하는 minSdk로 사용 설정해야 합니다.
두 개의 APK로 배포
자동차 앱 라이브러리를 사용하는 APK와 MediaBrowserService 또는 MediaLibraryService을 사용하는 APK를 별도로 배포하려면 다음 단계에 따라 올바른 차량 기능을 올바르게 타겟팅해야 합니다.
자동차 앱 라이브러리 버전의 앱에 별도의 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를 사용 설정하세요.
Play 배포
자동차 앱 라이브러리를 사용하는 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을 사용하는 경우 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에 미디어 재생 정보와 컨트롤을 노출하는 데 필요합니다. 이는 호스트가 미디어 관련 알림을 만드는 데도 중요합니다.
표준 MediaSessionCompat.Token 대신 PlatformToken를 사용하는 Media3 라이브러리를 사용하는 앱의 경우 세션의 기본 플랫폼 토큰인 session.platformToken를 반환하는 MediaLibrarySession.Callback에서 맞춤 SessionCommand를 구현해야 합니다. CarAppService에서 이 맞춤 명령어를 세션에 전송합니다. 플랫폼 토큰을 수신하면 MediaSessionCompat.Token.fromToken(platformToken)를 사용하여 변환하고 이 호환성 토큰을 .registerMediaPlaybackToken()의 자동차 앱 라이브러리에 전달합니다.
템플릿을 사용하여 미디어 정리
노래나 앨범과 같은 미디어를 탐색할 수 있도록 정리하려면 SectionedItemTemplate를 사용하는 것이 좋습니다. SectionedItemTemplate를 사용하면 GridSection 및 RowSection를 함께 사용하여 이미지 목록과 텍스트 항목을 혼합하는 레이아웃을 만들 수 있습니다.
그림 2: RowSection 뒤에 GridSection이 오는 SectionedItemTemplate
TabTemplate 내에서 SectionedItemTemplate 사용
앱 내에서 미디어를 분류하는 편리한 방법 중 하나는 TabTemplate 내에서 SectionedItemTemplate를 사용하는 것입니다.
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에서는 칩, 진행률 표시줄, 축약된 항목, 대화형 및 확장된 헤더, 스포트라이트 섹션, 배너와 같은 고유한 탐색 기능을 위한 맞춤 구성요소를 도입합니다.
그림 3: Chips, Condensed Items, Interactive Header, Grid Items, Minimized Control Panel이 포함된 SectionedItemTemplate
그림 4: Expanded Header, Spotlight Sections, Progress Bars이 표시된 미디어 탐색 화면 2개
이러한 템플릿을 사용하여 미디어 앱의 사용자 인터페이스를 디자인하는 방법에 관한 자세한 내용은 미디어 앱을 참고하세요.
재생 컨트롤로 이동
미디어를 탐색할 때 사용자가 방해 요소를 최소화하면서 MediaPlaybackTemplate로 빠르게 이동할 수 있어야 합니다.MFT-1 품질 요구사항을 충족하려면 앱에 모든 미디어 탐색 화면에서 MediaPlaybackTemplate에 액세스할 수 있는 방법이 있어야 합니다.
SectionedItemTemplate를 사용하는 경우 미디어 재생 화면으로 이동하는 작업 버튼을 추가하여 이를 달성할 수 있습니다. 표준 자동차 앱 라이브러리 Action.MEDIA_PLAYBACK 작업을 사용합니다. 미디어 앱은 이 작업을 최소화된 제어판으로 표시합니다. 자동차 앱 라이브러리 API 1.9 이상을 사용하는 경우 MFT-1 품질 요구사항을 충족해야 합니다. 다른 템플릿의 경우 헤더 작업을 통해 이를 달성할 수 있습니다.
시스템 미디어 재생 인텐트 처리
미디어 카드와 같은 미디어를 재생하는 시스템 표면에서 애플리케이션을 실행할 때 사용자를 MediaPlaybackTemplate로 안내해야 합니다. 사용자를 위해 원활한 환경을 제공하려면 미디어 애플리케이션이 이 Intent Action를 처리해야 합니다.
자동차 앱 라이브러리 구성요소 (CarAppActivity 또는 트램펄린 Activity)의 intent-filter에 androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK 작업을 추가합니다.
onNewIntent()가 호출되도록 활동이 singleTask 또는 singleTop의 launchMode를 사용해야 합니다.
<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() 내에서 인텐트 작업을 확인합니다. finish()를 호출하기 전에 이 작업을 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();
}
}