Notificação de Engajamento Pelo Firebase Console - Parte 12

Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação. Você receberá um email de confirmação. Somente depois de confirmar é que poderei lhe enviar o conteúdo exclusivo por email.

Email inválido.
Blog /Android /Notificação de Engajamento Pelo Firebase Console - Parte 12

Notificação de Engajamento Pelo Firebase Console - Parte 12

Vinícius Thiengo04/07/2016
(1890) (26) (16)
Go-ahead
"Se você está planejando em fazer alguma coisa diferente, você será criticado por causa disso. Tenha fé de que o que você está fazendo é o certo e bloqueie qualquer negatividade."
Herb Kelleher
Código limpo
Capa do livro Refatorando Para Programas Limpos
TítuloRefatorando Para Programas Limpos
CategoriaEngenharia de Software
AutorVinícius Thiengo
Edição
Ano2017
Capítulos46
Páginas598
Comprar Livro
Conteúdo Exclusivo
Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Opa, blz?

Nesse post damos continuidade a série Firebase Android, dessa vez mostrando como enviar notificações push message via Firebase Notification Console.

Apesar de esse ser o post 12 da série Firebase Android, ao menos a parte de notificação foi desvinculada da APP de Chat, pois ainda não é o momento de colocarmos notificação nela, primeiro vamos ter de estrutura a base de dados.

Entenda que foi utilizado o termo Engajamento, pois um dos principais motivos para fazer uso da notificação via Firebase Console é com o objetivo de os usuários da APP voltarem a se engajar nela.

Com isso podemos prosseguir com o software de exemplo.

Crie um projeto com o nome FirebaseNotification. Utilize as configurações padrões do Android Studio para um novo projeto. Somente na escolha da Activity que você deve utilizar uma BaseActivity para seguir o exemplo na integra.

Logo depois configure o Firebase em seu novo projeto, como realizado aqui: Add Firebase To Your Android Project

Depois da configuração nosso primeiro passo é configurar o gradle do projeto, mais especificamente o gradle app level, build.gradle (Module: app), para que ele possa trabalhar com notificações:

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:design:23.4.0'
compile 'com.google.firebase:firebase-messaging:9.0.2' /* daqui para baixo */
compile 'org.greenrobot:eventbus:3.0.0'
}

 

EventBus vai ser utilizado?

Isso mesmo, no decorrer do post você vai entender.

Agora podemos ir ao layout, começando pelo activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="br.com.thiengo.firebasenotification.MainActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>

 

E então o content_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="br.com.thiengo.firebasenotification.MainActivity"
tools:showIn="@layout/activity_main"
android:orientation="vertical">

<TextView
android:textSize="20sp"
android:gravity="center"
android:id="@+id/id_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""/>

<TextView
android:textSize="20sp"
android:id="@+id/id_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""/>

<View
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:background="#888"
android:layout_width="match_parent"
android:layout_height="0.8dp"/>

<TextView
android:textSize="20sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Topics"/>
<CheckBox
android:textSize="20sp"
android:id="@+id/id_chk_soccer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Futebol"/>
<CheckBox
android:textSize="20sp"
android:id="@+id/id_chk_basketball"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Basquete"/>
<CheckBox
android:textSize="20sp"
android:id="@+id/id_chk_volley"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Volei"/>
<CheckBox
android:textSize="20sp"
android:id="@+id/id_chk_swimming"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Natação"/>
<CheckBox
android:textSize="20sp"
android:id="@+id/id_chk_news"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Notícias gerais"/>

<View
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:background="#888"
android:layout_width="match_parent"
android:layout_height="0.8dp"/>

<TextView
android:textSize="20sp"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Unique token id"/>
<TextView
android:textSize="20sp"
android:id="@+id/id_token"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""/>
</LinearLayout>

 

Esse layout vai permitir que o usuário defina os tópicos que ele quer ou não participar, além de apresentar a última mensagem enviada do Firebase Notification Console e também o token único do usuário.

Token único?

Sim, nesse post vamos falar mais sobre esse token, por enquanto vamo voltar ao projeto e ir colocando as partes já definidas sem a utilização do Firebase Notification.

Nosso próximo passo é definir o MainActivity. Começando com as variáveis de instância e o método onCreate():

