Criar uma notificação

As notificações fornecem informações breves e oportunas sobre eventos no seu app quando ele não está em uso. Este documento mostra como criar uma notificação com vários recursos. Para uma introdução sobre como as notificações aparecem no Android, consulte a visão geral de notificações. Para conferir um exemplo de código que usa notificações, consulte o exemplo do SociaLite no GitHub.

O código nesta página usa as NotificationCompat APIs da biblioteca AndroidX. Essas APIs permitem adicionar recursos disponíveis apenas em versões mais recentes do Android, mantendo a compatibilidade com o Android 9 (nível 28 da API). No entanto, alguns recursos, como a ação de resposta in-line, resultam em um ambiente autônomo em versões anteriores.

Criar uma notificação básica

Uma notificação na forma mais básica e compacta, também conhecida como recolhida forma, mostra um ícone, um título e uma pequena quantidade de conteúdo de texto. Esta seção mostra como criar uma notificação que o usuário pode tocar para iniciar uma atividade no seu app.

Figura 1. Uma notificação com um ícone, um título e algum texto.

Para mais detalhes sobre cada parte de uma notificação, leia sobre anatomia da notificação.

Declarar a permissão de execução

O Android 13 (nível 33 da API) e versões mais recentes oferecem suporte a uma permissão de execução para postar notificações não isentas (incluindo serviços em primeiro plano, FGS) de um app.

O snippet de código abaixo mostra a permissão que precisa ser declarada no arquivo de manifesto do app:

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

Para mais detalhes sobre permissões de execução, consulte Permissão de execução de notificação.

Definir o conteúdo das notificações

Para começar, defina o conteúdo e o canal da notificação usando um NotificationCompat.Builder objeto. O exemplo a seguir mostra como criar uma notificação com o seguinte:

  • Um ícone pequeno, definido por setSmallIcon(). Esse é o único conteúdo visível ao usuário necessário.

  • Um título, definido por setContentTitle().

  • O corpo do texto, definido por setContentText().

  • A prioridade de notificação, definida por setPriority(). A prioridade determina o quão intrusiva a notificação é no Android 7.1 e versões anteriores. Para o Android 8.0 e versões mais recentes, defina a importância do canal, conforme mostrado na próxima seção.

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle(textTitle)
        .setContentText(textContent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

O construtor NotificationCompat.Builder exige que você forneça um ID do canal. Isso é necessário para que haja compatibilidade com o Android 8.0 (nível 26 da API) e versões mais recentes, mas é ignorado pelas versões mais antigas.

Por padrão, o conteúdo do texto da notificação é truncado para caber em uma única linha. Você pode mostrar mais informações criando uma notificação expansível.

Figura 2. Uma notificação expansível nas formas recolhida e expandida.

Para que sua notificação seja mais longa, é possível ativar uma notificação expansível ao adicionar um modelo de estilo com setStyle(). Por exemplo, o código a seguir cria uma área de texto maior:

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Much longer text that cannot fit one line...")
        .setStyle(NotificationCompat.BigTextStyle()
                .bigText("Much longer text that cannot fit one line..."))
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)

Para saber mais sobre outros estilos de notificação grandes, incluindo como adicionar uma imagem e controles de reprodução de mídia, consulte Criar uma notificação expansível.

Criar um canal e definir a importância

Antes de entregar a notificação no Android 8.0 e versões mais recentes, registre o canal de notificação do seu app no sistema, transmitindo uma instância de NotificationChannel para createNotificationChannel(). O código a seguir é bloqueado por uma condição na SDK_INT versão:

private fun createNotificationChannel(context: Context) {
    // Create the NotificationChannel, but only on API 26+ because
    // the NotificationChannel class is not in the Support Library.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val name = context.getString(R.string.channel_name)
        val descriptionText = context.getString(R.string.channel_description)
        val importance = NotificationManager.IMPORTANCE_DEFAULT
        val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
            description = descriptionText
        }
        // Register the channel with the system.
        val notificationManager: NotificationManager =
            context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.createNotificationChannel(channel)
    }
}

