Creare app di messaggistica per Android Auto

La categoria Comunicazioni sarà disponibile a breve
La categoria Messaggistica viene ampliata per includere il supporto di nuove funzionalità, tra cui la cronologia dei messaggi e le esperienze di chiamata

Rimanere connessi attraverso i messaggi è importante per molti conducenti. Le app di chat consentono agli utenti di sapere se è necessario ritirare un bambino o se è stato cambiato un luogo per la cena. Il framework Android consente alle app di messaggistica di estendere i loro servizi nell'esperienza di guida utilizzando un'interfaccia utente standard che consente ai conducenti di tenere gli occhi sulla strada.

Le app che supportano la messaggistica possono estendere le notifiche dei messaggi per consentire ad Android Auto di utilizzarle quando Auto è in esecuzione. Queste notifiche vengono visualizzate in modalità automatica e consentono agli utenti di leggere e rispondere ai messaggi in un'interfaccia coerente e poco distratta. Inoltre, quando utilizzi l' API MessaggigingStyle, ricevi notifiche di messaggi ottimizzate per tutti i dispositivi Android, incluso Android Auto. Le ottimizzazioni includono un'interfaccia utente specializzata per notifiche di messaggi, animazioni migliorate e supporto per le immagini in linea.

Questa guida ti mostra come estendere un'app che mostra messaggi all'utente e riceve le risposte dell'utente, ad esempio un'app di chat, per visualizzare i messaggi e rispondere alla ricezione su un dispositivo Auto. Per indicazioni sulla progettazione correlate, vedi App di messaggistica sul sito Progetta per la guida.

Inizia

Per fornire un servizio di messaggistica per i dispositivi Auto, la tua app deve dichiarare il proprio supporto per Android Auto nel file manifest ed essere in grado di:

  • Crea e invia oggetti NotificationCompat.MessagingStyle che contengono oggetti Action di risposta e contrassegna come letto.
  • Gestisci la risposta e contrassegnando una conversazione come letta con un Service.

Concetti e oggetti

Prima di iniziare a progettare la tua app, è utile capire in che modo Android Auto gestisce i messaggi.

Un singolo blocco di comunicazione è chiamato messaggio ed è rappresentato dalla classe MessagingStyle.Message. Un messaggio contiene un mittente, il contenuto del messaggio e l'ora in cui è stato inviato.

La comunicazione tra gli utenti è chiamata conversazione ed è rappresentata da un oggetto MessagingStyle. Una conversazione, o MessagingStyle, contiene un titolo, i messaggi e indica se la conversazione fa parte di un gruppo di utenti.

Per informare gli utenti degli aggiornamenti di una conversazione, ad esempio un nuovo messaggio, le app pubblicano una Notification nel sistema Android. Notification utilizza l'oggetto MessagingStyle per visualizzare l'UI specifica per i messaggi nell'area notifiche. La piattaforma Android passa anche Notification ad Android Auto, che viene estratto e utilizzato per pubblicare una notifica tramite il display dell'auto MessagingStyle.

Android Auto richiede inoltre alle app di aggiungere oggetti Action a un Notification per consentire all'utente di rispondere rapidamente a un messaggio o di contrassegnarlo come letto direttamente dall'area notifiche.

Riassumendo, una singola conversazione è rappresentata da un oggetto Notification a cui è applicato lo stile di un oggetto MessagingStyle. MessagingStyle contiene tutti i messaggi all'interno di quella conversazione in uno o più oggetti MessagingStyle.Message. Inoltre, per essere conforme ad Android Auto, un'app deve allegare oggetti Action di risposta e contrassegna come letti a Notification.

Flusso di messaggi

In questa sezione viene descritto un flusso di messaggi tipico tra la tua app e Android Auto.

  1. L'app riceve un messaggio.
  2. L'app genera una notifica MessagingStyle con oggetti Action risposta e contrassegna come letto.
  3. Android Auto riceve l'evento "Nuova notifica" dal sistema Android e trova MessagingStyle, rispondi Action e contrassegna come letto Action.
  4. Android Auto genera e mostra una notifica nell'auto.
  5. Se l'utente tocca la notifica sul display dell'auto, Android Auto attiva l'icona Segna come letto Action.
    • In background, l'app deve gestire questo evento Contrassegna come letto.
  6. Se l'utente risponde alla notifica utilizzando la voce, Android Auto inserisce una trascrizione della risposta dell'utente nella risposta Action e la attiva.
    • In background, l'app deve gestire questo evento di risposta.

