Android Auto 用ナビゲーション アプリ、駐車場アプリ、充電アプリを作成する

自動車向け Android アプリ ライブラリは、ナビゲーション アプリ、駐車場アプリ、充電アプリを車で利用できるようにするためのライブラリです。ドライバーの注意散漫に関する基準に則して設計された一連のテンプレートのセットを提供し、さらに、さまざまな車の画面の要素や入力モダリティなどの細部も考慮されています。

このガイドでは、ライブラリの主な機能と概念について概説し、簡単なアプリをセットアップする手順を示します。

始める前に

  1. 自動車向け Android アプリ ライブラリの設計ガイドラインを確認してください。
  2. このセクションに記載されている主な用語と概念を確認してください。
  3. Android Auto システム UI について十分に理解しておいてください。
  4. リリースノートを確認してください。
  5. サンプルを確認してください。
  6. [クローズド ソース ライブラリのみ] 自動車向け Android アプリ ライブラリの利用規約を確認してください。

主な用語と概念

モデルとテンプレート
ユーザー インターフェースはモデル オブジェクトのグラフで表されます。モデル オブジェクトは、帰属するテンプレートで許可されているさまざまな方法で配置できます。テンプレートとは、これらのグラフでルートとして機能するモデルのサブセットです。モデルには、テキストや画像の形式でユーザーに表示される情報と、このような情報の見た目の各部分を構成する属性(テキストの色や画像サイズなど)が含まれています。ホストは、モデルを、ドライバーの注意散漫に関する基準に則したデザインのビューに変換し、さまざまな車の画面の要素や入力モダリティなど、詳細な処理を行います。
ホスト
ホストは、ライブラリの API によって提供される、アプリを車で実行するための機能を実装するバックエンド コンポーネントです。ホストは、アプリの検出とそのライフサイクルの管理、モデルのビューへの変換、ユーザー操作のアプリへの通知にいたるまで、幅広い役割を担います。モバイル デバイスでは、このホストは Android Auto によって実装されます。
テンプレートの制限事項
テンプレートに応じて、そのモデルのコンテンツに制限が課されます。たとえば、リスト テンプレートには、ユーザーに提示できるアイテムの数に制限があります。テンプレートには、タスクのフローを形成するためにテンプレートを接続する方法に対しても制限があります。たとえば、アプリが画面スタックにプッシュできるテンプレートは最大 5 つです。詳しくは、テンプレートの制限事項をご覧ください。
画面
Screen は、ライブラリによって提供されるクラスであり、アプリはこのクラスを実装して、ユーザーに提示されるインターフェースを管理します。Screen には lifecycle があり、画面の表示時に表示されるテンプレートをアプリが送信するメカニズムを提供します。また、Screen インスタンスを 画面スタックにプッシュ(push)し、ポップ(pop)して、テンプレート フローの制限事項に従わせることもできます
CarAppService
CarAppService は、抽象 Service クラスです。アプリがホストによって検出、管理されるためには、このクラスを実装し、エクスポートする必要があります。アプリの CarAppService は、CarAppService.createHostValidator を使用してホスト接続が信頼できることを検証し、次いで、CarAppService.onCreateSession を使用して接続ごとに Session インスタンスを提供します。
セッション
Session は抽象クラスであり、アプリは CarAppService.onCreateSession を使用してこのクラスを実装し、結果を返す必要があります。このクラスは、車の画面に情報を表示するためのエントリ ポイントとして機能し、車の画面にアプリの現在の状態(アプリが表示されているか、非表示になっているかなど)を通知するライフサイクルを持ちます。

Session が開始されると(アプリの初回起動時など)、ホストは 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.PARKING"/>
      </intent-filter>
    </service>

    ...
<application>

サポートされているアプリのカテゴリ

