ナビゲーション アプリを作成する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

このセクションでは、ターンバイターン方式のナビゲーション アプリの機能を実装するための、ライブラリの各機能について詳しく説明します。

マニフェストでナビゲーション サポートを宣言する

ナビゲーション アプリでは、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>

ナビゲーション インテントをサポートする

アプリに送信されるナビゲーション インテント(音声クエリを使用して Google アシスタントから取得したものを含む)をサポートするために、アプリは Session.onCreateScreenSession.onNewIntent の中で CarContext.ACTION_NAVIGATE インテントを処理する必要があります。

インテントの形式について詳しくは、CarContext.startCarApp のドキュメントをご覧ください。

ナビゲーション テンプレートにアクセスする

ナビゲーション アプリは、ナビゲーション アプリ専用に設計された以下のテンプレートにアクセスできます。これらのテンプレートはすべて、地図を描画するためにアプリがアクセスできるサーフェスを背景に表示するとともに、テンプレートごとに異なる、アプリ提供の情報を表示します。

  • NavigationTemplate: 地図を表示します。必要に応じて、アクティブなナビゲーション中に、情報メッセージ、経路案内、所要時間も表示します。
  • PlaceListNavigationTemplate: 場所のリストを表示します。これらの場所に対応するマーカーを地図上に描画できます。
  • RoutePreviewNavigationTemplate: 経路のリストを表示します。これらの経路のひとつを選択して、地図上でハイライト表示できます。

これらのテンプレートを使用してナビゲーション アプリのユーザー インターフェースを設計する方法について詳しくは、自動車向け Android アプリ ライブラリの設計ガイドラインをご覧ください。

ナビゲーション テンプレートにアクセスするには、アプリは AndroidManifest.xmlandroidx.car.app.NAVIGATION_TEMPLATES 権限を宣言する必要があります。

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

地図の描画

ナビゲーション アプリは、適用するテンプレート上で地図を描画するために Surface にアクセスできます。

SurfaceContainer オブジェクトにアクセスするには、SurfaceCallback インスタンスを AppManager 自動車サービスに設定します。

Kotlin

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

Java

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

SurfaceCallback は、SurfaceContainer が利用可能な場合のコールバックのほか、Surface のプロパティが変更された場合の他のコールバックを提供します。

サーフェスにアクセスするには、アプリは AndroidManifest.xmlandroidx.car.app.ACCESS_SURFACE 権限を宣言する必要があります。

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

地図の表示エリア

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

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

ダークモード

条件が満たされているとホストが判断した場合、ナビゲーション アプリは、Android Auto アプリの品質に関するガイドラインの説明のとおり、適切な暗い色を使用して Surface インスタンスに地図を再描画する必要があります。

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

ナビゲーション アプリは、ホストに追加のナビゲーション メタデータを伝える必要があります。ホストはこの情報を使用して、車のヘッドユニットに情報を伝え、共有リソース上のナビゲーション アプリ間の競合を防ぎます。

ナビゲーション メタデータは、CarContext からアクセスできる NavigationManager 自動車サービスを通じて取得できます。

Kotlin

val navigationManager = carContext.getCarService(NavigationManager::class.java)

Java

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);

ナビゲーションの開始、終了、停止

複数のナビゲーション アプリ、経路通知、自動車クラスタデータを管理するには、ホストはナビゲーションの現在の状態を認識する必要があります。ユーザーがナビゲーションを開始したときに、アプリは NavigationManager.navigationStarted を呼び出す必要があります。同様に、ユーザーが目的地に到着したときや、ナビゲーションをキャンセルしたときなど、ナビゲーションが終了したときに、アプリは NavigationManager.navigationEnded を呼び出す必要があります。

ユーザーが操作を終了したときのみ、NavigationManager.navigationEnded を呼び出してください。たとえば、ルートの途中で経路を再計算する必要がある場合は、Trip.Builder.setLoading(true) を使用します。

