Navigations-App erstellen

Auf dieser Seite werden die verschiedenen Funktionen der Car App Library beschrieben, mit denen Sie die Funktionalität Ihrer detaillierten Routenführungs-App implementieren können.

Navigationsunterstützung im Manifest deklarieren

Ihre Navigations-App muss die androidx.car.app.category.NAVIGATION App-Kategorie im Intent Filter ihres CarAppService deklarieren:

<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>

Navigations-Intents unterstützen

Verschiedene Intent-Formate ermöglichen es Navigations-Apps, mit anderen Apps wie POI-Apps und Sprachassistenten zu arbeiten.

Wenn Sie diese Intent-Formate unterstützen möchten, müssen Sie zuerst die Unterstützung deklarieren, indem Sie Intent-Filter im Manifest Ihrer App hinzufügen. Der Speicherort dieser Intent-Filter hängt von der Plattform ab:

  • Android Auto: Im <activity> Manifestelement für die Activity, die den Intent verarbeitet, wenn ein Nutzer Android Auto nicht verwendet.
  • Android Automotive OS: Im <activity> Manifestelement für die CarAppActivity.

Lesen und verarbeiten Sie dann die Intents in den onCreateScreen() und onNewIntent() Callbacks in der Session Implementierung Ihrer App.

Erforderliche Intent-Formate

Um die NF-6 Qualitätsanforderung zu erfüllen, muss Ihre App Navigations-Intents verarbeiten.

Optionale Intent-Formate

Sie können auch die folgenden Intent-Formate unterstützen, um die Interoperabilität Ihrer App weiter zu verbessern:

Auf die Navigationsvorlagen zugreifen

Navigations-Apps können auf die folgenden Vorlagen zugreifen, die im Hintergrund eine Oberfläche mit der Karte und während der aktiven Navigation detaillierte Routenführung anzeigen.

  • NavigationTemplate: Eine Vorlage, die während der aktiven Navigation eine optionale Informationsnachricht und Reisezeitprognosen anzeigt.
  • MapWithContentTemplate: Eine Vorlage, mit der eine App Kartenkacheln mit einer Art von Inhalt rendern kann (z. B. eine Liste). Der Inhalt wird in der Regel als Overlay über den Kartenkacheln gerendert, wobei die Karte sichtbar ist und stabile Bereiche an den Inhalt angepasst werden.

Weitere Informationen zum Entwerfen der Benutzeroberfläche Ihrer Navigations-App mit diesen Vorlagen finden Sie unter Navigations-Apps.

Damit Ihre App auf die Navigationsvorlagen zugreifen kann, muss sie die Berechtigung androidx.car.app.NAVIGATION_TEMPLATES in der Datei AndroidManifest.xml deklarieren:

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

Zum Zeichnen von Karten ist eine zusätzliche Berechtigung erforderlich.

Zu MapWithContentTemplate migrieren

Ab Car App API Level 7 sind die MapTemplate, PlaceListNavigationTemplate, und RoutePreviewNavigationTemplate verworfen. Verworfene Vorlagen werden weiterhin unterstützt, aber die Migration zu MapWithContentTemplate wird dringend empfohlen.

Die von diesen Vorlagen bereitgestellte Funktionalität kann mit MapWithContentTemplate implementiert werden. Beispiele finden Sie in den folgenden Snippets:

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()

Navigations-Apps müssen zusätzliche Navigationsmetadaten mit dem Host kommunizieren. Der Host verwendet die Informationen, um Informationen für das Infotainmentsystem des Fahrzeugs bereitzustellen und zu verhindern, dass Navigations-Apps um gemeinsam genutzte Ressourcen konkurrieren.

Navigationsmetadaten werden über den NavigationManager Car-Dienst bereitgestellt, auf den über den CarContextzugegriffen werden kann:

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

Navigation starten, beenden und stoppen

