Tratando notificações recebidas do Firebase no Android
Vamos ver como tratar as notificações do Firebase.
No artigo onde vimos como configurar o FCM (Firebase Cloud Messaging) e integrar com a nossa App Android. Fizemos um pequeno exemplo de envio de notificação, porém, no exemplo que vimos, a notificação não aparecia em foreground, ou seja, enquanto estávamos com a App aberta. Em outras palavras, queremos obter um resultado conforme o exemplo abaixo:
Como podemos fazer isso? Atualmente, a classe que fica responsável em receber as mensagens do FCM está com o seguinte código:
public class CDCMessasingService extends FirebaseMessagingService { }
O que será que podemos fazer para que, de alguma forma, consigamos receber a mensagem do FCM dentro dessa classe? Será que existe algum método que podemos sobrescrever? Para a nossa felicidade, existe sim! :D
Pegando a mensagem do FCM
De acordo com a documentação do FCM para envio de mensagens, para que consigámos receber e tratar mensagens dentro da nossa App, precisamos sobrescrever o método onMessageReceive()
dentro da classe FirebaseMessagingService
. Portanto, faremos isso:
public class CDCMessasingService extends FirebaseMessagingService {
@Override public void onMessageReceived(RemoteMessage remoteMessage) { }
}
A partir desse método, quando a nossa App estiver aberta, receberemos a notificação do FCM e ela será representada pelo parâmetro remoteMessage
. Mas e depois? Aparecerá a notificação automaticamente como vimos anteriormente? Nesse caso não, pois recebemos apenas a mensagem. Em outras palavras, precisamos criar a notificação manualmente.
Criando a notificação no Android
Já que estamos recebendo uma notificação, podemos extraí-la para um objeto do tipo RemoteMessage.Notification
:
@Override public void onMessageReceived(RemoteMessage remoteMessage) {
RemoteMessage.Notification notification = remoteMessage.getNotification();
}
Em seguida, vamos criar um método que vai criar e exibir uma notificação do Android. Portanto, vamos criar o método mostrarNotificacao()
que recebe um objeto do tipo RemoteMessage.Notification
:
public class CDCMessasingService extends FirebaseMessagingService {
@Override public void onMessageReceived(RemoteMessage remoteMessage) {
RemoteMessage.Notification notification = remoteMessage.getNotification();
mostrarNotificacao(notification);
}
public void mostrarNotificacao(RemoteMessage.Notification notification) { } }
Agora que criamos o método, o que precisamos fazer? Criar de fato a notificação que será exibida para o usuário, certo? No Android, temos a classe Notification que permite criarmos notificações. Porém, da mesma forma como vimos no AlertDialo`, é recomendado que utilize a classe interna Builder
para criarmos as notificações. Portanto vamos utilizá-la:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
Notification.Builder builder = new Notification.Builder(this);
}
Por questões de compatibilidade, é recomendado que utilizemos a classe NotificationCompat. Em outras palavras, por meio dela, daremos suporte para versões do Android mais antigas. Portanto, vamos utilizá-la ao invés da Notification
:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
}
Você pode estar se perguntando do motivo de estarmos passando o this
por parâmetro do NotificationCompat.Builder()
, já que não estamos em uma Activity
ou Context
. Um detalhe curioso sobre a classe FirebaseMessagingService
é que ela, bem internamente, contém uma herança de Context
, portanto, podemos utilizá-la! :D
Agora que instanciamos o builder
, precisamos montar a notificação. Para isso temos que, pelo menos, preencher 3 informações:
- Título.
- Mensagem.
- Ícone.
Para adicionar um título, basta apenas utilizarmos o método setContentTitle()
enviando uma String
como parâmetro. Mas calma aí, qual String
mandaremos? Lembra que temos o parâmetro notification
? Então, nesse parâmetro temos todos os dados que uma notificação do FCM pode ter, portanto, vamos extrair o título por meio do método getTitle()
:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
String titulo = notification.getTitle();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(titulo);
}
E pra adicionar uma mensagem? Simples, da mesma forma como vimos no builder do AlertDialog
, a maioria dos métodos do builder
também nos devolve o objeto. Em outras palavras, basta apenas chamarmos o método que adiciona a mensagem após o momento em que inserimos o título, nesse caso, o método setContentText()
:
String titulo = notification.getTitle();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(titulo).setContentText("");
Mas qual mensagem enviamos para ele? Da mesma forma como fizemos com o título, podemos também pegar a mensagem do objeto notification
a partir do método getBody()
:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
String titulo = notification.getTitle();
String mensagem = notification.getBody();
builder.setContentTitle(titulo).setContentText(mensagem);
}
Agora que temos um título e uma mensagem, precisamos apenas adicionar um ícone. Podemos fazer isso por meio do método setSmallIcon()
. Mas qual ícone podemos utilizar? Nesse projeto, temos o ícone da própria App no local R.drawable.casadocodigo
, ou seja, vamos utilizá-lo:
.setSmallIcon(R.drawable.casadocodigo) }
Da mesma forma como vimos no AlertDialog
, finalizamos a builder
com o método build()
:
builder.setContentTitle(titulo)
.setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
Utilizando o serviço de notificações do Android
Se executarmos a nossa App nesse exato momento, a notificação não aparece! Por que será? Diferente do Dialog
, as notificações fazem parte de um serviço gerenciado pelo sistema operacional, nesse caso o Android, ou seja, precisamos pedir para o Android esse serviço. Para isso, utilizamos o método getSystemService()
:
builder.setContentTitle(titulo)
.setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
getSystemService();
Porém, precisamos especificar qual serviço queremos enviando uma String
por parâmetro. Qual String
podemos enviar? A classe Context
contém algumas constantes referentes a esses serviços e, para a nossa felicidade, ela contém a constante NOTIFICATION_SERVICE
que refere-se justamente ao serviço de notificação que desejamos. Portanto vamos pedir esse serviço:
builder.setContentTitle(titulo)
.setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
getSystemService(Context.NOTIFICATION_SERVICE);
Agora precisamos referenciar esse serviço. Por padrão, ele nos devolve um Object
, porém, precisamos de alguém mais específico! Qual classe podemos referenciar? No Android, temos a classe NotificationManager que é um classe responsável em gerenciar notificações do Android. Então vamos refenciar essa classe adicionando um cast, pois temos a "certeza" que é essa classe que será retornada:
builder.setContentTitle(titulo)
.setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
NotificationManager notificationManager = (NotificationManager)
getSystemService(Context.NOTIFICATION_SERVICE);
Exibindo a notificação
A partir do objeto notificationManager
, para exibirmos a notificação, basta apenas chamarmos o método notify()
enviando dois parâmetros:
- 1º parâmetro: id da notificação (Esse id é um valor único para a notificação que está sendo criada dentro da App).
- 2º parâmetro: um objeto do tipo Notification.
Como primeiro parâmetro, podemos enviar qualquer valor, portanto, colocarei como 0. Porém, e o objeto do tipo Notification
? Como podemos enviá-lo sendo que temos apenas o builder
do tipo NotificationCompat.Builder
?
Lembra do método build()
? Adivinha o que ele retorna? É exatamente o que você está pensando! Um objeto do tipo Notification
, em outras palavras, basta apenas refenciá-lo para um objeto do tipo Notification
:
Notification notificacao = builder.setContentTitle(titulo)
.setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
Então enviamos o objeto notificacao
para o notify()
:
public void mostrarNotificacao(String mensagem) {
String titulo = notification.getTitle();
String mensagem = notification.getBody();
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
Notification notificacao = builder.setContentTitle(titulo) .setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificacao);
}
Testando a nossa App e enviando uma mensagem a partir do firebase, temos o seguinte resultado:
Adicionando uma ação na notificação
Ué, por que será que a notificação não faz nada? Um detalhe importante sobre notificações, é que por padrão, elas não possuem ações de clique, portanto, precisamos dar uma ação pra ela!
Geralmente as ações de uma notificação é justamente levar o usuário para uma activity, na maioria dos casos a Launcher da App, nesse caso a MainActivity
. Portanto, o nosso primeiro passo é justamente criar uma Intent
para essa activity:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
String titulo = notification.getTitle();
String mensagem = notification.getBody();
Intent intent = new Intent(this, MainActivity.class);
//restante do código
}
Porém, a classe Notification
não possui nenhum método que receba uma Intent
... Então como podemos fazer com que a nossa notificação abra essa activity? No Android, temos a classe PadingIntent que além de nos permitir instanciar activities ou services do Android, permite dar uma ação para a notificação. Portanto, vamos implementá-la:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
String titulo = notification.getTitle();
String mensagem = notification.getBody();
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent .getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
Observe que não instânciamos a PedingIntent
na mão, justamente porque ela mesmo já nos fornece instâncias com os seus valores necessários já inicializados. Nesse caso, chamamos o método getActivity()
que nos devolve um objeto do tipo PedingIntent
que inicializa uma activity. Observe também que já enviamos alguns valores como parâmetro que tem o seguinte significado:
- 1º parâmetro: Contexto onde será inicializado.
- 2º parâmetro: refere-se ao request code que é um id para identificar o peding intent dentro da App. (Nesse caso, podemos simplesmente passar qualquer valor).
- 3º parâmetro:
Intent
que será executada. - 4º parâmetro: Flags que indicam o comportamento do pending intent, nesse exemplo utilizei a constante PendingIntent.FLAG_UPDATE_CURRENT que indica que a peding intent já existe e quero apenas atualizar os dados recebidos pelos extras. (Para mais detalhes das flags consulte a documentação)
Agora que temos o objeto pedingIntent
, precisamos apenas settá-lo no objeto notification
a partir do método setContentIntent()
:
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()); Notification notificacao = builder.setContentTitle(titulo) .setContentText(mensagem);
.setSmallIcon(R.drawable.casadocodigo)
.setContentIntent(pendingIntent)
.build();
Se executarmos novamente a nossa App, temos o seguinte resultado:
Agora a notificação tem uma ação! Nesse caso, ela abre a MainActivity
, entretanto, observe que a notificação, mesmo sendo tocada, ainda permanece na barrinha superior do Android! Por que será? Por padrão, quando criamos uma notificação ela só sai da barrinha do Android caso o usuário feche manualmente...
Mas o ideal seria fechar assim que tocar, certo? Será que podemos fazer isso? Sim, podemos! E é mais fácil do que parece, ou seja, basta apenas pedir para o builder
que a notificação é auto cancelada assim que tocamos chamando o método setAutoCancel()
que recebe um booleano como parâmetro, ou seja, passamos o valor true
:
public void mostrarNotificacao(RemoteMessage.Notification notification) {
String titulo = notification.getTitle();
String mensagem = notification.getBody();
Intent intent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent .getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
Notification notificacao = builder
.setSmallIcon(R.drawable.casadocodigo)
.setContentTitle(titulo)
.setContentText(mensagem)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(0, notificacao);
}
Pronto! Agora temos o mesmo resultado como vimos no início do post. Em outras palavras, estamos recebendo e tratando uma mensagem do FCM em foreground.
Que tal aprender hoje mesmo a desenvolver a sua App Android desde o zero? Na Alura, temos uma formação Android para que você crie sua primeira App com os principais conceitos necessários para desenvolver uma App.