自動車向け 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 を表示するようリクエストします。

自動車向けアプリ ライブラリをインストールする

アプリにライブラリを追加する手順については、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 を更新するときは、ホストが新しいテンプレートをテンプレートの割り当てにカウントしないように、更新可能なテンプレート内の具体的なコンテンツを把握することが重要です。詳しくは、テンプレートの制限をご覧ください。

画面を構成する際は、ScreenonGetTemplate 実装を通じて返されるテンプレートのタイプが 1 対 1 で対応するように画面を構成することをおすすめします。

地図を描画する

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

Template テンプレートの権限 カテゴリに関するガイダンス
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ナビゲーション
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES または
androidx.car.app.MAP_TEMPLATES
ナビゲーションスポット
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 のみが再び呼び出されます。

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

条件が満たされているとホストが判断した場合、アプリは適切なダークカラーで Surface インスタンスに地図を再描画する必要があります。詳しくは、自動車向け Android アプリの品質をご覧ください。

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

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

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

Template インタラクティビティをサポートする 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());

また、ユーザーが通知インターフェースでアクションを選択し、データ URI を含むインテントで CarContext.startCarApp を呼び出したときに呼び出される BroadcastReceiver も宣言する必要があります。

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

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

テンプレートの制限事項

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

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

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

テンプレートの更新

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

戻る操作

タスク内でサブフローを有効にするために、ホストは、アプリが ScreenManager スタックから Screen をポップするタイミングを検出し、アプリが戻るテンプレートの数に基づいて残りの割り当てを更新します。

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

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

リセット操作

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

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

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

接続 API

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

たとえば、自動車アプリの Session で、CarConnection を初期化し、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();
}

制約 API

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

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

Kotlin

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

Java

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

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

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

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

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

アプリでユーザーにログイン エクスペリエンスを提供する場合は、Car App API レベル 2 以上で SignInTemplateLongMessageTemplate などのテンプレートを使用して、車のヘッドユニットでアプリへのログインを処理できます。

SignInTemplate を作成するには、SignInMethod を定義します。自動車向けアプリ ライブラリは現在、次のログイン方法をサポートしています。

  • ユーザー名/パスワードによるログイン用の InputSignInMethod
  • PIN ログイン用の PinSignInMethod。ユーザーは、ヘッドユニットに表示された PIN を使用してスマートフォンからアカウントをリンクします。
  • プロバイダ ログイン用の ProviderSignInMethodGoogle ログインワンタップなど)。
  • 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 を使用する必要があります。

  • UX の向上とアカウント管理の容易さ: ユーザーは、ログインやログアウトを含むシステム設定のアカウント メニューから、すべてのアカウントを簡単に管理できます。
  • 「ゲスト」エクスペリエンス: 自動車は共有デバイスであるため、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();

この CarText を、GridItem のプライマリ テキストなどに使用できます。

Kotlin

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

Java

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

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

行にインライン CarIcons を追加する

CarIconSpan を使用すると、テキストにアイコンをインラインで追加して、アプリの視覚的な魅力を充実させることができます。これらのスパンの作成の詳細については、CarIconSpan.create のドキュメントをご覧ください。スパンを使用したテキスト スタイル設定の仕組みの概要については、スパンを使用した空間テキストのスタイル設定をご覧ください。

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 の場合は、androidx.car.app:app-automotive への依存関係を Android Automotive OS モジュールの build.gradle ファイルに追加します。

また、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 このデータは、Google Play ストアからインストールしたアプリでは Android Automotive OS では利用できません。 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 のライフサイクル

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 内で各問題の横にあるスターアイコンをクリックすると、問題を登録して投票することができます。詳細については、問題を登録する手順をご覧ください。