Como você precisa criar o canal de notificação antes de postar notificações no Android 8.0 e versões mais recentes, execute esse código assim que o app for iniciado. Não há problemas em chamá-lo repetidamente, porque a criação de um canal de notificação já existente não realiza uma operação.

O construtor NotificationChannel exige um importance, usando uma das constantes da classe NotificationManager. Esse parâmetro determina como interromper o usuário para mostrar uma notificação que pertença a esse canal. Defina a prioridade com setPriority() para oferecer suporte ao Android 7.1 e versões anteriores, conforme mostrado no exemplo anterior.

Embora você precise definir a importância ou prioridade da notificação, conforme mostrado no exemplo a seguir, o sistema não garante o comportamento do alerta. Em alguns casos, o sistema pode alterar o nível de importância com base em outros fatores, e o usuário pode sempre redefinir o nível de importância para determinado canal.

Para saber mais sobre o significado dos diferentes níveis, leia sobre os níveis de importância da notificação.

Definir a ação de toque da notificação

Cada notificação precisa responder a um toque, geralmente para abrir uma atividade no seu app que corresponda à notificação. Para fazer isso, especifique uma intent de conteúdo definida com um PendingIntent objeto e a transmita para setContentIntent().

O seguinte snippet mostra como criar uma intent básica para abrir uma atividade quando o usuário toca na notificação:

// Create an explicit intent for an Activity in your app.
val intent = Intent(context, AlertDetails::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent =
    PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        // Set the intent that fires when the user taps the notification.
        .setContentIntent(pendingIntent)
        .setAutoCancel(true)

Esse código chama setAutoCancel(), que remove a notificação automaticamente quando o usuário toca nela.

As flags de intent no exemplo anterior preservam a experiência de navegação esperada pelo usuário depois que ele abre o app usando a notificação. Talvez você queira usá-la dependendo do tipo de atividade que está iniciando, que pode ser uma das seguintes:

  • Uma atividade que existe exclusivamente para respostas à notificação. Não há motivo para o usuário navegar para essa atividade durante o uso normal do app, portanto, a atividade inicia uma nova tarefa, em vez de ser adicionada à tarefa e à backstack existentes no seu app. Esse é o tipo de intent criado no exemplo anterior.

  • Uma atividade que existe no fluxo regular do seu app. Nesse caso, o início da Activity cria um backstack para que as expectativas do usuário para os botões Voltar e Para cima sejam preservadas.

Mostrar a notificação

Para que a notificação seja exibida, chame NotificationManagerCompat.notify(), transmitindo um ID exclusivo para a notificação e o resultado de NotificationCompat.Builder.build(). Isso é mostrado no exemplo a seguir:

with(NotificationManagerCompat.from(context)) {
    if (ActivityCompat.checkSelfPermission(
            context,
            Manifest.permission.POST_NOTIFICATIONS
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // TODO: Consider calling ActivityCompat#requestPermissions here
        // to request the missing permissions, and then overriding
        // public fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>,
        //                                        grantResults: IntArray)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.

        return@with
    }
    // notificationId is a unique int for each notification that you must define.
    notify(NOTIFICATION_ID, builder.build())
}

Salve o ID da notificação que você transmitir para NotificationManagerCompat.notify(), porque você precisará dele quando quiser atualizar ou remover a notificação.

Além disso, para testar notificações básicas em dispositivos com o Android 13 e versões mais recentes, ative as notificações manualmente ou crie uma caixa de diálogo para solicitar notificações.

Adicionar botões de ação

Uma notificação pode oferecer até três botões de ação que permitem ao usuário responder rapidamente, como adiar um lembrete ou responder a uma mensagem de texto. Mas esses botões de ação não podem duplicar a ação executada quando o usuário toca na notificação.

Figura 3. Uma notificação com um botão de ação.

Para adicionar um botão de ação, transmita um PendingIntent para o addAction() método. Esse processo é parecido com configurar a ação de toque padrão da notificação, exceto que, em vez de iniciar uma atividade, você pode fazer outras coisas, como iniciar um BroadcastReceiver que execute um job em segundo plano para que a ação não interrompa o app que já está aberto.

Por exemplo, o seguinte código mostra como enviar uma transmissão a um receptor específico:

val ACTION_SNOOZE = "snooze"

val snoozeIntent = Intent(context, MyBroadcastReceiver::class.java).apply {
    action = ACTION_SNOOZE
    putExtra(EXTRA_NOTIFICATION_ID, 0)
}
val snoozePendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context, 0, snoozeIntent, PendingIntent.FLAG_IMMUTABLE)
val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setContentIntent(pendingIntent)
        .addAction(R.drawable.ic_snooze, context.getString(R.string.snooze),
                snoozePendingIntent)

Para saber mais sobre como criar um BroadcastReceiver para executar um trabalho em segundo plano, consulte a visão geral de transmissões.

Se você estiver tentando criar uma notificação com os botões de reprodução de mídia, por exemplo, pausar e pular faixas, veja como criar uma notificação com controles de mídia.

Adicionar uma ação de resposta direta

A ação de Resposta direta, introduzida no Android 7.0 (nível 24 da API), permite aos usuários inserir texto diretamente na notificação. O texto é entregue ao seu app sem abrir uma atividade. Por exemplo, você pode usar uma ação de "Resposta direta" para permitir que os usuários respondam a mensagens de texto ou atualizem listas de tarefas na notificação.

Figura 4. Tocar no botão "Responder" abre a entrada de texto.

A ação de "Resposta direta" aparece como um outro botão na notificação que abre uma entrada de texto. Quando o usuário termina de digitar, o sistema anexa a resposta de texto à intent especificada para a ação de notificação e envia a intent ao seu app.

Adicionar o botão de resposta

Para criar uma ação de notificação que ofereça suporte à resposta direta, siga estas etapas:

Crie uma instância de RemoteInput.Builder que possa ser adicionada à ação de notificação. O construtor dessa classe aceita uma string, usada pelo sistema como chave da entrada de texto. Seu app usa essa chave mais tarde para recuperar o texto da entrada.

// Key for the string that's delivered in the action's intent.
private val KEY_TEXT_REPLY = "key_text_reply"
val replyLabel: String = context.resources.getString(R.string.reply_label)
val remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
    setLabel(replyLabel)
    build()
}

