Utilizzare la libreria di app di Android for Cars

La libreria di app Android for Cars ti consente di portare in auto le tue app di navigazione, punto d'interesse (PDI) e Internet delle cose (IOT). Per farlo, fornisce una serie di modelli progettati per soddisfare gli standard in materia di distrazione della guida e curando dettagli come la varietà di fattori dello schermo dell'auto e modalità di inserimento.

Questa guida fornisce una panoramica delle funzionalità e dei concetti chiave della libreria e ti guida nella procedura di configurazione di un'app semplice. Per un'introduzione completa passo passo, consulta il codelab sulle nozioni di base della libreria di app per auto.

Prima di iniziare

  1. Leggi le pagine Design per la guida relative alla raccolta di app per auto.
  2. Esamina i termini e i concetti chiave nella sezione seguente.
  3. Acquisisci familiarità con l'UI di sistema Android Auto e la progettazione del sistema operativo Android Automotive.
  4. Consulta le Note di rilascio.
  5. Esamina i Samples.

Termini e concetti chiave

Modelli e modelli
L'interfaccia utente è rappresentata da un grafico di oggetti modello che possono essere organizzati insieme in modi diversi, a seconda di quanto consentito dal modello a cui appartengono. I modelli sono un sottoinsieme dei modelli che possono fungere da radice in questi grafici. I modelli includono le informazioni da mostrare all'utente sotto forma di testo e immagini, nonché attributi per configurare aspetti dell'aspetto visivo di tali informazioni, ad esempio i colori del testo o le dimensioni delle immagini. L'host converte i modelli in visualizzazioni progettate per soddisfare gli standard di distrazione del conducente e si occupa di dettagli come la varietà di fattori dello schermo dell'auto e modalità di input.
Moderatore
L'host è il componente di backend che implementa la funzionalità offerta dalle API della libreria per consentire l'esecuzione dell'app nell'auto. Le responsabilità dell'host spaziano dal rilevamento dell'app alla gestione del suo ciclo di vita, fino alla conversione dei modelli in visualizzazioni e alla notifica all'app delle interazioni degli utenti. Sui dispositivi mobili, questo host è implementato da Android Auto. Sul sistema operativo Android Automotive, questo host è installato come app di sistema.
Limitazioni relative ai modelli
I vari modelli applicano restrizioni a livello di contenuti dei modelli. Ad esempio, i modelli di elenco hanno dei limiti per il numero di elementi che possono essere presentati all'utente. I modelli hanno anche limitazioni relative al modo per collegarli per formare il flusso di un'attività. Ad esempio, l'app può inviare solo un massimo di cinque modelli allo stack dello schermo. Consulta Limitazioni dei modelli per maggiori dettagli.
Screen
Screen è una classe fornita dalla libreria che le app implementano per gestire l'interfaccia utente presentata all'utente. Un elemento Screen ha un ciclo di vita e fornisce all'app il meccanismo per inviare il modello da visualizzare quando lo schermo è visibile. Le istanze Screen possono anche essere inviate tramite push e popolato da e verso uno stack Screen, in modo da garantire il rispetto delle restrizioni del flusso dei modelli.
CarAppService
CarAppService è una classe Service astratta che l'app deve implementare ed esportare per poter essere rilevata e gestita dall'host. L'elemento CarAppService della tua app è responsabile della convalida dell'attendibilità di una connessione host utilizzando createHostValidator e, in seguito, fornisce le istanze Session per ogni connessione utilizzando onCreateSession.
Session

Session è una classe astratta che l'app deve implementare e restituire utilizzando CarAppService.onCreateSession. È il punto di accesso per visualizzare le informazioni sullo schermo dell'auto. Ha un ciclo di vita che informa lo stato attuale dell'app sullo schermo dell'auto, ad esempio quando è visibile o nascosta.

Quando viene avviata una Session, ad esempio al primo avvio dell'app, l'host richiede la visualizzazione dell'elemento Screen iniziale utilizzando il metodo onCreateScreen.

Installa la libreria di app per auto

Consulta la pagina di rilascio della libreria Jetpack per istruzioni su come aggiungerla alla tua app.

Configura i file manifest dell'app

Prima di creare l'app per la tua auto, configura i file manifest dell'app come segue.

Dichiara il tuo CarAppService

L'host si connette alla tua app tramite la tua implementazione di CarAppService. Puoi dichiarare questo servizio nel file manifest per consentire all'host di rilevare e connettersi alla tua app.

