mcpField
A
android-for-cars-parked-apps

android-for-cars-parked-apps

github.com/android開発10.0k199 回インストール2 日前に更新
リポジトリ

android-for-cars-parked-apps について

既存の動画、ゲーム、ブラウザーアプリを Android Auto または Android Automotive OS の駐車中向けアプリに対応させるスキルです。プラットフォーム設定、車載 UI、安全要件、公開前の確認を扱います。

主な機能

  1. 01駐車中向けカテゴリと対象の判定
  2. 02マニフェストと車載互換性の設定
  3. 03戻る操作と画面方向への対応
  4. 04システムバーと画面切り欠きへの対応
  5. 05走行中のメディア再生の一時停止

活用シーン

  1. 01動画アプリの車載システム対応
  2. 02ゲームの駐車中利用への対応
  3. 03車載ブラウザーのデータ保護

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/android/skills --skill android-for-cars-parked-apps

インストール後、Claude Code で/android-for-cars-parked-appsを入力して読み込まれたか確認します。

同梱ファイルreferences/android/docs/quality-guidelines/car-app-quality/index.md

詳細

既存の Android 動画アプリ、ゲーム、ウェブブラウザーを、Android Auto および Android Automotive OS 上で駐車中アプリとして動作するように対応させます。Google Play で配信するため、以下の手順に従ってカーアプリの品質ガイドラインを満たしてください。

前提条件

  • 対応している駐車中アプリのカテゴリ(動画ストリーミングアプリ、ゲーム、ウェブブラウザー)のいずれかに該当する、既存のアダプティブ Android アプリから始めます。

用語集

  • 駐車中アプリ:駐車中アプリは、車両の駐車中にのみ Android Auto および Android Automotive OS 上で動作します。駐車中アプリとして公開できるのは、動画、ゲーム、ブラウザーの 3 つのカテゴリです。それ以外のカテゴリは公開できません。
  • Android Auto:Android Auto は、スマートフォンの機能を利用するプロジェクションシステムです。アプリはユーザーのスマートフォン上で実行され、有線またはワイヤレス接続を通じて車載ディスプレイに投影されます。Android Auto は Android Automotive OS とは別のプラットフォームです。
  • Android Automotive OS:Android Automotive OS(AAOS)は、車両自体で直接動作する Android のバリアントです。Android Automotive OS は Android Auto とは別のプラットフォームです。
  • 動画カテゴリ:動画アプリでは、駐車中にストリーミング動画を表示します。Android Automotive OS では一般提供されていますが、Android Auto ではサポートされていません。
  • ゲームカテゴリ:ゲームアプリでは、駐車中にゲームをプレイできます。Android Automotive OS および Android Auto で一般提供されています。
  • ブラウザーカテゴリ:ウェブブラウザーでは、駐車中にウェブを閲覧できます。Android Automotive OS ではベータ版として利用できますが、Android Auto ではサポートされていません。
  • メディアカテゴリ:駐車中アプリのカテゴリではありません。メディアアプリでは、走行中または駐車中に音声コンテンツ(動画を除く音楽、ラジオ、オーディオブック)を再生します。
  • Android for Cars プラットフォーム:Android for Cars は、Android Auto と Android Automotive OS を含む総称です。
  • 一般提供カテゴリ:対応プラットフォームの Google Play の内部テスト、クローズドテスト、オープンテスト、本番トラックで利用できます。
  • ベータカテゴリ:内部テストまたはクローズドテストのトラックで利用できます。オープンテストおよび本番トラックで利用するには、早期アクセスプログラムへの参加が必要です。
  • 走行中の音声再生:Android Automotive OS のベータ機能です。動画アプリで、車両の走行中に音声を再生できます。
  • カーアプリの品質ガイドライン:Tier 3(Car ready)、Tier 2(Car optimized)、または Tier 1(Car differentiated)の基準に照らしてアプリを監査する際は、カーアプリの品質ガイドラインを必ず確認してください。
  • Android Automotive OS の互換モード:互換モード(android.software.car.display_compatibility)は、一部の車両に搭載されたシステム機能です。システムの戻る操作、セーフエリアでの描画、密度のスケーリング、不透明な走行モード中のブロック(アクティビティを Stopped に移行)を提供します。互換モードは Android Automotive OS に対応するアプリにのみ関係し、Android Auto のみに対応するアプリには関係しません。

コードベースの調査

AndroidManifest.xml(または Unity プロジェクトでは Assets/Plugins/Android/AndroidManifest.xml を作成します)、res/xml/automotive_app_desc.xml、build.gradle、build.gradle.kts、 または gradle/libs.versions.toml ファイルをコードベース内で検索します。既存の動画プレーヤー、 MediaSession クラス、requestedOrientation の呼び出し、WindowInsets ハンドラーを探します。ユーザーから指示がない限り、依存関係を追加する前に許可を求めてください。

