Android avançado no Kotlin 01.2: Firebase Cloud Messaging para Android

Este codelab faz parte do curso Android avançado no Kotlin. Você aproveitará mais o curso se fizer os codelabs em sequência, mas isso não é obrigatório. Todos os codelabs do curso estão listados na página de destino dos codelabs avançados do Android em Kotlin (link em inglês).

Introdução

No codelab anterior, você adicionou notificações ao timer de ovo que foram criadas e acionadas no seu app. Outro caso de uso importante das notificações é enviar notificações push remotamente, que podem ser recebidas mesmo quando o app não está em execução.

O que é uma notificação push?

As notificações push são notificações que o servidor envia para dispositivos móveis. Elas podem ser entregues a um dispositivo independentemente de o app estar em execução ou não.

As notificações push são uma ótima maneira de informar os usuários sobre uma atualização ou lembrá-los de uma tarefa ou recurso. Imagine esperar que um produto esteja em estoque novamente. Com uma notificação push, um app de compras pode avisar sobre atualizações de ações em vez de verificar o status das ações diariamente.

As notificações push usam o padrão publish/subscribe, que permite que aplicativos de back-end enviem conteúdo relevante para clientes interessados. Sem um modelo de publicação/assinatura, os usuários precisam verificar periodicamente se há atualizações no app. Esse processo é tedioso e pouco confiável. Além disso, conforme o número de clientes cresce, essas verificações periódicas sobrecarregam os recursos de rede e de processamento, tanto do servidor do app quanto do dispositivo do usuário.

Assim como todos os outros tipos de notificação, respeite seus usuários com notificações push. Se o conteúdo da notificação não for interessante ou oportuno para o usuário, ele poderá desativar todas as notificações do app.

O que é o Firebase Cloud Messaging?

O Firebase Cloud Messaging faz parte da plataforma Firebase para o desenvolvimento em dispositivos móveis. Geralmente, é preciso configurar um servidor do zero que possa se comunicar com dispositivos móveis para acionar notificações. Com o Firebase Cloud Messaging, você pode enviar notificações a todos os usuários do seu app instalados ou a um subconjunto deles sem configurar um servidor. Por exemplo, é possível enviar um lembrete aos usuários ou fazer uma promoção especial, como um brinde.Você pode enviar uma notificação remotamente para um ou vários dispositivos.

Também é possível usar o Firebase Cloud Messaging para transferir dados do seu app de back-end ou de um projeto do Firebase para seus usuários.

Neste codelab, você aprenderá a usar o Firebase Cloud Messaging para enviar notificações push para seu app Android e enviar dados.

Se você encontrar algum problema (bugs no código, erros gramaticais, instruções pouco claras, etc.) neste codelab, informe o problema no link Informar um erro no canto inferior esquerdo do codelab.

O que você já precisa saber

Você precisa:

  • Como criar apps Android em Kotlin. Especificamente, trabalhe com o SDK do Android.
  • Como projetar seu app usando componentes de arquitetura e vinculação de dados.
  • Conhecimentos básicos sobre broadcast receivers.
  • Conhecimento básico do AlarmManager.
  • Como criar e enviar notificações usando o Gerenciador de notificações.

O que você vai aprender

  • Como enviar mensagens ao usuário por meio do Firebase Cloud Messaging.
  • Como enviar dados de um back-end para seu app usando mensagens de dados, que fazem parte do Firebase Cloud Messaging.

Atividades do laboratório

  • Adicione notificações push ao app inicial.
  • Processar o Firebase Cloud Messaging enquanto seu app está em execução.
  • Transfira dados com o Firebase Cloud Messaging.

Neste codelab, você trabalhará no código do codelab "Como usar notificações em apps para Android" anterior. No codelab anterior, você criou um app de timer de ovo que envia notificações quando o timer de cozimento termina. Neste codelab, você adicionará o Firebase Cloud Messaging para enviar notificações push aos usuários do app e lembrá-los de comer ovos.

Para fazer o download do app de exemplo:

Clone o repositório do GitHub e mude para a ramificação starter:

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications-fcm


Como alternativa, é possível fazer o download do repositório como um arquivo ZIP, descompactá-lo e abri-lo no Android Studio.

Fazer o download do ZIP