Crie uma PendingIntent para a ação de resposta.

// Build a PendingIntent for the reply action to trigger.
val replyPendingIntent: PendingIntent =
    PendingIntent.getBroadcast(context,
        conversation.getConversationId(),
        getMessageReplyIntent(conversation.getConversationId()),
        PendingIntent.FLAG_MUTABLE)

Anexe o RemoteInput objeto a uma ação usando addRemoteInput().

// Create the reply action and add the remote input.
val action: NotificationCompat.Action =
    NotificationCompat.Action.Builder(R.drawable.ic_reply_icon,
        context.getString(R.string.label), replyPendingIntent)
        .addRemoteInput(remoteInput)
        .build()

Aplique a ação a uma notificação e emita a notificação.

// Build the notification and add the action.
val newMessageNotification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_message)
    .setContentTitle(context.getString(R.string.title))
    .setContentText(context.getString(R.string.content))
    .addAction(action)
    .build()

// Issue the notification.
NotificationManagerCompat.from(context).notify(notificationId, newMessageNotification)

O sistema pede ao usuário para inserir uma resposta quando ele aciona a ação de notificação, conforme mostrado na Figura 4.

Recuperar entrada do usuário na resposta

Para receber a entrada do usuário da IU de resposta da notificação, chame RemoteInput.getResultsFromIntent(), transmitindo a Intent recebida por seu BroadcastReceiver:

private fun getMessageText(intent: Intent): CharSequence? {
    return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(KEY_TEXT_REPLY)
}

Depois de processar o texto, atualize a notificação chamando NotificationManagerCompat.notify() com o mesmo ID e tag, se usados. Isso é necessário para ocultar a IU de resposta direta e confirmar ao usuário que a resposta foi recebida e processada corretamente.

// Build a new notification, which informs the user that the system
// handled their interaction with the previous notification.
val repliedNotification = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_message)
        .setContentText(context.getString(R.string.replied))
        .build()