Ipotesi preliminari

Questa pagina non ti guida nella creazione di un'intera app di messaggistica. Il seguente esempio di codice include alcuni dei requisiti necessari alla tua app prima di iniziare a supportare la messaggistica con Android Auto:

data class YourAppConversation(
        val id: Int,
        val title: String,
        val recipients: MutableList<YourAppUser>,
        val icon: Bitmap) {
    companion object {
        /** Fetches [YourAppConversation] by its [id]. */
        fun getById(id: Int): YourAppConversation = // ...
    }

    /** Replies to this conversation with the given [message]. */
    fun reply(message: String) {}

    /** Marks this conversation as read. */
    fun markAsRead() {}

    /** Retrieves all unread messages from this conversation. */
    fun getUnreadMessages(): List<YourAppMessage> { return /* ... */ }
}
data class YourAppUser(val id: Int, val name: String, val icon: Uri)
data class YourAppMessage(
    val id: Int,
    val sender: YourAppUser,
    val body: String,
    val timeReceived: Long)

Dichiara il supporto di Android Auto

Quando Android Auto riceve una notifica da un'app di messaggistica, verifica che l'app abbia dichiarato il supporto per Android Auto. Per attivare questo supporto, includi la seguente voce nel file manifest dell'app:

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

Questa voce del file manifest si riferisce a un altro file XML che devi creare con il seguente percorso: YourAppProject/app/src/main/res/xml/automotive_app_desc.xml. Nella sezione automotive_app_desc.xml dichiara le funzionalità di Android Auto supportate dalla tua app. Ad esempio, per dichiarare il supporto delle notifiche, includi quanto segue:

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

Se la tua app può essere impostata come gestore SMS predefinito, assicurati di includere il seguente elemento <uses>. In caso contrario, se la tua app è impostata come gestore SMS predefinito verrà utilizzato un gestore predefinito integrato in Android Auto per gestire i messaggi SMS/MMS in arrivo, il che potrebbe comportare notifiche duplicate.

<automotiveApp>
    ...
    <uses name="sms" />
</automotiveApp>

Importa la libreria di base AndroidX

Per creare notifiche da usare con i dispositivi Auto è necessaria la libreria di base AndroidX. Importa la libreria nel tuo progetto nel seguente modo:

  1. Nel file build.gradle di primo livello, includi una dipendenza dal repository Maven di Google, come mostrato nell'esempio seguente:

trendy

allprojects {
    repositories {
        google()
    }
}

Kotlin

allprojects {
    repositories {
        google()
    }
}
  1. Nel file build.gradle del modulo dell'app, includi la dipendenza della libreria AndroidX Core, come mostrato nell'esempio seguente:

trendy

dependencies {
    // If your app is written in Java
    implementation 'androidx.core:core:1.13.1'

    // If your app is written in Kotlin
    implementation 'androidx.core:core-ktx:1.13.1'
}

Kotlin

dependencies {
    // If your app is written in Java
    implementation("androidx.core:core:1.13.1")

    // If your app is written in Kotlin
    implementation("androidx.core:core-ktx:1.13.1")
}

Gestire le azioni degli utenti

La tua app di messaggistica richiede un modo per gestire l'aggiornamento di una conversazione tramite un Action. Per Android Auto, l'app deve gestire due tipi di oggetti Action: Rispondi e Contrassegna come letto. Ti consigliamo di gestirle utilizzando un elemento IntentService, che offre la flessibilità di gestire chiamate potenzialmente costose in background, liberando il thread principale dell'app.

Definisci le azioni relative all'intent

Le azioni Intent sono stringhe semplici che identificano lo scopo dell'elemento Intent. Poiché un singolo servizio può gestire più tipi di intent, è più facile definire più stringhe di azione anziché definire più componenti IntentService.

L'app di messaggistica di esempio di questa guida prevede i due tipi di azioni richiesti: risposta e contrassegna come letto, come mostrato nel seguente esempio di codice.