Damit der Host mehrere Navigations-Apps, Routingbenachrichtigungen und Fahrzeugclusterdaten verwalten kann, muss er den aktuellen Status der Navigation kennen. Wenn ein Nutzer die Navigation startet, rufen Sie NavigationManager.navigationStarted auf. Wenn die Navigation beendet wird, z. B. wenn der Nutzer sein Ziel erreicht oder die Navigation abbricht, rufen Sie NavigationManager.navigationEnded auf.

Rufen Sie NavigationManager.navigationEnded nur auf, wenn der Nutzer die Navigation beendet. Wenn Sie beispielsweise die Route während einer Fahrt neu berechnen müssen, verwenden Sie stattdessen Trip.Builder.setLoading(true).

Gelegentlich muss der Host eine App anweisen, die Navigation zu beenden, und ruft onStopNavigation in einem NavigationManagerCallback Objekt auf, das von Ihrer App über NavigationManager.setNavigationManagerCallback bereitgestellt wird. Die App muss dann die Ausgabe von Informationen zur nächsten Abzweigung auf dem Cluster-Display, Navigationsbenachrichtigungen und Sprachführung beenden.

Fahrtinformationen aktualisieren

Rufen Sie während der aktiven Navigation NavigationManager.updateTrip auf. Die in diesem Aufruf bereitgestellten Informationen können vom Cluster und den Heads-up-Displays des Fahrzeugs verwendet werden. Je nach Fahrzeug werden nicht alle Informationen für den Nutzer angezeigt. Die Desktop Head Unit (DHU) zeigt beispielsweise den Step an, der der Trip hinzugefügt wurde, aber nicht die Destination -Informationen.

Auf dem Cluster-Display zeichnen

Um eine möglichst immersive Nutzererfahrung zu bieten, können Sie mehr als nur grundlegende Metadaten auf dem Cluster-Display des Fahrzeugs anzeigen. Ab Car App API Level 6 können Navigations-Apps ihre eigenen Inhalte direkt auf dem Cluster-Display rendern (in unterstützten Fahrzeugen). Dabei gelten die folgenden Einschränkungen:

  • Die Cluster-Display-API unterstützt keine Eingabesteuerelemente.
  • Qualitätsrichtlinie für Car-Apps NF-9: Auf dem Cluster-Display sollten nur Kartenkacheln angezeigt werden. Optional kann auf diesen Kacheln eine aktive Navigationsroute angezeigt werden.
  • Die Cluster-Display-API unterstützt nur die Verwendung von NavigationTemplate
    • Im Gegensatz zu Hauptdisplays werden auf Cluster-Displays möglicherweise nicht alle UI-Elemente von NavigationTemplate angezeigt, z. B. detaillierte Wegbeschreibungen, Karten mit geschätzter Ankunftszeit und Aktionen. Kartenkacheln sind das einzige UI-Element, das immer angezeigt wird.

Clusterunterstützung deklarieren

Damit die Host-Anwendung weiß, dass Ihre App das Rendern auf Cluster Displays unterstützt, müssen Sie dem <intent-filter> Ihres CarAppService ein androidx.car.app.category.FEATURE_CLUSTER <category> Element hinzufügen, wie im folgenden Snippet gezeigt:

<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>

Lebenszyklus- und Statusverwaltung

Ab API Level 6 bleibt der Lebenszyklusablauf der Car-App gleich , aber jetzt verwendet CarAppService::onCreateSession einen Parameter vom Typ SessionInfo, der zusätzliche Informationen zur erstellten Session enthält (nämlich den Display typ und die Menge der unterstützten Vorlagen).

Apps können entweder dieselbe Session Klasse verwenden, um sowohl das Cluster- als auch das Hauptdisplay zu verarbeiten, oder displayspezifische Sessions erstellen, um das Verhalten auf jedem Display anzupassen (wie im folgenden Snippet gezeigt).

override fun onCreateSession(sessionInfo: SessionInfo): Session {
    return if (sessionInfo.displayType == SessionInfo.DISPLAY_TYPE_CLUSTER) {
        ClusterSession()
    } else {
        MainDisplaySession()
    }
}

