Firebase Cloud Messaging (FCM) - Push Notification (Java)

1

I'm working on a backend I'd like to use to send / Push notifications to android app using Firebase Cloud Messaging (FCM).

I've read the documentation ( FCM a few times), but I still have many questions.

I know you can use the Admin SDK or some other server protocol, but I decided to use the Admin SDK.

I have already registered in the Firebase Console, generated the server key and other necessary keys.

Here is an example of a message:

POST https://fcm.googleapis.com/v1/projects/nome_projeto/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
 "message":{
   "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
   "notification":{
     "body" : "Primeira Mensagem FCM!",
     "title" : "Mensagem FCM"
  }
}

Follow part of my source:

<dependency>
  <groupId>com.google.firebase</groupId>
  <artifactId>firebase-admin</artifactId>
  <version>5.8.0</version>
</dependency>


private Resource resource = new ClassPathResource("./service-account.json");
private InputStream resourceInputStream;

public String obterAccessToken() throws IOException {
    resourceInputStream = resource.getInputStream();
    GoogleCredential googleCredential = GoogleCredential
        .fromStream(resourceInputStream)
        .createScoped(Collections.singletonList("https://www.googleapis.com/auth/firebase.messaging"));
    googleCredential.refreshToken();
    return googleCredential.getAccessToken();
}


public void iniciarFirebase() throws IOException {
    resourceInputStream = resource.getInputStream();
    FirebaseOptions options = new FirebaseOptions.Builder()
        .setCredentials(GoogleCredentials.fromStream(resourceInputStream))
        .build();
        FirebaseApp.initializeApp(options);
}

What now? How do I send the message to an App?

I need to generate a Json with the information of these two methods?

    
asked by anonymous 12.04.2018 / 01:56

2 answers

3

Using the Retrofit2 library to submit Request in Java, I implemented the following classes and methods

class NotificationService {

    private FirebaseCall firebaseCall;

    public NotificationService() {

        Retrofit r = new Retrofit.Builder().baseUrl("https://fcm.googleapis.com/").build();

        firebaseCall = r.create(FirebaseCall.class);

    }

    /**
     * Envia notificação para um dispositivo unico
     * 
     * @param mensagem
     * @param action
     * @param device
     */
    public void sendNotificationDevice(String mensagem, String action, String device) {

        Map<String, String> data = new HashMap<>();
        data.put("message", mensagem);
        data.put("action", action);
        sendPush(getDataDevice(device, data), this);

    }

    /**
     * Envia notificação para um canal
     * 
     * @param channel
     * @param mensagem
     * @param title
     * @param action
     */
    public void sendNotification(String channel, String mensagem, String title, String action) {

        Map<String, String> data = new HashMap<>();
        data.put("message", mensagem);
        data.put("title", title);
        data.put("action", action);
        sendPush(getDataTopic(channel, data), this);
    }

    /**
     * Aqui é feita a chamada da requisição
     */
    public FirebasePushResponse sendPush(Map<String, ?> param, Callback<FirebasePushResponse> callback) {
        Response<FirebasePushResponse> response = null;
        try {
            response = firebaseCall.sendPush(param).execute();
            if (response.isSuccessful()) {
                logger.info("FirebasePush: sucess");
            } else {
                logger.error("FirebasePush: erro");
                logger.error("Code: " + response.code() + "\n\rmessege: " + response.message());
            }

        } catch (IOException e) {
            showErro(e);
        }

        return response.body();

    }



    /**
     * Map para notificar um dispositivo unico
     * 
     * @param to
     *            codigo do FirebaseInstanceId do dispositivo data channel
     * @param data
     * @return map
     */
    public Map<String, ?> getDataDevice(String to, Map<String, String> data) {

        Map<String, Object> map = new HashMap<>();
        String toRaw = to;
        map.put("to", toRaw);
        map.put("data", data);

        return map;
    }

