Enviar notificações push dispositivo a dispositivo sem código do lado do servidor

Tempo de leitura: 7 minutes

O Firebase Cloud Messaging (FCM) permite que você envie notificações push diretamente do Firebase console ou com um servidor de aplicativos ou algum outro ambiente confiável onde a lógica do servidor é executada. No entanto, você pode querer enviar notificações push entre dispositivos, como na maioria dos aplicativos de bate-papo, mas sem escrever nenhum código do lado do servidor. Claro, é muito possível. Este tutorial explica exatamente como você pode conseguir isso em quatro etapas fáceis.

Você precisará das seguintes ferramentas e conjunto de habilidades:

  • Android Studio (versão 3.x ou posterior)
  • Conta Firebase (inscreva-se aqui)
  • Conhecimento básico de desenvolvimento Android

Se você tiver esses pré-requisitos, o resto será mais fácil do que você pensava.

 

1. Crie seu projeto Android e vincule ao Firebase

A primeira etapa é criar seu projeto no Android Studio e, em seguida, vinculá-lo ao Firebase. Se precisar de ajuda com isso, já escrevi sobre isso aqui.

Além disso, adicione a dependência de mensagens do Firebase ao nível do aplicativo build.gradle

dependencies {
...
implementation 'com.google.firebase:firebase-core:17.0.0'
implementation 'com.google.firebase:firebase-messaging:19.0.0'
}

// Bottom of your file
apply plugin: 'com.google.gms.google-services'

 

2. Crie serviços Firebase

A próxima etapa é criar dois serviços do Firebase:

MyFirebaseInstanceIDService e MyFirebaseMessagingService. O primeiro serviço tratará do processo de registro do dispositivo e o segundo tratará da recepção e exibição de notificações. Ao contrário das atividades, os serviços não possuem uma interface de usuário visual. Eles são usados ​​para implementar operações em segundo plano de longa duração ou uma API de comunicação avançada que pode ser chamada por outros aplicativos. Para criar um serviço, clique com o botão direito na pasta do aplicativo e selecione: Novo> Serviço> Serviço (New >Service>Service)

Digite o nome do seu serviço e clique no botão Concluir. Repita as mesmas etapas para o segundo serviço.

Acesse o arquivo AndroidManifest.xml e atualize as declarações de serviço na tag do aplicativo. Além disso, adicione as permissões INTERNET e CLOUD TO DEVICE MESSAGING para que seu app possa interagir com o servidor FCM.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sample.notify">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

        <service
            android:name=".MyFirebaseMessagingService"
            android:permission="com.google.android.c2dm.permission.SEND">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>
    </application>

</manifest>

 

3. Configure os serviços

i. FirebaseInstanceIdService

MyFirebaseInstanceIDService deve estender a classe FirebaseInstanceIdService para lidar com o processo de registro do dispositivo. Dentro desse serviço, substitua o método onTokenRefresh () para que ele seja chamado sempre que o sistema determinar a atualização dos tokens. Isso geralmente acontece quando o usuário instala / reinstala o aplicativo ou quando o usuário limpa os dados do aplicativo.

Visto que você está enviando notificações entre dispositivos, cada usuário deve se inscrever em um tópico com um user_id distinto. Isso garante que os usuários recebam notificações enviadas ao tópico que corresponda ao seu user_id. Aqui está a implementação de MyFirebaseInstanceIDServiceclass:

public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {

    private static final String TAG = "mFirebaseIIDService";
    private static final String SUBSCRIBE_TO = "userABC";
    
    @Override
    public void onTokenRefresh() {
        /* 
          Este método é invocado sempre que o token é atualizado
          OPCIONAL: se você deseja enviar mensagens para esta instância do aplicativo
          ou gerenciar essas assinaturas de aplicativos no lado do servidor,
          você pode enviar este token para o seu servidor.
        */
        String token = FirebaseInstanceId.getInstance().getToken();

        // Assim que o token for gerado, inscreva-se no tópico com o userId
        FirebaseMessaging.getInstance().subscribeToTopic(SUBSCRIBE_TO);
        Log.i(TAG, "onTokenRefresh completed with token: " + token);
    }
}

 

