自動車向け Android アプリ ライブラリを使用する

自動車向け Android アプリ ライブラリを使用すると、ナビゲーション、スポット(POI)、モノのインターネット(IOT)アプリを自動車に導入できます。これは、ドライバーの注意散漫に関する基準を満たすように設計された一連のテンプレートを提供し、さまざまな車載画面の要素や入力モダリティなどの詳細を処理することで実現されています。

このガイドでは、ライブラリの主な機能とコンセプトについて概説し、基本的なアプリをセットアップする手順を示します。

始める前に

  1. 自動車アプリ ライブラリに関する運転中の設計のページを確認します。
  2. 次のセクションの主な用語と概念を確認してください。
  3. Android Auto システム UIAndroid Automotive OS の設計について十分に理解しておいてください。
  4. リリースノートを確認してください。
  5. サンプルを確認してください。

主な用語と概念

モデルとテンプレート
ユーザー インターフェースはモデル オブジェクトのグラフで表されます。モデル オブジェクトは、帰属するテンプレートで許可されているさまざまな方法で配置できます。テンプレートとは、これらのグラフでルートとして機能するモデルのサブセットです。モデルには、テキストや画像の形式でユーザーに表示される情報と、このような情報の見た目の各部分を構成する属性(テキストの色や画像サイズなど)が含まれています。ホストは、ドライバーの注意散漫基準を満たすように設計されたビューにモデルを変換し、さまざまな車載画面の要素や入力モダリティなどの詳細を処理します。
ホスト
ホストは、ライブラリの API によって提供される、アプリを車で実行するための機能を実装するバックエンド コンポーネントです。ホストは、アプリの検出とそのライフサイクルの管理、モデルのビューへの変換、ユーザー操作のアプリへの通知にいたるまで、幅広い役割を担います。モバイル デバイスでは、このホストは Android Auto によって実装されます。Android Automotive OS では、このホストはシステムアプリとしてインストールされます。
テンプレートの制限事項
テンプレートに応じて、そのモデルのコンテンツに制限が課されます。たとえば、リスト テンプレートには、ユーザーに提示できるアイテムの数に制限があります。テンプレートには、タスクのフローを形成するための接続方法にも制限があります。たとえば、アプリが画面スタックにプッシュできるテンプレートは最大 5 つです。詳しくは、テンプレートの制限をご覧ください。
Screen
Screen は、ライブラリによって提供されるクラスであり、アプリはこのクラスを実装して、ユーザーに提示されるインターフェースを管理します。Screen にはライフサイクルがあり、画面の表示時に表示されるテンプレートをアプリが送信するメカニズムを提供します。Screen インスタンスは、Screen スタックとの間で push およびポップできるため、テンプレート フローの制限を遵守できます。
CarAppService
CarAppServiceService 抽象クラスです。ホストから検出および管理するには、アプリに実装してエクスポートする必要があります。アプリの CarAppService は、createHostValidator を使用してホスト接続が信頼できることを検証し、次いで、onCreateSession を使用して接続ごとに Session インスタンスを提供します。
Session

Session は抽象クラスであり、アプリは CarAppService.onCreateSession を使用して実装し、返す必要があります。このクラスは、車の画面に情報を表示するためのエントリ ポイントとして機能します。このクラスは、車の画面にアプリの現在の状態(アプリが表示されているか、非表示になっているかなど)を通知するライフサイクルを持ちます。

Session が開始されると(アプリの初回起動時など)、ホストは onCreateScreen メソッドを使用して、最初の Screen の表示をリクエストします。

Car App Library をインストールする

ライブラリをアプリに追加する方法については、Jetpack ライブラリのリリースページをご覧ください。

アプリのマニフェスト ファイルを構成する

自動車アプリを作成する前に、アプリのマニフェスト ファイルを次のように構成します。

CarAppService を宣言する

ホストは CarAppService の実装を通じてアプリに接続します。ホストがアプリを検出して接続できるようにするには、マニフェストでこのサービスを宣言する必要があります。

また、アプリのインテント フィルタの <category> 要素でアプリのカテゴリを宣言する必要もあります。この要素に指定できる値については、サポートされているアプリのカテゴリのリストをご覧ください。

次のコード スニペットは、マニフェストでスポット アプリ用の自動車アプリサービスを宣言する方法を示しています。

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.POI"/>
      </intent-filter>
    </service>

    ...
<application>

サポートされているアプリのカテゴリ

アプリのカテゴリを宣言するには、前のセクションで説明したように CarAppService を宣言するときに、次のカテゴリ値をインテント フィルタに 1 つ以上追加します。

各カテゴリと、アプリが各カテゴリに属するための条件の詳細については、自動車向け Android アプリの品質をご覧ください。

アプリの名前とアイコンを指定する

ホストがシステム UI でアプリを表すために使用するアプリ名とアイコンを指定する必要があります。