Es gibt keine Garantie dafür, wann oder ob das Cluster-Display bereitgestellt wird. Es ist auch möglich, dass die Cluster-Session die einzige Session ist (z. B. wenn der Nutzer das Hauptdisplay zu einer anderen App gewechselt hat, während Ihre App aktiv navigiert). Standardmäßig erhält die App erst dann die Kontrolle über das Cluster-Display, wenn NavigationManager::navigationStarted aufgerufen wurde. Es ist jedoch möglich, dass die App das Cluster-Display erhält, obwohl keine aktive Navigation stattfindet, oder dass sie das Cluster-Display nie erhält. Es liegt an Ihrer App, diese Szenarien zu verarbeiten, indem sie den inaktiven Zustand der Kartenkacheln Ihrer App rendert.

Der Host erstellt separate Binder- und CarContext-Instanzen pro Session. Wenn Sie Methoden wie ScreenManager::push oder Screen::invalidate verwenden, ist nur die Session betroffen, aus der sie aufgerufen werden. Apps sollten eigene Kommunikationskanäle zwischen diesen Instanzen erstellen, wenn eine Session Kommunikation erforderlich ist (z. B. durch Broadcasts, ein gemeinsam genutztes Singleton oder etwas anderes).

Clusterunterstützung testen

Sie können Ihre Implementierung sowohl unter Android Auto als auch unter Android Automotive OS testen. Unter Android Auto konfigurieren Sie dazu die Desktop Head Unit so, dass sie ein sekundäres Cluster-Display emuliert. Unter Android Automotive OS emulieren die generischen System-Images für API Level 30 und höher ein Cluster-Display.

Reisezeitprognose mit Text oder einem Symbol anpassen

Wenn Sie die Reisezeitprognose mit Text, einem Symbol oder beidem anpassen möchten, verwenden Sie die TravelEstimate.Builder Klasse setTripIcon oder setTripText Methoden. The NavigationTemplate verwendet TravelEstimate, um optional Text und Symbole neben oder anstelle der geschätzten Zeit der Ankunft, der verbleibenden Zeit und der verbleibenden Entfernung festzulegen.

Abbildung 1. Reisezeitprognose mit benutzerdefiniertem Symbol und Text.

Im folgenden Snippet werden setTripIcon und setTripText verwendet, um die Reisezeitprognose anzupassen:

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()

Detaillierte Routenführung bereitstellen

Stellen Sie detaillierte Routenführung mit einer häufig aktualisierten Navigationsbenachrichtigung bereit. Damit Ihre Benachrichtigung auf dem Autobildschirm als Navigationsbenachrichtigung behandelt wird, muss der Builder Ihrer Benachrichtigung Folgendes tun:

  1. Markieren Sie die Benachrichtigung mit der NotificationCompat.Builder.setOngoing Methode als laufend.
  2. Legen Sie die Kategorie der Benachrichtigung auf Notification.CATEGORY_NAVIGATION fest.
  3. Erweitern Sie die Benachrichtigung mit einem CarAppExtender.

Eine Navigationsbenachrichtigung wird im Widget in der Leiste unten auf dem Autobildschirm angezeigt. Wenn die Wichtigkeit der Benachrichtigung auf IMPORTANCE_HIGH festgelegt ist, wird sie auch als wichtige Benachrichtigung (HUN) angezeigt. Wenn die Wichtigkeit nicht mit der CarAppExtender.Builder.setImportance Methode festgelegt wird, wird die Wichtigkeit des Benachrichtigungskanals verwendet.

Die App kann in das CarAppExtender ein PendingIntent festlegen, das an die App gesendet wird, wenn der Nutzer auf die HUN oder das Widget in der Leiste tippt.

Wenn NotificationCompat.Builder.setOnlyAlertOnce mit dem Wert true aufgerufen wird, wird eine Benachrichtigung mit hoher Wichtigkeit nur einmal in der HUN angezeigt.