Etapa 1: criar um projeto do Firebase

Antes de adicionar o Firebase ao app Android, é preciso criar um projeto do Firebase e conectá-lo ao app.

  1. Faça login no Console do Firebase.
  2. Clique em Adicionar projeto e selecione ou insira o Nome do projeto. Nomeie seu projeto como fcm-codelab.
  3. Clique em Continuar.
  4. Para ignorar a configuração do Google Analytics, desative o botão Ativar o Google Analytics para este projeto.
  5. Clique em Criar projeto para concluir a configuração do projeto do Firebase.

Etapa 2: registrar seu app com o Firebase

Agora que você tem um projeto do Firebase, é possível adicionar seu app para Android a ele.

  1. No centro da página de visão geral do projeto do Console do Firebase, clique no ícone do Android para iniciar o fluxo de trabalho de configuração.

  1. No campo Nome do pacote Android, insira com.example.android.eggtimernotifications.
  2. Clique em Register app.

Importante: insira o ID correto do app, porque não será possível adicionar nem modificar esse valor depois de registrá-lo no projeto do Firebase.

Etapa 3: adicionar o arquivo de configuração do Firebase ao projeto

Adicione o arquivo de configuração do Firebase para Android ao app.

  1. Clique em Fazer o download do google-services.json para receber o arquivo de configuração do Firebase para Android (google-services.json). Verifique se o arquivo de configuração não está anexado com caracteres adicionais e se tem o nome google-services.json.
  2. Mova seu arquivo de configuração para o diretório de módulos do seu app.

Etapa 4: configurar seu projeto Android para ativar produtos do Firebase

Para ativar os produtos do Firebase no seu app, adicione o plug-in google-services aos seus arquivos do Gradle.

  1. No arquivo do Gradle (build.gradle) no nível raiz, verifique se você tem o repositório Maven do Google.
  2. Em seguida, adicione regras para incluir o plug-in dos Serviços do Google.

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.2'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. No arquivo do Gradle (geralmente app/build.gradle) do módulo (nível do app), adicione uma linha para aplicar o plug-in na parte inferior do arquivo.

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

Nesta tarefa, você adicionará o Firebase Cloud Messaging (FCM) ao seu projeto para usar notificações push.

O código de serviço do Android para o FCM deste codelab é fornecido em MyFirebaseMessagingService.kt. Nas etapas a seguir, você adicionará código ao app Android.

Você usará o Editor do Notificações para testar a implementação. O Editor do Notificações é uma ferramenta que ajuda a escrever e enviar mensagens do site do Console do Firebase.

  1. Abrir MyFirebaseMessagingService.kt
  2. Inspecione o arquivo e, em especial, as seguintes funções:
  • onNewToken(): chamado automaticamente se o serviço estiver registrado no manifesto do Android. Essa função é chamada quando você executa o app pela primeira vez e sempre que o Firebase emite um novo token. O token é uma chave de acesso ao seu projeto de back-end do Firebase. Ele é gerado para seu dispositivo cliente específico. Com esse token, o Firebase sabe para qual cliente o back-end deve enviar mensagens. O Firebase também sabe se esse cliente é válido e tem acesso a esse projeto.
  • onMessageReceived: chamado quando seu app está em execução e o Firebase envia uma mensagem para ele. Essa função recebe um objeto RemoteMessage, que pode carregar uma notificação ou payload de mensagem de dados. Você aprenderá mais sobre as diferenças entre notificações e payloads de mensagens de dados neste codelab.

Etapa 1: enviar notificações do FCM para um único dispositivo

O Console de notificações permite testar o envio de uma notificação. Para enviar uma mensagem a um dispositivo específico usando o console, você precisa saber o token de registro do dispositivo.

Quando o back-end do Firebase gera um token novo ou atualizado, a função onNewToken() é chamada, com o novo token transmitido como um argumento. Se você quiser segmentar um único dispositivo ou criar um grupo de dispositivos para enviar uma mensagem de transmissão, precisará acessar esse token estendendo FirebaseMessagingService e modificando onNewToken().

  1. Abra AndroidManifest.xml e remova a marca de comentário do código a seguir para ativar o MyFirebaseMessagingService para o app de timer de ovo. Os metadados de serviço no manifesto do Android registram MyFirebaseMessagingService como um serviço e adicionam um filtro de intent para que esse serviço receba mensagens enviadas do FCM. A última parte dos metadados declara breakfast_notification_channel_id como default_notification_channel_id para o Firebase. Você usará esse ID na próxima etapa.