以下のように、CarAppServicelabel 属性と icon 属性を使用して、アプリを表すために使用されるアプリ名とアイコンを指定できます。

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

<service> 要素でラベルまたはアイコンが宣言されていない場合、ホストは <application> 要素に指定された値にフォールバックします。

カスタムテーマを設定する

自動車向けアプリのカスタムテーマを設定するには、次のようにマニフェスト ファイルに <meta-data> 要素を追加します。

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

次に、スタイル リソースを宣言して、カスタム自動車アプリテーマに次の属性を設定します。

<resources>
  <style name="MyCarAppTheme">
    <item name="carColorPrimary">@layout/my_primary_car_color</item>
    <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item>
    <item name="carColorSecondary">@layout/my_secondary_car_color</item>
    <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item>
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

自動車向けアプリの API レベル

自動車向けアプリ ライブラリは独自の API レベルを定義するため、車両のテンプレート ホストでサポートされているライブラリ機能を確認できます。ホストでサポートされている自動車向けアプリの最も高い API レベルを取得するには、getCarAppApiLevel() メソッドを使用します。

アプリでサポートされる自動車向けアプリの最小 API レベルは、AndroidManifest.xml ファイルで次のように宣言する必要があります。

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

下位互換性を維持し、機能を使用するために必要な最小 API レベルを宣言する方法については、RequiresCarApi アノテーションのドキュメントをご覧ください。自動車向けアプリ ライブラリの特定の機能を使用するために必要な API レベルの定義については、CarAppApiLevels のリファレンス ドキュメントをご確認ください。

CarAppService とセッションを作成する

アプリは、CarAppService クラスを拡張し、その onCreateSession メソッドを実装する必要があります。このメソッドは、ホストへの現在の接続に対応する Session インスタンスを返します。

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

public final class HelloWorldService extends CarAppService {
    ...
    @Override
    @NonNull
    public Session onCreateSession() {
        return new HelloWorldSession();
    }
    ...
}

Session インスタンスは、アプリの初回起動時に使用される Screen インスタンスを返す役割を担います。

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen(carContext)
    }
    ...
}

Java

public final class HelloWorldSession extends Session {
    ...
    @Override
    @NonNull
    public Screen onCreateScreen(@NonNull Intent intent) {
        return new HelloWorldScreen(getCarContext());
    }
    ...
}

自動車アプリがアプリのホーム画面やランディング スクリーンではない画面から起動する必要があるシナリオ(ディープリンクの処理など)を処理する場合は、onCreateScreen から戻る前に ScreenManager.push を使用して、画面のバックスタックを事前にシードできます。事前に埋め込むことで、ユーザーはアプリによって表示された最初の画面から前の画面に戻ることができます。

起動画面を作成する

アプリに表示される画面を作成するには、Screen クラスを拡張するクラスを定義し、その onGetTemplate メソッドを実装します。このメソッドは、車の画面に表示する UI の状態を表す Template インスタンスを返します。

次のスニペットは、PaneTemplate テンプレートを使用してシンプルな「Hello world!」文字列を表示する Screen を宣言する方法を示しています。

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

public class HelloWorldScreen extends Screen {
    @NonNull
    @Override
    public Template onGetTemplate() {
        Row row = new Row.Builder().setTitle("Hello world!").build();
        Pane pane = new Pane.Builder().addRow(row).build();
        return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
    }
}

CarContext クラス

CarContext クラスは、Session インスタンスと Screen インスタンスからアクセスできる ContextWrapper サブクラスです。画面スタックを管理するための ScreenManager、一般的なアプリ関連機能(地図の描画用の Surface オブジェクトへのアクセスなど)のための自動車サービスへのアクセスを提供します。また、NavigationManager ターンバイターン ナビゲーション アプリがナビゲーション メタデータやその他のナビゲーション イベントを伝達するために使用する AppManager

ナビゲーション アプリで使用できるライブラリ機能の包括的なリストについては、ナビゲーション テンプレートにアクセスするをご覧ください。

CarContext は、車の画面の構成を使用してドローアブル リソースを読み込むことを許可する機能、インテントを使用して車内のアプリを起動する機能、アプリがダークモードで地図を表示する必要があるかどうかを通知する機能なども提供します。

画面ナビゲーションを実装する

アプリでは通常、多くの画面が表示されます。各画面はさまざまなテンプレートを利用し、ユーザーの操作に応じて移り変わるインターフェースを表示します。

ScreenManager クラスは、ユーザーが車の画面で [戻る] ボタンを選択したとき、または一部の自動車で利用可能なハードウェアの [戻る] ボタンを使用した場合に自動的にポップできる画面をプッシュするために使用できる画面スタックを提供します。

次のスニペットは、メッセージ テンプレートに「戻る」というアクションと、これをユーザーが選択したときに新しい画面をプッシュするアクションを追加する方法を示しています。

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(
                () -> getScreenManager().push(new NextScreen(getCarContext())))
            .build())
    .build();