public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
private TextView tvTitle;
private TextView tvContent;
private TextView tvToken;
private int[] checkBoxIds = {
R.id.id_chk_soccer,
R.id.id_chk_basketball,
R.id.id_chk_volley,
R.id.id_chk_swimming,
R.id.id_chk_news
};
    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

initViews();
}
    ...
}

 

Note que temos um array de IDs. Esse array é necessário para que possamos colocar algumas configurações nas Views de Checkbox que estão em nosso layout. Com os IDs em array, somente um loop será o suficiente para a configuração de cada um dos Checkboxes. No código de configuração, initCheckboxes(), ficará mais claro o porquê de utilizarmos um array de IDs.

Continuando com o código, vamos colocar os métodos de inicialização das views:

private void initViews(){
tvTitle = (TextView) findViewById(R.id.id_title);
tvContent = (TextView) findViewById(R.id.id_content);
tvToken = (TextView) findViewById(R.id.id_token);

initCheckBoxes();
}
private void initCheckBoxes(){
for( int id : checkBoxIds ){
retrieveSPCheckboxValue( id );
setOnCheckedChangeListener( id );
}
}

private void setOnCheckedChangeListener( int viewId ){
((CheckBox) findViewById( viewId )).setOnCheckedChangeListener(this);
}

private void retrieveSPCheckboxValue( int id ){
CheckBox checkBox = (CheckBox) findViewById(id);
SharedPreferences sp = getSharedPreferences("sp", MODE_PRIVATE);
boolean value = sp.getBoolean( String.valueOf( id ), false );

checkBox.setChecked( value );
}

private void setSPCheckboxValue( int id, boolean value ){
SharedPreferences sp = getSharedPreferences("sp", MODE_PRIVATE);
SharedPreferences.Editor edit = sp.edit();

edit.putBoolean( String.valueOf( id ), value );
edit.apply();
}

 

O método retrieveSPCheckboxValue() é responsável por acessar o SharedPreferences e então obter o valor real daquele Checkbox em teste, de acordo com a última escolha do usuário (registrado ou não no tópico). Com isso conseguimos manter o Checkbox sempre com o valor que o usuário escolheu sem precisar de utilizar SQLite, Realm ou Firebase database.

Veja que o ID do Checkbox no layout é a chave de acesso ao valor dele no SharedPreferences, dessa forma não precisamos ficar criando em código uma chave para cada Checkbox, tendo em mente que o ID utilizado é único e não muda.

O método setSPCheckboxValue() é responsável por colocar o novo valor do Checkbox em teste no SharedPreferences. Esse método, diferente do retrieveSPCheckboxValue(), é utilizado no listener de valor alterado no Checkbox, que vamos definir logo abaixo:

public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
...

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String topic = getTopic( buttonView );

if( isChecked ){
displayMessage( "Vc foi inscrito no tópico: "+topic );
}
else{
displayMessage( "Vc foi removido do tópico: "+topic );
}

setSPCheckboxValue( buttonView.getId(), isChecked );
}

private String getTopic( CompoundButton buttonView ){
switch( buttonView.getId() ){
case R.id.id_chk_soccer:
return "soccer";
case R.id.id_chk_basketball:
return "basketball";
case R.id.id_chk_volley:
return "volley";
case R.id.id_chk_swimming:
return "swimming";
default:
return "news";
}
}

private void displayMessage( String message ){
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}

 

Implementando a Interface OnCheckedChangeListener temos o método onCheckedChanged() que além de alterar o valor do Checkbox no SharedPreferences apresenta uma mensagem ao usuário sobre como ele está em relação ao tópico escolhido, registrado ou não.

O método getTopic() tem os rótulos de tópicos que vamos estar utilizando no Firebase Console, para enviar mensagens somente para usuários que assinam determinados tópicos.

Nosso próximo passo é criarmos uma notificação no Firebase Console para ver a reação da APP assim que a notification é ativada pelo usuário.

Acesse o painel do Firebase e logo no menu esquerdo clique em Notifications. Logo depoiis clique em CRIAR MENSAGEM ou em NOVA MENSAGEM. Em seguida preencha os campos como abaixo. Segue primeira parte do formulário:

No campo Texto da mensagem temos o texto que vai aparecer como corpo da mensagem na área de notificações do aparelho.