状況によって、ホストは、アプリがナビゲーションを停止することを必要とする場合があります。このような場合、NavigationManager.setListener を使用してアプリが提供する NavigationManagerListener オブジェクトの stopNavigation が呼び出されます。するとアプリは、クラスタ表示、ナビゲーション通知、音声案内で、次のターンの情報の発行を停止しなければならなくなります。

ルート情報

アクティブなナビゲーション中に、アプリは NavigationManager.updateTrip を呼び出す必要があります。この呼び出しで提供される情報は、車のクラスタとヘッドアップ ディスプレイで使用されます。運転中の車によっては、一部の情報がユーザーに表示されない場合があります。たとえば、デスクトップ ヘッドユニットには、Trip に追加された Step は表示されますが、Destination 情報は表示されません。

情報がクラスタに到達しているかどうかをテストするため、簡単なクラスタ表示を行うようにデスクトップ ヘッドユニット(DHU)ツールを構成できます。次の内容の cluster.ini ファイルを作成します。

[general]
instrumentcluster = true

その後、コマンドライン パラメータを追加して DHU を呼び出すことができます。

dhu -c cluster.ini

テキストやアイコンを使用して TravelEstimate をカスタマイズする

テキストやアイコンで推定所要時間をカスタマイズするには、TravelEstimate.BuildersetTripIcon メソッド、または setTripText メソッドを使用します。NavigationTemplate では、必要に応じて TravelEstimate を使用して、予定到着時刻、残り時間、残りの距離とともに(またはその代わりに)テキストやアイコンを表示します。

図 1. カスタムのアイコンとテキストを含む推定所要時間

次のスニペットでは、TravelEstimate.BuildersetTripIcon メソッドと setTripTextメソッドで推定所要時間をカスタマイズします。

Kotlin

TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...))
      ...
      .setTripIcon(CarIcon.Builder(...).build())
      .setTripText(CarText.create(...))
      .build()

Java

new TravelEstimate.Builder(Distance.create(...), DateTimeWithZone.create(...))
      ...
      .setTripIcon(CarIcon.Builder(...).build())
      .setTripText(CarText.create(...))
      .build();

ターンバイターン通知

頻繁に更新されるナビゲーション通知とともに、ターンバイターン(TBT)方式のナビゲーション指示を提供できます。車の画面でナビゲーション通知として処理されるようにするには、通知ビルダーで以下を行う必要があります。

  1. NotificationCompat.Builder.setOngoing メソッドを使用して、通知を進行中としてマークします。
  2. 通知のカテゴリを Notification.CATEGORY_NAVIGATION に設定します。
  3. CarAppExtender を使用して通知を拡張します。

ナビゲーション通知は車の画面下部にあるレール ウィジェットに表示されます。通知の重要度レベルが IMPORTANCE_HIGH に設定されている場合は、ヘッドアップ通知(HUN)としても表示されます。CarAppExtender.Builder.setImportance メソッドで重要度が設定されていない場合は、通知チャンネルの重要度が使用されます。

アプリは、ユーザーが HUN またはレール ウィジェットをタップしたときにアプリに送信される PendingIntentCarAppExtender で設定できます。

値を true に指定して NotificationCompat.Builder.setOnlyAlertOnce が呼び出された場合、重要度の高い通知は HUN で 1 回だけアラートを送信します。

次のスニペットは、ナビゲーション通知を作成する方法を示しています。

Kotlin

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)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build()

Java

new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    ...
    .setOnlyAlertOnce(true)
    .setOngoing(true)
    .setCategory(NotificationCompat.CATEGORY_NAVIGATION)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(carScreenTitle)
            ...
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_OPEN_APP.hashCode(),
                    new Intent(ACTION_OPEN_APP).setComponent(
                        new ComponentName(context, MyNotificationReceiver.class)),
                        0))
            .setImportance(NotificationManagerCompat.IMPORTANCE_HIGH)
            .build())
    .build();

ターンバイターン通知に関するガイドライン