Das folgende Snippet zeigt, wie Sie eine Navigationsbenachrichtigung erstellen:

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()

Aktualisieren Sie die detaillierte Wegbeschreibung regelmäßig bei Änderungen der Entfernung, wodurch das Widget in der Leiste aktualisiert wird, und zeigen Sie die Benachrichtigung nur als HUN an. Sie können das HUN-Verhalten steuern, indem Sie die Wichtigkeit der Benachrichtigung mit CarAppExtender.Builder.setImportance festlegen. Wenn Sie die Wichtigkeit auf IMPORTANCE_HIGH festlegen, wird eine HUN angezeigt. Wenn Sie einen anderen Wert festlegen, wird nur das Widget in der Leiste aktualisiert.

Inhalte von PlaceListNavigationTemplate aktualisieren

Sie können Fahrern ermöglichen, Inhalte mit einem Fingertipp zu aktualisieren, während sie Listen von Orten durchsuchen, die mit PlaceListNavigationTemplate erstellt wurden. Wenn Sie die Aktualisierung von Listen aktivieren möchten, implementieren Sie die OnContentRefreshListener Schnittstelle's onContentRefreshRequested Methode und verwenden Sie PlaceListNavigationTemplate.Builder.setOnContentRefreshListener um den Listener für die Vorlage festzulegen.

Das folgende Snippet zeigt, wie Sie den Listener für die Vorlage festlegen:

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

Die Schaltfläche „Aktualisieren“ wird nur im Header von PlaceListNavigationTemplate angezeigt, wenn der Listener einen Wert hat.

Wenn der Nutzer auf die Schaltfläche „Aktualisieren“ klickt, wird die Methode onContentRefreshRequested Ihrer OnContentRefreshListener-Implementierung aufgerufen. Rufen Sie in onContentRefreshRequested die Screen.invalidate Methode auf. Der Host ruft dann die Methode Screen.onGetTemplate Ihrer App auf, um die Vorlage mit den aktualisierten Inhalten abzurufen. Weitere Informationen zum Aktualisieren von Vorlagen finden Sie unter Inhalte einer Vorlage aktualisieren. Solange die nächste Vorlage zurückgegeben von onGetTemplate vom selben Typ ist, gilt sie als Aktualisierung und wird nicht auf das Vorlagenkontingent angerechnet.

Sprachansagen bereitstellen

Wenn Sie Navigationsanweisungen über die Autolautsprecher wiedergeben möchten, muss Ihre App den Audiofokus anfordern. Legen Sie im Rahmen Ihrer AudioFocusRequest die Verwendung als AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE fest. Legen Sie außerdem die Fokusverstärkung als AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK fest.

Navigation simulieren

Wenn Sie die Navigationsfunktion Ihrer App überprüfen möchten, bevor Sie sie im Google Play Store einreichen, muss Ihre App den NavigationManagerCallback.onAutoDriveEnabled Callback implementieren. Wenn dieser Callback aufgerufen wird, muss Ihre App die Navigation zum ausgewählten Ziel simulieren, wenn der Nutzer die Navigation startet. Ihre App kann diesen Modus beenden, wenn der Lebenszyklus der aktuellen Session den Lifecycle.Event.ON_DESTROY Status erreicht.

Sie können testen, ob Ihre Implementierung von onAutoDriveEnabled aufgerufen wird, indem Sie den folgenden Befehl über die Befehlszeile ausführen:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

Dies ist im folgenden Beispiel dargestellt:

adb shell dumpsys activity service androidx.car.app.samples.navigation.car.NavigationCarAppService AUTO_DRIVE

Standardmäßige Navigations-Car-App

In Android Auto entspricht die standardmäßige Navigations-Car-App der letzten Navigations-App, die der Nutzer gestartet hat. Die Standard-App empfängt Navigations-Intents, wenn der Nutzer Navigationsbefehle über den Assistant aufruft oder wenn eine andere App einen Intent zum Starten der Navigation sendet.

Kontextbezogene Navigationsbenachrichtigungen anzeigen