<!-- AndroidManifest.xml -->
<!-- TODO: Step 3.0 uncomment to start the service  -->

        <service
                android:name=".MyFirebaseMessagingService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
        <!-- [START fcm_default_icon] -->
        <!--
 Set custom default icon. This is used when no icon is set for incoming notification messages.
             See README(https://goo.gl/l4GJaQ) for more.
        -->
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_icon"
                android:resource="@drawable/common_google_signin_btn_icon_dark"/>
        <!--
 Set color used with incoming notification messages. This is used when no color is set for the incoming
             notification message. See README(https://goo.gl/6BKBk7) for more.
        -->
        <meta-data
                android:name="com.google.firebase.messaging.default_notification_color"
                android:resource="@color/colorAccent"/> <!-- [END fcm_default_icon] -->
        <!-- [START fcm_default_channel] -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/breakfast_notification_channel_id" />
        <!-- [END fcm_default_channel] -->

Recomenda-se criar um novo canal de notificação para o FCM, já que os usuários podem querer ativar/desativar o timer de ovo ou as notificações push do FCM separadamente.

  1. Abra ui/EggTimerFragment.kt . Em onCreateView(), adicione o seguinte código de criação de canal.
// EggTimerFragment.kt

   // TODO: Step 3.1 create a new channel for FCM
    createChannel(
        getString(R.string.breakfast_notification_channel_id),
        getString(R.string.breakfast_notification_channel_name)
    )
  1. Abra MyFirebaseMessagingService.kt e remova a marca de comentário da função onNewToken(). Essa função será chamada quando um novo token for gerado.
// MyFirebaseMessagingService.kt

   // TODO: Step 3.2 log registration token
    // [START on_new_token]
    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the     
     * InstanceID token is initially generated so this is where you would retrieve     
     * the token.
     */
    override fun onNewToken(token: String?) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // Instance ID token to your app server.
        sendRegistrationToServer(token)
    }
    // [END on_new_token]
  1. Execute o app de timer de ovo.
  2. Observe o logcat (View > Tool Windows > Logcat). Você verá uma linha de registro mostrando o token como este: Esse é o token necessário para enviar uma mensagem a este dispositivo. Essa função só é chamada quando um novo token é criado.
2019-07-23 13:09:15.243 2312-2459/com.example.android.eggtimernotifications D/MyFirebaseMsgService: Refreshed token: f2esflBoQbI:APA91bFMzNNFaIskjr6KIV4zKjnPA4hxekmrtbrtba2aDbh593WQnm11ed54Mv6MZ9Yeerver7pzgwfKx7R9BHFffLBItLEgPvrtF0TtX9ToCrXZ5y7Hd-m

Observação: se o token não aparecer nas mensagens do Logcat, talvez o app já tenha recebido o token. Nesse caso, a desinstalação ajudará você a receber um novo token.

Agora você pode testar enviando uma notificação. Para enviar uma notificação, você usará o Editor do Notificações.

  1. Abra o Console do Firebase e selecione o projeto.
  2. Em seguida, selecione Cloud Messaging na navegação à esquerda.
  3. Clique em Enviar sua primeira mensagem.

  1. Insira Time for Breakfast! como o título da notificação e Don't forget to eat eggs! como o texto da notificação e selecione Enviar mensagem de teste. A caixa de diálogo pop-up Testar no dispositivo será exibida, solicitando que você forneça um token de registro do FCM.

  1. Copie o token do app do logcat.

  1. Cole esse token no campo Adicionar um token de registro do FCM na janela pop-up e clique no botão Adicionar ao lado do token.
  2. Na lista da caixa de seleção exibida, selecione o token. O botão Test precisa ser ativado.

  1. No seu dispositivo, coloque o app Egg Timer em segundo plano.
  2. No pop-up, clique em Testar.
  1. Depois de clicar em Testar, o dispositivo cliente com o app em execução em segundo plano receberá a notificação na bandeja de notificações do sistema. Você verá mais sobre como lidar com as mensagens do FCM quando o app estiver em primeiro plano mais tarde.

Tarefa: como enviar notificações do FCM para um tópico

As mensagens de tópicos do FCM são baseadas no modelo de publicação/assinatura.

Um app de mensagens pode ser um bom exemplo para o modelo Publicar/Assinar. Imagine que um app verifica se há novas mensagens a cada 10 segundos. Isso não só descarrega a bateria do smartphone, mas também usa recursos de rede desnecessários e cria uma carga desnecessária no servidor do app. Em vez disso, um dispositivo cliente poderá se inscrever e ser notificado quando houver novas mensagens entregues pelo app.

Os tópicos permitem enviar uma mensagem para vários dispositivos que ativaram esse tópico específico. Para os clientes, os tópicos são fontes de dados específicas em que o cliente tem interesse. Para o servidor, os tópicos são grupos de dispositivos que ativaram o recebimento de atualizações sobre uma fonte de dados específica. Os temas podem ser usados para exibir categorias de notificações, como notícias, previsão do tempo e resultados esportivos. Nesta parte do codelab, você vai criar um tópico "breakfast" para lembrar os usuários interessados de que eles comem ovos no café da manhã.

Para se inscrever em um tópico, o app cliente chama a função subscribeToTopic() do Firebase Cloud Messaging com o nome do tópico breakfast. Essa chamada pode ter dois resultados. Se o autor da chamada for bem-sucedido, o callback OnCompleteListener será chamado com a mensagem assinada. Se o cliente não for inscrito, o retorno de chamada receberá uma mensagem de erro.

No seu app, você inscreverá automaticamente os usuários no tópico de café da manhã. No entanto, na maioria dos aplicativos de produção, é melhor dar aos usuários controle sobre quais tópicos devem ser assinados.

  1. Abra EggTimerFragment.kt e localize a função subscribeTopic() vazia.
  2. Receba uma instância de FirebaseMessaging e chame a função subscibeToTopic() com o nome do tópico.
  3. Adicione um addOnCompleteListener para ser notificado do FCM sobre a conclusão ou falha da assinatura.
// EggTimerFragment.kt

   // TODO: Step 3.3 subscribe to breakfast topic
    private fun subscribeTopic() {
        // [START subscribe_topics]
        FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
            .addOnCompleteListener { task ->
                var msg = getString(R.string.message_subscribed)
                if (!task.isSuccessful) {
                    msg = getString(R.string.message_subscribe_failed)
                }
                Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
            }
        // [END subscribe_topics]
    }
  1. Chame a função subscribeTopic() para se inscrever em um tópico quando o app for iniciado. Role para cima até o onCreateView() e adicione uma chamada para subscribeTopic().
// EggTimerFragment.kt

   // TODO: Step 3.4 call subscribe topics on start
    subscribeTopic()

    return binding.root
  1. Para se inscrever no tópico de café da manhã, execute o app novamente. Você verá uma mensagem de aviso com a mensagem "Inscrito no tópico".

Agora você pode testar o envio de mensagens a um tópico:

  1. Abra o Editor do Notificações e selecione Notificação do Compose.
  2. Defina a Notificação do título e o Texto da notificação como antes.
  3. Desta vez, em vez de enviar a mensagem para um único dispositivo, clique em Tópico em Destino e digite breakfast como o tópico da mensagem.

  1. Selecione Agora em "Programação".

  1. Verifique se o app está sendo executado em segundo plano no dispositivo de teste.
  1. Clique em Revisar e em Publicar. Se você puder executar o app em mais de um dispositivo, poderá testar e observar se a notificação é recebida em todos os dispositivos inscritos no tópico.

Agora o app tem os seguintes canais de notificação: Egg e Breakfast. Em um dispositivo cliente, toque e mantenha o ícone do app pressionado, selecione Informações e clique em Notificações. Você verá os canais de notificação Egg e Breakfast, conforme mostrado na captura de tela a seguir. Se você desmarcar o canal Café da manhã, seu app não receberá notificações enviadas por esse canal.

Ao usar as notificações, lembre-se de que os usuários podem desativar qualquer canal de notificação a qualquer momento.

Etapa 1: mensagens de dados

As mensagens do FCM também podem conter um payload de dados que processa as mensagens no app cliente, usar mensagens de dados em vez de mensagens de notificação.

Para processar mensagens de dados, você precisa processar o payload de dados na função onMessageReceived() de MyFirebaseMessagingService. O payload é armazenado na propriedade data do objeto remoteMessage. O objeto remoteMessage e a propriedade data podem ser null.

  1. Abrir MyFirebaseMessagingService.
  2. Verifique se a propriedade data do objeto remoteMessage tem algum valor e exiba os dados no registro.
// MyFirebaseMessagingService.kt

    // [START receive_message]
    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
        Log.d(TAG, "From: ${remoteMessage?.from}")
        
       // TODO: Step 3.5 check messages for data
        // Check if the message contains a data payload.
        remoteMessage?.data?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
        }

    }
    // [END receive_message]