Action.BACK オブジェクトは、自動的に ScreenManager.pop を呼び出す標準的な Action です。この動作は、CarContext で利用可能な OnBackPressedDispatcher インスタンスを使用してオーバーライドできます。

運転中にアプリの安全性を確保するため、画面スタックには最大 5 つまで画面を保持できます。詳しくは、テンプレートの制限事項をご覧ください。

テンプレートのコンテンツを更新する

アプリは Screen.invalidate メソッドを呼び出すことで、Screen のコンテンツを無効にするようリクエストできます。その後、ホストはアプリの Screen.onGetTemplate メソッドにコールバックして、新しいコンテンツを含むテンプレートを取得します。

Screen を更新するときに、新しいテンプレートがホストによってテンプレートの割り当てにカウントされないように、テンプレート内のどのコンテンツが更新可能かを把握することが重要です。詳しくは、テンプレートの制限をご覧ください。

Screen と、onGetTemplate の実装を通じて返されるテンプレートのタイプとが 1 対 1 の関係でマッピングされるように、画面を構成することをおすすめします。

地図を描画する

次のテンプレートを使用するナビゲーション アプリとスポット(POI)アプリは、Surface にアクセスして地図を描画できます。

テンプレート テンプレートの権限 カテゴリに関するガイダンス
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ナビゲーション
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES または
androidx.car.app.MAP_TEMPLATES
ナビゲーションPOI
MapTemplate非推奨 androidx.car.app.NAVIGATION_TEMPLATES ナビゲーション
PlaceListNavigationTemplate非推奨 androidx.car.app.NAVIGATION_TEMPLATES ナビゲーション
RoutePreviewNavigationTemplate非推奨 androidx.car.app.NAVIGATION_TEMPLATES ナビゲーション

サーフェス権限を宣言する

サーフェスにアクセスするには、アプリが使用しているテンプレートに必要な権限に加えて、AndroidManifest.xml ファイルで androidx.car.app.ACCESS_SURFACE 権限を宣言する必要があります。

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

サーフェスにアクセスする

ホストが提供する Surface にアクセスするには、SurfaceCallback を実装し、その実装を AppManager カーサービスに提供する必要があります。現在の Surface は、onSurfaceAvailable() コールバックと onSurfaceDestroyed() コールバックの SurfaceContainer パラメータで SurfaceCallback に渡されます。

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

サーフェスの表示可能領域を把握する

ホストはテンプレートのユーザー インターフェース要素を地図上に描画できます。ホストは SurfaceCallback.onVisibleAreaChanged メソッドを呼び出して、遮るものがない、ユーザーに全体が表示されることが確実なサーフェスの領域を伝えます。また、変更回数を最小限に抑えるために、ホストは SurfaceCallback.onStableAreaChanged メソッドを呼び出します。この呼び出しでは現在のテンプレートに基づいて常に表示される最小の長方形が使用されます。

たとえば、ナビゲーション アプリがNavigationTemplate 上部にアクション ストリップがある場合、ユーザーが画面をしばらく操作していないと、アクション ストリップ自体を非表示にして、地図のスペースを増やすことができます。この場合、onStableAreaChangedonVisibleAreaChanged へのコールバックがあり、同じ長方形が使用されます。アクション ストリップが非表示になると、onVisibleAreaChanged のみがより大きな領域で呼び出されます。ユーザーが画面を操作すると、最初の長方形で onVisibleAreaChanged のみが再度呼び出されます。

ダークモードをサポートする

条件を満たしているとホストが判断した場合、アプリは自動車向け Android アプリの品質の説明のとおり、適切な暗い色を使用して Surface インスタンスに地図を再描画する必要があります。

暗い色の地図を描画するかどうかの判断には、CarContext.isDarkMode メソッドを使用できます。ダークモードのステータスが変更されるたびに、Session.onCarConfigurationChanged の呼び出しを受け取ります。

ユーザーが地図を操作できるようにする

次のテンプレートを使用する場合は、ユーザーが描画した地図を操作できるようにサポートを追加できます。たとえば、ズームやパンによって地図のさまざまな部分を表示できるようにします。

テンプレート インタラクティビティをサポートする Car App API レベル
NavigationTemplate 2
PlaceListNavigationTemplate非推奨 4
RoutePreviewNavigationTemplate非推奨 4
MapTemplate非推奨 5(テンプレートの導入)
MapWithContentTemplate 7(テンプレートの導入)

インタラクティビティ コールバックを実装する

SurfaceCallback インターフェースには、実装して前のセクションのテンプレートで作成した地図にインタラクティビティを追加できるコールバック メソッドがいくつかあります。