Android Auto 用のアプリが Play ストアに掲載されるためには、サポートされている自動車アプリのカテゴリに属している必要があります。アプリのカテゴリを宣言するには、自動車アプリサービスを宣言するときに、サポートされている次のカテゴリ値をインテント フィルタに 1 つ以上追加します。

  • androidx.car.app.category.NAVIGATION: ターンバイターン方式のナビゲーションの経路を提供するアプリ。
  • androidx.car.app.category.PARKING: 駐車位置の検索に関連する機能を提供するアプリ。
  • androidx.car.app.category.CHARGING: 電気自動車充電スタンドの検索に関連する機能を提供するアプリ。

各カテゴリに属するアプリの詳細な説明と条件については、自動車向け Android アプリの品質をご覧ください。

アプリの名前とアイコンを指定する

ホストがシステム UI でアプリを表すために使用するアプリ名とアイコンを指定する必要があります。

以下のように、CarAppServicelabel 要素と icon 要素を使用して、アプリを表すために使用されるアプリ名とアイコンを指定できます。

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

service 要素でラベルまたはアイコンが宣言されていない場合、ホストはアプリに指定された値にフォールバックします。

アプリの minSdkVersion を設定する

Android Auto 用のアプリの場合、Android 6.0(API レベル 23)以降をターゲットに設定する必要があります。

プロジェクト内でこの値を指定するには、スマートフォン アプリ モジュールの AndroidManifest.xml ファイル内で、uses-sdk 要素の minSdkVersion 属性を 23 以上に設定します。たとえば、次のようになります。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ... >
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
    ...
</manifest>

Android Auto のサポートを宣言する

Android Auto ホストは、アプリが Android Auto のサポートを宣言していることを確認します。このサポートを有効にするには、アプリのマニフェストに次のエントリを追加します。

<application>
    ...
    <meta-data
        android:name="com.google.android.gms.car.application"
        android:resource="@xml/automotive_app_desc"/>
    ...
</application>

このマニフェスト エントリは、パス「YourAppProject/app/src/main/res/xml/automotive_app_desc.xml」で作成する必要がある別の XML ファイルを参照します。このファイルでは、アプリがサポートする Android Auto 機能を宣言します。

自動車向け Android アプリ ライブラリを使用するアプリは、automotive_app_desc.xml ファイルで template 機能を宣言する必要があります。

<automotiveApp>
    <uses name="template" />
</automotiveApp>

CarAppService とセッションを作成する

アプリは、CarAppService クラスを拡張し、CarAppService.onCreateSession メソッドを実装する必要があります。このメソッドは、ホストへの現在の接続に対応する Session インスタンスを返します。

public final class HelloWorldService extends CarAppService {
  ...
  @Override
  @NonNull
  public Session onCreateSession() {
    return new HelloWorldSession();
  }
  ...
}

Session インスタンスは、アプリの初回起動時に使用される Screen インスタンスを返す役割を担います。

public final class HelloWorldSession extends Session {
  ...
  @Override
  @NonNull
  public Screen onCreateScreen(@NonNull Intent intent) {
    return new HelloWorldScreen();
  }
  ...
}

自動車アプリがアプリのホーム画面やランディング スクリーンではない画面から起動する必要があるシナリオ(ディープリンクの処理など)を処理する場合は、ScreenManager.push を使用してonCreateScreen から戻る前に画面のバックスタックを事前に埋め込むことができます。事前に埋め込むことで、ユーザーはアプリによって表示された最初の画面から前の画面に戻ることができます。

起動画面を作成する

アプリに表示される画面を作成するには、Screen クラスを拡張するクラスを定義し、Screen.onGetTemplate メソッドを実装します。このメソッドは、車の画面に表示する UI の状態を表す Template インスタンスを返します。

次のスニペットは、PaneTemplate テンプレートを使用してシンプルな「Hello world!」文字列を表示する Screen を宣言する方法を示しています。

public class HelloWorldScreen extends Screen {
  @NonNull
  @Override
  public Template onGetTemplate() {
    Pane pane = new Pane.Builder()
        .addRow(new Row.Builder()
            .setTitle("Hello world!")
            .build())
        .build();
    return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
  }
}

CarContext クラス

