Crea un'app di navigazione

In questa pagina vengono descritte le diverse funzionalità della Raccolta di app per auto che puoi utilizzare per implementare la funzionalità dell'app di navigazione passo passo.

Dichiara il supporto per la navigazione nel tuo file manifest

La tua app di navigazione deve dichiarare androidx.car.app.category.NAVIGATION categoria di app auto nell'intent del relativo CarAppService:

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

Supporta gli intent di navigazione

Per supportare gli intent di navigazione verso la tua app, inclusi quelli provenienti da l'Assistente Google tramite una query vocale, l'app deve gestire CarContext.ACTION_NAVIGATE l'intento Session.onCreateScreen e Session.onNewIntent

Consulta la documentazione su CarContext.startCarApp per informazioni dettagliate sul formato dell'intent.

Accedere ai modelli di navigazione

Le app di navigazione possono accedere ai seguenti modelli, che mostrano un'area in lo sfondo con la mappa e, durante la navigazione attiva, istruzioni passo passo indicazioni stradali.

  • NavigationTemplate: mostra anche un messaggio informativo facoltativo e le stime di viaggio durante la navigazione attiva.
  • MapWithContentTemplate: Un modello che consente a un'app di visualizzare riquadri di mappe con un qualche tipo di contenuto (ad ad esempio un elenco). In genere, i contenuti vengono visualizzati come overlay sulla parte superiore riquadri della mappa, in cui le aree visibili e stabili si adattano ai contenuti.

Per ulteriori dettagli su come progettare l'interfaccia utente della tua app di navigazione utilizzando questi modelli, vedi App di navigazione.

Per ottenere l'accesso ai modelli di navigazione, la tua app deve dichiarare l'autorizzazione androidx.car.app.NAVIGATION_TEMPLATES nel relativo AndroidManifest.xml file:

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

Per disegnare le mappe è necessaria un'autorizzazione aggiuntiva.

Esegui la migrazione a MapWithContentTemplate

A partire dal Livello 7 dell'API Car App, MapTemplate, PlaceListNavigationTemplate, e RoutePreviewNavigationTemplate sono deprecate. I modelli deprecati continueranno a essere supportati, ti consigliamo vivamente di eseguire la migrazione a MapWithContentTemplate.

La funzionalità fornita da questi modelli può essere implementata utilizzando l'MapWithContentTemplate. Consulta i seguenti snippet per avere degli esempi:

Modello di mappa

Kotlin

// MapTemplate (deprecated)
val template = 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()

Java

// MapTemplate (deprecated)
MapTemplate template = new MapTemplate.Builder()
    .setPane(paneBuilder.build())
    .setActionStrip(actionStrip)
    .setHeader(header)
    .setMapController(mapController)
    .build();

// MapWithContentTemplate
MapWithContentTemplate template = new MapWithContentTemplate.Builder()
    .setContentTemplate(new PaneTemplate.Builder(paneBuilder.build())
        .setHeader(header)
        build())
    .setActionStrip(actionStrip)
    .setMapController(mapController)
    .build();

Modello di navigazione per l'elenco dei luoghi

Kotlin

// PlaceListNavigationTemplate (deprecated)
val template = 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()

Java

// PlaceListNavigationTemplate (deprecated)
PlaceListNavigationTemplate template = new PlaceListNavigationTemplate.Builder()
    .setItemList(itemListBuilder.build())
    .setHeader(header)
    .setActionStrip(actionStrip)
    .setMapActionStrip(mapActionStrip)
    .build();

// MapWithContentTemplate
MapWithContentTemplate template = new MapWithContentTemplate.Builder()
    .setContentTemplate(new ListTemplate.Builder()
        .setSingleList(itemListBuilder.build())
        .setHeader(header)
        .build())
    .setActionStrip(actionStrip)
    .setMapController(new MapController.Builder()
        .setMapActionStrip(mapActionStrip)
        .build())
    .build();

Modello di navigazione in anteprima route

Kotlin

// RoutePreviewNavigationTemplate (deprecated)
val template = RoutePreviewNavigationTemplate.Builder()
    .setItemList(
        ItemList.Builder()
            .addItem(
                Row.Builder()
                    .setTitle(title)
                    .build())
            .build())
    .setHeader(header)
    .setNavigateAction(
        Action.Builder()
            .setTitle(actionTitle)
            .setOnClickListener { ... }
            .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 { ... }
                                    .build())
                            .build())
                    .build())
            .setHeader(header)
            .build())
    .setActionStrip(actionStrip)
    .setMapController(
        MapController.Builder()
            .setMapActionStrip(mapActionStrip)
            .build())
    .build()

Java

