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

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes! Você receberá um email de confirmação. Somente depois de confirma-lo é que eu poderei lhe enviar os conteúdos semanais exclusivos. Os artigos em PDF são entregues somente para os inscritos na lista.

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 Thiengo
(7917) (25)
Go-ahead
"O método consciente de tentativa e erro é mais bem-sucedido que o planejamento de um gênio isolado."
Peter Skillman
Prototipagem Android
Capa do curso Prototipagem Profissional de Aplicativos
TítuloAndroid: Prototipagem Profissional de Aplicativos
CategoriasAndroid, Design, Protótipo
AutorVinícius Thiengo
Vídeo aulas186
Tempo15 horas
ExercíciosSim
CertificadoSim
Acessar Curso
Quer aprender a programar para Android? Acesse abaixo o curso gratuito no Blog.
Lendo
TítuloTest-Driven Development: Teste e Design no Mundo Real
CategoriaEngenharia de Software
Autor(es)Mauricio Aniche
EditoraCasa do Código
Edição1
Ano2012
Páginas194
Conteúdo Exclusivo
Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba gratuitamente conteúdos Android sem precedentes!
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.

Investir em Você é Barra de Ouro a R$ 2,00. Cadastre-se e receba grátis conteúdos Android sem precedentes!
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

Comentários Blog (25)

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...
Marcos Vinicius (1) (0)
27/07/2017
Fala vinicius beleza?

Então, eu ja coloquei a notificação no meu app, e quanto o usuário clica nela, ela redireciona para o app, porém, como eu exibiria a notificação dentro do app, tipo, eu clico na notificação e ela abre como o "dialog" dentro da app? abraço.
Responder
Vinícius Thiengo (1) (0)
28/07/2017
Marcos, tudo bem aqui.

Junto a notificação é possível enviar alguns dados. Com isso, assim que a notificação recebe o acionamento do usuário, na atividade principal do app é possível obter esses dados pelo getIntent().

Assim, obtendo um dado que indique que o aplicativo está sendo aberto via notificação, você consegue colocar um condicional de acionamento de um Dialog.

Se não me engano falei sobre, acima, como enviar e obter dados também via Firebase Notification.

Tente assim que deve funcionar sem problemas. Abraço.
Responder
Marcos Vinicius (1) (0)
28/07/2017
E aí vinicius,

cara, eu me precipitei e passei despercebido, mas enfim consegui, só to testando agora, pois acho que meu icone da app não ta numa resolução boa e não aparece na notificação. valeu, abraço.
Responder
Marcos Vinícius (1) (0)
28/07/2017
fala vinicius, beleza? sou eu de novo,

fiz mais e mais testes e ainda nao consegui exibir o icone da minha app na notificação ainda mesmo com o mecanismo no manifest, <meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />


não consegui, mesmo com isso, fiz algumas pesquisas e o pessoal lá no stackoverflow gringo, tão desconfiando das versões da biblioteca do firebase que está com bug na notificação de exibição...enfim, se souber de alguma coisa que possa ajudar, agradeço, outra coisa, defini já meu icone como JPG,PNG, E mnp, com 24x24, 48x48, px, e não funciona. abraço.
Responder
Vinícius Thiengo (0) (0)
30/07/2017
Marcos, tudo bem aqui.

Como um paliativo, caso esteja precisando com urgência desse comportamento com ícone customizado, utilize o OneSignal. Ele é similar ao Firebase Notification, inclusive na facilidade de integração ao app, porém com ele você facilmente consegue utilizar o ícone que quiser.

Mais sobre no link a seguir: http://www.thiengo.com.br/onesignal-para-notificacoes-em-massa-no-android

Abraço.
Responder
Marcos Vinicius (1) (0)
05/07/2018
Fala Vinicius beleza?

Então cara, eu tô tentando abrir um link a partir de uma key:[] value:[]enviada para o app, e não estou conseguindo, o último teste que coloquei foi esse, através de vários artigos do stackoverflow. E não consigo implementar.
            