// Issue the new notification.
NotificationManagerCompat.from(context).notify(notificationId, repliedNotification)

Recuperar outros dados

O processamento de outros tipos de dados funciona de maneira semelhante com RemoteInput. O exemplo a seguir usa a imagem como entrada.

val KEY_REPLY = "key_reply"
val replyLabel: String = context.resources.getString(R.string.reply_label)
val remoteInput: RemoteInput = RemoteInput.Builder(KEY_REPLY).run {
    setLabel(replyLabel)
    // Allow for image data types in the input.
    // This method can be used again to allow for other data types.
    setAllowDataType("image/*", true)
    build()
}

Chame RemoteInput#getDataResultsFromIntent e extraia os dados correspondentes.

class ReplyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val dataResults = RemoteInput.getDataResultsFromIntent(intent, KEY_REPLY)
        val imageUri: Uri? = dataResults?.get("image/*") as? Uri

        if (imageUri != null) {
            // Extract the image
            try {
                val inputStream = context.contentResolver.openInputStream(imageUri)
                val bitmap = BitmapFactory.decodeStream(inputStream)
                // Display the image
                // ...
            } catch (e: Exception) {
                Log.e("ReplyReceiver", "Failed to process image URI", e)
            }
        }
    }
}

Ao trabalhar com essa nova notificação, use o contexto que é transmitido para o método onReceive() do receptor.

Anexe a resposta à parte de baixo da notificação chamando setRemoteInputHistory(). No entanto, se você estiver criando um app de mensagens, crie uma notificação em estilo de mensagem e anexe a nova mensagem à conversa.

Para mais dicas sobre notificações em apps de mensagens, consulte a seção sobre práticas recomendadas para apps de mensagens.

Mostrar uma mensagem urgente

Seu app pode precisar exibir uma mensagem urgente e com limite de tempo, por exemplo, para uma chamada telefônica recebida ou um alarme tocando. Nessas situações, você pode associar uma intent de tela cheia à sua notificação.

Quando a notificação é invocada, os usuários veem um dos seguintes itens, dependendo do status de bloqueio do dispositivo:

  • Se o dispositivo do usuário estiver bloqueado, uma atividade de tela cheia será exibida, cobrindo a tela de bloqueio;
  • Se o dispositivo do usuário estiver desbloqueado, a notificação aparecerá de forma estendida, incluindo opções para interagir ou descartar a notificação.

O snippet de código a seguir demonstra como associar sua notificação a uma intent de tela cheia:

val fullScreenIntent = Intent(context, ImportantActivity::class.java)
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0,
    fullScreenIntent, PendingIntent.FLAG_IMMUTABLE)

val builder = NotificationCompat.Builder(context, CHANNEL_ID)
        .setSmallIcon(R.drawable.notification_icon)
        .setContentTitle("My notification")
        .setContentText("Hello World!")
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .setFullScreenIntent(fullScreenPendingIntent, true)

Definir a visibilidade da tela de bloqueio

Para controlar o nível de detalhes visíveis na notificação na tela de bloqueio, chame setVisibility() e especifique um dos seguintes valores:

  • VISIBILITY_PUBLIC: o conteúdo completo da notificação é mostrado na tela de bloqueio.

  • VISIBILITY_SECRET: nenhuma parte da notificação é mostrada na tela de bloqueio.

  • VISIBILITY_PRIVATE: apenas informações básicas, como o ícone da notificação e o título do conteúdo, são mostradas na tela de bloqueio. O conteúdo completo da notificação não é mostrado.

Quando você define VISIBILITY_PRIVATE, também é possível fornecer uma versão alternativa do conteúdo da notificação que oculta determinados detalhes. Por exemplo, um app de SMS pode exibir uma notificação que mostre "Você tem três novas mensagens de texto", mas ocultar o conteúdo e o remetente das mensagens. Para fornecer essa notificação alternativa, primeiro crie a notificação alternativa com NotificationCompat.Builder como de costume. Em seguida, anexe a notificação alternativa à notificação normal com setPublicVersion().

Lembre-se de que o usuário sempre tem o controle final sobre a exibição de notificações na tela de bloqueio e pode controlá-las com base nos canais de notificação do seu app.

