Tratando notificações recebidas do Firebase no Android

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

(Última atualização em: 3 de maio de 2017)

Content Editor at Alura and Software Developer

  • Lucas Amarante

    Boa noite..Teria como enviar a mensagem da notificação para um TextView na MainActivity? Não estou conseguindo achar algum conteúdo para isso. Muito obrigado.

    • Alex Felipe

      Opa Lucas, blz? Vejamos se eu entendi, você quer clicar na notificação e depois aparecer uma tela com um textview com o conteúdo da notificação?

      E obrigado pelo feedback

      • Lucas Amarante

        Bom dia Alex. graças a DEUS td joia e contigo?

        Então seria isso mesmo. Estou recebendo a mensagem do firebase certinho na notificação e quando clico abre a MainActivity.. Eu queria pegar essa mensagem da notificação e tambem mostra-la num TextView dentro da MainActivity.

        Estou tentando com Intent, intent.putExtra e getIntent com Bundle na classe MainActivity só que o valor esta nulo.

        muito obrigado

  • Pingback: Integrando App Android com o Firebase Cloud Messaging - Blog da Alura: desenvolvimento, design e muita tecnologia()

  • Everton

    Como personalizo ícone de notificação do firebase? só fica cinza já fiz de tudo e não altera.

    • Alex Felipe

      Ajustamos o ícone da notificação pelo método setSmallIcon, caso o ícone que você está usando tiver fundo, vai aparecer o ícone todo preenchido.

      []s

  • Henrique Mendes

    Há algum tutorial de como enviar o push através de um app utilizando o firebase?

    • Rogerio Perinazzo

      No curso da UDEMY do Jamilson tem um curso do whatss clone que ensina o envio e recebimento de msg pelo firebase.

    • Alex Felipe

      Oi Henrique, tudo bem?

      Um tutorial mostrando como o servidor manda as requisições por enquanto não temos, porém, temos o curso de sincronização entre Apps Android com Web Service, nele é abordado a forma como podemos lidar com os push do FCM.

      []s

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