// RoutePreviewNavigationTemplate (deprecated)
RoutePreviewNavigationTemplate template = new RoutePreviewNavigationTemplate.Builder()
    .setItemList(new ItemList.Builder()
        .addItem(new Row.Builder()
            .setTitle(title))
            .build())
        .build())
    .setHeader(header)
    .setNavigateAction(new Action.Builder()
        .setTitle(actionTitle)
        .setOnClickListener(() -> { ... })
        .build())
    .setActionStrip(actionStrip)
    .setMapActionStrip(mapActionStrip)
    .build();

// MapWithContentTemplate
MapWithContentTemplate template = new MapWithContentTemplate.Builder()
    .setContentTemplate(new ListTemplate.Builder()
        .setSingleList(new ItemList.Builder()
            .addItem(new Row.Builder()
                  .setTitle(title))
                  .addAction(new Action.Builder()
                      .setTitle(actionTitle)
                      .setOnClickListener(() -> { ... })
                      .build())
                  .build())
            .build()))
        .setHeader(header)
        .build())
    .setActionStrip(actionStrip)
    .setMapController(new MapController.Builder()
        .setMapActionStrip(mapActionStrip)
        .build())
    .build();

Le app di navigazione devono comunicare metadati di navigazione aggiuntivi con il . L'organizzatore utilizza le informazioni per fornire informazioni al all'unità principale del veicolo ed evitare conflitti tra le applicazioni di navigazione e risorse condivise.

I metadati di navigazione vengono forniti tramite NavigationManager accessibile dal CarContext:

Kotlin

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

Java

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

Avviare, terminare e interrompere la navigazione

Per consentire all'organizzatore di gestire più app di navigazione, notifiche di percorso, e dati cluster del veicolo, deve conoscere lo stato attuale per la navigazione. Quando un utente avvia la navigazione, chiama NavigationManager.navigationStarted Analogamente, al termine della navigazione, ad esempio quando l'utente arriva al suo destinazione o l'utente annulla la navigazione: NavigationManager.navigationEnded

Chiama solo NavigationManager.navigationEnded quando l'utente termina la navigazione. Ad esempio, se devi ricalcolare il percorso a metà di una corsa, usa Trip.Builder.setLoading(true) .

A volte l'organizzatore ha bisogno di un'app per interrompere la navigazione e le chiamate onStopNavigation in NavigationManagerCallback fornito dalla tua app tramite NavigationManager.setNavigationManagerCallback. L'app deve quindi interrompere l'invio di informazioni relative alla svolta successiva nel display del cluster. notifiche di navigazione e la guida vocale.

Aggiorna le informazioni sulla corsa

Durante la navigazione attiva, chiama NavigationManager.updateTrip Le informazioni fornite in questa chiamata possono essere utilizzate dal cluster del veicolo e di avvisi. A seconda del veicolo specifico, non tutti le informazioni vengono mostrate all'utente. Ad esempio, l'unità principale desktop (DHU) mostra Step aggiunto al Trip, ma non viene visualizzato Destination informazioni.

Disegno sulla visualizzazione del cluster

Per offrire l'esperienza utente più immersiva, potresti voler andare oltre mostrare i metadati di base sul display del cluster del veicolo. A partire dal Livello 6 dell'API Car App, è possibile eseguire il rendering delle app di navigazione i propri contenuti direttamente sul display del cluster (nei veicoli supportati), con le seguenti limitazioni:

  • L'API cluster display non supporta i controlli di input
  • La visualizzazione del cluster dovrebbe mostrare solo riquadri della mappa. La navigazione di un percorso attivo può può essere visualizzata su questi riquadri.
  • L'API di visualizzazione del cluster supporta solo l'uso NavigationTemplate
    • A differenza delle schermate principali, le visualizzazioni del cluster potrebbero non mostrare in modo coerente tutti Elementi UI di NavigationTemplate, come istruzioni passo passo, orario di arrivo stimato schede e azioni. I riquadri della mappa sono l'unica UI visualizzata in modo coerente .

Dichiara l'assistenza per i cluster

Comunicare all'applicazione host che la tua app supporta il rendering sul cluster devi aggiungere androidx.car.app.category.FEATURE_CLUSTER <category> al <intent-filter> di CarAppService come mostrato in seguente snippet:

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

Ciclo di vita e gestione degli stati

A partire dal livello API 6, l'app Auto flusso del ciclo di vita rimane invariato, ma ora CarAppService::onCreateSession utilizza il parametro tipo SessionInfo che fornisce informazioni aggiuntive sul Session da creare (vale a dire, lo spazio e l'insieme di modelli supportati).

Le app possono utilizzare la stessa classe Session per gestire sia cluster e display principale oppure crea Sessions specifici per il display da personalizzare su ogni display (come illustrato nello snippet seguente).

Kotlin

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

Java

@Override
@NonNull
public Session onCreateSession(@NonNull SessionInfo sessionInfo) {
  if (sessionInfo.getDisplayType() == SessionInfo.DISPLAY_TYPE_CLUSTER) {
    return new ClusterSession();
  } else {
    return new MainDisplaySession();
  }
}