Para testar o código, use o Editor do Notificações novamente.

  1. Abra o Editor do Notificações, crie uma nova mensagem definindo o Destino como o tópico "quoquo;breakfast".
  2. Desta vez, na Etapa 4, Opções adicionais, defina as propriedades da chave e do valor de Dados personalizados:
  1. Chave: eggs
  2. Valor: 3

  1. Verifique se o app está sendo executado em primeiro plano. Se o app estiver em segundo plano, a mensagem do FCM acionará uma notificação automática e a função onMessageReceived() receberá apenas o objeto remoteMessage quando o usuário clicar na notificação.
  2. Envie a mensagem do Editor do Notificações e observe o registro de mensagens de dados que aparece no logcat.

Etapa 2: gerenciamento de mensagens em primeiro e segundo plano

Quando um dispositivo cliente que executa seu app recebe uma mensagem que inclui payloads de notificação e de dados, o comportamento do app depende de o app estar em segundo plano ou em primeiro plano nesse dispositivo:

  • Se o app estiver executando o plano de fundo, se a mensagem tiver um payload, a notificação será exibida automaticamente na bandeja. Se a mensagem também tiver um payload de dados, ele será processado pelo app quando o usuário tocar na notificação.
  • Se o app estiver sendo executado em primeiro plano e a notificação tiver um payload, ela não aparecerá automaticamente. O app precisa decidir como lidar com a notificação na função onMessageReceived(). Se a mensagem também tiver payload de dados, ambos serão processados pelo app.