操作 SurfaceCallback メソッド サポートする Car App API レベル
タップ onClick 5
ピンチしてズーム onScale 2
シングルタップ ドラッグ onScroll 2
シングルタップ フリング onFling 2
ダブルタップ onScale(スケーリング ファクタはテンプレート ホストによって決まります) 2
パンモードの回転ナッジ onScroll(距離ファクタはテンプレート ホストによって決まる) 2

マップ アクション ストリップを追加する

これらのテンプレートには、地図のアクション ストリップ(ズームインとズームアウト、センタリング、コンパスの表示など、表示を選択した操作など)用の地図アクション ストリップを含めることができます。マップ アクション ストリップには、タスクの階層を変更することなく更新できる、アイコンのみのボタンを 4 つまで設定できます。このボタンはアイドル状態では非表示になり、アクティブな状態になると再び表示されます。

マップ インタラクティビティ コールバックを受け取るようにするには、マップ アクション ストリップに Action.PAN ボタンを追加する必要があります。次のセクションで説明しているとおり、ユーザーがパンボタンを押すと、ホストはパンモードに入ります。

アプリのマップ アクション ストリップに Action.PAN ボタンを設定しない場合、SurfaceCallback メソッドからユーザー入力を取得できず、ホストで有効になっていたパンモードが終了します。

パンボタンはタッチスクリーンには表示されません。

パンモードについて

パンモードでは、ノンタッチの入力デバイス(ロータリー コントローラやタッチパッドなど)からのユーザー入力が、テンプレート ホストによって適切な SurfaceCallback メソッドに変換されます。パンモードを開始または終了するユーザー操作には、NavigationTemplate.BuildersetPanModeListener メソッドで応答します。ユーザーがパンモードのときは、テンプレートの他の UI コンポーネントを非表示にできます。

ユーザーとやり取りする

アプリは、モバイルアプリと同様のパターンを使用してユーザーとやり取りできます。

ユーザー入力を処理する

アプリは、適切なリスナーをそれらをサポートするモデルに渡すことで、ユーザー入力に応答できます。次のスニペットは、アプリのコードで定義されたメソッドにコールバックする OnClickListener を設定する Action モデルを作成する方法を示しています。

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

次に、onClickNavigate メソッドは CarContext.startCarApp メソッドを使用して、デフォルトのナビゲーション自動車アプリを起動します。

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

private void onClickNavigate() {
    Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
    getCarContext().startCarApp(intent);
}

ACTION_NAVIGATE インテントの形式など、アプリの起動方法について詳しくは、インテントを使用して自動車アプリを起動するをご覧ください。

インタラクションの続きをモバイル デバイスで行うようにユーザーをガイドするアクションなど、特定のアクションは車がパーキング状態にあるときに限り許可されます。このようなアクションの実装には、ParkedOnlyOnClickListener を使用します。車がパーキング状態にない場合、ホストはユーザーにメッセージを表示して、その状態ではアクションが許可されないことを示します。車がパーキング状態にある場合、コードは正常に実行されます。次のスニペットは、ParkedOnlyOnClickListener を使用してモバイル デバイスで設定画面を開く方法を示しています。

Kotlin

val row = Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone))
    .build()

Java

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone))
    .build();

通知を表示する

モバイル デバイスに送信された通知は、CarAppExtender で拡張された場合にのみ車の画面に表示されます。コンテンツのタイトル、テキスト、アイコン、アクションなど、一部の通知属性は CarAppExtender でも設定でき、車の画面に表示されるときは、この設定が通知の属性をオーバーライドします。

次のスニペットは、モバイル デバイスとは異なるタイトルで、通知を車の画面に送信する方法を示しています。

Kotlin

val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build()

Java

Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build();

通知は、ユーザー インターフェースの次の部分に影響する場合があります。

  • ヘッドアップ通知(HUN)がユーザーに表示されることがあります。
  • 通知センターにエントリが追加されたり、必要に応じて、レールにバッジが表示されたりすることがあります。
  • ナビゲーション アプリの場合、ターンバイターン通知で説明されているように、通知はレール ウィジェットに表示されることがあります。

CarAppExtender のドキュメントで説明されているように、通知の優先度を使用して、これらの各ユーザー インターフェース要素に影響するようにアプリの通知を設定する方法を選択できます。

true の値で NotificationCompat.Builder.setOnlyAlertOnce が呼び出された場合、優先度の高い通知は HUN として 1 回だけ表示されます。

自動車向けアプリの通知の設計方法については、Google Design for Driving ガイドの通知をご覧ください。

トーストを表示する

次のスニペットに示すように、アプリでは CarToast を使用してトーストを表示できます。

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

権限をリクエストする

アプリが制限付きのデータやアクション(位置情報など)にアクセスする必要がある場合、Android 権限の標準ルールが適用されます。権限をリクエストするには、CarContext.requestPermissions() メソッドを使用します。

標準 Android API ではなく CarContext.requestPermissions() を使用すると、権限ダイアログを作成するだけのために独自の Activity を起動しなくてよいというメリットがあります。さらに、プラットフォームに依存するフローを作成する必要はなく、Android Auto と Android Automotive OS の両方で同じコードを使用できます。