O campo Rótulo da mensagem é utilizado para que possamos identificar a mensagem no dashboard do Firebase Notification. O campo Data envio permite a seleção de Agora ou uma data e horário em específico, incluindo o fuso.

Segue segunda parte do formulário, mais precisamente a parte que selecionaremos quem receberá a mensagem. Segmentação:

Os rádio buttons já nos indicam o bastante sobre como é realizada a segmentação, por aplicativos, tópicos ou individualmente. Em nosso exemplo vamos utilizar a versão de segmentação por aplicativo, todos os usuários cadastrados no aplicativo selecionado vão receber a mensagem.

Segue a terceira parte do formulário, Eventos de conversão, que por hora somente nos é útil em definir no relatório se as mensagens foram enviadas e se foram abertas. O outro evento de conversão é opcional e não vamos estar utilizando aqui:

Segue quarta e última parte do formulário, opções avançadas:

 

 

O campo Título defini o título que será apresentado junto a notificação na área de notificações do device. No select de Prioridade podemos colocar Alta ou Normal.

A grande diferença entre as prioridades é que quando Alta o device, mesmo que no estado idle (ocioso), a notificação será entregue, quando Normal o Firebase Cloud Message (FCM) aguarda até o device voltar a ser utilizado.

O select de Som dispensa comentários, somente escolha se quer ou não a ativação do som quando a notificação for entregue. Note que as configurações do device é que vão prevalecer.

Os selects de Expira em são referentes ao item time_to_live do JSON que é enviado como mensagem de notificação. Ele se refere ao máximo de tempo que uma mensagem permanece nos servidores do FCM.

Por que isso?

Porque pode ser que alguns dos devices como alvo de recebimento de mensagem não estejam conectados a rede e consequentemente os servidores do FCM têm de guardar as mensagens até poderem eniviar. Esse "guardar as mensagens" é que tem o tempo de time_to_live configurado. O valor padrão e máximo é de quatro semanas, mas você pode definir um personalizado. Passado o tempo e o device ainda não ativo para receber as push messages, as mensagens são descartadas.

Como o time_to_live, existem outros "n" campos, dê uma olhada aqui: Firebase Cloud Messaging HTTP Protocol

Com o decorrer dos posts sobre notificação e push message no Firebase Android nós vamos abordar alguns outros campos também importantes.

E agora sim os campos na seção Personalizar dados. Os dados chave/valor definidos nessa seção vem todos dentro de um campo data que está dentro do campo principal: notification. Se você acessou o conteúdo de Firebase Cloud Messaging HTTP Protocol vai perceber que pode utilizar alguns outros campos predefinidos dentro desse notification.

Em data não, pois ele está no mesmo nível que os campos predefinidos e os campos de data estão um nível abaixo, veja o JSON a seguir:

{
"to": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification": {
"body": "great match!",
"title": "Portugal vs. Denmark",
"icon": "myicon",
"data": {
"title": "test",
"content": "test"
}
}
}

 

O JSON acima não é o JSON que estaremos enviando como push message, é apenas um exemplo de uma notificação, veja o nível dos dados de data. Esses não têm efeitos predefinido no device, então coloque somente os dados úteis a sua lógica de negócio.

E tenha em mente que no total, digo, de todo o conteúdo dentro de um JSON com o exemplo acima você tem somente 2KB de dados.

Agora podemos clicar em ENVIAR MENSAGEM e teremos no device:

E clicando na notificação temos que nada dessa notification é apresentado na APP, APP foi apenas aberta:

Depois de alguns minutos, no Firebase Notification Console temos um relatório da mensagem enviada (essa foi uma mensagem anterior):

Agora vamos configurar o projeto FirebaseNotification para administrar o conteúdo da notificação, quando possível.

Quando possível?

Sim, com o FCM temos dois tipos de mensagens: Notification e Data.

O Data é possível utilizar somente com nossos próprios servidores, como fazíamos em Google Cloud Message. Ele, diferente do Notification, permite até 4KB de dados.

O Notification é ainda mais leve e podemos utilizá-lo tanto via nosso próprio servidor Web quanto pelo Firebase Notification Console, porém não mais como no GCM, nós temos acesso ao conteúdo de uma mensagem de notificação somente quando a APP estiver aberta, ou seja, no primeiro plano (foreground), caso onde não há necessidade de notificação na barra de status.