ii. FirebaseMessagingService

MyFirebaseMessagingService é realmente onde o trabalho principal ocorre. Ele estende a classe FirebaseMessagingService para receber mensagens do servidor FCM. Este serviço trata da recepção, personalização e exibição de notificações. OverrideOnMessageReceived () dentro do serviço para que seja chamado sempre que uma nova mensagem de notificação for recebida.

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    private final String ADMIN_CHANNEL_ID ="admin_channel";

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        final Intent intent = new Intent(this, MainActivity.class);
        NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        int notificationID = new Random().nextInt(3000);
      
      /*
        Aplicativos direcionados ao SDK 26 ou superior (Android O) devem implementar canais de notificação e adicionar suas notificações
        para pelo menos um deles. Portanto, confirme se a versão é Oreo ou superior e configure o canal de notificação
      */
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            setupChannels(notificationManager);
        }
        
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this , 0, intent,
                PendingIntent.FLAG_ONE_SHOT);

        Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
                R.drawable.notify_icon);

        Uri notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, ADMIN_CHANNEL_ID)
                .setSmallIcon(R.drawable.notify_icon)
                .setLargeIcon(largeIcon)
                .setContentTitle(remoteMessage.getData().get("title"))
                .setContentText(remoteMessage.getData().get("message"))
                .setAutoCancel(true)
                .setSound(notificationSoundUri)
                .setContentIntent(pendingIntent);

        // Defina a cor da notificação para corresponder ao modelo de cor do seu aplicativo
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            notificationBuilder.setColor(getResources().getColor(R.color.colorPrimaryDark));
        }
        notificationManager.notify(notificationID, notificationBuilder.build());
    }
}

Como você deve ter notado na (linha 15-17), você deve verificar se a versão do dispositivo do usuário, e se é Android Oreo ou uma versão superior, configure um canal de notificação, caso contrário, as notificações não seriam recebidas por esse dispositivo . Aqui está como configurar um canal de notificação.

@RequiresApi(api = Build.VERSION_CODES.O)
    private void setupChannels(NotificationManager notificationManager){
        CharSequence adminChannelName = "Nova notificação";
        String adminChannelDescription = "Dispositivo para notificação de dispositivo";

        NotificationChannel adminChannel;
        adminChannel = new NotificationChannel(ADMIN_CHANNEL_ID, adminChannelName, NotificationManager.IMPORTANCE_HIGH);
        adminChannel.setDescription(adminChannelDescription);
        adminChannel.enableLights(true);
        adminChannel.setLightColor(Color.RED);
        adminChannel.enableVibration(true);
        if (notificationManager != null) {
            notificationManager.createNotificationChannel(adminChannel);
        }
    }

 

4. Implementar a lógica de envio de notificação

Esta é a parte mais importante de todo o artigo. É aqui que você define o conteúdo da notificação e como ela será modelada. No entanto, antes de mergulhar na codificação, siga estas etapas para obter sua chave de servidor no console do Firebase.

  • Clique no ícone de engrenagem ao lado de Project Overview e clique em Project settings

  • Navegue até a guia Cloud Messaging e copie sua Server key

Projete sua interface de usuário

Isso é opcional, mas para o propósito deste tutorial, desenvolvi uma interface de usuário simples para ajudar o remetente a compor o conteúdo da notificação. A IU consiste em dois TextViews para título e mensagem, e um sendButton.

Implementar a lógica de envio

O envio de notificação push requer apenas uma solicitação HTTP post para o servidor FCM com as seguintes propriedades de solicitação:

Method Type: POST

URL: https://fcm.googleapis.com/fcm/send

Headers:

Authorization: key="Firebase server key"
Content-Type: application/json