ステップ 1:アプリマニフェストを設定する

AndroidManifest.xml で、アプリのカテゴリ、対象プラットフォーム、ハードウェア機能の要件、アクティビティの設定を宣言します。

1.1:アプリのカテゴリを特定する
  • 動画アプリ:動画アプリは、<application> に android:appCategory="video" を設定し、uses name="video" を含む app/src/main/res/xml/automotive_app_desc.xml を含めてはなりません。
  • ゲーム:ゲームは、<application> に android:appCategory="game" を設定する必要があります。アプリがコントローラー入力に対応している場合は、android.hardware.gamepad を android:required="false" とともに宣言してください。
  • ブラウザー:ブラウザーは、エクスポートされた <activity>(android:exported="true")の MAIN <intent-filter> に <category android:name="android.intent.category.APP_BROWSER"/> を含める必要があります。
1.2:対象プラットフォームとハードウェア機能を特定する
  • Android Auto:Android Auto(Android 15 以降)を対象とするアプリは、AndroidManifest.xml ファイル内の <intent-filter> に <category android:name="android.intent.category.CAR_LAUNCHER"/> を追加する必要があります。ユーザーが別のインテントフィルターを指定しない限り、MAIN <intent-filter> に追加してください。アプリが Android Automotive OS のみを対象とする場合は、このカテゴリを絶対に追加しないでください。
  • Android Automotive OS:Android Automotive OS を対象とするアプリは、AndroidManifest.xml ファイル内で <uses-feature android:name="android.hardware.type.automotive" android:required="false"/>(モバイルまたはマルチフォームファクターのトラックでは "false"、専用の Automotive OS トラックで公開する場合に限り "true")を宣言する必要があります。アプリが Android Auto のみを対象とする場合は、この機能タグを絶対に追加しないでください。
  • 必須ではないハードウェア機能(DO-1):Google Play の機能要件と DO-1 を満たすため、サポートしていない機能(android.hardware.wifi、android.hardware.screen.portrait、android.hardware.screen.landscape、android.hardware.camera)を android:required="false" とともに宣言し、車載環境にない機能の android:required="true" を削除してください。
  • ディスプレイの連続性(LS-C1):離れたディスプレイへの移動時にアクティビティの再作成や再生の途切れが発生しないよう、<activity> の android:configChanges に screenSize、smallestScreenSize、orientation、screenLayout、density を設定してください。
  • 互換モードと注意散漫:互換モードの宣言は任意であり、Android Automotive OS に対応するアプリにのみ関係します。Android Auto のみに対応するアプリには、互換モードを絶対に追加しないでください。android.hardware.type.automotive を追加すると、<application> に <meta-data android:name="android.software.car.display_compatibility" android:value="true"/> を追加しない限り、互換モードは無効になります。アプリが AndroidManifest.xml で android.software.car.display_compatibility を宣言する場合、実行時に PackageManager.hasSystemFeature("android.software.car.display_compatibility") を確認し、結果が true の場合はアプリ内の重複する戻るボタンや閉じるボタンを非表示にする必要があります。駐車中に使用するアプリは、いずれの <activity> にも <meta-data android:name="distractionOptimized" android:value="true"/> を含めてはなりません(DD-2、DD-3)。

ステップ 2:戻るナビゲーションと画面の向きへの対応

2.1:アプリ内の戻るボタンと閉じるボタンを追加する

Android Automotive OS 搭載車両には、ハードウェアまたはソフトウェアの 戻るボタンやジェスチャーナビゲーションが必須ではありません(AN-1)。詳細画面や全画面プレーヤーを含む、ルート以外のすべての画面に、明示的な戻るボタンと閉じるボタンを用意してください。

  • AndroidManifest.xml で android.software.car.display_compatibility を宣言していない場合:アプリ内の戻るボタンと閉じるボタンを常に表示します。
  • AndroidManifest.xml で android.software.car.display_compatibility を android:value="true" とともに宣言している場合:PackageManager.hasSystemFeature("android.software.car.display_compatibility") が false を返す場合にのみ、アプリ内の戻るボタンと閉じるボタンを表示します。
2.2: ランタイムの画面方向リクエストを制御する

車載ディスプレイの画面方向は固定されています。固定ポートレートの車載ディスプレイでランドスケープをリクエストすると、レターボックス表示やちらつきのループが発生します(DO-1)。マニフェストのハードコードされた android:screenOrientation 属性を削除し、requestedOrientation を設定する前に PackageManager.FEATURE_SCREEN_LANDSCAPE と PackageManager.FEATURE_SCREEN_PORTRAIT を確認してください。