Caso contrário o System Tray do Android se encarrega de ler as configurações da mensagem e então colocá-la como notificação na área de notificações do device.

Isso por um lado é até uma vantagem, pois ainda é possível configurar a mensagem, mas utilizando somente os campos do JSON dela, nada de código extra com a antiga entidade Notification.

Sobre o campo Data vamos falar mais nos posts posteriores da série.

Ok. O que é gerado com o clique na notificação?

Os dados dela vão para um Intent que pode ser acessado na Activity principal do projeto por meio da chamada a getIntent(). Logo vamos começar com essa configuração:

public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
private TextView tvTitle;
private TextView tvContent;
private TextView tvToken;
private int[] checkBoxIds = {
R.id.id_chk_soccer,
R.id.id_chk_basketball,
R.id.id_chk_volley,
R.id.id_chk_swimming,
R.id.id_chk_news
};


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

initViews();
intentData( getIntent() );
}

private void intentData(Intent intent){
/* Claúsula de Guarda */
if( intent == null || intent.getExtras() == null ){
return;
}

tvTitle.setText( intent.getExtras().getString("title") );
tvContent.setText( intent.getExtras().getString("content") );
}

...
}

 

Em intentData() estamos utilizando o padrão Cláusula de Guarda para somente prosseguirmos caso os dados de Intent sejam válidos.

Lembra de nossa mensagem de notificação de teste? Ela continha os campos title e content, eles serão utilizados para preecherem as TextViews tvTitle e tvContent.

Com o código acima já estamos administrando os dados que queremos da notificação quando clicada. Porém ainda temos o caso da APP estar aberta, temos também que obter os dados nessa condição. Para isso vamos criar um Service, dentro de um novo package em nosso projeto, o package service. Segue classe CustomFirebaseMessagingService que estará dentro desse novo pacote:

public class CustomFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
/* TODO */
}
}

 

Para obtermos os dados que estão no campo data de nosso JSON temos de realizar a chamada a remoteMessage.getData().get("title") e a remoteMessage.getData().get("content").

Mas antes temos de ter em mente que esses dados estão chegando em um Service que foi criado pelo Firebase e não pelo nosso código. Temos de enviar esses dados para a Activity. Para isso vamos utilizar o EventBus. Então voltamos a MainActivity para registrar nossa Activity como listener do EventBus:

public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
private TextView tvTitle;
private TextView tvContent;
private TextView tvToken;
private int[] checkBoxIds = {
R.id.id_chk_soccer,
R.id.id_chk_basketball,
R.id.id_chk_volley,
R.id.id_chk_swimming,
R.id.id_chk_news
};


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

EventBus.getDefault().register(this); /* ESSA LINHA */
initViews();
intentData( getIntent() );
}

@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); /* E ESSA LINHA */
}

...
}

 

O trabalho com o EventBus é bem simples. A utilização dele é opcional. Optei por ele pois é o que, a princípio, precisa de menos código e pode ser utulizada em qualquer parte da APP.

Feito isso precisamo definir um método subscriber para obter os dados de mensagens disparadas pelo EventBus, mas antes temos de ter uma classe que vai conter esses dados, essa classe vai servir de envelope para os dados uteis da notificação. Segue classe NotificationEvent:

public class NotificationEvent {
private Intent content;

public NotificationEvent(Intent content ){
this.content = content;
}

public Intent getContent(){
return content;
}
}

 

Ok, mas vamos utilizar uma Intent como variável de instância para que?

Lembra de nosso código de intentData()? Para que possamos reaproveitá-lo, trabalhar com um Intent em NotificationEvent é uma excelente opção. Para lembrar do código:

private void intentData(Intent intent){
if( intent == null || intent.getExtras() == null ){
return;
}

tvTitle.setText( intent.getExtras().getString("title") );
tvContent.setText( intent.getExtras().getString("content") );
}

 

Vamos realizar uma chamada a ele dentro de nosso método subscriber de eventos EventBus com a classe NotificationEvent, dentro de nossa MainActivity. Segue método:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onNewNotification(NotificationEvent notification){
intentData( notification.getContent() );
}

 