Atualizar uma notificação

Para atualizar uma notificação depois de emiti-la, chame NotificationManagerCompat.notify() novamente, transmitindo o mesmo ID usado antes. Se a notificação anterior for dispensada, uma nova notificação será criada.

Você também pode chamar setOnlyAlertOnce() para que a notificação interrompa o usuário (com som, vibração ou dicas visuais) apenas na primeira vez que aparecer, e não nas atualizações posteriores.

Remover uma notificação

As notificações permanecem visíveis até que um dos seguintes casos aconteça:

  • o usuário dispense a notificação;
  • o usuário toque na notificação, se você chamar setAutoCancel() ao criar a notificação;
  • você chame cancel() para um ID de notificação específico. Esse método também exclui notificações contínuas.
  • você chame cancelAll(), que remove todas as notificações emitidas anteriormente ;
  • o período especificado expirar, se você definir um tempo limite ao criar a notificação, usando setTimeoutAfter(). Se necessário, você pode cancelar uma notificação antes do término do tempo limite especificado.

Práticas recomendadas para apps de mensagens

Considere as práticas recomendadas listadas aqui ao criar notificações para seus apps de mensagens e chat.

Usar MessagingStyle

A partir do Android 7.0 (nível 24 da API), o Android fornece um modelo de estilo de notificação especificamente para o conteúdo de mensagens. Ao usar a NotificationCompat.MessagingStyle classe, você pode mudar vários dos rótulos exibidos na notificação, incluindo o título da conversa, outras mensagens e a visualização de conteúdo para a notificação.

O snippet de código a seguir demonstra como personalizar o estilo de uma notificação usando a classe MessagingStyle.

val user = Person.Builder()
    .setIcon(userIcon)
    .setName(userName)
    .build()

val notification = NotificationCompat.Builder(context, CHANNEL_ID)
    .setContentTitle("2 new messages with $sender")
    .setContentText(subject)
    .setSmallIcon(R.drawable.new_message)
    .setStyle(NotificationCompat.MessagingStyle(user)
        .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getPerson())
        .addMessage(messages[2].getText(), messages[2].getTime(), messages[2].getPerson())
    )
    .build()

A partir do Android 9.0 (nível 28 da API), também é necessário usar a Person classe para receber uma renderização ideal da notificação e dos avatares.

Ao usar NotificationCompat.MessagingStyle, faça o seguinte:

  • Chame MessagingStyle.setConversationTitle() para definir um título para chats em grupo (com mais de duas pessoas). Um bom título para uma conversa pode ser o nome do grupo de chat ou, se ele não tiver um nome, uma lista dos participantes da conversa. Sem isso, a mensagem pode ser entendida como uma conversa individual com o remetente da mensagem mais recente da conversa.
  • Use o método MessagingStyle.setData() para incluir mensagens de mídia como imagens. Os tipos MIME do padrão image/* são compatíveis.

Usar a resposta direta

A resposta direta permite que um usuário responda a uma mensagem in-line.

  • Depois que um usuário responder com uma ação de resposta in-line, use MessagingStyle.addMessage() para atualizar a MessagingStyle notificação e não retire nem cancele a notificação. O não cancelamento da notificação permite que o usuário envie várias respostas a partir da notificação.
  • Para tornar a ação de resposta in-line compatível com o Wear OS, chame Action.WearableExtender.setHintDisplayInlineAction(true).
  • Use o addHistoricMessage() método para fornecer contexto a uma conversa de resposta direta, adicionando mensagens históricas à notificação.

Ativar a Resposta inteligente

  • Para ativar o recurso "Resposta inteligente", chame setAllowGeneratedResponses(true) na ação de resposta. Isso faz com que as respostas do recurso "Resposta inteligente" estejam disponíveis aos usuários quando a notificação é conectada a um dispositivo Wear OS. As respostas do recurso "Resposta inteligente" são geradas por um modelo de aprendizado de máquina no próprio relógio, usando o contexto fornecido pela notificação NotificationCompat.MessagingStyle, e nenhum dado é enviado para a Internet para gerar as respostas.

Adicionar metadados de notificação