    /**
     * envia notificação para um TOPIC
     * 
     * @param to
     *            data channel
     * @param data
     * @return map
     */
    public Map<String, ?> getDataTopic(String to, Map<String, ? extends Object> data) {

        Map<String, Object> map = new HashMap<>();
        String toRaw = "/topics/" + to;
        map.put("to", toRaw);
        map.put("data", data);

        return map;
    }



    public FirebasePushResponse sendPush(Map<String, Object> param) {
        Response<FirebasePushResponse> response = null;
        try {
            response = firebaseCall.sendPush(param).execute();
            if (response.isSuccessful()) {
                logger.info("FirebasePush: sucess");
            } else {
                logger.error("FirebasePush: erro");
                logger.error("Code: " + response.code() + "\n\rmessege: " + response.message());
            }

        } catch (IOException e) {
            showErro(e);
        }

        return response.body();
    }
}

interface FirebaseCall {
    static final String PATH = "fcm/send";

    @POST(PATH)
    @Headers({ "Authorization:key=" + INSIRA_SUA_CHAVE_DO_FIREBASE_AQUI, "Content-Type:application/json" })
    public Call<FirebasePushResponse> sendPush(@Body Object object);

    @POST(PATH)
    @Headers({ "Authorization:key=" + INSIRA_SUA_CHAVE_DO_FIREBASE_AQUI, "Content-Type:application/json" })
    public Call<FirebasePushResponse> sendPush(@Body Map<String, Object> object);
}

class FirebasePushResponse {

    private String messageId, error;

}

And to use just call the class

NotificationService notification = new NotificationService();
FirebasePushResponse resposta = notification.sendNotification("default","Hello World","Hellow Title","br.com.action.sync");

To receive notification for a topic you must have the devices synchronized in the topic

FirebaseMessaging.getInstance().subscribeToTopic("default");
    
12.04.2018 / 17:33
2

Hello,

It's a pity that this post is in Portuguese because the documentation on Firebase Cloud Messaging (FCM) is completely sparse around the world and in English could help more people, but let's get the answers.

The example below is completely didactic and should be suitable for a production environment.

1 - You must access Firebase and reach the "Firebase Admin SDK", there you must click "Generate new private key" (if you have not yet generated one).

2 - This key is a json with the structure below and should be saved in a file:

{
  "type": "service_account",
  "project_id": "xxxxxxx",
  "private_key_id": "xxxxxxx",
  "private_key": "-----BEGIN PRIVATE KEY-----xxxxxxx -----END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "client_id": "xxxxxxx",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/xxxxxxx.iam.gserviceaccount.com"
}

3 - You should import the package below into your project.

    <dependency>
        <groupId>com.google.firebase</groupId>
        <artifactId>firebase-admin</artifactId>
        <version>6.3.0</version>
        <scope>compile</scope>
    </dependency>

4 - Below is the sample source code

public class FCMService  {

    public void send(String idPush, String msg)  {

        try {

            GoogleCredentials fromStream = GoogleCredentials.fromStream(new FileInputStream(new File("/path para o json gerado no passo 2")));

            FirebaseOptions firebaseOptions = new FirebaseOptions.Builder().setConnectTimeout(10000).setCredentials(fromStream).build();

            FirebaseApp firebaseApp = FirebaseApp.initializeApp(firebaseOptions);

            FirebaseMessaging firebaseMessaging = FirebaseMessaging.getInstance(firebaseApp);

            Message message = Message.builder().putData("msg", msg).setToken(idPush).build();

            String response = firebaseMessaging.send(message);

            System.out.println(response);

        } catch (FirebaseMessagingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

PS: It's worth remembering that Google is constantly changing the FCM which has left everyone lost. The above solution already uses the new FCM endpoint ( link {parent = projects / *} / messages: send) and no more the legacy endpoint that many libraries are still pointing to ( link ).

    
21.07.2018 / 06:55