Sempre que houver uma mensagem no EventBus com um objeto do tipo NotificationEvent esse método será disparado. Note que o código @Subscribe(threadMode = ThreadMode.MAIN) é necessário, caso contrário as views não são atualizadas dentro da Thread Principal.

Para saber mais sobre o EventBus acesse o post do blog sobre ele: EventBus Lib, Comunicação Entre Entidades Android. E também as docs dele em Green Robot EventBus

Agora voltando ao nosso Service CustomFirebaseMessagingService temos:

public class CustomFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = getIntentBody(remoteMessage );
NotificationEvent notificationEvent = new NotificationEvent( intent );
EventBus.getDefault().post(notificationEvent);
}

private Intent getIntentBody(RemoteMessage remoteMessage ){
Intent intent = new Intent();
Bundle extras = new Bundle();

for( String key : remoteMessage.getData().keySet() ){
extras.putString( key, remoteMessage.getData().get( key ) );
}
intent.putExtras(extras);
return intent;
}
}

 

O método getIntentBody() é utilizado para construir a exata estrutura necessária no método intentData() da MainActivity.

Agora somente devemos configurar esse Service no AndroidManifest.xml, segue:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.thiengo.firebasenotification">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<!-- ACRESCENTE O TRECHO ABAIXO -->
<service android:name=".service.CustomFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>

Com isso já estamos obtendo os dados da notificação quando a APP está no background ou foreground.

Nosso próximo passo é definirmos a inscrição e a remoção de inscrição nos tópicos de nosso projeto. Apenas vamos modificar o código de nosso listener onCheckedChanged(), segue:

...
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
String topic = getTopic( buttonView );

if( isChecked ){
FirebaseMessaging.getInstance().subscribeToTopic( topic );
displayMessage( "Vc foi inscrito no tópico: "+topic );
}
else{
FirebaseMessaging.getInstance().unsubscribeFromTopic( topic );
displayMessage( "Vc foi removido do tópico: "+topic );
}

setSPCheckboxValue( buttonView.getId(), isChecked );
}
...

 

Na chamada a FirebaseMessaging.getInstance().subscribeToTopic( topic ) é realmente onde o tópico está sendo criado. Nós não o criamos no dashboard do Firebase ou código backend de nossos servidores, ele é criado quando ao menos um usuário se inscreve nele e esse tópico ainda não está configurado no projeto.

Note que o tópico pode ter qualquer nome, porém somente com os possíveis caracteres (expressão regular): "a-zA-Z0-9-_%". Para o tópico criado aparecer no dashboard pode levar até um dia (bad!).

Com isso, já temos o código de configuração de tópico colocado no projeto. Agora somente nos resta acessar o token único do usuário, mais precisamente, colocá-lo na TextView tvToken.

Primeiro atualizamos o initViews():

...
private void initViews(){
tvTitle = (TextView) findViewById(R.id.id_title);
tvContent = (TextView) findViewById(R.id.id_content);
tvToken = (TextView) findViewById(R.id.id_token);
/* O TRECHo ABAIXO */
tvToken.setText( FirebaseInstanceId.getInstance().getToken() );
Log.i("log", "token: "+FirebaseInstanceId.getInstance().getToken());

initCheckBoxes();
}
...

 

Para obtermos o token somente devemos invocar FirebaseInstanceId.getInstance().getToken(). Caso o token não tenha sido gerado ainda (ele é gerado de forma assíncrona assim que a APP com o FCM configurado é aberta) o valor null é retornado.

Para que seja possível obter o token sempre que um novo é gerado, precisamos criar um outro Service, dessa vez que herde de FirebaseInstanceIdService, segue CustomFirebaseInstanceIdService:

public class CustomFirebaseInstanceIdService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
String token = FirebaseInstanceId.getInstance().getToken();
TokenEvent tokenEvent = new TokenEvent( token );
EventBus.getDefault().post(tokenEvent);
}
}

 

Para evitar a "fadiga" já colocamos o código do EventBus que nesse ponto não deve mais ser novidade para ti. Agora vamos a definição da classe TokenEvent:

public class TokenEvent {
private String token;

public TokenEvent(String token ){
this.token = token;
}

public String getToken(){
return token;
}
}

 