CarContext クラスは、Session インスタンスと Screen インスタンスからアクセスできる ContextWrapper サブクラスです。画面スタックを管理する ScreenManager、アプリの全般的な機能(ナビゲーション アプリで地図を描画する際の Surface オブジェクトへのアクセスなど)を担う AppManager、ターンバイターン方式のナビゲーション アプリでナビゲーション メタデータやその他のナビゲーション関連のイベントをホストに通信するために使用される NavigationManager などの自動車サービスへのアクセスを提供します。ナビゲーション アプリで利用できるライブラリ機能の全リストについては、ナビゲーション テンプレートにアクセスするをご覧ください。

また、CarContext は、車の画面の構成を使用してドローアブル リソースを読み込むことを許可する機能、インテントを使用して車内のアプリを起動する機能、ナビゲーション アプリがダークモードで地図を表示する必要があるかどうかを通知する機能なども提供します。

画面ナビゲーションを実装する

アプリでは通常、多くの画面が表示されます。各画面はさまざまなテンプレートを利用し、ユーザーの操作に応じて移り変わるインターフェースを表示します。

ScreenManager クラスは、画面をプッシュしておく画面スタックを提供します。スタックの画面は、ユーザーが車の画面で戻るボタンを選択するか、ハードウェアの戻るボタン(一部の車で提供されています)を使用すると、自動的にポップします。

次のスニペットは、メッセージ テンプレートに、「戻る」というアクションと、これをユーザーが選択したときに新しい画面をプッシュするアクションを追加する方法を示しています。

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(() -> getScreenManager().push(new NextScreen()))
            .build())
    .build();

Action.BACK オブジェクトは、自動的に ScreenManager.pop を呼び出す標準的な Action です。この動作は、CarContext が提供する OnBackPressedDispatcher インスタンスを使用してオーバーライドできます。

運転中にアプリの安全性を確保するため、画面スタックには最大 5 つまで画面を保持できます。詳しくは、テンプレートの制限事項をご覧ください。

テンプレートのコンテンツを更新する

アプリは、Screen.invalidate メソッドを呼び出すことで、Screen のコンテンツを無効化するようリクエストできます。次いで、ホストはアプリの Screen.onGetTemplate メソッドにコールバックして、新しいコンテンツを含むテンプレートを取得します。

Screen を更新するときに、新しいテンプレートがホストによってテンプレートの割り当てにカウントされないように、テンプレート内のどのコンテンツが更新可能かを把握することが重要です。 詳しくは、テンプレートの制限事項をご覧ください。

Screen と、Screen.onGetTemplate の実装を通じて返されるテンプレートのタイプとが 1 対 1 の関係でマッピングされるように、画面を構成することをおすすめします。

ユーザー入力を処理する

アプリは、適切なリスナーをそれらをサポートするモデルに渡すことで、ユーザー入力に応答できます。次のスニペットは、アプリのコードで定義されたメソッドにコールバックする OnClickListener を設定する Action モデルを作成する方法を示しています。

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

次に、onClickNavigate メソッドは CarContext.startCarApp メソッドを使用して、デフォルトのナビゲーション自動車アプリを起動します。

private void onClickNavigate() {
  Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
  getCarContext().startCarApp(intent);
}

ACTION_NAVIGATE インテントの形式など、アプリの起動方法について詳しくは、インテントを使用して自動車アプリを起動するをご覧ください。