Android Auto の権限ダイアログのスタイルを設定する

Android Auto では、ユーザーの権限ダイアログがスマートフォンに表示されます。デフォルトでは、ダイアログの背後に背景はありません。カスタム バックグラウンドを設定するには、AndroidManifest.xml ファイルで自動車アプリのテーマを宣言し、自動車アプリのテーマの carPermissionActivityLayout 属性を設定します。

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

次に、カー アプリのテーマの carPermissionActivityLayout 属性を設定します。

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

インテントを使用して自動車アプリを起動する

CarContext.startCarApp メソッドを呼び出して、次のアクションのいずれかを実行できます。

次の例は、駐車予約の詳細を表示する画面でアプリを開く、というアクションを含む通知を作成する方法を示しています。アプリのアクションに対する明示的インテントをラップする PendingIntent を含むコンテンツ インテントで通知インスタンスを拡張します。

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

Notification notification = notificationBuilder
    ...
    .extend(
        new CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    new Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .build());

また、アプリは BroadcastReceiver も宣言する必要があります。このクラスは、ユーザーが通知インターフェースで対応するアクションを選択すると、インテントを処理するために呼び出され、データ URI を含むインテントが記述されている CarContext.startCarApp を呼び出します。

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

public class MyNotificationReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
            CarContext.startCarApp(
                intent,
                new Intent(Intent.ACTION_VIEW)
                    .setComponent(new ComponentName(context, MyCarAppService.class))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
        }
    }
}

最後に、アプリの Session.onNewIntent メソッドがこのインテントを処理し、駐車予約画面をスタックにプッシュします(予約画面がまだ一番上にない場合)。

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

@Override
public void onNewIntent(@NonNull Intent intent) {
    ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
    Uri uri = intent.getData();
    if (uri != null
        && MY_URI_SCHEME.equals(uri.getScheme())
        && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
        && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())
    ) {
        Screen top = screenManager.getTop();
        if (!(top instanceof ParkingReservationScreen)) {
            screenManager.push(new ParkingReservationScreen(getCarContext()));
        }
    }
}

自動車アプリの通知処理方法について詳しくは、通知を表示するをご覧ください。

テンプレートの制限事項

ホストは、1 つのタスクに対して表示されるテンプレートの数を最大 5 つに制限します。これらの 5 つのテンプレートのうち、最後のテンプレートは、次のいずれかのタイプである必要があります。

この上限はテンプレートの数に適用されます。スタック内の Screen インスタンスの数には適用されません。たとえば、アプリが画面 A で 2 つのテンプレートを送信し、次いで画面 B をプッシュした場合、アプリはあと 3 つのテンプレートを送信できます。これに対し、各画面が 1 つのテンプレートを送信するように構成されている場合、アプリは 5 つの画面インスタンスを ScreenManager スタックにプッシュできます。

これらの制限については、テンプレートの更新、戻る操作、リセット操作など、特殊なケースがいくつかあります。

テンプレートの更新

特定のコンテンツの更新は、テンプレートの割り当てに対してカウントされません。一般的に、アプリがプッシュする新しいテンプレートの種類が以前のテンプレートと同じメイン コンテンツである場合、新しいテンプレートは割り当てにカウントされません。たとえば、ListTemplate 内で行の切り替え状態を更新しても、割り当てに対するカウントは行われません。どのような種類のコンテンツ更新がテンプレートの更新としてカウントされるかについて詳しくは、個々のテンプレートのドキュメントをご覧ください。

戻る操作

1 つのタスク内でサブフローを有効にできるように、アプリが ScreenManager スタックから Screen をポップすると、ホストがこれを検出し、アプリの戻る操作後のテンプレート数に基づいて残りの割り当てを更新します。

たとえば、アプリが画面 A で 2 つのテンプレートを送信してから、画面 B をプッシュし、さらに 2 つのテンプレートを送信した場合、アプリの残りの割り当ては 1 つになります。アプリが画面 A に戻った場合、ホストは割り当てを 3 にリセットします。これは、アプリが 2 つのテンプレート分前に戻ったためです。

画面にポップバックする場合、アプリはその画面で最後に送信されたテンプレートと同じタイプのテンプレートを送信する必要があります。それ以外のテンプレート タイプを送信すると、エラーが発生します。ただし、戻る操作時にタイプが同じのままであれば、アプリは割り当てに影響を与えることなくテンプレートのコンテンツを自由に変更できます。

リセット操作

一部のテンプレートには、タスクの終了を示す特別なセマンティクスがあります。たとえば、NavigationTemplate は、画面に表示され続け、ユーザーが消費できるように新しいターンバイターンの指示で更新されることが想定されるビューです。このテンプレートが使用されると、ホストはテンプレートの割り当てをリセットし、そのテンプレートを新しいタスクの最初のステップとして扱います。これにより、アプリは新しいタスクを開始できます。ホスト上でリセットがトリガーされるテンプレートについては、個々のテンプレートのドキュメントをご覧ください。