A configuração do CustomFirebaseInstanceIdService no AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="br.com.thiengo.firebasenotification">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

...
<service android:name=".service.CustomFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>

 

E então o método subscriber para mensagens do tipo TokenEvent no EventBus, onNewToken() em MainActivity:

...
@Subscribe(threadMode = ThreadMode.MAIN)
public void onNewToken(TokenEvent tokenEvent){
tvToken.setText( tokenEvent.getToken() );
}
...

 

Dessa forma temos já todo o necessário para o funcionamento de notificações em nossa APP. Duplicando a última mensagem enviada do Firebase Notification Console temos o seguinte resultado:

Com isso finalizamos o projeto de teste com o Firebase Notification. Acesse o Console de Notificação no Firebase e realize testes para ver o funcionamento com outras segmentações e valores.

Lembrando que esse post é ainda parte da série Firebase Android, porém esse de notificação foi construído fora do projeto de Chat, que ainda será utilizado na série.

Acesse esse projeto completo no GitHub: https://github.com/viniciusthiengo/FirebaseNotification

Abaixo o vídeo de implementação completo desse projeto:

Segue lista de todos os posts da série até aqui:

Crash Reporting, Firebase Android - Parte 11

Múltiplos Links de Autenticação e Correção de Código, Firebase Android - Parte 10

GitHub Login, Firebase Android - Parte 9

Recuperação de Senha, Firebase Atualizado - Parte 8

Twitter Login (Fabric), Firebase Android - Parte 7

Google SignIn API, Firebase Android - Parte 6

Facebook Login, Firebase Android - Parte 5

Remoção de Conta e Dados de Login, Firebase Android - Parte 4

Atualização de Dados, Firebase Android - Parte 3

Eventos de Leitura e Firebase UI Android - Parte 2

Persistência Com Firebase Android - Parte 1

Para complementar o entedimento do conteúdo desse post:

SharedPreferences no Android, Entendendo e Utilizando

Abaixo os links fontes para a construção do exemplo:

About FCM Messages

Send a Notification to a User Segment

Firebase Cloud Messaging HTTP Protocol

How to get started with EventBus in 3 steps

O bug comentado em vídeo:

Issue 212879: Google Play Services out of date on API 23 emulator

Vlw.

Receba em primeira mão o conteúdo exclusivo do Blog, além de promoções de livros e cursos de programação.
Email inválido

Relacionado

11º Sorteio Novatec e Blog Thiengo [Calopsita], livro Pense em Python11º Sorteio Novatec e Blog Thiengo [Calopsita], livro Pense em PythonSorteios
Padrão de Projeto: AdapterPadrão de Projeto: AdapterAndroid
Refatoração de Código: Extrair AdapterRefatoração de Código: Extrair AdapterAndroid
Construindo uma Loja VirtualConstruindo uma Loja VirtualLivros

Compartilhar

Comentários Facebook (10)

Comentários Blog (16)

Para código / script, coloque entre [code] e [/code] para receber marcação especifica.
Forneça seu nome válido.
Forneça seu email válido.
Forneça o comentário.
Enviando, aguarde...
06/06/2017
Parabéns Vinicius pela video-aula. Me ajudo bastante mesmo. Muito obrigado pela dedicação e empenho em nos ajudar. DEUS abençoe meu amigo. forte abraço
Responder
20/03/2017
Olá, Thiengo,tranquilo? tenho uma dúvida e se possível gostaria que você me ajudasse. no seu aplicativo você trata a notificação em primeiro plano da mesma  forma que a de segundo. como você faz isso? uso o Android 6 e a minha notification em primeiro plano fica do jeito que eu preciso,porém quando o app não está aberto abre somente o ícone da aplicação cercado de um fundo redondo cinza :/ . Obrigado pela atenção e Parabéns pelo excelente conteúdo.
Responder
Vinícius Thiengo (0) (0)
20/03/2017
Vitor, tudo bem?

Com essa versão de notificação, via dashboard, até a época do vídeo não era possível a definição de um ícone personalizado.

Esse ícone com fundo cinza que está sendo apresentado provavelmente é porque você não definiu para seu aplicativo um "round launch icon?.