Devi anche dichiarare la categoria dell'app nell'elemento <category> del filtro per intenzione dell'app. Consulta l'elenco delle categorie di app supportate per i valori consentiti per questo elemento.

Il seguente snippet di codice mostra come dichiarare un servizio di app per auto per un'app per un punto di interesse nel file manifest:

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

    ...
<application>

Categorie di app supportate

Dichiara la categoria della tua app aggiungendo uno o più dei seguenti valori di categoria nel filtro per intent quando dichiari CarAppService come descritto nella sezione precedente:

  • androidx.car.app.category.NAVIGATION: un'app che fornisce indicazioni di navigazione passo passo. Consulta Creare app di navigazione per le auto per ulteriore documentazione su questa categoria.
  • androidx.car.app.category.POI: un'app che fornisce funzionalità pertinenti per la ricerca di punti d'interesse, come parcheggi, stazioni di ricarica e stazioni di servizio. Consulta Creare app di punti di interesse per le auto per ulteriore documentazione su questa categoria.
  • androidx.car.app.category.IOT: un'app che consente agli utenti di intraprendere azioni pertinenti sui dispositivi connessi dall'interno dell'auto. Consulta Creazione di internet di app per auto per ulteriore documentazione su questa categoria.

Consulta la sezione Qualità delle app Android per le auto per descrizioni dettagliate di ogni categoria e criteri di appartenenza delle app.

Specifica il nome e l'icona dell'app

Devi specificare il nome e l'icona dell'app che l'host può utilizzare per rappresentare la tua app nell'interfaccia utente di sistema.

Puoi specificare il nome e l'icona dell'app utilizzati per rappresentare l'app tramite gli attributi label e icon di CarAppService:

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

Se l'etichetta o l'icona non vengono dichiarate nell'elemento <service>, l'host utilizza i valori specificati per l'elemento <application>.

Impostare un tema personalizzato

Per impostare un tema personalizzato per l'app della tua auto, aggiungi un elemento <meta-data> al file manifest, come indicato di seguito:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Poi, dichiara la risorsa stile per impostare i seguenti attributi per il tema personalizzato dell'app auto:

<resources>
  <style name="MyCarAppTheme">
    <item name="carColorPrimary">@layout/my_primary_car_color</item>
    <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item>
    <item name="carColorSecondary">@layout/my_secondary_car_color</item>
    <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item>
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Livello API Car App

La libreria di app per auto definisce i propri livelli API in modo che tu possa sapere quali funzionalità della libreria sono supportate dall'host del modello su un veicolo. Per recuperare il livello più elevato dell'API Car App supportato da un host, usa il metodo getCarAppApiLevel().

Dichiara nel tuo file AndroidManifest.xml il livello minimo dell'API Car App supportato dalla tua app:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

Consulta la documentazione relativa all'annotazione RequiresCarApi per i dettagli su come mantenere la compatibilità con le versioni precedenti e dichiarare il livello API minimo richiesto per utilizzare una funzionalità. Per una definizione del livello API necessario per utilizzare una determinata funzionalità della libreria di app per auto, consulta la documentazione di riferimento per CarAppApiLevels.

Crea CarAppService e Session

L'app deve estendere la classe CarAppService e implementare il proprio metodo onCreateSession, che restituisce un'istanza Session corrispondente alla connessione corrente all'host:

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

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

L'istanza Session deve restituire l'istanza Screen da utilizzare al primo avvio dell'app:

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen(carContext)
    }
    ...
}

Java

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

Per gestire gli scenari in cui l'app dell'auto deve iniziare da una schermata diversa dalla schermata Home o di destinazione dell'app, ad esempio la gestione dei link diretti, puoi pre-effettuare uno stack posteriore di schermate utilizzando ScreenManager.push prima di tornare da onCreateScreen. Il pre-seeding consente agli utenti di tornare alle schermate precedenti dalla prima schermata su cui viene visualizzata l'app.

Crea la tua schermata iniziale

Puoi creare le schermate visualizzate dalla tua app definendo classi che estendono la classe Screen e implementando il relativo metodo onGetTemplate, che restituisce l'istanza Template che rappresenta lo stato dell'interfaccia utente da visualizzare sullo schermo dell'auto.

