Os apps compatíveis com mensagens podem estender as notificações de mensagens para permitir que o
Android Auto as use quando estiver em execução. Essas notificações são exibidas
pelo Android Auto e permitem que os usuários leiam e respondam às mensagens em uma interface consistente
e com pouca distração. E quando você usa a API MessagingStyle, recebe notificações de mensagens otimizadas para todos os dispositivos Android, incluindo o Android Auto. As otimizações incluem uma interface especializada para notificações de mensagens, animações aprimoradas e suporte para imagens inline.
Este guia mostra como estender um app que mostra mensagens e recebe as respostas do usuário, como um app de chat, para entregar a mensagem e enviar a resposta com um Android Auto. Com essa integração, os usuários só podem ver o histórico de mensagens das notificações recebidas durante a sessão ativa do Android Auto. Para mostrar mensagens de antes do início da sessão ativa do Android Auto, crie uma experiência de mensagens com modelo.
Para conferir orientações de design relacionadas, consulte Apps de comunicação no hub Design para carros.
Primeiros passos
Para oferecer serviço de mensagens ao Android Auto, o app precisa declarar suporte para o Android Auto no manifesto e fazer o seguinte:
- Declarar suporte ao Android Auto
- Criar e enviar objetos
NotificationCompat.MessagingStyleque contenham objetosActionpara responder e marcar a mensagem como lida. - Processar as ações de responder e marcar uma conversa como lida com um
Service.
Conceitos e objetos
Antes de começar a projetar seu app, é útil entender como o Android Auto processa as mensagens.
Um bloco individual de comunicação é chamado de mensagem e é representado
pela classe MessagingStyle.Message. Uma mensagem tem um remetente, o conteúdo e a hora em que ela foi enviada.
A comunicação entre usuários é chamada de conversa e é representada por um
objeto MessagingStyle. Uma conversa, ou MessagingStyle, contém um título,
as mensagens e se a conversa está em um grupo de usuários.
Para notificar os usuários sobre atualizações em uma conversa, como uma nova mensagem, os apps enviam uma
Notification ao sistema Android. Essa Notification usa o
objeto MessagingStyle para mostrar a interface específica da mensagem na aba de
notificações. A plataforma Android também transmite essa Notification para o Android Auto, e o MessagingStyle é extraído e usado para postar uma notificação na tela do carro.
O Android Auto também exige que os apps adicionem objetos Action a um Notification
para permitir que o usuário responda a uma mensagem ou a marque como lida diretamente na
tela do carro.
Em resumo, uma única conversa é representada por um objeto Notification que
é estilizado com um objeto MessagingStyle. O MessagingStyle contém todas as
mensagens dessa conversa em um ou mais objetos MessagingStyle.Message. Além disso, para ser compatível com o Android Auto, o app precisa anexar objetos Action para responder e
marcar a mensagem como lida à Notification.
Fluxo de mensagens
Esta seção descreve um fluxo de mensagens comum entre seu app e o Android Auto.
- Seu app recebe uma mensagem.
- Seu app gera uma notificação
MessagingStylecom objetosActionpara responder e marcar a mensagem como lida. - O Android Auto recebe o evento "notificação nova" do sistema Android
e encontra o
MessagingStyle, aActionpara responder e aActionpara marcar a mensagem como lida. - O Android Auto gera e exibe uma notificação no carro.
- Se o usuário tocar na notificação na tela do carro, o Android Auto
vai acionar a
Actionpara marcar a mensagem como lida.- Seu app precisa gerenciar esse evento "mark-as-read" em segundo plano.
- Se o usuário responder à notificação por voz, o Android Auto vai incluir uma
transcrição da resposta na
Actionde resposta e a acionará.- Seu app precisa gerenciar esse evento de resposta em segundo plano.
Hipóteses preliminares
Esta página não orienta você sobre como criar um app de mensagens inteiro. O exemplo de código a seguir inclui alguns elementos que seu app precisa ter antes de começar a oferecer suporte ao envio de mensagens pelo 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)
Declarar compatibilidade com o Android Auto
Quando o Android Auto recebe uma notificação de um app de mensagens, ele verifica se o app declarou compatibilidade com o Android Auto. Para ativar esse suporte, inclua esta entrada no manifesto do seu app:
<application>
...
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
...
</application>
Essa entrada de manifesto se refere a outro arquivo XML, automotive_app_desc.xml, que
você precisa criar no diretório res/xml do módulo do app. Em
automotive_app_desc.xml, declare os recursos do Android Auto com suporte do
app. Para declarar o suporte a notificações, inclua o seguinte:
<automotiveApp>
<uses name="notification" />
</automotiveApp>
Se o app puder ser definido como o gerenciador de SMS padrão, inclua o
elemento <uses> abaixo. Caso contrário, o Android Auto usará o gerenciador padrão
integrado para processar mensagens SMS/MMS recebidas quando seu app estiver configurado como o gerenciador
padrão, o que pode levar a notificações duplicadas.
<automotiveApp>
...
<uses name="sms" />
</automotiveApp>
Importar a biblioteca principal do AndroidX
A criação de notificações para uso com o Android Auto exige a biblioteca principal do AndroidX. Importe a biblioteca para o projeto desta forma:
- No arquivo
build.gradlede nível superior, inclua uma dependência no repositório Maven do Google, conforme mostrado no exemplo abaixo:
Groovy
allprojects { repositories { google() } }
Kotlin
allprojects { repositories { google() } }
- No arquivo
build.gradledo módulo do seu app, inclua a dependência da biblioteca AndroidX Core, conforme mostrado no exemplo abaixo:
Groovy
dependencies { // If your app is written in Java implementation 'androidx.core:core:1.17.0' // If your app is written in Kotlin implementation 'androidx.core:core-ktx:1.17.0' }
Kotlin
dependencies { // If your app is written in Java implementation("androidx.core:core:1.17.0") // If your app is written in Kotlin implementation("androidx.core:core-ktx:1.17.0") }
Processar ações do usuário
Seu app de mensagens precisa de uma maneira de processar atualizações em uma conversa por uma
Action. Para o Android Auto, existem dois tipos de objetos Action que o app
precisa processar: responder e marcar como lida. Recomendamos processá-los usando um
IntentService, que oferece a flexibilidade de processar chamadas potencialmente
caras em segundo plano, liberando a linha de execução principal do app.
Definir ações de intents
As ações Intent são strings básicas de sua escolha que identificam para que serve a
Intent. Como um único serviço pode processar vários tipos de intents,
é mais fácil definir várias strings de ação em vez de definir vários
componentes IntentService.
O app de mensagens de exemplo deste guia tem os dois tipos obrigatórios de ação: responder e marcar como lida, conforme mostrado no exemplo de código abaixo:
private const val ACTION_REPLY = "com.example.REPLY"
private const val ACTION_MARK_AS_READ = "com.example.MARK_AS_READ"
Criar o serviço
Para criar um serviço que gerencie esses objetos Action, você precisa do
ID da conversa, que é uma estrutura de dados arbitrária definida pelo seu app que
identifica a conversa. Você também precisa de uma chave de entrada remota, que
será discutida em detalhes mais adiante nesta seção. O exemplo de código abaixo cria um
serviço para processar as ações necessárias:
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()
}
}
}
Para associar esse serviço ao seu app, você também precisa registrá-lo no manifesto do app, como mostrado neste exemplo:
<application>
<service android:name="com.example.MessagingService" />
...
</application>
Gerar e processar intents
Outros apps, incluindo o Android Auto, não podem acessar a Intent que aciona o
MessagingService porque as intents são transmitidas para outros apps por uma
PendingIntent. Devido a essa limitação, crie um objeto RemoteInput
para permitir que outros apps forneçam texto de resposta ao seu app, conforme mostrado no
exemplo a seguir:
/**
* 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
}
Na cláusula de chave ACTION_REPLY dentro do MessagingService, extraia as
informações da Intent "responder", conforme mostrado no
exemplo abaixo:
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)
}
Gerencie a Intent "marcar mensagem como lida" de maneira semelhante. No entanto, ela não
exige uma RemoteInput, conforme mostrado neste exemplo:
/** 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
}
A cláusula de chave ACTION_MARK_AS_READ dentro do MessagingService não exige
mais lógica, como mostrado neste exemplo:
// Marking as read has no other logic.
ACTION_MARK_AS_READ -> conversation.markAsRead()
Notificar usuários de mensagens
Depois que o processamento da ação de conversa for concluído, a próxima etapa será gerar notificações compatíveis com o Android Auto.
Criar ações
Objetos Action podem ser transmitidos para outros apps usando uma Notification para acionar
métodos no app original. É assim que o Android Auto pode marcar uma conversa como
lida ou responder a ela.
Para criar uma Action, comece com uma Intent. O exemplo a seguir mostra como
criar uma Intent de "resposta" usando o método createReplyIntent() da
seção anterior:
fun createReplyAction(
context: Context, appConversation: YourAppConversation): Action {
val replyIntent: Intent = createReplyIntent(context, appConversation)
// ...
Em seguida, envolvemos essa Intent em uma PendingIntent que a prepara para o uso
externo do app. Uma PendingIntent bloqueia todo o acesso à Intent encapsulada
expondo apenas um conjunto selecionado de métodos que permitem que o app receptor dispare a
Intent ou receba o nome do pacote do app de origem. O app externo não pode
acessar a Intent ou os dados dela.
// ...
val replyPendingIntent = PendingIntent.getService(
context,
createReplyId(appConversation), // Method explained later.
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
// ...
Antes de configurar a Action reply, o Android Auto tem três
requisitos para essa Action:
- A ação semântica precisa ser definida como
Action.SEMANTIC_ACTION_REPLY. - A
Actionprecisa indicar que não vai mostrar nenhuma interface do usuário quando acionada. - A
Actionprecisa conter uma únicaRemoteInput.
O exemplo de código abaixo configura uma Action de resposta que atende aos
requisitos listados acima:
// ...
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
}
O processamento da ação "marcar como lida" é semelhante, exceto pelo fato de não haver uma RemoteInput.
O Android Auto tem dois requisitos para a Action de "marcar como lida":
- A ação semântica precisa ser definida como
Action.SEMANTIC_ACTION_MARK_AS_READ. - A ação indica que não vai mostrar nenhuma interface do usuário quando acionada.
O exemplo de código abaixo configura uma Action "marcar como lida" que atende a esses
requisitos:
fun createMarkAsReadAction(
context: Context, appConversation: YourAppConversation): Action {
val markAsReadIntent = createMarkAsReadIntent(context, appConversation)
val markAsReadPendingIntent = PendingIntent.getService(
context,
createMarkAsReadId(appConversation), // Method explained later.
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
}
Ao gerar as intents pendentes, você usa dois métodos: createReplyId() e createMarkAsReadId(). Esses métodos servem como códigos de solicitação para cada
PendingIntent, que são usados pelo Android para controlar intents pendentes.
Os métodos create() precisam retornar IDs exclusivos para cada conversa, mas
chamadas repetidas para a mesma conversa precisam retornar o ID exclusivo já
gerado.
Considere um exemplo com duas conversas: A e B: o ID de resposta da conversa A
é 100, e o ID de marcar como lida é 101. O ID de resposta da conversa B é 102, e
o ID de marcar como lida é 103. Se a conversa A for atualizada, os IDs de resposta e de
marcar como lida ainda serão 100 e 101. Para saber mais, consulte
PendingIntent.FLAG_UPDATE_CURRENT.
Criar um MessagingStyle
O MessagingStyle é a operadora das informações das mensagens, que é usada pelo Android
Auto para ler mensagens de uma conversa em voz alta.
Primeiro, especifique o usuário do dispositivo como um objeto Person, conforme
mostrado neste exemplo:
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()
// ...
Em seguida, construa o objeto MessagingStyle e forneça alguns detalhes
sobre a conversa.
// ...
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)
// ...
Por fim, adicione as mensagens não lidas.
// ...
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
}
Empacotar e enviar a notificação
Depois de gerar os objetos Action e MessagingStyle, você pode construir
e postar a 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)
}
Outros recursos
Informar um problema com notificações de mensagens do Android Auto
Se você encontrar um problema ao desenvolver notificações de mensagens para o Android Auto, informe-o usando o Google Issue Tracker. Preencha todas as informações solicitadas no modelo de problema.
Criar novo problema (link em inglês)
Antes de informar um novo problema, verifique se ele já foi comunicado na lista. Inscreva-se e vote nos problemas clicando na estrela de um deles na lista de problemas. Para mais informações, consulte Inscrever-se em um problema.