Da mesma forma que tem em seu <application> tag uma atributo android:icon=?@mipmap/ic_launcher" você deve colocar o atributo android:roundIcon=?@mipmap/ic_launcher_round" e criar seu ic_launcher_round.

Utilize o Android Assets para isso:

https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=clipart&foreground.clipart=android&foreground.space.trim=1&foreground.space.pad=0.25&foreColor=rgba(96%2C%20125%2C%20139%2C%200)&backColor=rgb(68%2C%20138%2C%20255)&crop=0&backgroundShape=square&effects=none&name=ic_launcher

Veja se somente essa alteração funciona para ti. Abraço.
Responder
21/03/2017
Bom dia jovem Thiengo! fiz o procedimento atualizando até mesmo meu jdk e versão SdkVersion para 1.8 e 25.0.2 mas continua do mesmo jeito :/ . o ponto de referência que eu usei para fazer a pergunta acima foi que o seu aplicativo oficial da google play (Thiengo [Calopsita]) possui um ícone de aplicativo de uma calopsita com fundo amarelo, a notificação chega somente uma calopsita cinza, e quando eu vejo na bandeja aparece um sinal de aspas e a calopsita cinza menor abaixo. no meu caso todos os ícones são os mesmos do aplicativo. o que poderia ser? muito obrigado pela atenção.
Responder
Vinícius Thiengo (0) (0)
24/03/2017
Vitor, no caso do aplicativo do Blog, que foi construído em 2015, ou seja, ainda não existia FCM. No caso dele eu utilizo ícone somente para a notificação, não é o mesmo do aplicativo, digo, o mesmo do launcher icon, que fica na área de Apps do device.

Para conseguir isso eu implementei o GCM no modo Third-party Server, igualmente faço nos três primeiros vídeos da Play List a seguir: https://www.youtube.com/playlist?list=PLBA57K2L2RII4K3RpWuWsLaCCyPTBb1gw

O segundo vídeo, sobre a classe Notification, é o mais importante para entender a ?jogada? com os ícones.

Hoje o Google recomenda o uso do FCM ante ao GCM, isso pois esse é mais atual. Porém o FCM em Third-party Server ainda não tenho conteúdo sobre. Caso mesmo assim queira seguir esse mais atual, recomendo os links a seguir:

https://firebase.google.com/docs/cloud-messaging/server
http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/

Uma maneira de simples de ter o ícone que quiser e ainda não precisar de ter seu próprio backend Web é utilizando o OneSignal: http://www.thiengo.com.br/onesignal-para-notificacoes-em-massa-no-android

Veja se assim consegue essa característica de ícone. Abraço.
Responder
20/02/2017
Olá Thiengo, tenho um problema com a notificação. Eu clico para abrir a aplicação na activity que recebe os dados da notificação e até ai tudo bem mas, quando eu clico no botão para voltar atrás a app fecha e eu pretendia que ela voltasse para a main activity. Segue aqui o código do manifest:
 

            

                

                

            



            

         
E o script php que envia a notificação:
 <?php

#API access key from Google API's Console

    define( 'API_ACCESS_KEY', 'API_KEY' );

    $topic = "/topics/todos";

$url = 'https://fcm.googleapis.com/fcm/send'; 
$fields = array (
        'to' => $topic,
        'notification' => array (
                "body" => "nova casa adicionada",
                "title" => "Nova casa",
"sound"=> "default",
"click_action"=> "casaactivity"
        ),
'data' => array(
"localcasa" => "local",
"precocasa"=> "preço",
"imgurl"=> "link imagem",
"infocs"=> "info da casa",
"csid"=> "id da casa"
)
);
$fields = json_encode ( $fields );
$headers = array (
        'Authorization: key=' . API_ACCESS_KEY,
        'Content-Type: application/json'
);

$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt ( $ch, CURLOPT_POST, true );
curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $fields );

$result = curl_exec ( $ch );
curl_close ( $ch );

?> 
Obrigado.
Responder
Vinícius Thiengo (0) (0)
24/02/2017
Bruno, tudo bem?

Faça a seguinte lógica:

- No clique na notificação abra a MainActivity;
- Logo no onCreate() verifique os dados da notificação e caso tenha o dado que indique que outra Activity deve ser aberta, abra essa outra Activity ainda no onCreate().