ホストが通知アクションまたはランチャーからアプリを起動するインテントを受け取ると、割り当てもリセットされます。このメカニズムにより、アプリがバインドされてフォアグラウンドで実行されている場合でも、アプリは通知から新しいタスクフローを開始できます。

車の画面にアプリの通知を表示する方法について詳しくは、通知を表示するをご覧ください。通知アクションからアプリを起動する方法については、インテントを使用して自動車アプリを起動するをご覧ください。

Connection API

アプリが Android Auto で実行されているか、Android Automotive OS で実行されているかを判断するには、CarConnection API を使用して実行時に接続情報を取得します。

たとえば、自動車向けアプリの SessionCarConnection を初期化し、LiveData の更新をサブスクライブします。

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

オブザーバーで、接続状態の変化に対応できます。

Kotlin

fun onConnectionStateUpdated(connectionState: Int) {
  val message = when(connectionState) {
    CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit"
    CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS"
    CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto"
    else -> "Unknown car connection type"
  }
  CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show()
}

Java

private void onConnectionStateUpdated(int connectionState) {
  String message;
  switch(connectionState) {
    case CarConnection.CONNECTION_TYPE_NOT_CONNECTED:
      message = "Not connected to a head unit";
      break;
    case CarConnection.CONNECTION_TYPE_NATIVE:
      message = "Connected to Android Automotive OS";
      break;
    case CarConnection.CONNECTION_TYPE_PROJECTION:
      message = "Connected to Android Auto";
      break;
    default:
      message = "Unknown car connection type";
      break;
  }
  CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show();
}

Constraints API

自動車によって、ユーザーに同時に表示される Item インスタンスの数が異なる場合があります。ConstraintManager を使用して、実行時にコンテンツの上限を確認し、テンプレートで適切なアイテム数を設定します。

まず、CarContext から ConstraintManager を取得します。

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

取得した ConstraintManager オブジェクトに対してクエリを実行し、関連するコンテンツの上限を確認できます。たとえば、グリッドに表示できるアイテムの数を取得するには、CONTENT_LIMIT_TYPE_GRIDgetContentLimit を呼び出します。

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

ログインフローを追加する

アプリでユーザー ログイン中の操作を提供している場合は、自動車向けアプリの API レベル 2 以上で SignInTemplateLongMessageTemplate などのテンプレートを使用して、車のヘッドユニットでアプリへのログインを処理できます。

SignInTemplate を作成するには、SignInMethod を定義します。自動車向けアプリ ライブラリは現在、次の 3 つの方法でのログインが可能です。

  • ユーザー名/パスワードによるログイン用の InputSignInMethod
  • PinSignInMethod を使用した PIN によるログイン。ユーザーはヘッドユニットに表示された PIN を使用して、スマートフォンからアカウントをリンクします。
  • ProviderSignInMethod を使用したプロバイダ ログイン(Google ログインワンタップなど)
  • QRCodeSignInMethod: QR コードによるログイン。ユーザーが QR コードをスキャンしてスマートフォンでログインを完了します。これは、Car API レベル 4 以降で使用できます。

たとえば、ユーザーのパスワードを収集するテンプレートを実装するには、まず InputCallback を作成してユーザー入力の処理と検証を行います。

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
};

InputSignInMethod Builder には InputCallback が必要です。

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

最後に、新しい InputSignInMethod を使用して SignInTemplate を作成します。

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

AccountManager を使用する

認証が必要な Android Automotive OS アプリでは、次の理由により AccountManager を使用する必要があります。

  • 優れたユーザー エクスペリエンスと容易なアカウント管理: ユーザーは、システム設定のアカウント メニューから、すべてのアカウントを簡単に管理(ログイン / ログアウトを含む)できます。
  • 「ゲスト」エクスペリエンス: 自動車は共有デバイスであるため、OEM は車両でゲスト エクスペリエンスを有効にして、アカウントの追加が行われないようにできます。

テキスト文字列のバリエーションを追加する

自動車の画面サイズに応じて、表示されるテキストの量は異なります。自動車向けアプリの API レベル 2 以上では、画面に合う最適なテキスト文字列のバリエーションを複数指定できます。テキストのバリエーションを使用できる場所を確認するには、CarText を使用するテンプレートとコンポーネントを探してください。

テキスト文字列のバリエーションを CarText に追加するには、CarText.Builder.addVariant() メソッドを使用します。

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

たとえば、この CarTextGridItem のメインテキストとして使用できます。

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

文字列は優先度の高い順(たとえば文字列が長い順)に追加します。ホストは車の画面上のスペースに応じて、適切な長さの文字列を選択します。

