Tratando notificações recebidas do Firebase no Android

(Last Updated On: 3 de maio de 2017)

No post 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:

recebendo_notificacao_foreground

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! 😀

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 AlertDialog, é 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! 😀

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:

notificacao_sem_acao

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:

notificao_com_acao_mas_nao_sai

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.

E aí, o que achou de criar uma notificação no Android? Mais fácil do que parecia? Não? Então compartilhe conosco sobre sua experiência durante a implementação 🙂

Que tal aprender hoje mesmo a desenvolver a sua App Android desde o zero? Na Alura, temos a carreira desenvolvedor Android Júnior com 4 cursos de Android para que você crie sua primeira App com os principais conceitos necessários para desenvolver uma App.

FIQUE POR DENTRO

Content Editor at Alura and Software Developer

Próximo Artigo2 passos para o e-mail marketing não ser marcado como spam