インタラクションの続きをモバイル デバイスで行うようにユーザーをガイドするアクションなど、特定のアクションは車がパーキング状態にあるときに限り許可されます。このようなアクションの実装には、ParkedOnlyOnClickListener を使用します。車がパーキング状態にない場合、ホストはユーザーにメッセージを表示して、その状態ではアクションが許可されないことを示します。車がパーキング状態にある場合、コードは正常に実行されます。次のスニペットは、ParkedOnlyOnClickListener を使用してモバイル デバイスで設定画面を開く方法を示しています。

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(
        ParkedOnlyOnClickListener.create(this::openSettingsOnPhone)
    .build();

通知を表示する

モバイル デバイスに送信された通知は、CarAppExtender で拡張された場合にのみ車の画面に表示されます。コンテンツのタイトル、テキスト、アイコン、アクションなど、一部の通知属性は CarAppExtender でも設定でき、車の画面に表示されるときは、この設定が通知の属性をオーバーライドします。

次のスニペットは、モバイル デバイスとは異なるタイトルで、通知を車の画面に送信する方法を示しています。

Notification notification =
    new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
        .setContentTitle(titleOnThePhone)
        .extend(
            new CarAppExtender.Builder()
                .setContentTitle(titleOnTheCar)
                ...
                .build())
        .build();

通知は、ユーザー インターフェースの次の部分に影響する場合があります。

  • ヘッドアップ通知(HUN)がユーザーに表示されることがあります。
  • 通知センターにエントリが追加されたり、必要に応じて、レールにバッジが表示されたりすることがあります。
  • ナビゲーション アプリの場合、ターンバイターン通知の説明にあるように、レール ウィジェットに通知が表示されることがあります。

アプリでは、CarAppExtender ドキュメントの説明のとおり、どのユーザー インターフェース要素に影響する通知にするかを、通知の優先度を使って構成できます。

NotificationCompat.Builder.setOnlyAlertOnce を値 true で呼び出した場合、優先度の高い通知は HUN として 1 回だけ表示されます。

自動車アプリの通知を設計する方法について詳しくは、通知をご覧ください。

トーストを表示する

次のスニペットに示すように、アプリで CarToast を使用してトーストを表示できます。

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

インテントを使用して自動車アプリを起動する

CarContext.startCarApp メソッドを呼び出して、次のアクションのいずれかを実行できます。

次の例は、駐車予約の詳細を表示する画面でアプリを開く、というアクションを含む通知を作成する方法を示しています。コンテンツ インテントを使用して通知インスタンスを拡張します。コンテンツ インテントには、アプリのアクションに対する明示的なインテントをラップした PendingIntent が含まれます。

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 を呼び出します。

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 メソッドがこのインテントを処理し、駐車予約画面をスタックにプッシュします(予約画面がまだ一番上にない場合)。

@Override
public void onNewIntent(@NonNull Intent intent) {
  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 screenManager = getCarContext().getCarService(ScreenManager.class);
      screenManager.push(new ParkingReservationScreen(getCarContext()));
    }
  }
}

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

テンプレートの制限事項

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

  1. NavigationTemplate
  2. PaneTemplate
  3. MessageTemplate

テンプレートの割り当てが上限に達した状態で、アプリが新しいテンプレートを送信しようとすると、ホストはユーザーにエラー メッセージを表示します。なお、この制限はテンプレートの数に適用されますが、スタック内の Screen インスタンスの数には適用されません。たとえば、画面 A でアプリが 2 つのテンプレートを送信し、次いで画面 B をプッシュした場合、アプリはあと 3 つのテンプレートを送信できます。これに対し、各画面が 1 つのテンプレートを送信するように構成されている場合、アプリは 5 つの画面インスタンスを ScreenManager スタックにプッシュできます。

これらの制限に対して、テンプレートの更新、戻る操作、リセット操作など、特殊なケースがいくつかあります。

テンプレートの更新

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

戻る操作

1 つのタスク内でサブフローを有効にできるように、アプリが ScreenManager スタックから Screen をポップすると、ホストがこれを検出し、アプリの戻る操作後のテンプレート数に基づいて残りの割り当てを更新します。

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

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

リセット操作

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

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

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

駐車場アプリまたは充電アプリを作成する

このセクションでは、駐車場アプリまたは充電アプリの機能を実装するための、ライブラリの各機能について詳しく説明します。

マニフェストでカテゴリのサポートを宣言する

アプリは、CarAppService のインテント フィルタで自動車アプリのカテゴリとして androidx.car.app.category.PARKING または androidx.car.app.category.CHARGING のいずれかを宣言する必要があります。次に例を示します。

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.PARKING"/>
      </intent-filter>
    </service>
    ...
<application>

地図のテンプレートにアクセスする

アプリは、ホストによってレンダリングされる PlaceListMapTemplate にアクセスできます。これは、地図とともにスポットのリストを表示するように設計されたテンプレートです。

