Car App Library のテンプレートを使用するメディアアプリは、メディアのブラウジングと再生の操作をカスタマイズしながら、車の画面に最適化され、運転中の注意散漫を最小限に抑えることができます。
このガイドは、音声を再生するメディアアプリがスマートフォンにすでにインストールされており、そのメディアアプリが Android メディアアプリ アーキテクチャに準拠していることを前提としています。Car App Library を使用すると、自動車向けメディアアプリを作成するの 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 で自動車向けアプリ ライブラリ対応のメディアアプリを配布する方法は、1 つの APK として配布する方法と、2 つの別々の APK として配布する方法の 2 つがあります。単一の APK を配布する場合、自動車向けアプリ ライブラリ ホストで Android Automotive OS が有効になっている車両をサポートし、そうでない場合は、古い Android バージョン(Android 10 ~ Android 13)でも MediaBrowserService または MediaLibraryService アプリケーションにフォールバックします。2 つの別々の APK を配信することを選択した場合、アプリの MediaBrowserService バージョンまたは MediaLibraryService バージョンに影響を与えることを恐れることなく、Car App Library バージョンへの新しい追加をより簡単に更新できます。
単一の 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" に設定する必要があります。次に、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 は、より高いバージョン コードと Android 14(34)を対象とする minSdk で有効にする必要があります。
2 つの APK で配信する
自動車アプリ ライブラリを使用する APK と MediaBrowserService または MediaLibraryService を使用する APK の 2 つの別々の 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 を返すカスタム SessionCommand を MediaLibrarySession.Callback に実装する必要があります。CarAppService で、このカスタム コマンドをセッションに送信します。プラットフォーム トークンを受け取ったら、MediaSessionCompat.Token.fromToken(platformToken) を使用して変換し、この互換性トークンを .registerMediaPlaybackToken() の Car App Library に渡します。
テンプレートを使用してメディアを整理する
曲やアルバムなどのメディアをブラウジング用に整理するには、SectionedItemTemplate を使用することをおすすめします。これにより、GridSection と RowSection を組み合わせて、画像とテキスト アイテムのリストを混在させたレイアウトを作成できます。
図 2: RowSection の後に GridSection が続く SectionedItemTemplate
TabTemplate 内で SectionedItemTemplate を使用する
アプリ内のメディアを分類する便利な方法の 1 つは、TabTemplate 内で SectionedItemTemplate を使用することです。
val template =
SectionedItemTemplate.Builder()...build();
val tabTemplate =
TabTemplate.Builder(tabCallback)
.setTabContents(TabContents.Builder(template).build)
.setHeaderAction(Action.APP_ICON)
…
.build();
Car アプリ ライブラリ 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 を処理する必要があります。
Car App Library コンポーネント(CarAppActivity またはトランポリン Activity)の intent-filter に androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK アクションを追加します。
アクティビティが 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() 内のインテントのアクションを確認します。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();
}
}