Lo snippet seguente mostra come dichiarare un Screen che utilizza un modello PaneTemplate per visualizzare una semplice stringa "Hello world!":

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

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

La classe CarContext

La classe CarContext è una sottoclasse ContextWrapper accessibile alle istanze Session e Screen. Fornisce l'accesso a servizi auto, come ScreenManager per la gestione dello stack di schermate, AppManager per funzionalità generali relative alle app, ad esempio l'accesso all'oggetto Surface per disegnare la mappa dell'app di navigazione e NavigationManager utilizzato dalle app di navigazione passo passo per comunicare la navigazione passo passo con gli altri metadati

Consulta la sezione Accedere ai modelli di navigazione per un elenco completo delle funzionalità delle librerie disponibili per le app di navigazione.

CarContext offre anche altre funzionalità, come la possibilità di caricare risorse disegnabili utilizzando la configurazione dallo schermo dell'auto, di avviare un'app in auto utilizzando gli intent e di indicare se l'app di navigazione deve mostrare la mappa in modalità Buio.

Implementare la navigazione nelle schermate

Le app presentano spesso varie schermate, ognuna delle quali utilizza modelli diversi che l'utente può esplorare mentre interagisce con l'interfaccia sullo schermo.

La classe ScreenManager fornisce uno stack di schermate che puoi usare per eseguire il push delle schermate che possono essere attivate automaticamente quando l'utente seleziona un pulsante Indietro sullo schermo dell'auto o usa il pulsante Indietro hardware disponibile in alcune auto.

Il seguente snippet mostra come aggiungere un'azione Indietro a un modello di messaggio e un'azione che esegue il push di una nuova schermata quando viene selezionata dall'utente:

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

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

L'oggetto Action.BACK è un Action standard che richiama automaticamente ScreenManager.pop. È possibile eseguire l'override di questo comportamento utilizzando l'istanza OnBackPressedDispatcher disponibile in CarContext.

Per garantire che l'app sia sicura durante la guida, lo stack di schermate può avere una profondità massima di cinque schermate. Per ulteriori dettagli, consulta la sezione Limitazioni dei modelli.

Aggiornare i contenuti di un modello

La tua app può richiedere l'annullamento della validità dei contenuti di un Screen chiamando il metodo Screen.invalidate. Successivamente, l'host richiama il metodo Screen.onGetTemplate della tua app per recuperare il modello con i nuovi contenuti.

Quando aggiorni un elemento Screen, è importante comprendere i contenuti specifici del modello che possono essere aggiornati, in modo che l'host non conti il nuovo modello nella quota del modello. Per ulteriori dettagli, consulta la sezione Limitazioni dei modelli.

Ti consigliamo di strutturare le schermate in modo che vi sia una mappatura one-to-one tra un elemento Screen e il tipo di modello che restituisce tramite l'implementazione onGetTemplate.

Interagire con l'utente

La tua app può interagire con l'utente utilizzando pattern simili a quelli di un'app mobile.

Gestire l'input utente

L'app può rispondere all'input dell'utente passando i listener appropriati ai modelli che li supportano. Il seguente snippet mostra come creare un modello Action che imposti un OnClickListener che richiami un metodo definito dal codice della tua app:

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

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

Il metodo onClickNavigate può quindi avviare l'app di navigazione predefinita per l'auto utilizzando il metodo CarContext.startCarApp:

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

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

Per maggiori dettagli su come avviare le app, incluso il formato dell'intent ACTION_NAVIGATE, consulta la sezione Avviare un'app per auto con un intent.

Alcune azioni, come quelle che richiedono di invitare l'utente a continuare l'interazione sui propri dispositivi mobili, sono consentite solo quando l'auto è parcheggiata. Puoi utilizzare la ParkedOnlyOnClickListener per implementare queste azioni. Se l'auto non è parcheggiata, l'host mostra all'utente un'indicazione che l'azione non è consentita in questo caso. Se l'auto è parcheggiata, il codice viene eseguito normalmente. Lo snippet seguente mostra come utilizzare ParkedOnlyOnClickListener per aprire la schermata delle impostazioni sul dispositivo mobile:

Kotlin

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

Java

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

Mostra notifiche

Le notifiche inviate al dispositivo mobile vengono visualizzate sullo schermo dell'auto solo se a cui è stato aggiunto un CarAppExtender. Alcuni attributi di notifica, come titolo, testo, icona e azioni dei contenuti, possono essere impostati in CarAppExtender, sostituendo gli attributi della notifica quando vengono visualizzati sullo schermo dell'auto.