Para os fins deste codelab, você quer lembrar o usuário do app de preparar alguns ovos para o café da manhã. Você não planeja enviar dados, mas também quer garantir que a notificação de lembrete sempre apareça, independentemente de o app estar em primeiro ou segundo plano.

Quando você envia uma mensagem do FCM para dispositivos em que o app de timer do ovo está instalado, a mensagem de notificação é exibida automaticamente se o app não está em execução ou está em segundo plano. No entanto, se o aplicativo estiver sendo executado em primeiro plano, a notificação não será exibida automaticamente. Em vez disso, o código do aplicativo decide o que fazer com a mensagem. Se o app estiver em primeiro plano quando receber uma mensagem do FCM, a função onMessageReceived() será acionada automaticamente com a mensagem. É aqui que o app pode processar silenciosamente os payloads de notificação e dados ou acionar uma notificação.

Para o app, você quer garantir que o usuário receba o lembrete quando ele estiver em primeiro plano. Por isso, vamos implementar um código para acionar uma notificação:

  1. Abra a função onMessageReceived() em MyFirebaseMessagingService novamente.
  2. Imediatamente após o código adicionado recentemente para verificar a mensagem de dados, adicione o código a seguir, que envia uma notificação usando o framework de notificações.
// MyFirebaseMessagingService.kt

    // TODO: Step 3.6 check messages for notification and call sendNotification
    // Check if the message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
        sendNotification(it.body as String)
    }
  1. Se você executar o app novamente e enviar uma notificação usando o Editor do Notificações, verá uma notificação como fazia na primeira parte do codelab, independentemente de o app estar em primeiro ou segundo plano.

O código da solução está na ramificação mestre do código transferido por download.

Curso da Udacity:

Documentação do Firebase:

Para ver links de outros codelabs deste curso, consulte a página de destino sobre os codelabs avançados do Android em Kotlin (link em inglês).