Non vi sono garanzie su quando e se la visualizzazione del cluster viene fornita è anche possibile che il cluster Session sia l'unico Session (per Ad esempio, l'utente ha cambiato il display principale con un'altra app mentre la tua app la navigazione attiva). Lo "standard" concordano sul fatto che l'app acquisisce il controllo la visualizzazione del cluster solo dopo che è stato eseguito NavigationManager::navigationStarted chiamato. Tuttavia, è possibile che all'app venga fornito il display del cluster quando non è in corso alcuna navigazione attiva o che non venga mai fornito il cluster display. Sta all'app gestire questi scenari eseguendo il rendering stato di inattività dei riquadri della mappa.

L'host crea binder e istanze CarContext separate per Session. Questo significa che, quando utilizzi metodi come ScreenManager::push o Screen::invalidate, solo il Session da cui vengono chiamati è interessati. Le app devono creare i propri canali di comunicazione tra questi istanze se è necessaria una comunicazione tra Session (ad esempio, utilizzando trasmissioni, un singleton condiviso o qualcosa di simile altro).

Test del supporto dei cluster

Puoi testare la tua implementazione sia su Android Auto sia sul sistema operativo Android Automotive. Per Android Auto, questo viene fatto configurando l'unità principale desktop per emulare di un cluster secondario. Per Android Automotive OS, immagini di sistema generiche per API Livello 30 e livelli successivi emulano la visualizzazione di un cluster.

Personalizza Stima di viaggio con testo o un'icona

Per personalizzare la stima di viaggio con testo, un'icona o entrambi, utilizza la TravelEstimate.Builder del corso setTripIcon o setTripText di machine learning. La NavigationTemplate utilizza TravelEstimate per impostare facoltativamente testo e icone accanto o al posto del tempo stimato di arrivo, del tempo rimanente e della distanza rimanente.

Figura 1. Stima di viaggio con icona e testo personalizzati.

Il seguente snippet utilizza setTripIcon e setTripText per personalizzare stima del viaggio:

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

Fornire notifiche passo passo

Fornisci istruzioni di navigazione passo passo (TBT) utilizzando un notifica di navigazione aggiornata. Da trattare come una navigazione sullo schermo dell'auto, il generatore della notifica deve eseguire seguenti:

  1. Contrassegna la notifica come in corso con il NotificationCompat.Builder.setOngoing .
  2. Imposta la categoria della notifica su Notification.CATEGORY_NAVIGATION.
  3. Estendi la notifica con un CarAppExtender

Viene visualizzata una notifica di navigazione nel widget della barra laterale nella parte inferiore di sullo schermo dell'auto. Se il livello di importanza della notifica è impostato su IMPORTANCE_HIGH, viene visualizzata anche come notifica di avviso (HUN). Se l'importanza non è impostata con il CarAppExtender.Builder.setImportance , importanza del canale di notifica .

L'app può impostare un valore PendingIntent nel CarAppExtender che viene inviato all'app quando l'utente tocca il widget HUN o il widget Rail.

Se NotificationCompat.Builder.setOnlyAlertOnce viene richiamata con il valore true, un avviso di notifica di importanza elevata una volta nello HUN.

Il seguente snippet mostra come creare una notifica di navigazione:

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

Aggiorna regolarmente la notifica TBT per la distanza che aggiorna il widget ferroviario e mostra la notifica solo come HUN. Puoi controllare il comportamento HUN impostando l'importanza della notifica con CarAppExtender.Builder.setImportance. Dando importanza a IMPORTANCE_HIGH mostra uno HUN. Impostazione a qualsiasi altro valore aggiorna solo il widget ferroviario.

Aggiorna i contenuti PlaceListNavigatorTemplate

Puoi consentire ai conducenti di aggiornare i contenuti semplicemente toccando un pulsante mentre navigano di luoghi con PlaceListNavigationTemplate Per attivare l'aggiornamento dell'elenco, implementa OnContentRefreshListener dell'interfaccia utente onContentRefreshRequested e utilizzare PlaceListNavigationTemplate.Builder.setOnContentRefreshListener per impostare il listener sul modello.

Lo snippet seguente mostra come impostare il listener sul modello:

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

Il pulsante di aggiornamento viene visualizzato solo nell'intestazione del PlaceListNavigationTemplate se il listener ha un valore.

Quando l'utente fa clic sul pulsante di aggiornamento, onContentRefreshRequested metodo di Viene richiamata l'implementazione di OnContentRefreshListener. Entro onContentRefreshRequested, chiama il Screen.invalidate. L'organizzatore richiama quindi Screen.onGetTemplate per recuperare il modello con i contenuti aggiornati. Consulta Aggiorna i contenuti di un modello per ulteriori informazioni sull'aggiornamento dei modelli. Purché il modello successivo restituito da onGetTemplate è di lo stesso tipo, viene conteggiato come un aggiornamento e non viene conteggiato ai fini quota del modello.