Il seguente snippet mostra come inviare allo schermo dell'auto una notifica che mostra un titolo diverso da quello mostrato sul dispositivo mobile:

Kotlin

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

Java

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

Le notifiche possono influire sulle seguenti parti dell'interfaccia utente:

  • All'utente potrebbe essere mostrata una notifica di avviso (HUN).
  • È possibile aggiungere una voce nel centro notifiche, facoltativamente con un badge visibile sulla barra.
  • Per le app di navigazione, la notifica potrebbe essere visualizzata nel widget ferroviario come descritto in Notifiche passo passo.

Puoi scegliere come configurare le notifiche dell'app in modo che vengano applicate a ciascuno di questi elementi dell'interfaccia utente utilizzando la priorità della notifica, come descritto nella documentazione di CarAppExtender.

Se NotificationCompat.Builder.setOnlyAlertOnce viene chiamato con un valore di true, una notifica ad alta priorità viene visualizzata come HUN solo una volta.

Per ulteriori informazioni su come progettare le notifiche dell'app della tua auto, consulta la guida di Google Design per la guida relativa alle notifiche.

Mostra toast

La tua app può mostrare un toast utilizzando CarToast come mostrato in questo snippet:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

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

Richiedi autorizzazioni

Se la tua app ha bisogno di accedere ad azioni o dati limitati, ad esempio la posizione, si applicano le regole standard delle autorizzazioni Android. Per richiedere un'autorizzazione, puoi utilizzare il metodo CarContext.requestPermissions().

Il vantaggio di utilizzare CarContext.requestPermissions() anziché utilizzare le API Android standard è che non è necessario avviare il tuo Activity per creare la finestra di dialogo delle autorizzazioni. Inoltre, puoi utilizzare lo stesso codice sia sul sistema operativo Android Auto sia sul sistema operativo Android Automotive, anziché dover creare flussi dipendenti dalla piattaforma.

Applicare uno stile alla finestra di dialogo delle autorizzazioni su Android Auto

Su Android Auto, sullo smartphone viene visualizzata la finestra di dialogo delle autorizzazioni dell'utente. Per impostazione predefinita, la finestra di dialogo non mostra alcuno sfondo. Per impostare uno sfondo personalizzato, dichiara un tema dell'app per auto nel file AndroidManifest.xml e imposta l'attributo carPermissionActivityLayout per il tema dell'app auto.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

Dopodiché, imposta l'attributo carPermissionActivityLayout per il tema dell'app della tua auto:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Avviare un'app per auto con un intent

Puoi chiamare il metodo CarContext.startCarApp per eseguire una delle seguenti azioni:

L'esempio seguente mostra come creare una notifica con un'azione che apre la tua app con una schermata che mostra i dettagli di una prenotazione di parcheggio. Estendi l'istanza di notifica con un intent di contenuto che contiene un PendingIntent che include un intent esplicito all'azione della tua app:

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

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

L'app deve anche dichiarare un elemento BroadcastReceiver richiamato per elaborare l'intent quando l'utente seleziona l'azione nell'interfaccia di notifica e richiama CarContext.startCarApp con un intent che includa l'URI dei dati:

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

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

Infine, il metodo Session.onNewIntent nell'app gestisce questo intento spingendo la schermata di prenotazione del parcheggio nello stack, se non è già in cima:

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

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

Consulta la sezione Mostra notifiche per ulteriori informazioni su come gestire le notifiche per l'app dell'auto.

Limitazioni relative ai modelli

L'host limita a un massimo di cinque il numero di modelli da visualizzare per una determinata attività, di cui l'ultimo modello deve essere uno dei seguenti tipi:

Tieni presente che questo limite si applica al numero di modelli e non al numero di Screen istanze nello stack. Ad esempio, se un'app invia due modelli mentre è nella schermata A e poi invia la schermata B, ora può inviare altri tre modelli. In alternativa, se ogni schermata è strutturata in modo da inviare un singolo modello, l'app può eseguire il push di cinque istanze dello schermo nello stack ScreenManager.

Esistono casi speciali a queste limitazioni: aggiornamenti dei modelli e operazioni di ripristino e reimpostazione dei modelli.

Aggiornamenti dei modelli