ステップ 3: システムバーと不規則なディスプレイの切り欠きに対応する

3.1: 制御可能なシステムバーのインセットを追跡する

Android Automotive OS では、空調や車両のコントロールにアクセスできるよう、OEM がシステムバーを表示したまま固定することが許可されています(AR-1)。OnControllableInsetsChangedListener で制御可能なインセットを追跡し、制御可能なバーだけを非表示にして、制御できない statusBars、navigationBars、displayCutout のインセットには windowInsetsPadding を適用してください。

3.2: 不規則なディスプレイの切り欠き領域に安全に描画する

曲面または非矩形の車載ディスプレイで Tier 1 の基準 AR-2 を満たすには、LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT(0)または LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES(1)を絶対に使用しないでください。car リソース修飾子を使用して、res/values-car-v30/integers.xml で LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS(3)または LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER(2)を設定してください。

<integer name="windowLayoutInDisplayCutoutMode">3</integer>

インタラクティブな UI、アプリバー、スクロール可能なリストは、WindowInsets.safeDrawing で保護してください。

ステップ 4: ドライバーの注意散漫防止とメディア再生の要件を満たす

4.1: ライフサイクルの遷移時に再生を一時停止し、走行中の再開をブロックする

車両が走行を開始すると、OS は停止中のアクティビティをブロッキングオーバーレイで覆い、onPause(互換モードの車両では onStop も)をトリガーします。DD-2、DD-3、MC-1 を満たすには、次のようにします。

  1. androidx.lifecycle:lifecycle-runtime-compose の LifecycleEventEffect を使用し、ON_PAUSE または ON_STOP で再生を一時停止します。
  2. 再生、一時停止、停止コマンドと項目のメタデータを含む MediaSession(androidx.media3:media3-session)を公開し(MC-1)、onCleared で解放します。
  3. build.gradle.kts に useLibrary("android.car") を追加し、ExoPlayer を ForwardingSimpleBasePlayer でラップして、CarUxRestrictionsManager をリッスンします。isRequiresDistractionOptimization が true になったら再生を一時停止し、getState で COMMAND_PLAY_PAUSE を削除して、外部の MediaSession コントロールから走行中に再生を再開できないようにします。
@UnstableApi
class CarAwarePlayer(context: Context) :
    ForwardingSimpleBasePlayer(ExoPlayer.Builder(context).build()) {
    private var shouldPreventPlay = false
    private var pausedByUxRestrictions = false
    private lateinit var carUxRestrictionsManager: CarUxRestrictionsManager

    init {
        if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
            val car = Car.createCar(context)
            carUxRestrictionsManager =
                car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
            shouldPreventPlay =
                carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
            invalidateState()

            carUxRestrictionsManager.registerListener { restrictions: CarUxRestrictions ->
                shouldPreventPlay = restrictions.isRequiresDistractionOptimization
                if (!shouldPreventPlay && pausedByUxRestrictions) {
                    handleSetPlayWhenReady(true)
                    invalidateState()
                } else if (shouldPreventPlay && isPlaying) {
                    pausedByUxRestrictions = true
                    handleSetPlayWhenReady(false)
                    invalidateState()
                }
            }
        }
    }

    override fun getState(): State {
        val state = super.getState()
        return state.buildUpon()
            .setAvailableCommands(
                state.availableCommands.buildUpon()
                    .removeIf(COMMAND_PLAY_PAUSE, shouldPreventPlay)
                    .build()
            )
            .build()
    }

    override fun handleRelease(): ListenableFuture<*> {
        if (::carUxRestrictionsManager.isInitialized) {
            carUxRestrictionsManager.unregisterListener()
        }
        return super.handleRelease()
    }
}

4.2: 走行中のバックグラウンド音声をサポートする(オプションのベータ機能)

これはオプションのベータ機能であるため、ユーザーからリクエストがない限り、この機能を絶対に実装しないでください。実装するには、次のようにします。

  1. AndroidManifest.xml に <uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false" /> を宣言し、FOREGROUND_SERVICE と FOREGROUND_SERVICE_MEDIA_PLAYBACK の権限も追加します。
  2. androidx.car.app:app の CarFeatures.isFeatureEnabled(context, CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING) を使用して、実行時に車両のサポートを確認します。!isBackgroundAudioWhileDrivingSupported && restrictions.isRequiresDistractionOptimization の場合に限り、shouldPreventPlay を設定します。
  3. MediaSession とプレーヤーを、android:foregroundServiceType="mediaPlayback" を指定したエクスポート済みの MediaSessionService 内に配置し、UI から MediaController.Builder と buildAsync を使用して接続します。