行にインラインの CarIcon を追加

CarIconSpan を使用して、テキストにインラインでアイコンを追加し、アプリの視覚的な魅力を高めることができます。これらのスパンの作成について詳しくは、CarIconSpan.create のドキュメントをご覧ください。スパンを使用したテキスト スタイリングの仕組みの概要については、Spantastic text styling with Spans をご覧ください。

Kotlin

  
val rating = SpannableString("Rating: 4.5 stars")
rating.setSpan(
    CarIconSpan.create(
        // Create a CarIcon with an image of four and a half stars
        CarIcon.Builder(...).build(),
        // Align the CarIcon to the baseline of the text
        CarIconSpan.ALIGN_BASELINE
    ),
    // The start index of the span (index of the character '4')
    8,
    // The end index of the span (index of the last 's' in "stars")
    16,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE
)

val row = Row.Builder()
    ...
    .addText(rating)
    .build()
  
  

Java

  
SpannableString rating = new SpannableString("Rating: 4.5 stars");
rating.setSpan(
        CarIconSpan.create(
                // Create a CarIcon with an image of four and a half stars
                new CarIcon.Builder(...).build(),
                // Align the CarIcon to the baseline of the text
                CarIconSpan.ALIGN_BASELINE
        ),
        // The start index of the span (index of the character '4')
        8,
        // The end index of the span (index of the last 's' in "stars")
        16,
        Spanned.SPAN_INCLUSIVE_INCLUSIVE
);
Row row = new Row.Builder()
        ...
        .addText(rating)
        .build();
  
  

自動車ハードウェア API

自動車向けアプリの API レベル 3 以降、自動車向けアプリ ライブラリには、車両のプロパティとセンサーへのアクセスに使用できる API があります。

要件

Android Auto で API を使用するには、まず androidx.car.app:app-projected への依存関係を Android Auto モジュールの build.gradle ファイルに追加します。Android Automotive OS の場合は、Android Automotive OS モジュールの build.gradle ファイルに androidx.car.app:app-automotive の依存関係を追加します。

また、AndroidManifest.xml ファイルで、使用する自動車データをリクエストするために必要な関連する権限を宣言する必要があります。これらの権限は、ユーザーから付与される必要があります。プラットフォームによって異なるフローを作成することなく、Android Auto と Android Automotive OS の両方で同じコードを使用できます。ただし、必要な権限は異なります。

自動車情報

次の表に、CarInfo API によって公開されるプロパティと、それらのプロパティを使用する際にリクエストする必要がある権限を示します。

メソッド プロパティ Android Auto の権限 Android Automotive OS の権限 サポートする Car App API レベル
fetchModel メーカー、モデル、年式 android.car.permission.CAR_INFO 3
fetchEnergyProfile EV コネクタの種類、燃料の種類 com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

このデータは、API 30 以降を搭載した一部の Android Automotive OS 搭載車でのみ利用できます

外寸 なし android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
通行料金カードの状態、通行料金カードの種類 3
addEnergyLevelListener
removeEnergyLevelListener
バッテリー残量、燃料残量、燃料残量が少ない、残りの航続距離 com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_ENERGY
android.car.permission.CAR_ENERGY_PORTS
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addSpeedListener
removeSpeedListener
実際の速度、表示速度(車のクラスタ ディスプレイに表示) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener
走行距離計の距離 com.google.android.gms.permission.CAR_MILEAGE Android Automotive OS では、Play ストアからインストールしたアプリに対してこのデータを使用できません。 3

たとえば、残りの範囲を取得するには、CarInfo オブジェクトをインスタンス化し、OnCarDataAvailableListener を作成して登録します。

Kotlin

val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo

val listener = OnCarDataAvailableListener<EnergyLevel> { data ->
    if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) {
      val rangeRemaining = data.rangeRemainingMeters.value
    } else {
      // Handle error
    }
  }

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo();

OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
  if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) {
    float rangeRemaining = data.getRangeRemainingMeters().getValue();
  } else {
    // Handle error
  }
};

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

自動車からのデータが常に利用できるとは限りません。エラーが発生した場合は、リクエストした値のステータスを確認して、リクエストしたデータを取得できなかった理由を詳しく把握します。CarInfo クラスの完全な定義については、リファレンス ドキュメントをご覧ください。

自動車用センサー

CarSensors クラスを使用すると、車両の加速度計、ジャイロスコープ、コンパス、位置情報にアクセスできます。これらの値が利用できるかどうかは OEM によって異なります。加速度計、ジャイロスコープ、コンパスからのデータの形式は、SensorManager API で取得するものと同じです。たとえば、車両の向きをチェックするには:

Kotlin

val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors

val listener = OnCarDataAvailableListener<Compass> { data ->
    if (data.orientations.status == CarValue.STATUS_SUCCESS) {
      val orientation = data.orientations.value
    } else {
      // Data not available, handle error
    }
  }

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors();