Alcuni aggiornamenti dei contenuti non vengono conteggiati ai fini del limite dei modelli. In generale, se un'app esegue il push di un nuovo modello dello stesso tipo che contiene gli stessi contenuti principali del modello precedente, il nuovo modello non viene conteggiato nella quota. Ad esempio, l'aggiornamento dello stato di attivazione/disattivazione di una riga in una ListTemplate non conta ai fini della quota. Consulta la documentazione dei singoli modelli per scoprire di più su quali tipi di aggiornamenti dei contenuti possono essere considerati un aggiornamento.

Operazioni Indietro

Per abilitare i flussi secondari all'interno di un'attività, l'host rileva quando un'app sta eseguendo un'estrazione Screen dallo stack ScreenManager e aggiorna la quota rimanente in base al numero di modelli di cui l'app passa indietro.

Ad esempio, se l'app invia due modelli nella schermata A, poi esegue il push della schermata B e invia altri due modelli, l'app ha una quota rimanente. Se l'app torna alla schermata A, l'host reimposta la quota a tre, perché l'app è tornata indietro di due modelli.

Tieni presente che, quando torni a una schermata, un'app deve inviare un modello dello stesso tipo dell'ultimo inviato da quella schermata. L'invio di qualsiasi altro tipo di modello causa un errore. Tuttavia, fintanto che il tipo rimane lo stesso durante un'operazione indietro, un'app può modificare liberamente i contenuti del modello senza influire sulla quota.

Reimposta operazioni

Alcuni modelli hanno una semantica speciale che indica la fine di un'attività. Ad esempio, NavigationTemplate è una visualizzazione che dovrebbe rimanere sullo schermo e aggiornata con nuove istruzioni passo passo per il consumo da parte dell'utente. Quando raggiunge uno di questi modelli, l'host reimposta la quota del modello, trattando quel modello come se fosse il primo passaggio di una nuova attività. In questo modo l'app può iniziare una nuova attività. Consulta la documentazione dei singoli modelli per vedere quali attivano una reimpostazione sull'host.

Se l'host riceve l'intento di avviare l'app da un'azione di notifica o da Avvio app, viene reimpostata anche la quota. Questo meccanismo consente a un'app di avviare un nuovo flusso di attività dalle notifiche e vale anche se un'app è già associata e in primo piano.

Consulta la sezione Mostra notifiche per ulteriori dettagli su come visualizzare le notifiche dell'app nello schermo dell'auto. Consulta la sezione Avviare un'app per auto con un intent per informazioni su come avviare l'app da un'azione di notifica.

API Connection

Puoi determinare se la tua app è in esecuzione sul sistema operativo Android Auto o Android Automotive utilizzando l'API CarConnection per recuperare le informazioni sulla connessione in fase di runtime.

Ad esempio, nell'app Session dell'auto, inizializza CarConnection e abbonati agli aggiornamenti LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

Nell'osservatore puoi quindi reagire ai cambiamenti dello stato della connessione:

Kotlin

fun onConnectionStateUpdated(connectionState: Int) {
  val message = when(connectionState) {
    CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit"
    CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS"
    CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto"
    else -> "Unknown car connection type"
  }
  CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show()
}

Java

private void onConnectionStateUpdated(int connectionState) {
  String message;
  switch(connectionState) {
    case CarConnection.CONNECTION_TYPE_NOT_CONNECTED:
      message = "Not connected to a head unit";
      break;
    case CarConnection.CONNECTION_TYPE_NATIVE:
      message = "Connected to Android Automotive OS";
      break;
    case CarConnection.CONNECTION_TYPE_PROJECTION:
      message = "Connected to Android Auto";
      break;
    default:
      message = "Unknown car connection type";
      break;
  }
  CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show();
}

API Constraints

Auto diverse potrebbero consentire all'utente di visualizzare contemporaneamente un numero diverso di istanze Item. Utilizza ConstraintManager per verificare il limite di contenuti in fase di runtime e impostare il numero appropriato di elementi nei tuoi modelli.

Inizia acquistando un ConstraintManager da CarContext:

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

Puoi quindi eseguire una query sull'oggetto ConstraintManager recuperato per conoscere il limite di contenuti pertinente. Ad esempio, per ottenere il numero di elementi che possono essere visualizzati in una griglia, chiama getContentLimit con CONTENT_LIMIT_TYPE_GRID:

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

Aggiungi un flusso di accesso