ステップ 5:ブラウザーアプリで機密データを保護する

車両は共有デバイスであるため、ブラウザーはプロファイルロック(SD-1)で保護されていない限り、保存済みのパスワードや支払い方法へのアクセスを必ずブロックする必要があります。パスワードや支払いデータを車両に同期する前に、ブラウザーはユーザーに認証を求め、データが車両に同期されることを車載画面に通知する必要があります(SD-2)。

KeyguardManager を使用して PIN、パターン、またはパスワードが設定されているか確認し、未設定の場合は Settings.ACTION_SECURITY_SETTINGS を開きます。車両には生体認証ハードウェアがないため、BiometricManager.Authenticators.DEVICE_CREDENTIAL のみを使用して BiometricPrompt で認証します。

val keyguardManager = context.getSystemService<KeyguardManager>()
val isDeviceSecure = keyguardManager?.isDeviceSecure == true
lateinit var biometricPrompt: BiometricPrompt

if (!isDeviceSecure) {
    context.startActivity(
        Intent(Settings.ACTION_SECURITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    )
} else {
    val promptInfo = BiometricPrompt.PromptInfo.Builder()
        .setTitle(context.getString(R.string.auth_title))
        .setSubtitle(context.getString(R.string.sync_data_to_car_notice))
        .setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
        .build()
    biometricPrompt.authenticate(promptInfo)
}

ステップ 6:実装を検証する

./gradlew assembleDebug を使用してアプリをビルドします。ビルドエラーがあれば特定して修正します。

実装後のガイダンス

実装後、ユーザーに次のガイダンスを提供します。

  • Android Auto アプリの場合は、アプリをスマートフォンにインストールし、Desktop Head Unit(DHU)を使用してテストします。
  • Android Automotive OS の場合は、Android Studio の Android Automotive OS エミュレーターにアプリをインストールします。
  • アプリが対象プラットフォームのベータカテゴリに該当する場合、オープンテストまたは本番トラックにアプリを提出する前に、早期アクセスプログラムへの参加承認が必要です。
  • アプリがベータ版の運転中オーディオ機能を実装している場合、オープンテストまたは本番トラックにアプリを提出する前に、早期アクセスプログラムへの参加承認が必要です。
  • アプリを提出する前に、車載アプリの品質ガイドラインの該当する基準に照らしてテストします。

アンチパターン

  • 停車中アプリのいずれの <activity> にも <meta-data android:name="distractionOptimized" android:value="true"/> を絶対に含めないでください。また、動画アプリに uses name="video" を含む res/xml/automotive_app_desc.xml を追加しないでください。
  • Android Automotive OS にハードウェアまたはソフトウェアの戻るボタンがあると絶対に想定しないでください。
  • システムバーが常に画面の上端と下端に配置されている、または常に非表示にできると絶対に想定しないでください。
  • Android Automotive OS では LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT または LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES を絶対に使用しないでください。
  • CarUxRestrictionsManager から isRequiresDistractionOptimization を読み取る代わりに、車速やギアの状態から UX 制限を推測しないでください。
  • Android Automotive OS 上のブラウザーアプリで、BiometricPrompt に生体認証器(BIOMETRIC_STRONG、BIOMETRIC_WEAK)を絶対に使用しないでください。DEVICE_CREDENTIAL のみを使用してください。
  • ユーザーが Android Automotive OS のサポートのみを求めている場合、CAR_LAUNCHER インテントカテゴリを絶対に追加しないでください。
  • ユーザーが Android Auto のサポートのみを求めている場合、android.hardware.type.automotive 機能タグを絶対に追加しないでください。

ベストプラクティス

  • サポートされていないハードウェア機能(wifi、screen.portrait、screen.landscape、camera)には android:required="false" を必ず宣言し、FEATURE_SCREEN_LANDSCAPE と FEATURE_SCREEN_PORTRAIT を確認してから requestedOrientation の呼び出しを行います(DO-1)。
  • 互換モードでない Android Automotive OS では、アプリ内に戻る操作と閉じる操作を必ず提供し(AN-1)、WindowInsets.safeDrawing と OnControllableInsetsChangedListener を使用して、すべてのインタラクティブ UI とテキストをシステムバーおよびディスプレイの切り欠きから保護します(AR-1、AR-2)。
  • UX 制限が有効になったら再生を必ず一時停止し、運転中のオーディオがサポートされ有効になっている場合を除き、運転中は MediaSession から COMMAND_PLAY_PAUSE を除外します(DD-2、DD-4、MC-1)。
  • ブラウザーのパスワードまたは支払い情報を保存、アクセス、同期する前に、プロファイルロックによる認証を必ず要求し、画面上に同期に関する通知を表示します(SD-1、SD-2)。

コメント