このページでは、ターンバイターン方式のナビゲーション アプリの機能を実装するために使用できる、自動車向けアプリ ライブラリのさまざまな機能について詳しく説明します。
マニフェストでナビゲーション サポートを宣言する
ナビゲーション アプリでは、CarAppService のインテント フィルタで自動車アプリのカテゴリとして androidx.car.app.category.NAVIGATION を宣言する必要があります。
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
...
</application>
ナビゲーション インテントをサポートする
さまざまなインテント形式を使用することで、ナビゲーション アプリはスポットアプリや音声アシスタントなどの他のアプリと連携できます。
これらのインテント形式をサポートするには、まずアプリのマニフェストにインテント フィルタを追加して、サポートを宣言します。これらのインテント フィルタの場所は、プラットフォームによって異なります。
- Android Auto: ユーザーが Android Auto を使用していないときにインテントを処理するために使用される
Activityの<activity>マニフェスト要素内。 - Android Automotive OS:
CarAppActivityの<activity>マニフェスト要素内。
次に、アプリの Session 実装の onCreateScreen() コールバックと
onNewIntent() コールバックの両方でインテントを読み取って処理します。
必須のインテント形式
NF-6 の品質要件を満たすには、アプリで
ナビゲーション インテントを処理する必要があります。
省略可能なインテント形式
次のインテント形式をサポートして、アプリの相互運用性をさらに高めることもできます。
ナビゲーション テンプレートにアクセスする
ナビゲーション アプリは、次のテンプレートにアクセスできます。これらのテンプレートは、背景のサーフェスに地図を表示し、ナビゲーション使用時にはターンバイターン方式のルート案内を表示します。
NavigationTemplate: ナビゲーション使用時に任意の情報メッセージと推定所要時間を表示するテンプレート。MapWithContentTemplate: アプリが何らかのコンテンツ(リストなど)を含む地図タイルをレンダリングできるようにするテンプレート。通常、コンテンツは地図タイルの上にオーバーレイとしてレンダリングされ、地図の表示領域と安定領域はコンテンツに合わせて調整されます。
これらのテンプレートを使用してナビゲーション アプリのユーザー インターフェースを設計する方法について詳しくは、ナビゲーション アプリをご覧ください。
ナビゲーション テンプレートにアクセスするには、AndroidManifest.xml ファイルで androidx.car.app.NAVIGATION_TEMPLATES 権限を宣言する必要があります。
<manifest ...>
...
<uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
...
</manifest>
MapWithContentTemplate に移行する
自動車向けアプリの API レベル 7 以降では、
MapTemplate、
PlaceListNavigationTemplate、
および RoutePreviewNavigationTemplate
は非推奨となっています。非推奨のテンプレートは引き続きサポートされますが、MapWithContentTemplate への移行を強くおすすめします。
これらのテンプレートで提供される機能は、MapWithContentTemplate を使用して実装できます。例については、以下のスニペットをご覧ください。
MapTemplate
// MapTemplate (deprecated) val templateDeprecated = MapTemplate.Builder() .setPane(paneBuilder.build()) .setActionStrip(actionStrip) .setHeader(header) .setMapController(mapController) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( PaneTemplate.Builder(paneBuilder.build()) .setHeader(header) .build() ) .setActionStrip(actionStrip) .setMapController(mapController) .build()
PlaceListNavigationTemplate
// PlaceListNavigationTemplate (deprecated) val templateDeprecated = PlaceListNavigationTemplate.Builder() .setItemList(itemListBuilder.build()) .setHeader(header) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList(itemListBuilder.build()) .setHeader(header) .build() ) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build() ) .build()
RoutePreviewNavigationTemplate
// RoutePreviewNavigationTemplate (deprecated) val templateDeprecated = RoutePreviewNavigationTemplate.Builder() .setItemList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .build() ) .build() ) .setHeader(header) .setNavigateAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { /* onClick */ } .build() ) .setActionStrip(actionStrip) .setMapActionStrip(mapActionStrip) .build() // MapWithContentTemplate val template = MapWithContentTemplate.Builder() .setContentTemplate( ListTemplate.Builder() .setSingleList( ItemList.Builder() .addItem( Row.Builder() .setTitle(title) .addAction( Action.Builder() .setTitle(actionTitle) .setOnClickListener { /* onClick */ } .build() ) .build() ) .build() ) .setHeader(header) .build() ) .setActionStrip(actionStrip) .setMapController( MapController.Builder() .setMapActionStrip(mapActionStrip) .build() ) .build()
ナビゲーション メタデータを伝える
ナビゲーション アプリは、ホストに追加のナビゲーション メタデータを伝える必要があります。ホストはこの情報を使用して、車のヘッドユニットに情報を伝え、共有リソース上のナビゲーション アプリ間の競合を防ぎます。
ナビゲーション メタデータは、
NavigationManager
からアクセスできる
CarContext自動車サービスより取得できます。
val navigationManager = carContext.getCarService(NavigationManager::class.java)
ナビゲーションを開始、終了、停止する
ホストが複数のナビゲーション アプリ、経路通知、自動車クラスタデータを管理するためには、ナビゲーションの現在の状態を把握している必要があります。ユーザーがナビゲーションを開始したときには、
NavigationManager.navigationStarted を呼び出します。
同様に、ユーザーが
目的地に到着したときや、ナビゲーションをキャンセルしたときなど、ナビゲーションを終了したときには、
NavigationManager.navigationEnded を呼び出します。
NavigationManager.navigationEnded はユーザーがナビゲーションを終了したときのみ、呼び出します。たとえば、ルートの途中で経路を再計算する必要がある場合は、Trip.Builder.setLoading(true) を使用します。
状況によって、ホストは、アプリによるナビゲーションの停止を必要とします。このような場合、NavigationManager.setNavigationManagerCallback を使用してアプリが提供する NavigationManagerCallback オブジェクトの onStopNavigation が呼び出されます。この場合、アプリはクラスタ ディスプレイ、ナビゲーション通知、音声案内で、次のターンの情報の提供を停止する必要があります。
ルート情報を更新する
ナビゲーション使用時に、
NavigationManager.updateTrip を呼び出します。
この呼び出しで提供される情報は、車のクラスタ ディスプレイとヘッドアップ ディスプレイで使用される場合があります。運転中の車の種類によっては、すべての情報がユーザーに表示されるわけではありません。
たとえば、デスクトップ ヘッドユニット(DHU)には
Step に追加された
Trip は表示されますが、
Destination
情報は表示されません。
クラスタ ディスプレイに描画する
車のクラスタ ディスプレイに基本的なメタデータを表示するだけでなく、さらに臨場感のあるユーザー エクスペリエンスを提供したい場合があります。自動車向けアプリの API レベル 6 以降では、ナビゲーション アプリに独自のコンテンツをクラスタ ディスプレイ(サポートしている車の場合)に直接レンダリングするオプションがあります。ただし、次の制限があります。
- クラスタ ディスプレイ API は入力コントロールをサポートしていません。
- 自動車向けアプリの品質に関するガイドライン
NF-9: クラスタ ディスプレイには地図タイルのみが表示されます。これらのタイルには使用中のナビゲーション ルートを表示することもできます。 - クラスタ ディスプレイ API は
NavigationTemplate-
の使用のみをサポートしています。
- メイン ディスプレイとは異なり、クラスタ ディスプレイにはターンバイターン方式の指示、ETA カード、アクションなどの
NavigationTemplateの UI 要素がすべて、常に表示されるとは限りません。地図タイルのみが常に表示される UI 要素です。
- メイン ディスプレイとは異なり、クラスタ ディスプレイにはターンバイターン方式の指示、ETA カード、アクションなどの
クラスタのサポートを宣言する
アプリがクラスタ ディスプレイでのレンダリングをサポートしていることをホストアプリに伝えるには、次のスニペットのとおり、androidx.car.app.category.FEATURE_CLUSTER <category> 要素を CarAppService の <intent-filter> に追加する必要があります。
<application>
...
<service
...
android:name=".MyNavigationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
<category android:name="androidx.car.app.category.FEATURE_CLUSTER"/>
</intent-filter>
</service>
...
</application>
ライフサイクルと状態管理
API レベル 6 以降、自動車アプリのライフサイクル フローに変更はありませんが、CarAppService::onCreateSession は作成された Session に関する追加情報を提供する SessionInfo タイプのパラメータを取得するようになりました(すなわち、ディスプレイのタイプとサポートされているテンプレートのセット)。
アプリでは同じ Session クラスを使用して、クラスタとメイン ディスプレイの両方を処理するか、ディスプレイ固有の Sessions を作成して、各ディスプレイの動作をカスタマイズするか(以下のスニペットをご覧ください)を選択できます。
override fun onCreateSession(sessionInfo: SessionInfo): Session { return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) { ClusterSession() } else { MainDisplaySession() } }
クラスタ ディスプレイが表示されるタイミングと表示されるか否かは確実ではありません。また、クラスタ Session が唯一の Session になる可能性もあります(たとえば、ユーザーがアプリでナビゲーションを使用中に、メイン ディスプレイを他のアプリに切り替えた場合)。「標準」の利用規約では、アプリは NavigationManager::navigationStarted が呼び出された後にのみ、クラスタ ディスプレイをコントロールできるようになります。ただし、ナビゲーションを使用していないときにアプリがクラスタ ディスプレイに表示される場合や、クラスタ ディスプレイにまったく表示されない場合もあります。アプリのアイドル状態時の地図タイルをレンダリングして、このようなシナリオに対応するかどうかはアプリによります。
ホストは Session ごとに別々のバインダと CarContext インスタンスを作成します。つまり、ScreenManager::push や Screen::invalidate などのメソッドを使用する場合、呼び出し元の Session のみが影響を受けます。Session をまたぐ通信が必要な場合、アプリはこれらのインスタンス間の独自の通信チャネルを作成する必要があります(たとえば、ブロードキャストや共有シングルトンなどを使用)。
クラスタのサポートをテストする
Android Auto と Android Automotive OS の両方で実装をテストできます。Android Auto の場合は、デスクトップ ヘッドユニットを構成して セカンダリ クラスタ ディスプレイをエミュレートすることで、テストできます。Android Automotive OS の場合は、 API レベル 30 以上の Generic System Image でクラスタ ディスプレイをエミュレートします。
テキストまたはアイコンを使用して TravelEstimate をカスタマイズする
推定所要時間をテキスト、アイコン、またはその両方を使ってカスタマイズするには、TravelEstimate.Builder クラスの setTripIcon または setTripText メソッドを使用します。NavigationTemplate では TravelEstimate を使用して、予定到着時刻、残り時間、残りの距離とともに(またはその代わりに)テキストやアイコンを表示することもできます。
次のスニペットでは、setTripIcon と setTripText を使って推定所要時間をカスタマイズしています。
TravelEstimate.Builder( Distance.create(350.0, Distance.UNIT_METERS), arrivalTimeAtDestination ) .setTripIcon( CarIcon.Builder( IconCompat.createWithResource(carContext, R.drawable.ic_garage) ).build() ) .setTripText(CarText.create("Custom Text")) .build()
ターンバイターン通知を提供する
頻繁に更新されるナビゲーション通知を使用して、ターンバイターン(TBT)ナビゲーション指示を提供します。車の画面でナビゲーション通知として処理されるようにするには、通知ビルダーで次のことを行う必要があります。
-
NotificationCompat.Builder.setOngoingメソッドを使用して、通知を進行中としてマークします。 - 通知のカテゴリを
Notification.CATEGORY_NAVIGATIONに設定します。 - 通知を
CarAppExtenderで拡張します。
ナビゲーション通知は車の画面下部にあるレール ウィジェットに表示されます。通知の重要度レベルが IMPORTANCE_HIGH に設定されている場合は、ヘッドアップ通知(HUN)としても表示されます。CarAppExtender.Builder.setImportance メソッドで重要度が設定されていない場合は、通知チャンネルの重要度が使用されます。
CarAppExtender で PendingIntent を設定すると、ユーザーが HUN またはレール ウィジェットをタップしたときにアプリに送信されるようにできます。
値が true の NotificationCompat.Builder.setOnlyAlertOnce が呼び出された場合、重要度の高い通知は HUN で 1 回のみアラートとして表示されます。
次のスニペットは、ナビゲーション通知を作成する方法を示しています。
NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setOnlyAlertOnce(true) .setOngoing(true) .setCategory(NotificationCompat.CATEGORY_NAVIGATION) .extend( CarAppExtender.Builder() .setContentTitle(carScreenTitle) .setContentIntent( PendingIntent.getBroadcast( context, ACTION_OPEN_APP.hashCode(), Intent(ACTION_OPEN_APP).setComponent( ComponentName(context, MyNotificationReceiver::class.java) ), PendingIntent.FLAG_IMMUTABLE ) ) .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH) .build() ) .build()
距離の変化に応じて TBT 通知を定期的に更新します。これにより、レール ウィジェットが更新され、通知は HUN としてのみ表示されます。
CarAppExtender.Builder.setImportance で通知の重要度を設定することにより、HUN の動作を制御できます。重要度を IMPORTANCE_HIGH に設定すると、HUN が表示されます。他の値に設定した場合は、レール ウィジェットのみ更新されます。
PlaceListNavigationTemplate コンテンツを更新する
ドライバーが PlaceListNavigationTemplate で作成された場所のリストを閲覧しながら、ボタンをタップしてコンテンツを更新できるようにします。リストの更新を有効にするには、OnContentRefreshListener インターフェースの onContentRefreshRequested メソッドを実装し、PlaceListNavigationTemplate.Builder.setOnContentRefreshListener を使用して、テンプレートにリスナーを設定します。
次のスニペットは、テンプレートにリスナーを設定する方法を示しています。
PlaceListNavigationTemplate.Builder() .setOnContentRefreshListener { // Execute any desired logic // Then call invalidate() so onGetTemplate() is called again screen.invalidate() } .build()
更新ボタンは、リスナーに値がある場合にのみ、PlaceListNavigationTemplate のヘッダーに表示されます。
ユーザーが更新ボタンをクリックすると、OnContentRefreshListener 実装の onContentRefreshRequested メソッドが呼び出されます。
onContentRefreshRequested 内で、
Screen.invalidate メソッドを呼び出します。
次に、ホストはアプリの
Screen.onGetTemplate
メソッドにコールバックして、更新されたコンテンツを含むテンプレートを取得します。テンプレートの更新について詳しくは、
テンプレートのコンテンツを更新するをご覧ください。onGetTemplate から返される次のテンプレートが同じタイプであれば、更新としてカウントされ、テンプレート割り当てにはカウントされません。
音声によるガイダンスを提供する
車載スピーカーでナビゲーション ガイダンスを再生するには、アプリで音声フォーカスをリクエストする必要があります。AudioFocusRequest の一部として、使用量を AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE として設定します。フォーカス ゲインも AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK として設定します。
ナビゲーションをシミュレートする
Google Play ストアへのアプリの送信時に、アプリのナビゲーション機能を検証するために、アプリで NavigationManagerCallback.onAutoDriveEnabled コールバックを実装する必要があります。このコールバックが呼び出されると、ユーザーがナビゲーションを開始したときに、選択された目的地へのナビゲーションをシミュレートする必要があります。現在の Session のライフサイクルが Lifecycle.Event.ON_DESTROY 状態になると、アプリはこのモードを終了できます。
コマンドラインから以下を実行することで、onAutoDriveEnabled の実装が呼び出されることをテストできます。
adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE
例は次のとおりです。
adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE
デフォルトのナビゲーション自動車アプリ
Android Auto では、デフォルトのナビゲーション自動車アプリは、ユーザーが最後に起動したナビゲーション アプリに対応します。ユーザーがアシスタントを介してナビゲーション コマンドを呼び出した場合や、他のアプリがナビゲーションを開始するインテントを送信した場合に、デフォルトのアプリ がナビゲーション インテントを受信します。
コンテキスト内のナビゲーション アラートを表示する
Alert は、ナビゲーション画面のコンテキストを離れることなく、ドライバーに重要な情報をオプションのアクションとともに表示します。ドライバーに優れたエクスペリエンスを提供するために、
Alert は
NavigationTemplate
内機能し、ルートのナビゲーションを妨げることなく、できる限りドライバーが注意散漫にならないようにします。
Alert は NavigationTemplate 内でのみ使用できます。NavigationTemplate の外部でユーザーに通知するには、通知を表示するの説明のとおり、ヘッドアップ通知(HUN)の使用を検討してください。
たとえば、Alert を使用して次のことができます。
- 交通状況の変化など、現在のナビゲーションに関連する最新情報をドライバーに通知します。
- ドライバーに現在のナビゲーションに関する最新情報(スピード違反取締の有無など)を確認します。
- ドライバーが途中で誰かを迎えに行くかどうかなど、今後のタスクを提案し、ドライバーがそのタスクを実行するかどうかを確認します。
基本的な形式では、Alert はタイトルと Alert 継続時間で構成されます。継続時間は進行状況バーで表されます。必要に応じて、サブタイトル、アイコン、最大 2 つの Action オブジェクトを追加できます。
Alert が表示されると、ドライバーの操作によって NavigationTemplate から離れても、その情報が別のテンプレートに引き継がれることはありません。Alert がタイムアウトするか、ユーザーが操作を行うか、アプリが Alert を解除するまで、元の NavigationTemplate が維持されます。
アラートを作成する
Alert.Builder
を使用して、Alert インスタンスを作成します。
Alert.Builder( 1, // alertId CarText.create("Hello"), // title 5000 // durationMillis ) // The fields below are optional .addAction(firstAction) .addAction(secondAction) .setSubtitle(CarText.create("Subtitle")) .setIcon(CarIcon.APP_ICON) .setCallback(alertCallback) .build()
キャンセルまたは解除をリッスンするには、Alert
インターフェースの実装を作成します。
AlertCallback
AlertCallback の呼び出しパスは次のとおりです。
Alertがタイムアウトした場合、ホストはAlertCallback.onCancelメソッドをAlertCallback.REASON_TIMEOUTの値を指定して呼び出します。その後AlertCallback.onDismissメソッドを呼び出します。ドライバーが操作ボタンのいずれかをクリックすると、ホストは
Action.OnClickListenerを呼び出してから、AlertCallback.onDismissを呼び出します。Alertがサポートされていない場合、ホストはAlertCallback.onCancelをAlertCallback.REASON_NOT_SUPPORTEDの値を指定して呼び出します。ホストはAlertCallback.onDismissを呼び出しません。Alertは表示されていないためです。
アラート時間を構成する
アプリのニーズに合わせて、Alert の時間を選択します。ナビゲーション Alert の推奨時間は 10 秒です。詳しくは、ナビゲーション アラート
をご覧ください。
アラートを表示する
Alert を表示するには、アプリの
CarContextから使用できる
AppManager.showAlert
メソッドを呼び出します。
carContext.getCarService(AppManager::class.java).showAlert(alert)
Alertの ID と同じalertIdを持つAlertでshowAlertを呼び出すと、すでに表示されているAlertと同じ ID を持つAlertでshowAlertを呼び出すと、何も起こりません。Alertは更新されません。Alertを更新するには、新しいalertIdで再作成する必要があります。showAlertを、すでに表示されているAlertとは違うalertIdのAlertで呼び出すと、表示されているAlertアラートが終了します。
アラートを解除する
Alert はタイムアウトやドライバーの操作により自動的に解除されますが、情報が古くなった場合などに
Alertを手動で解除することもできます。Alert を解除するには、その Alert の
alertId
で
dismissAlert
メソッドを呼び出します。
carContext.getCarService(AppManager::class.java).dismissAlert(alert.id)
すでに
表示されている Alert と一致しない alertId で dismissAlert を呼び出した場合、何も起こりません。例外はスローされません。