Se la tua app offre un'esperienza di accesso agli utenti, puoi utilizzare modelli come SignInTemplate e LongMessageTemplate con l'API Car App di livello 2 e versioni successive per gestire l'accesso all'app dall'unità principale dell'auto.

Per creare un SignInTemplate, definisci un SignInMethod. Al momento, la libreria di app per auto supporta i seguenti metodi di accesso:

  • InputSignInMethod per l'accesso con nome utente/password.
  • PinSignInMethod per l'accesso tramite PIN, in cui l'utente collega il proprio account dal telefono usando il PIN visualizzato sull'unità principale.
  • ProviderSignInMethod per l'accesso del fornitore, ad esempio Accedi con Google e One Tap.
  • QRCodeSignInMethod per l'accesso con codice QR, in cui l'utente scansiona un codice QR per completare l'accesso sul lo smartphone. Questa funzionalità è disponibile con l'API Car Livello 4 e versioni successive.

Ad esempio, per implementare un modello che raccolga la password dell'utente, inizia creando una InputCallback per elaborare e convalidare l'input utente:

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
};

È necessario un InputCallback per InputSignInMethod Builder.

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

Infine, usa il tuo nuovo InputSignInMethod per creare un SignInTemplate.

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

Utilizza AccountManager

Le app del sistema operativo Android Automotive dotate di autenticazione devono utilizzare AccountManager per i seguenti motivi:

  • Esperienza utente migliorata e facilità di gestione degli account: gli utenti possono gestire facilmente tutti i propri account dal menu Account nelle impostazioni di sistema, compresi l'accesso e la disconnessione.
  • Esperienze"ospiti": poiché le auto sono dispositivi condivisi, gli OEM possono abilitare le esperienze degli ospiti nel veicolo, in cui non è possibile aggiungere account.

Aggiungi varianti delle stringhe di testo

Dimensioni dello schermo dell'auto diverse possono mostrare quantità diverse di testo. Con l'API Car App di livello 2 e superiore, puoi specificare più varianti di una stringa di testo per adattarla meglio allo schermo. Per sapere dove sono accettate le varianti di testo, cerca i modelli e i componenti che richiedono una CarText.

Puoi aggiungere varianti della stringa di testo a un elemento CarText con il metodo CarText.Builder.addVariant():

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

Potrai quindi utilizzare questo elemento CarText, ad esempio come testo principale di un elemento GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

Aggiungi le stringhe in ordine decrescente, ad esempio dalla più lunga alla più breve. L'organizzatore sceglie la stringa di lunghezza appropriata in base alla quantità di spazio disponibile sullo schermo dell'auto.

Aggiungi CarIcon incorporate per le righe

Puoi aggiungere icone incorporate nel testo per rendere più accattivante l'app usando CarIconSpan. Per ulteriori informazioni sulla creazione di questi intervalli, consulta la documentazione relativa a CarIconSpan.create. Per una panoramica del funzionamento dello stile di testo con intervalli, consulta la sezione Stili di testo espansi con intervalli.

Kotlin

  
val rating = SpannableString("Rating: 4.5 stars")
rating.setSpan(
    CarIconSpan.create(
        // Create a CarIcon with an image of four and a half stars
        CarIcon.Builder(...).build(),
        // Align the CarIcon to the baseline of the text
        CarIconSpan.ALIGN_BASELINE
    ),
    // The start index of the span (index of the character '4')
    8,
    // The end index of the span (index of the last 's' in "stars")
    16,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE
)

val row = Row.Builder()
    ...
    .addText(rating)
    .build()
  
  

Java

  
SpannableString rating = new SpannableString("Rating: 4.5 stars");
rating.setSpan(
        CarIconSpan.create(
                // Create a CarIcon with an image of four and a half stars
                new CarIcon.Builder(...).build(),
                // Align the CarIcon to the baseline of the text
                CarIconSpan.ALIGN_BASELINE
        ),
        // The start index of the span (index of the character '4')
        8,
        // The end index of the span (index of the last 's' in "stars")
        16,
        Spanned.SPAN_INCLUSIVE_INCLUSIVE
);
Row row = new Row.Builder()
        ...
        .addText(rating)
        .build();
  
  

API per hardware auto

A partire dal livello 3 dell'API Car App, la libreria di app per auto contiene API che puoi usare per accedere alle proprietà e ai sensori dei veicoli.

Requisiti