OnCarDataAvailableListener<Compass> listener = (data) -> {
  if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) {
    List<Float> orientations = data.getOrientations().getValue();
  } else {
    // Data not available, handle error
  }
};

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

自動車から位置情報にアクセスするには、android.permission.ACCESS_FINE_LOCATION 権限を宣言してリクエストする必要があります。

テスト

Android Auto でテストするときにセンサーデータをシミュレートするには、デスクトップ ヘッドユニット ガイドのセンサーセンサーの構成のセクションをご覧ください。Android Automotive OS でテストするときにセンサーデータをシミュレートするには、Android Automotive OS エミュレータ ガイドのハードウェアの状態をシミュレートするセクションをご覧ください。

CarAppService、セッション、画面のライフサイクル

Session クラスと Screen クラスは LifecycleOwner インターフェースを実装します。ユーザーがアプリを操作すると、Session オブジェクトと Screen オブジェクトのライフサイクル コールバックが呼び出されます。次の図をご覧ください。

CarAppService とセッションのライフサイクル

図 1. Session ライフサイクル。

詳細については、Session.getLifecycle メソッドのドキュメントをご覧ください。

画面のライフサイクル

図 2. Screen ライフサイクル。

詳細については、Screen.getLifecycle メソッドのドキュメントをご覧ください。

車のマイクから録音する

アプリの CarAppServiceCarAudioRecord API を使用して、アプリにユーザーの自動車のマイクへのアクセス権を付与できます。ユーザーは、車のマイクへのアクセスをアプリに許可する必要があります。アプリは、アプリ内でユーザーの入力を記録して処理できます。

録音の許可

音声を録音する前に、まず AndroidManifest.xml で録音の権限を宣言し、ユーザーに権限を付与するようリクエストする必要があります。

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

録音する権限は実行時にリクエストする必要があります。自動車アプリで権限をリクエストする方法については、権限をリクエストするをご覧ください。

音声を記録する

ユーザーが録音を許可すると、音声を録音して録音を処理できます。

Kotlin

val carAudioRecord = CarAudioRecord.create(carContext)
        carAudioRecord.startRecording()

        val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE)
        while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording()
 

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        carAudioRecord.startRecording();

        byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE];
        while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording();
 

音声フォーカス

車のマイクから録音するときは、まず音声フォーカスを取得して、進行中のメディアが停止していることを確認します。音声フォーカスが失われた場合は、録音を停止します。

音声フォーカスを取得する方法の例を次に示します。

Kotlin

 
val carAudioRecord = CarAudioRecord.create(carContext)
        
        // Take audio focus so that user's media is not recorded
        val audioAttributes = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            // Use the most appropriate usage type for your use case
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
            .build()
        
        val audioFocusRequest =
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener { state: Int ->
                    if (state == AudioManager.AUDIOFOCUS_LOSS) {
                        // Stop recording if audio focus is lost
                        carAudioRecord.stopRecording()
                    }
                }
                .build()
        
        if (carContext.getSystemService(AudioManager::class.java)
                .requestAudioFocus(audioFocusRequest)
            != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
        ) {
            // Don't record if the focus isn't granted
            return
        }
        
        carAudioRecord.startRecording()
        // Process the audio and abandon the AudioFocusRequest when done

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        // Take audio focus so that user's media is not recorded
        AudioAttributes audioAttributes =
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        // Use the most appropriate usage type for your use case
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        .build();

        AudioFocusRequest audioFocusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                        .setAudioAttributes(audioAttributes)
                        .setOnAudioFocusChangeListener(state -> {
                            if (state == AudioManager.AUDIOFOCUS_LOSS) {
                                // Stop recording if audio focus is lost
                                carAudioRecord.stopRecording();
                            }
                        })
                        .build();

        if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest)
                != AUDIOFOCUS_REQUEST_GRANTED) {
            // Don't record if the focus isn't granted
            return;
        }

        carAudioRecord.startRecording();
        // Process the audio and abandon the AudioFocusRequest when done
 

テスト ライブラリ

自動車向け Android のテスト ライブラリは、テスト環境でのアプリの動作検証に使用できる補助クラスを提供します。たとえば、SessionController を使用すると、ホストへの接続をシミュレートして、正しい ScreenTemplate が作成されて返されることを確認できます。

使用例については、サンプルをご覧ください。

自動車向け Android アプリ ライブラリに関する問題を報告する

ライブラリに問題が見つかった場合は、Google Issue Tracker を使用して報告します。問題テンプレートに必要な情報をすべて記入してください。

新しい問題を報告する

新しい問題を報告する前に、その問題がライブラリのリリースノートに記載されていないか、すでに問題リスト内で報告されていないかご確認ください。Issue Tracker 内で各問題の横にあるスターアイコンをクリックすると、問題を登録して投票することができます。詳細については、問題を登録する手順をご覧ください。