ナビゲーション アプリは距離の変化に応じて TBT 通知を定期的に更新する必要があります。これにより、レール ウィジェットが更新され、通知は HUN としてのみ表示されます。アプリは CarAppExtender.Builder.setImportance メソッドで通知の重要度を設定することで、HUN の動作を制御できます。重要度を IMPORTANCE_HIGH に設定すると HUN が表示され、他の値に設定すると、レール ウィジェットの更新のみが行われます。

PlaceListNavigationTemplate コンテンツを更新する

ドライバーが PlaceListNavigationTemplate で作成された場所のリストを閲覧しながら、ボタンをタップするだけでコンテンツを更新できるようにします。 OnContentRefreshListener インターフェースの onContentRefreshRequested メソッドを実装し、PlaceListNavigationTemplate.Builder.setOnContentRefreshListener を使用してテンプレートのリスナーを設定することで、リストの更新が有効になります。

次のスニペットで、テンプレートにリスナーを設定する方法を示します。

Kotlin

PlaceListNavigationTemplate.Builder()
    ...
    .setOnContentRefreshListener {
        // Execute any desired logic
        ...
        // Then call invalidate() so onGetTemplate() is called again
        invalidate()
    }
    .build()

Java

new PlaceListNavigationTemplate.Builder()
        ...
        .setOnContentRefreshListener(() -> {
            // Execute any desired logic
            ...
            // Then call invalidate() so onGetTemplate() is called again
            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 では、デフォルトのナビゲーション自動車アプリは、ユーザーが最後に起動したナビゲーション アプリに対応します。たとえば、ユーザーがアシスタントを介してナビゲーション コマンドを呼び出した場合や、他のアプリからナビゲーションを開始するためにインテントを送信すると、このアプリがナビゲーション インテントを受信します。

ユーザーに地図の操作を許可する

地図のズームやパンなど、ユーザーが地図のさまざまな部分を表示できるようサポートする機能を追加できます。テンプレートごとに、Car App API の最小レベル要件が異なります。実装するテンプレートの最小レベルについては、下記の表をご覧ください。

テンプレートインタラクティビティをサポートする Car App API レベル
NavigationTemplate2
PlaceListNavigationTemplate4
RoutePreviewNavigationTemplate4
MapTemplate5

SurfaceCallback メソッド

SurfaceCallback には、NavigationTemplatePlaceListNavigationTemplateRoutePreviewNavigationTemplate、または MapTemplate テンプレートで作成したマップにマップ インタラクティビティを追加するためのコールバック メソッドが用意されています(onClickonScrollonScaleonFling)。これらのコールバックとユーザーの操作との関連については、次の表をご覧ください。

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

マップ アクション ストリップ

NavigationTemplatePlaceListNavigationTemplateRoutePreviewNavigationTemplate、および MapTemplate テンプレートでは、地図に関連する操作(拡大 / 縮小、センタリング、コンパスの表示など、アプリで可能にする操作)に対応する、マップ アクション ストリップを設定できます。マップ アクション ストリップには、タスクの階層を変更することなく更新できる、アイコンのみのボタンを 4 つまで設定できます。アクション ストリップと同様に、マップ アクション ストリップはアイドル状態には非表示になり、アクティブ状態になると表示されます。

マップ インタラクティビティ コールバックを受け取るようにするには、マップ アクション ストリップに Action.PAN ボタンを追加する必要があります。アプリのマップ アクション ストリップに Action.PAN ボタンを設定しないと、SurfaceCallback メソッドからユーザー入力を取得できず、ホストで有効になっていたパンモードが終了します。ユーザーがパンボタンを押すと、ホストがパンモードに切り替わります。パンボタンはタッチスクリーンには表示されません。

パンモード

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

安定領域

安定領域は、アイドル状態とアクティブ状態の間に更新されます。アプリで速度、速度制限、道路警告などの運転関連の情報を表示させる場合は、地図上の重要な情報がマップ アクション ストリップによって遮られないように、安定領域のサイズに応じて情報が取得されるようにします。

コンテキスト内のナビゲーション アラート

Alert は、ナビゲーション画面のコンテキストを離れることなく、ドライバーに重要な情報をオプションのアクションとともに表示します。ドライバーに最高のエクスペリエンスを提供するために、AlertNavigationTemplate 内で動作し、ルートのナビゲーションを妨げることなく、できるだけドライバーの注意がおろそかにならないようにします。

AlertNavigationTemplate 内でのみ使用できます。NavigationTemplate の外部でユーザーに通知するには、ディスプレイ通知で説明されているように、ヘッドアップ通知(HUN)の使用を検討してください。

たとえば、Alert を使用して次のことを行います。

  • 交通状況の変化など、現在のナビゲーションに関連する最新情報をドライバーに通知します。
  • ドライバーに、現在のナビゲーションに関する最新情報(スピード違反取締の有無など)を確認します。
  • ドライバーが途中で誰かを迎えに行くかどうかなど、予定されるタスクを提案し、ドライバーがそれを受け入れるかどうか確認します。

基本的な形式では、Alert はタイトルと Alert 継続時間で構成されます。継続時間は進行状況バーで表されます。必要に応じて、サブタイトル、アイコン、最大 2 つの Action を追加できます。

図 1. コンテキスト内のナビゲーション アラート

Alert が表示されると、ドライバーの操作によって NavigationTemplate から離れても、対応する情報が別のテンプレートに引き継がれることはありません。 Alert がタイムアウトするか、ユーザーが操作を行うか、アプリが Alert を解除するまで、元の NavigationTemplate が維持されます。

アラート時間を設定する

アプリのニーズに合わせて、Alert の時間を選択します。ナビゲーション Alert の推奨時間は 10 秒です。自動車向け Android の設計ガイドラインを参考にしてください。

アラートを作成する

Alert.Builder を使用して、Alert インスタンスを作成します。

Kotlin

Alert.Builder(
        /*alertId*/ 1,
        /*title*/ CarText.create("Hello"),
        /*durationMillis*/ 5000
    )
    // The fields below are optional
    .addAction(firstAction)
    .addAction(secondAction)
    .setSubtitle(CarText.create(...))
    .setIcon(CarIcon.APP_ICON)
    .setCallback(...)
    .build()

Java

new Alert.Builder(
        /*alertId*/ 1,
        /*title*/ CarText.create("Hello"),
        /*durationMillis*/ 5000
    )
    // The fields below are optional
    .addAction(firstAction)
    .addAction(secondAction)
    .setSubtitle(CarText.create(...))
    .setIcon(CarIcon.APP_ICON)
    .setCallback(...)
    .build();

Alert のキャンセルまたは解除をリッスンするには、AlertCallback インターフェースの実装を作成します。AlertCallback の呼び出しパスは次のとおりです。

アラートを表示する

Alert を表示するには、アプリの CarContext を通じて利用できる AppManager.showAlert メソッドを呼び出します。

// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
  • showAlert を呼び出すときに、使用する Alert が現在表示されている Alert と同じ alertId を持つ場合、何も起こりません(Alert は更新されません。Alert を更新するには、Alert を新しい alertId で再作成する必要があります)。
  • showAlert を呼び出すとき、使用する Alert が現在表示されている Alert とは異なる alertId を持つ場合、現在表示されている Alert は解除されます。

アラートを解除する

Alert はタイムアウトやドライバーの操作により自動的に解除されますが、Alert を手動で解除することもできます。たとえば、Alert の情報が古くなったため、これを解除したいとします。Alert を解除するには、AlertalertIddismissAlert メソッドを呼び出します。

// Dismiss the same alert
carContext.getCarService(AppManager.class).dismissAlert(alert.getId())

dismissAlert を呼び出すとき、使用する alertId が現在表示されている Alert(存在する場合)と一致しない場合、何も起こりません(例外はスローされません)。