このテンプレートにアクセスするには、アプリは AndroidManifest.xmlandroidx.car.app.MAP_TEMPLATES 権限を宣言する必要があります。

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

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

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

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

ナビゲーション アプリでは、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 自動車サービスに設定します。

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 自動車サービスを通じて取得できます。

NavigationManager navigationManager = carContext.getCarService(NavigationManager.class);
ナビゲーションの開始、終了、停止

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

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

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

ルート情報

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

情報がクラスタに到達したことを確認するため、デスクトップ ヘッドユニット(DHU)ツールに、簡単なクラスタ表示を設定できます。次の内容の cluster.ini ファイルを作成します。

[general]
instrumentcluster = true

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

dhu -c cluster.ini

ターンバイターン通知

頻繁に更新されるナビゲーション通知とともに、ターンバイターン(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 回だけアラートを送信します。

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

new NotificationCompat.Builder(context, myNotificationChannelId)
    ...
    .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())
ターンバイターン通知に関するガイドライン

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

音声案内

車載スピーカーでナビゲーション ガイダンスを再生するには、アプリで音声フォーカスをリクエストする必要があります。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 では、デフォルトのナビゲーション自動車アプリは、ユーザーが最後に起動したナビゲーション アプリに対応します。たとえば、ユーザーがアシスタントを介してナビゲーション コマンドを呼び出した場合や、他のアプリからナビゲーションを開始するためにインテントを送信すると、このアプリがナビゲーション インテントを受信します。

CarAppService、セッション、画面のライフサイクル

Session クラスと Screen クラスは、LifecycleOwner インターフェースを実装します。ユーザーがアプリを操作すると、Session オブジェクトと Screen オブジェクトのライフサイクル コールバックが呼び出されます。次の図をご覧ください。

CarAppService とセッションのライフサイクル

図 1. Session ライフサイクル。

詳しくは、Session.getLifecycle メソッドのドキュメントをご覧ください。

画面のライフサイクル

図 2. Screen ライフサイクル。

詳しくは、Screen.getLifecycle のドキュメントをご覧ください。

テスト ライブラリ

自動車向け Android のテスト ライブラリは、テスト環境でのアプリの動作検証に使用できる補助クラスを提供します。たとえば、SessionController を使用すると、ホストへの接続をシミュレートして、正しい ScreenTemplate が作成されて返されることを確認できます。

使用例については、サンプルをご覧ください。

実際のヘッドユニットでアプリを実行する

Google が提供するデスクトップ ヘッドユニットではなく、実際のヘッドユニットでアプリを実行するには、Google Play ストアでアプリを配布する必要があります。これにより、Google のガイドラインに従って、アプリのテストと審査が確実に行われます。このガイドラインに基づいて、アプリが自動車環境に適していることと、ドライバーの注意散漫テストに合格していることが保証されます。

開発中にテストする方法として、デスクトップ ヘッドユニットを使用する方法と、Google Play ストアの「内部テストトラック」にアプリをプッシュする方法の 2 つがあります。内部テストトラックを使用すると、手動でチームを追加して内部テストを行うことができます。このトラックのリリースでは Play ストアのレビューは必要ありません。

クローズド トラックなどの他のトラックに APK をリリースするたびに、アプリは、Play ストアでそのトラックに承認される前に審査を受けることになります。審査に合格しなかった場合、不合格であった理由に関する情報が送信されます。このプロセスでは、ガイドラインを遵守するように問題を修正できます。

自動車向け Android アプリ ライブラリに関する問題を報告する

ライブラリに問題が見つかった場合は、Google Issue Tracker を使用して報告します。問題テンプレートに必要な情報をすべて記入してください。

新しい問題を報告する

新しい問題を報告する前に、その問題がライブラリのリリースノートに記載されていないか、すでに問題リスト内で報告されていないかご確認ください。Issue Tracker 内で各問題の横にあるスターアイコンをクリックすると、問題を登録して投票することができます。詳細については、問題を登録する手順をご覧ください。