private const val ACTION_REPLY = "com.example.REPLY"
private const val ACTION_MARK_AS_READ = "com.example.MARK_AS_READ"

Crea il servizio

Per creare un servizio che gestisca questi oggetti Action, devi avere l'ID conversazione, ovvero una struttura di dati arbitraria definita dalla tua app che identifica la conversazione. Occorre anche un tasto di input remoto, di cui parleremo dettagliatamente più avanti in questa sezione. Il seguente esempio di codice crea un servizio per gestire le azioni richieste:

private const val EXTRA_CONVERSATION_ID_KEY = "conversation_id"
private const val REMOTE_INPUT_RESULT_KEY = "reply_input"

/**
 * An [IntentService] that handles reply and mark-as-read actions for
 * [YourAppConversation]s.
 */
class MessagingService : IntentService("MessagingService") {
    override fun onHandleIntent(intent: Intent?) {
        // Fetches internal data.
        val conversationId = intent!!.getIntExtra(EXTRA_CONVERSATION_ID_KEY, -1)

        // Searches the database for that conversation.
        val conversation = YourAppConversation.getById(conversationId)

        // Handles the action that was requested in the intent. The TODOs
        // are addressed in a later section.
        when (intent.action) {
            ACTION_REPLY -> TODO()
            ACTION_MARK_AS_READ -> TODO()
        }
    }
}

Per associare questo servizio alla tua app, devi registrarlo anche nel file manifest dell'app, come mostrato nell'esempio seguente:

<application>
    <service android:name="com.example.MessagingService" />
    ...
</application>

Genera e gestisci gli intent

Le altre app, inclusa Android Auto, non possono ottenere in alcun modo Intent che attiva MessagingService, perché i Intent vengono trasmessi ad altre app tramite un PendingIntent. A causa di questo limite, devi creare un oggetto RemoteInput per consentire ad altre app di fornire il testo di risposta alla tua app, come mostrato nell'esempio seguente:

/**
 * Creates a [RemoteInput] that lets remote apps provide a response string
 * to the underlying [Intent] within a [PendingIntent].
 */
fun createReplyRemoteInput(context: Context): RemoteInput {
    // RemoteInput.Builder accepts a single parameter: the key to use to store
    // the response in.
    return RemoteInput.Builder(REMOTE_INPUT_RESULT_KEY).build()
    // Note that the RemoteInput has no knowledge of the conversation. This is
    // because the data for the RemoteInput is bound to the reply Intent using
    // static methods in the RemoteInput class.
}

/** Creates an [Intent] that handles replying to the given [appConversation]. */
fun createReplyIntent(
        context: Context, appConversation: YourAppConversation): Intent {
    // Creates the intent backed by the MessagingService.
    val intent = Intent(context, MessagingService::class.java)

    // Lets the MessagingService know this is a reply request.
    intent.action = ACTION_REPLY

    // Provides the ID of the conversation that the reply applies to.
    intent.putExtra(EXTRA_CONVERSATION_ID_KEY, appConversation.id)

    return intent
}

Nella clausola switch ACTION_REPLY all'interno di MessagingService, estrai le informazioni che vanno nella risposta Intent, come mostrato nell'esempio seguente:

ACTION_REPLY -> {
    // Extracts reply response from the intent using the same key that the
    // RemoteInput uses.
    val results: Bundle = RemoteInput.getResultsFromIntent(intent)
    val message = results.getString(REMOTE_INPUT_RESULT_KEY)

    // This conversation object comes from the MessagingService.
    conversation.reply(message)
}

Gestisci Intent contrassegna come letto in modo simile. Tuttavia, non richiede un elemento RemoteInput, come mostrato nell'esempio seguente:

/** Creates an [Intent] that handles marking the [appConversation] as read. */
fun createMarkAsReadIntent(
        context: Context, appConversation: YourAppConversation): Intent {
    val intent = Intent(context, MessagingService::class.java)
    intent.action = ACTION_MARK_AS_READ
    intent.putExtra(EXTRA_CONVERSATION_ID_KEY, appConversation.id)
    return intent
}

La clausola switch ACTION_MARK_AS_READ all'interno di MessagingService non richiede ulteriori logiche, come mostrato nell'esempio seguente:

// Marking as read has no other logic.
ACTION_MARK_AS_READ -> conversation.markAsRead()

Avvisare gli utenti della presenza di messaggi

Una volta completata la gestione delle azioni di conversazione, il passaggio successivo è generare notifiche conformi ad Android Auto.

Crea azioni

Action oggetti possono essere trasmessi ad altre app utilizzando un Notification per attivare metodi nell'app originale. In questo modo Android Auto può contrassegnare una conversazione come letta o rispondere.

Per creare un Action, inizia con un Intent. L'esempio seguente mostra come creare una "risposta" Intent:

fun createReplyAction(
        context: Context, appConversation: YourAppConversation): Action {
    val replyIntent: Intent = createReplyIntent(context, appConversation)
    // ...

Poi aggrega questo elemento Intent in un PendingIntent, per prepararlo all'utilizzo di app esterne. Un PendingIntent blocca tutti gli accessi all'elemento Intent con wrapping, esponendo solo un insieme selezionato di metodi che consentono all'app ricevente di attivare Intent o di ottenere il nome del pacchetto dell'app di origine. L'app esterna non potrà mai accedere al Intent sottostante o ai dati al suo interno.

    // ...
    val replyPendingIntent = PendingIntent.getService(
        context,
        createReplyId(appConversation), // Method explained later.
        replyIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
    // ...

Prima di configurare la risposta Action, tieni presente che Android Auto ha tre requisiti per la risposta Action:

  • L'azione semantica deve essere impostata su Action.SEMANTIC_ACTION_REPLY.
  • Action deve indicare che non mostrerà alcuna interfaccia utente quando verrà attivato.
  • Action deve contenere un singolo RemoteInput.

Il seguente esempio di codice configura una risposta Action che soddisfa i requisiti sopra elencati:

    // ...
    val replyAction = Action.Builder(R.drawable.reply, "Reply", replyPendingIntent)
        // Provides context to what firing the Action does.
        .setSemanticAction(Action.SEMANTIC_ACTION_REPLY)

        // The action doesn't show any UI, as required by Android Auto.
        .setShowsUserInterface(false)

        // Don't forget the reply RemoteInput. Android Auto will use this to
        // make a system call that will add the response string into
        // the reply intent so it can be extracted by the messaging app.
        .addRemoteInput(createReplyRemoteInput(context))
        .build()

    return replyAction
}

La gestione dell'azione contrassegna come lettura è simile, ad eccezione del fatto che non esiste un valore RemoteInput. Di conseguenza, Android Auto ha due requisiti per l'attributo Contrassegna come letto Action:

  • L'azione semantica è impostata su Action.SEMANTIC_ACTION_MARK_AS_READ.
  • L'azione indica che, una volta attivata, non mostrerà alcuna interfaccia utente.

Il seguente esempio di codice configura un valore Action di contrassegnazione come letto che soddisfa questi requisiti:

fun createMarkAsReadAction(
        context: Context, appConversation: YourAppConversation): Action {
    val markAsReadIntent = createMarkAsReadIntent(context, appConversation)
    val markAsReadPendingIntent = PendingIntent.getService(
            context,
            createMarkAsReadId(appConversation), // Method explained below.
            markAsReadIntent,
            PendingIntent.FLAG_UPDATE_CURRENT  or PendingIntent.FLAG_IMMUTABLE)
    val markAsReadAction = Action.Builder(
            R.drawable.mark_as_read, "Mark as Read", markAsReadPendingIntent)
        .setSemanticAction(Action.SEMANTIC_ACTION_MARK_AS_READ)
        .setShowsUserInterface(false)
        .build()
    return markAsReadAction
}

Durante la generazione degli intent in attesa, vengono utilizzati due metodi: createReplyId() e createMarkAsReadId(). Questi metodi fungono da codici di richiesta per ogni PendingIntent, che vengono utilizzati da Android per controllare gli intent in attesa esistenti. I metodi create() devono restituire ID univoci per ogni conversazione, ma le chiamate ripetute per la stessa conversazione devono restituire l'ID univoco già generato.

Considera un esempio con due conversazioni, A e B: l'ID risposta della conversazione A è 100 e l'ID contrassegna come letto è 101. L'ID risposta della conversazione B è 102 e l'ID contrassegna come letto è 103. Se la conversazione A viene aggiornata, gli ID risposta e contrassegna come letto sono ancora 100 e 101. Per maggiori informazioni, consulta PendingIntent.FLAG_UPDATE_CURRENT.

Creazione di un MessagingStyle

MessagingStyle è l'operatore delle informazioni sui messaggi ed è ciò che Android Auto utilizza per leggere ad alta voce ogni messaggio di una conversazione.

Innanzitutto, l'utente del dispositivo deve essere specificato sotto forma di oggetto Person, come mostrato nell'esempio seguente:

fun createMessagingStyle(
        context: Context, appConversation: YourAppConversation): MessagingStyle {
    // Method defined by the messaging app.
    val appDeviceUser: YourAppUser = getAppDeviceUser()

    val devicePerson = Person.Builder()
        // The display name (also the name that's read aloud in Android auto).
        .setName(appDeviceUser.name)

        // The icon to show in the notification shade in the system UI (outside
        // of Android Auto).
        .setIcon(appDeviceUser.icon)

        // A unique key in case there are multiple people in this conversation with
        // the same name.
        .setKey(appDeviceUser.id)
        .build()
    // ...

Puoi quindi creare l'oggetto MessagingStyle e fornire alcuni dettagli sulla conversazione.

    // ...
    val messagingStyle = MessagingStyle(devicePerson)

    // Sets the conversation title. If the app's target version is lower
    // than P, this will automatically mark the conversation as a group (to
    // maintain backward compatibility). Use `setGroupConversation` after
    // setting the conversation title to explicitly override this behavior. See
    // the documentation for more information.
    messagingStyle.setConversationTitle(appConversation.title)

    // Group conversation means there is more than 1 recipient, so set it as such.
    messagingStyle.setGroupConversation(appConversation.recipients.size > 1)
    // ...

Infine, aggiungi i messaggi da leggere.

    // ...
    for (appMessage in appConversation.getUnreadMessages()) {
        // The sender is also represented using a Person object.
        val senderPerson = Person.Builder()
            .setName(appMessage.sender.name)
            .setIcon(appMessage.sender.icon)
            .setKey(appMessage.sender.id)
            .build()

        // Adds the message. More complex messages, like images,
        // can be created and added by instantiating the MessagingStyle.Message
        // class directly. See documentation for details.
        messagingStyle.addMessage(
                appMessage.body, appMessage.timeReceived, senderPerson)
    }

    return messagingStyle
}

Raccogliere ed eseguire il push della notifica

Dopo aver generato gli oggetti Action e MessagingStyle, puoi creare e pubblicare Notification.

fun notify(context: Context, appConversation: YourAppConversation) {
    // Creates the actions and MessagingStyle.
    val replyAction = createReplyAction(context, appConversation)
    val markAsReadAction = createMarkAsReadAction(context, appConversation)
    val messagingStyle = createMessagingStyle(context, appConversation)

    // Creates the notification.
    val notification = NotificationCompat.Builder(context, channel)
        // A required field for the Android UI.
        .setSmallIcon(R.drawable.notification_icon)

        // Shows in Android Auto as the conversation image.
        .setLargeIcon(appConversation.icon)

        // Adds MessagingStyle.
        .setStyle(messagingStyle)

        // Adds reply action.
        .addAction(replyAction)

        // Makes the mark-as-read action invisible, so it doesn't appear
        // in the Android UI but the app satisfies Android Auto's
        // mark-as-read Action requirement. Both required actions can be made
        // visible or invisible; it is a stylistic choice.
        .addInvisibleAction(markAsReadAction)

        .build()

    // Posts the notification for the user to see.
    val notificationManagerCompat = NotificationManagerCompat.from(context)
    notificationManagerCompat.notify(appConversation.id, notification)
}

Risorse aggiuntive

Segnalare un problema di Android Auto Messaging

Se riscontri un problema durante lo sviluppo della tua app di messaggistica per Android Auto, puoi segnalarlo utilizzando 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 è già stato segnalato nell'elenco dei problemi. Puoi iscriverti e votare i problemi facendo clic sulla stella accanto a un problema nel tracker. Per ulteriori informazioni, consulta la sezione Iscrizione a un problema.