@Override
    public void onMessageReceived(RemoteMessage remoteMessage{
  String message = "";

              String obj = remoteMessage.getData().get("link");
              if (obj != null) {
                  try {
                      link = obj.toString();
                  } catch (Exception e) {
                      link = "";
                      e.printStackTrace();
                  }
              }

              Intent intent = null;
              PendingIntent pendingIntent;
              if (link.equals("")) {
                  intent = new Intent(this, MainActivity.class);
                  intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
              } else { // open a link
        
                  if (!link.equals("")) {
                      intent = new Intent(Intent.ACTION_VIEW);
                      intent.setData(Uri.parse(link));
                      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                  }
              }
              pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
                      PendingIntent.FLAG_ONE_SHOT);


              NotificationCompat.Builder notificationBuilder = null;

              try {
                  notificationBuilder = new NotificationCompat.Builder(this)
                          .setSmallIcon(R.drawable.ic_launcher_background)          // don't need to pass icon with your message if it's already in your app !
                          .setContentTitle(URLDecoder.decode(getString(R.string.app_name), "UTF-8"))
                          .setContentText(URLDecoder.decode(message, "UTF-8"))
                          .setAutoCancel(true)
                          .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                          .setContentIntent(pendingIntent);
              } catch (UnsupportedEncodingException e) {
                  e.printStackTrace();
              }

              if (notificationBuilder != null) {
                  NotificationManager notificationManager =
                          (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                  notificationManager.notify(1, notificationBuilder.build());
              } else {
                  Log.d(TAG, "error NotificationManager");
              }


          }
    }
}
Responder
Vinícius Thiengo (1) (0)
31/07/2018
Marcos Vinicius, tudo bem aqui.

Olhei seu código e se nenhuma exceção estiver sendo gerada, aparentemente o problema está na atividade que é acionada pela notificação.

A lógica de negócio dessa atividade pode não estar conseguindo obter o link em Intent e posteriormente abrindo ele em um WebView, por exemplo.

Certifique-se, utilizando Log.i(), por exemplo, que o link está chegando em onMessageReceived(). Se sim, então certifique-se de que o link está também sendo enviado a atividade acionada com a notificação, como falei: é provável que o problema esteja na atividade.

Abraço.
Responder
Robson (1) (0)
07/07/2017
Olá Thiengo blz, cara, muito bom este seu post sobre o Firebase eu nunca tinha feito uso dele apenas do GCM, e mesmo assim fiz uso do GCM através do oneSignal e lógico como sempre peguei uma carona no seu vídeo.

Fiz um novo projeto, no console do Firebase criei um projeto, depois cliquei no icone do android e "Adicionei o FB ao meu app Android" com isto foi feito um download de um arquivo google-services.json, coloquei este no meu projeto seguindo o passo a passo do assistente de configuração la do console, tranquilo feito, projeto configurado!

Gostaria de saber como consigo a URL para o server, se preciso fazer uma requisição para o servidor fiz pesquisas e o que eu encontro, para classes que estendem FirebaseInstanceIdService  é o metodo sendRegistrationToServer(String token) sem nada mais do que // TODO: Send any registration to your app's servers.  como neste outro artigo que obtive http://blog.bananacoding.com/2016/06/22/android-push-notification-using-firebase/#.WWAEqojyvIU , provavelmente ali dentro vou chamar o retrofit ou o volley e a url que eu vou utilizar para enviar o token é alguma informação que esta no arquivo json baixado? ou seria a url de alguma aplicação minha tipo... um backend?
Responder
Vinícius Thiengo (0) (0)
11/07/2017
Robson, tudo bem?

Essa URL, se me lembro bem, é interna ao Google play Services nos devices reais ou interna ao Google APIs em emuladores AVDs.

Mesmo conseguindo acesso a ela, você muito provavelmente estará violando termos e condições de uso do Google e seu aplicativo poderá ser punido devido a isso. Pois com acesso a essa URL e um mal uso dela é possível ao menos tentar uma sobrecarga aos servidores do Google Android.

Caso realmente precise de acesso ao token do usuário, incluindo comunicação com seu pr?pprio servidor backend, recomendo que utilize a versão de notificação com um servidor de aplicativos que apresento no link a seguir:

http://www.thiengo.com.br/fcm-android-dominio-do-problema-implementacao-e-testes-com-servidor-de-aplicativo-parte-1

O ponto negativo do FCM com Servidor de Aplicativos é que a implementação é um pouco mais complexa, incluindo a exclusão de tokens inativos.

A vantagem é que você tem controle total sobre os envios de push notification, incluindo envios individuais, característica não trivial quando utilizando o Firebase Notification ou o OneSignal. Esses que foram feitos com foco em envios em massa.

Abraço.
Responder
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)
08/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)
08/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