Alert zeigt dem Fahrer wichtige Informationen mit optionalen Aktionen an, ohne den Kontext des Navigationsbildschirms zu verlassen. Um dem Fahrer die bestmögliche Erfahrung zu bieten, Alert funktioniert in NavigationTemplate , um die Navigationsroute nicht zu blockieren und die Ablenkung des Fahrers zu minimieren.

Alert ist nur in NavigationTemplate verfügbar. Wenn Sie den Nutzer außerhalb von NavigationTemplate benachrichtigen möchten, sollten Sie eine wichtige Benachrichtigung (HUN) verwenden, wie unter Benachrichtigungen anzeigen beschrieben.

Verwenden Sie Alert beispielsweise für Folgendes:

  • Den Fahrer über ein Update informieren, das für die aktuelle Navigation relevant ist, z. B. eine Änderung der Verkehrsbedingungen.
  • Den Fahrer um ein Update zur aktuellen Navigation bitten, z. B. über das Vorhandensein einer Geschwindigkeitskontrolle.
  • Eine bevorstehende Aufgabe vorschlagen und fragen, ob der Fahrer sie akzeptiert, z. B. ob der Fahrer bereit ist, unterwegs jemanden abzuholen.

In der einfachsten Form besteht ein Alert aus einem Titel und der Dauer des Alert. Die Dauer wird durch eine Fortschrittsleiste dargestellt. Optional können Sie einen Untertitel, ein Symbol und bis zu zwei Action-Objekte hinzufügen.

Abbildung 2. Kontextbezogene Navigationsbenachrichtigung.

Sobald ein Alert angezeigt wird, wird er nicht auf eine andere Vorlage übertragen, wenn die Fahrerinteraktion dazu führt, dass NavigationTemplate verlassen wird. Er bleibt in der ursprünglichen NavigationTemplate, bis das Zeitlimit für Alert abläuft, der Nutzer eine Aktion ausführt oder die App den Alert schließt.

Benachrichtigung erstellen

Verwenden Sie Alert.Builder zum Erstellen einer Alert-Instanz:

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()

Wenn Sie auf das Alert Abbrechen oder Schließen reagieren möchten, erstellen Sie eine Implementierung der AlertCallback Schnittstelle. Die Aufruf-Pfade von AlertCallback sind:

Dauer der Benachrichtigung konfigurieren

Wählen Sie eine Alert-Dauer aus, die den Anforderungen Ihrer App entspricht. Die empfohlene Dauer für eine Navigations Alert beträgt 10 Sekunden. Weitere Informationen finden Sie unter Navigationsbenachrichtigungen.

Benachrichtigung anzeigen

Wenn Sie ein Alert anzeigen möchten, rufen Sie die AppManager.showAlert Methode auf, die über CarContext Ihrer App verfügbar ist.

carContext.getCarService(AppManager::class.java).showAlert(alert)

  • Wenn Sie showAlert mit einer Alert aufrufen, die eine alertId hat, die mit der ID der bereits angezeigten Alert identisch ist, geschieht nichts. Die Alert wird nicht aktualisiert. Wenn Sie eine Alert aktualisieren möchten, müssen Sie sie mit einer neuen alertId neu erstellen.
  • Wenn Sie showAlert mit einer Alert aufrufen, die eine andere alertId als die bereits angezeigte Alert hat, wird die angezeigte Alert geschlossen.

Benachrichtigung schließen

Eine Alert wird automatisch geschlossen, wenn das Zeitlimit abläuft oder der Fahrer interagiert. Sie können eine Alert aber auch manuell schließen, z. B. wenn die Informationen veraltet sind. Rufen Sie dazu die dismissAlert Methode mit der alertId des Alert auf.Alert

carContext.getCarService(AppManager::class.java).dismissAlert(alert.id)

Wenn Sie dismissAlert mit einer alertId aufrufen, die nicht mit der bereits angezeigten Alert übereinstimmt, geschieht nichts. Es wird keine Ausnahme ausgelöst.