Fornire indicazioni audio

Per riprodurre le indicazioni di navigazione sugli altoparlanti dell'auto, la tua app deve richiedere audio focus. Nell'ambito della tua AudioFocusRequest, imposta l'utilizzo come AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE. Inoltre, imposta il guadagno dello stato attivo su AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Simula navigazione

Per verificare la funzionalità di navigazione dell'app quando la invii al Google Play Store, la tua app deve implementare NavigationManagerCallback.onAutoDriveEnabled di Google. Quando viene chiamato questo callback, la tua app deve simulare la navigazione verso la destinazione scelta quando l'utente inizia la navigazione. L'app può uscire da questa impostazione ogni volta che il ciclo di vita dell'attuale Session raggiunge Lifecycle.Event.ON_DESTROY stato.

Puoi verificare che la tua implementazione di onAutoDriveEnabled venga chiamata che esegue il comando seguente da una riga di comando:

adb shell dumpsys activity service CAR_APP_SERVICE_NAME AUTO_DRIVE

Ciò è mostrato nell'esempio seguente:

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

App navigazione predefinita per auto

In Android Auto, l'app di navigazione predefinita per auto corrisponde all'ultima app di navigazione avviata dall'utente. App predefinita riceve intent di navigazione quando l'utente richiama i comandi di navigazione tramite l'assistente o quando un'altra app invia un per avviare la navigazione.

Mostra avvisi di navigazione contestualizzati

Alert mostra importanti informazioni al conducente con azioni facoltative, senza lasciare il contesto nella schermata di navigazione. Per offrire un'esperienza ottimale al conducente, Alert funziona all'interno di NavigationTemplate per evitare di bloccare il percorso di navigazione e ridurre al minimo le distrazioni per chi guida.

Alert è disponibile solo in NavigationTemplate. Per inviare una notifica all'utente al di fuori di NavigationTemplate, valuta la possibilità di utilizzare una notifica di avviso (HUN), come spiegato nella Mostra notifiche.

Ad esempio, utilizza Alert per:

  • Comunica al conducente un aggiornamento relativo alla navigazione corrente, ad esempio una variazione delle condizioni del traffico.
  • Chiedi al conducente un aggiornamento relativo alla navigazione corrente, ad esempio la l'esistenza di un autovelox mobile.
  • Proponi un'attività imminente e chiedi al conducente se la accetta. ad esempio se il conducente è disposto a prendere qualcuno lungo la strada.

Nella forma di base, un Alert è costituito da un titolo e dal Alert durata massima. La durata è rappresentata da una barra di avanzamento. Se vuoi, puoi aggiungere un sottotitolo, un'icona e fino Action oggetti.

Figura 2. Avviso di navigazione contestuale.

Una volta mostrato, un Alert non viene trasferito in un altro modello se l'interazione con il conducente comporta l'uscita dal NavigationTemplate. Rimane nel file NavigationTemplate originale fino al Alert di timeout, l'utente esegue un'azione oppure l'app ignora Alert.

Crea un avviso

Utilizza Alert.Builder per creare un'istanza 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();

Se vuoi ascoltare Alert la cancellazione o il rifiuto, crea un'implementazione Interfaccia di AlertCallback. I percorsi di chiamata AlertCallback sono:

Configura la durata dell'avviso

Scegli una durata per Alert che in base alle esigenze della tua app. La durata consigliata per una navigazione Alert dura 10 secondi. Consulta Avvisi di navigazione. per ulteriori informazioni.

Mostra un avviso

Per visualizzare una Alert, chiama il metodo AppManager.showAlert di fatturazione disponibile tramite CarContext

// Show an alert
carContext.getCarService(AppManager.class).showAlert(alert)
  • Chiamata a showAlert con un Alert con un alertId: che corrisponde all'ID di Alert attualmente in esposizione non produce alcun effetto. Alert non si aggiorna. Per aggiornare un Alert, devi ricreare con un nuovo alertId.
  • Chiamata a showAlert con un Alert con un altro alertId rispetto alla metrica Alert attualmente in esposizione ignora la Alert attualmente visualizzati.

Ignorare un avviso

Mentre una Alert ignora automaticamente a causa di un timeout o di un'interazione con il conducente, puoi anche ignorare manualmente Alert, ad esempio se le sue informazioni diventano obsolete. Per ignorare un Alert, chiama il dismissAlert con il alertId di Alert.

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

Chiamata a dismissAlert con un alertId che non corrisponde a quello attualmente visualizzato, Alert non ha alcun effetto. Non genera un'eccezione.