Per utilizzare le API con Android Auto, inizia aggiungendo una dipendenza su androidx.car.app:app-projected al file build.gradle per il modulo Android Auto. Per il sistema operativo Android Automotive, aggiungi una dipendenza su androidx.car.app:app-automotive al file build.gradle per il modulo del sistema operativo Android Automotive.

Inoltre, nel file AndroidManifest.xml, devi dichiarare le autorizzazioni pertinenti necessarie per richiedere i dati dell'auto che vuoi utilizzare. Tieni presente che queste autorizzazioni devono anche concederti dall'utente. Puoi utilizzare lo stesso codice sia sul sistema operativo Android Auto sia sul sistema operativo Android Automotive, anziché dover creare flussi dipendenti dalla piattaforma. Tuttavia, le autorizzazioni necessarie sono diverse.

Informazioni auto

Questa tabella descrive le proprietà visualizzate dalle API CarInfo e le autorizzazioni necessarie per utilizzarle:

Metodi Proprietà Autorizzazioni di Android Auto Autorizzazioni del sistema operativo Android Automotive
fetchModel Marca, modello, anno android.car.permission.CAR_INFO
fetchEnergyProfile Tipi di connettori EV, tipi di carburante com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO
addTollListener
removeTollListener
Stato carta pedaggio, tipo carta pedaggi
addEnergyLevelListener
removeEnergyLevelListener
Livello batteria, livello carburante, livello carburante basso, autonomia rimanente com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_ENERGY,
android.car.permission.CAR_ENERGY_PORTS,
android.car.permission.READ_CAR_DISPLAY_UNITS
addSpeedListener
removeSpeedListener
Velocità non elaborata, velocità di visualizzazione (visualizzata sul display del cluster dell'auto) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED,
android.car.permission.READ_CAR_DISPLAY_UNITS
addMileageListener
removeMileageListener
Distanza del contachilometri com.google.android.gms.permission.CAR_MILEAGE Questi dati non sono disponibili sul sistema operativo Android Automotive per le app installate dal Play Store.

Ad esempio, per ottenere l'intervallo rimanente, crea un'istanza di un oggetto CarInfo, quindi crea e registra un OnCarDataAvailableListener:

Kotlin

val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo

val listener = OnCarDataAvailableListener<EnergyLevel> { data ->
    if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) {
      val rangeRemaining = data.rangeRemainingMeters.value
    } else {
      // Handle error
    }
  }

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo();

OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
  if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) {
    float rangeRemaining = data.getRangeRemainingMeters().getValue();
  } else {
    // Handle error
  }
};

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

Non dare per scontato che i dati dell'auto siano disponibili in ogni momento. Se visualizzi un messaggio di errore, controlla lo stato del valore richiesto per capire meglio perché non è stato possibile recuperare i dati richiesti. Consulta la documentazione di riferimento per la definizione completa della classe CarInfo.

Sensori per auto

La classe CarSensors ti consente di accedere ai dati relativi all'accelerometro, al giroscopio, alla bussola e alla posizione del veicolo. La disponibilità di questi valori può dipendere dall'OEM. Il formato dei dati dell'accelerometro, del giroscopio e della bussola è lo stesso utilizzato dall'API SensorManager. Ad esempio, per controllare l'orientamento del veicolo:

Kotlin

val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors

val listener = OnCarDataAvailableListener<Compass> { data ->
    if (data.orientations.status == CarValue.STATUS_SUCCESS) {
      val orientation = data.orientations.value
    } else {
      // Data not available, handle error
    }
  }

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors();

OnCarDataAvailableListener<Compass> listener = (data) -> {
  if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) {
    List<Float> orientations = data.getOrientations().getValue();
  } else {
    // Data not available, handle error
  }
};

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

Per accedere ai dati sulla posizione dell'auto, devi anche dichiarare e richiedere l'autorizzazione android.permission.ACCESS_FINE_LOCATION.

Test

Per simulare i dati dei sensori durante i test su Android Auto, consulta le sezioni sui sensori e sulla configurazione dei sensori della guida sull'unità principale desktop. Per simulare i dati dei sensori durante i test sul sistema operativo Android Automotive, consulta la sezione Emula stato hardware della guida all'emulatore del sistema operativo Android Automotive.

I cicli di vita di CarAppService, sessioni e schermate