Body:

{
  "to": "/topics/notification_userId",
  "data": {
    "title": "Título de Notificação",
    "message": "Mensagem de notificação",
    "key1" : "value1",
    "key2" : "value2" // dados adicionais que você deseja passar
   }
}

Com esses conceitos em mente, primeiro você criará um JsonObject do corpo da notificação em sua classe de atividade. Este objeto conterá o tópico do receptor, o título da notificação, a mensagem de notificação e outros pares de chave / valor que você deseja adicionar.

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.android.volley.AuthFailureError;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {
    EditText edtTitle;
    EditText edtMessage;
    final private String FCM_API = "https://fcm.googleapis.com/fcm/send";
    final private String serverKey = "key=" + "Sua chave de servidor Firebase";
    final private String contentType = "application/json";
    final String TAG = "NOTIFICATION TAG";
    
    String NOTIFICATION_TITLE;
    String NOTIFICATION_MESSAGE;
    String TOPIC;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edtTitle = findViewById(R.id.edtTitle);
        edtMessage = findViewById(R.id.edtMessage);
        Button btnSend = findViewById(R.id.btnSend);

        btnSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                TOPIC = "/topics/userABC"; // o tópico deve corresponder ao que o receptor assinou
                NOTIFICATION_TITLE = edtTitle.getText().toString();
                NOTIFICATION_MESSAGE = edtMessage.getText().toString();

                JSONObject notification = new JSONObject();
                JSONObject notifcationBody = new JSONObject();
                try {
                    notifcationBody.put("title", NOTIFICATION_TITLE);
                    notifcationBody.put("message", NOTIFICATION_MESSAGE);

                    notification.put("to", TOPIC);
                    notification.put("data", notifcationBody);
                } catch (JSONException e) {
                    Log.e(TAG, "onCreate: " + e.getMessage() );
                }
                sendNotification(notification);
            }
        });
    }

    private void sendNotification(JSONObject notification) {
    ...
    }
}

A próxima etapa é fazer uma solicitação de rede para o servidor FCM usando a biblioteca Volley, então o servidor usará os parâmetros de solicitação para rotear a notificação para o dispositivo de destino.

public class MainActivity extends AppCompatActivity {

    ....

    private void sendNotification(JSONObject notification) {
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(FCM_API, notification,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.i(TAG, "onResponse: " + response.toString());
                        edtTitle.setText("");
                        edtMessage.setText("");
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Toast.makeText(MainActivity.this, "Request error", Toast.LENGTH_LONG).show();
                        Log.i(TAG, "onErrorResponse: Didn't work");
                    }
                }){
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> params = new HashMap<>();
                params.put("Authorization", serverKey);
                params.put("Content-Type", contentType);
                return params;
            }
        };
        MySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
    }
}

Por fim, adicione a classe MySingleton que servirá como RequestQueue para as solicitações de notificação.

public class MySingleton {
    private  static MySingleton instance;
    private RequestQueue requestQueue;
    private Context ctx;

    private MySingleton(Context context) {
        ctx = context;
        requestQueue = getRequestQueue();
    }

    public static synchronized MySingleton getInstance(Context context) {
        if (instance == null) {
            instance = new MySingleton(context);
        }
        return instance;
    }

    public RequestQueue getRequestQueue() {
        if (requestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            requestQueue = Volley.newRequestQueue(ctx.getApplicationContext());
        }
        return requestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }
}

Com isso, você concluiu a construção de seu aplicativo. Você pode começar a enviar notificações push entre dispositivos sem escrever nenhum código do lado do servidor. Certifique-se sempre de obter o tópico do destinatário correto, caso contrário, a notificação não será entregue. Se você fez tudo certo, você terá um resultado semelhante a este.

Espero que este artigo tenha sido bastante útil. Fiz upload do aplicativo de demonstração no GitHub para que você possa conferir.

Visits: 1 Visits: 1200560