Com isso você terá a MainActivity também na pilha de Activities de sua aplicação. Abraço.
Responder
Antonio Johnathan (1) (0)
04/08/2016
Olá Thiengo tudo bem, você conhece alguma lib php que envia notification sem ser pelo firebase console usando tecnologia firebase, criando um admin personalizado e respondendo sua pergunta sobra genymotion android 6.0, o script que eu fiz rodou legal, pedia permissão de WRITE_EXTERNAL_STORAGE, não sei  as outas permissões.
Responder
Vinícius Thiengo (1) (0)
07/08/2016
Fala Antonio, blz aqui.
Não conheço um script pronto, mt pq esse script é dependente da lógica de negócio de sua aplicação, ou seja, se vai ou não permitir envios individuais ou somente em massa. De qualquer forma no Packagist tem algumas libraries nesse link: https://packagist.org/search/?q=push%20message

Abraço
Responder
Antonio Johnathan (1) (0)
11/08/2016
Obg Thiengo, eu encontrei algumas libs de FCM  más não solucionava  aquele problema de enviar 1000 por 1000  então tiver fazer na mão, mais qualquer modo obrigado.
Responder
Rafael (1) (0)
12/07/2016
Olá Thiengo, em meus testes funcionaram bem, mas após eu colocar os anuncios admob, começou com o erro ao receber a notificação, já procurei em foruns e não encontrei nada parecido, segue o erro:
E/CommitToConfigurationOperation: Malformed snapshot token (size):

FATAL EXCEPTION: main
                                                   Process: br.com.meusiteteste.testepush, PID: 24809
                                                   java.lang.NoSuchMethodError: No static method zzUr()Landroid/content/Intent; in class Lcom/google/firebase/iid/FirebaseInstanceIdInternalReceiver; or its super classes (declaration of 'com.google.firebase.iid.FirebaseInstanceIdInternalReceiver' appears in /data/app/br.com.meusiteteste.testepush-1/base.apk)
                                                       at com.google.firebase.messaging.FirebaseMessagingService.zzz(Unknown Source)
                                                       at com.google.firebase.iid.zzb.onStartCommand(Unknown Source)
                                                       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3979)
                                                       at android.app.ActivityThread.access$2300(ActivityThread.java:210)
                                                       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1802)
                                                       at android.os.Handler.dispatchMessage(Handler.java:102)
                                                       at android.os.Looper.loop(Looper.java:145)
                                                       at android.app.ActivityThread.main(ActivityThread.java:6938)
                                                       at java.lang.reflect.Method.invoke(Native Method)
                                                       at java.lang.reflect.Method.invoke(Method.java:372)
                                                       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
                                                       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
07-12 20:18:31.302 957-24840/? E/android.os.Debug: ro.product_ship = true
07-12 20:18:31.302 957-24840/? E/android.os.Debug: ro.debug_level = 0x4f4c.

Já viu algo parecido? onde posso estar errando, meu código esta igual ao tutorial.
Desde já muito obrigado.
Responder
Vinícius Thiengo (0) (0)
14/07/2016
Fala Rafael, blz?
Provavelmente o problema está nas versões de libraries firebase sendo utilizadas, veja essa resposta para um problema similar ao seu: http://stackoverflow.com/a/38202751/2578331
Responder
Antonio Johnathan (1) (0)
09/07/2016
Tudo bem Thiengo, dá uma olhada na nova versão do SO android 6.0( baixar novamente) Genymotion, deve estar consertado as requisições de permissão .
Responder
Vinícius Thiengo (1) (0)
09/07/2016
Fala Antonio, blz?
Vc teve problemas com notificações no Android 6?
Responder
06/07/2016
Grande Thiengo, beleza?
Cara, sabe me dizer se é possível realizar o disparo de uma notificação do Firebase através de uma ferramenta externa (sem ser via console), assim como funcionava com o push?
Responder
Vinícius Thiengo (0) (0)
09/07/2016
Fala Érick, blz?
A princípio somente com seu servidor trabalhando como sender. Igualmente tinhamos quando era GCM. Nesse caso vc terá de ter tb uma base de dados com os tokens de cada usuário de sua APP. Vamos abordar essa modelo aqui na série tb. Abraço
Responder