自動車向け Android アプリ ライブラリを使用すると、ナビゲーション アプリ、スポット(POI)、モノのインターネット(IOT)アプリを自動車に取り込むことができます。これは、ドライバーの注意散漫に関する基準を満たすように設計された一連のテンプレートを提供し、車のさまざまな画面要素や入力モダリティなどの詳細に配慮することで実現しています。
このガイドでは、ライブラリの主な機能とコンセプトの概要を示し、シンプルなアプリを設定するプロセスについて説明します。詳しい手順については、自動車向けアプリ ライブラリの基礎に関する Codelab をご覧ください。
始める前に
- 自動車向けアプリ ライブラリに関する運転のための設計ページを確認する
- ナビゲーション アプリとその他の運転関連アプリのカテゴリの概要
- テンプレートを使用したアプリの作成の概要
- テンプレートとテンプレート コンポーネントを扱う構成要素
- 一般的な 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
の表示をリクエストします。
自動車向けアプリ ライブラリをインストールする
ライブラリをアプリに追加する方法については、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
クラスを拡張して、ホストへの現在の接続に対応する Session
インスタンスを返す onCreateSession
メソッドを実装する必要があります。
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
オブジェクトへのアクセスなど)のための AppManager
、ターンバイターン ナビゲーション アプリがナビゲーション関連のメタデータと通信するために使用する NavigationManager
などのカーサービスへのアクセスを提供します。
ナビゲーション アプリで使用できるライブラリ機能の包括的なリストについては、ナビゲーション テンプレートにアクセスするをご覧ください。
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 でマッピングされるように画面を構成することをおすすめします。
ユーザーとやり取りする
アプリは、モバイルアプリに似たパターンを使用してユーザーとやり取りできます。
ユーザー入力を処理する
アプリは、適切なリスナーをそれらをサポートするモデルに渡すことで、ユーザー入力に応答できます。次のスニペットは、アプリのコードで定義されたメソッドにコールバックする 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 の運転向けデザインガイドの通知をご覧ください。
トーストを表示する
アプリでは、次のスニペットに示すように、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
スタックに push できます。
これらの制限には、テンプレートの更新、戻る操作、リセット操作などの特殊なケースがあります。
テンプレートの更新
一部のコンテンツの更新は、テンプレートの上限にカウントされません。一般に、アプリが以前のテンプレートと同じタイプの新しいテンプレートを push する場合、前のテンプレートと同じメイン コンテンツが含まれている場合、新しいテンプレートは割り当てにカウントされません。たとえば、ListTemplate
内で行の切り替え状態を更新しても、割り当てに対するカウントは行われません。どのような種類のコンテンツ更新がテンプレートの更新としてカウントされるかについて詳しくは、個々のテンプレートのドキュメントをご覧ください。
戻る操作
タスク内のサブフローを有効にするために、ホストはアプリが 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
を定義します。自動車向けアプリ ライブラリは現在、次のログイン方法をサポートしています。
InputSignInMethod
: ユーザー名とパスワードによるログイン用。- PIN ログイン用の
PinSignInMethod
。ユーザーはヘッドユニットに表示される PIN を使用してスマートフォンからアカウントをリンクします。 ProviderSignInMethod
: Google ログインや One Tap などのプロバイダのログインに使用します。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.Builder.addVariant()
メソッドを使用して、CarText
にテキスト文字列のバリアントを追加できます。
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
のドキュメントをご覧ください。スパンを使用したテキスト スタイルの仕組みの概要については、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();
Car Hardware API
自動車向けアプリの API レベル 3 以降、自動車向けアプリ ライブラリには、車両のプロパティとセンサーへのアクセスに使用できる API が用意されています。
要件
Android Auto で API を使用するには、まず Android Auto モジュールの build.gradle
ファイルに androidx.car.app:app-projected
への依存関係を追加します。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 の権限 |
---|---|---|---|
fetchModel |
メーカー、モデル、年 | android.car.permission.CAR_INFO |
|
fetchEnergyProfile |
EV コネクタの種類、燃料の種類 | com.google.android.gms.permission.CAR_FUEL |
android.car.permission.CAR_INFO |
addTollListener
removeTollListener |
通行料金カードの状態、通行料金カードの種類 | ||
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 |
addSpeedListener
removeSpeedListener |
未加工速度、表示速度(車のクラスタ ディスプレイに表示) | com.google.android.gms.permission.CAR_SPEED |
android.car.permission.CAR_SPEED 、android.car.permission.READ_CAR_DISPLAY_UNITS |
addMileageListener
removeMileageListener |
走行距離 | com.google.android.gms.permission.CAR_MILEAGE |
このデータは、Google Play ストアからインストールしたアプリで Android Automotive OS では使用できません。 |
たとえば、残りの範囲を取得するには、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 とセッションのライフサイクル
詳細については、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 内で各問題の横にあるスターアイコンをクリックすると、問題を登録して投票することができます。詳細については、問題を登録する手順をご覧ください。