Le classi Session e Screen implementano l'interfaccia LifecycleOwner. Quando l'utente interagisce con l'app, vengono richiamati i callback del ciclo di vita degli oggetti Session e Screen, come descritto nei diagrammi seguenti.

I cicli di vita di un CarAppService e di una sessione

Figura 1. Il ciclo di vita di Session.

Per informazioni dettagliate, consulta la documentazione relativa al metodo Session.getLifecycle.

Il ciclo di vita di una schermata

Figura 2. Il ciclo di vita di Screen.

Per informazioni dettagliate, consulta la documentazione relativa al metodo Screen.getLifecycle.

Registra dal microfono dell'auto

Utilizzando l'app CarAppService e l'API CarAudioRecord dell'app, puoi concedere all'app l'accesso al microfono dell'auto dell'utente. Gli utenti devono concedere all'app l'autorizzazione per accedere al microfono dell'auto. L'app può registrare ed elaborare l'input dell'utente al suo interno.

Autorizzazione di registrazione

Prima di registrare qualsiasi audio, devi dichiarare l'autorizzazione a registrare nel tuo AndroidManifest.xml e richiedere all'utente di concederla.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

Devi richiedere l'autorizzazione per registrare in fase di runtime. Per i dettagli su come richiedere un'autorizzazione nell'app dell'auto, consulta la sezione Richiesta di autorizzazioni.

Registra l'audio

Dopo che l'utente concede l'autorizzazione per registrare, puoi registrare l'audio ed elaborare la registrazione.

Kotlin

val carAudioRecord = CarAudioRecord.create(carContext)
        carAudioRecord.startRecording()

        val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE)
        while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording()
 

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        carAudioRecord.startRecording();

        byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE];
        while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording();
 

Focus audio

Quando registri dal microfono dell'auto, acquisisci prima la messa a fuoco audio per assicurarti che i contenuti multimediali in corso vengano interrotti. Se perdi la messa a fuoco audio, interrompi la registrazione.

Ecco un esempio di come acquisire il focus audio:

Kotlin

 
val carAudioRecord = CarAudioRecord.create(carContext)
        
        // Take audio focus so that user's media is not recorded
        val audioAttributes = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            // Use the most appropriate usage type for your use case
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
            .build()
        
        val audioFocusRequest =
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener { state: Int ->
                    if (state == AudioManager.AUDIOFOCUS_LOSS) {
                        // Stop recording if audio focus is lost
                        carAudioRecord.stopRecording()
                    }
                }
                .build()
        
        if (carContext.getSystemService(AudioManager::class.java)
                .requestAudioFocus(audioFocusRequest)
            != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
        ) {
            // Don't record if the focus isn't granted
            return
        }
        
        carAudioRecord.startRecording()
        // Process the audio and abandon the AudioFocusRequest when done

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        // Take audio focus so that user's media is not recorded
        AudioAttributes audioAttributes =
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        // Use the most appropriate usage type for your use case
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        .build();

        AudioFocusRequest audioFocusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                        .setAudioAttributes(audioAttributes)
                        .setOnAudioFocusChangeListener(state -> {
                            if (state == AudioManager.AUDIOFOCUS_LOSS) {
                                // Stop recording if audio focus is lost
                                carAudioRecord.stopRecording();
                            }
                        })
                        .build();

        if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest)
                != AUDIOFOCUS_REQUEST_GRANTED) {
            // Don't record if the focus isn't granted
            return;
        }

        carAudioRecord.startRecording();
        // Process the audio and abandon the AudioFocusRequest when done
 

Libreria di test

La libreria di test di Android for Cars fornisce classi ausiliarie che puoi utilizzare per convalidare il comportamento della tua app in un ambiente di test. Ad esempio, SessionController consente di simulare una connessione all'host e di verificare che vengano creati e restituiti gli elementi Screen e Template corretti.

Consulta gli esempi per gli esempi di utilizzo.

Segnalare un problema della libreria di app Android for Cars

Se rilevi un problema con la libreria, segnalalo utilizzando lo strumento Issue Tracker di Google. Assicurati di compilare tutte le informazioni richieste nel modello del problema.

Crea un nuovo problema

Prima di inviare un nuovo problema, controlla se è elencato nelle note di rilascio della raccolta o nell'elenco dei problemi. Puoi iscriverti e votare i problemi facendo clic sulla stella che indica un problema nel tracker. Per ulteriori informazioni, consulta la sezione Iscrizione a un problema.