自動車向け Android アプリ ライブラリを使用すると、ナビゲーション、スポット(POI)、モノのインターネット(IOT)アプリを自動車に導入できます。これは、ドライバーの注意散漫に関する基準を満たすように設計された一連のテンプレートを提供し、さまざまな車載画面の要素や入力モダリティなどの詳細を処理することで実現されています。
このガイドでは、ライブラリの主な機能とコンセプトについて概説し、基本的なアプリをセットアップする手順を示します。
始める前に
- 自動車アプリ ライブラリに関する運転中の設計のページを確認します。
- ナビゲーション アプリとその他の運転関連アプリのカテゴリの概要
- テンプレートを使用してアプリを作成するの概要
- テンプレートとテンプレート コンポーネントに関するビルディング ブロック
- 一般的な UX パターンを示すサンプルフロー
- テンプレート化されたアプリの要件
- 次のセクションの主な用語と概念を確認してください。
- Android Auto システム UI と Android Automotive OS の設計について十分に理解しておいてください。
- リリースノートを確認してください。
- サンプルを確認してください。
主な用語と概念
- モデルとテンプレート
- ユーザー インターフェースはモデル オブジェクトのグラフで表されます。モデル オブジェクトは、帰属するテンプレートで許可されているさまざまな方法で配置できます。テンプレートとは、これらのグラフでルートとして機能するモデルのサブセットです。モデルには、テキストや画像の形式でユーザーに表示される情報と、このような情報の見た目の各部分を構成する属性(テキストの色や画像サイズなど)が含まれています。ホストは、ドライバーの注意散漫基準を満たすように設計されたビューにモデルを変換し、さまざまな車載画面の要素や入力モダリティなどの詳細を処理します。
- ホスト
- ホストは、ライブラリの API によって提供される、アプリを車で実行するための機能を実装するバックエンド コンポーネントです。ホストは、アプリの検出とそのライフサイクルの管理、モデルのビューへの変換、ユーザー操作のアプリへの通知にいたるまで、幅広い役割を担います。モバイル デバイスでは、このホストは Android Auto によって実装されます。Android Automotive OS では、このホストはシステムアプリとしてインストールされます。
- テンプレートの制限事項
- テンプレートに応じて、そのモデルのコンテンツに制限が課されます。たとえば、リスト テンプレートには、ユーザーに提示できるアイテムの数に制限があります。テンプレートには、タスクのフローを形成するための接続方法にも制限があります。たとえば、アプリが画面スタックにプッシュできるテンプレートは最大 5 つです。詳しくは、テンプレートの制限をご覧ください。
Screen
Screen
は、ライブラリによって提供されるクラスであり、アプリはこのクラスを実装して、ユーザーに提示されるインターフェースを管理します。Screen
にはライフサイクルがあり、画面の表示時に表示されるテンプレートをアプリが送信するメカニズムを提供します。Screen
インスタンスは、Screen
スタックとの間で push およびポップできるため、テンプレート フローの制限を遵守できます。CarAppService
CarAppService
はService
抽象クラスです。ホストから検出および管理するには、アプリに実装してエクスポートする必要があります。アプリの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 つ以上追加します。
androidx.car.app.category.NAVIGATION
: ターンバイターンのルート案内を提供するアプリ。 このカテゴリに関するその他のドキュメントについては、自動車向けナビゲーション アプリを作成するをご覧ください。androidx.car.app.category.POI
: 駐車場、充電スタンド、ガソリン スタンドなどのスポットの検索に関連する機能を提供するアプリ。このカテゴリに関するその他のドキュメントについては、自動車向けスポットアプリを作成するをご覧ください。androidx.car.app.category.IOT
: ユーザーが接続済みデバイスに対して車内から操作を行うことができるアプリ。このカテゴリに関するその他のドキュメントについては、自動車向けモノのインターネット アプリを作成するをご覧ください。
各カテゴリと、アプリが各カテゴリに属するための条件の詳細については、自動車向け Android アプリの品質をご覧ください。
アプリの名前とアイコンを指定する
ホストがシステム UI でアプリを表すために使用するアプリ名とアイコンを指定する必要があります。
以下のように、CarAppService
の label
属性と 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
上部にアクション ストリップがある場合、ユーザーが画面をしばらく操作していないと、アクション ストリップ自体を非表示にして、地図のスペースを増やすことができます。この場合、onStableAreaChanged
と onVisibleAreaChanged
へのコールバックがあり、同じ長方形が使用されます。アクション ストリップが非表示になると、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.Builder
の setPanModeListener
メソッドで応答します。ユーザーがパンモードのときは、テンプレートの他の 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 を使用して実行時に接続情報を取得します。
たとえば、自動車向けアプリの 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(); }
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_GRID
で getContentLimit
を呼び出します。
Kotlin
val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)
Java
int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);
ログインフローを追加する
アプリでユーザー ログイン中の操作を提供している場合は、自動車向けアプリの API レベル 2 以上で SignInTemplate
や LongMessageTemplate
などのテンプレートを使用して、車のヘッドユニットでアプリへのログインを処理できます。
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();
たとえば、この CarText
を GridItem
のメインテキストとして使用できます。
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 とセッションのライフサイクル
詳細については、Session.getLifecycle
メソッドのドキュメントをご覧ください。
画面のライフサイクル
詳細については、Screen.getLifecycle
メソッドのドキュメントをご覧ください。
車のマイクから録音する
アプリの CarAppService
と CarAudioRecord
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
を使用すると、ホストへの接続をシミュレートして、正しい Screen
と Template
が作成されて返されることを確認できます。
使用例については、サンプルをご覧ください。
自動車向け Android アプリ ライブラリに関する問題を報告する
ライブラリに問題が見つかった場合は、Google Issue Tracker を使用して報告します。問題テンプレートに必要な情報をすべて記入してください。
新しい問題を報告する前に、その問題がライブラリのリリースノートに記載されていないか、すでに問題リスト内で報告されていないかご確認ください。Issue Tracker 内で各問題の横にあるスターアイコンをクリックすると、問題を登録して投票することができます。詳細については、問題を登録する手順をご覧ください。