How to send one to one message using Firebase Messaging
Asked Answered
V

6

16

I have been trying to read the official docs and guides about how to send message from one device to another. I have saved registration token of both devices in the Real Time Database, thus I have the registration token of another device. I have tried the following way to send the message

RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken())
                    .setMessageId(incrementIdAndGet())
                    .addData("message", "Hello")
                    .build();
FirebaseMessaging.getInstance().send(message);

However this is not working. The other device doesn't receive any message. I am not even sure, if I can use upstream message sending to conduct device to device communication.

PS: I just want to know if device-to-device messaging is possible using FCM? If yes, then is the code I used have some issue? If yes, then what is the correct way.

Update:
My question was to ask whether device to device messaging without using any separate server other than firebase could messaging is possible or not, if yes than how, since there's no documentation about it. I do not understand what is left to explain here? Anyways I got the answer and will update it as an answer once the question gets reopened.

Vizcacha answered 23/6, 2016 at 11:18 Comment(6)
@Selvin If that someone can't understand the code I added, they are not eligible to answer anyways. If you have ever used FCM before, this code is taken directly from the official guide, except the registration token part. For the sake of understanding, please read the update.Vizcacha
there is an issue getInstance()send ... this would not even compileDannica
Typo, How about now?Vizcacha
@noob.. Please update your solution.. I am searching for it for last 3 days..!!Teller
@jankigadhiya I have posted the solution now.Vizcacha
use firebase functions for this purpose. It will be more secureRema
V
13

Warning There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that you put on every client device. It can (and thus will) be taken from there and may lead to abuse of your project. I highly recommend against taking this approach, except for apps that you only put on your own devices. – Frank van Puffelen

Ok, so the answer by Frank was correct that Firebase does not natively support device to device messaging. However there's one loophole in that. The Firebase server doesn't identify whether you have send the request from an actual server or are you doing it from your device.

So all you have to do is send a Post Request to Firebase's messaging server along with the Server Key. Just keep this in mind that the server key is not supposed to be on the device, but there's no other option if you want device-to-device messaging using Firebase Messaging.

I am using OkHTTP instead of default way of calling the Rest API. The code is something like this -

public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
OkHttpClient mClient = new OkHttpClient();
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {

        new AsyncTask<String, String, String>() {
            @Override
            protected String doInBackground(String... params) {
                try {
                    JSONObject root = new JSONObject();
                    JSONObject notification = new JSONObject();
                    notification.put("body", body);
                    notification.put("title", title);
                    notification.put("icon", icon);

                    JSONObject data = new JSONObject();
                    data.put("message", message);
                    root.put("notification", notification);
                    root.put("data", data);
                    root.put("registration_ids", recipients);

                    String result = postToFCM(root.toString());
                    Log.d(TAG, "Result: " + result);
                    return result;
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(String result) {
                try {
                    JSONObject resultJson = new JSONObject(result);
                    int success, failure;
                    success = resultJson.getInt("success");
                    failure = resultJson.getInt("failure");
                    Toast.makeText(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
                } catch (JSONException e) {
                    e.printStackTrace();
                    Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
                }
            }
        }.execute();
    }

String postToFCM(String bodyString) throws IOException {
        RequestBody body = RequestBody.create(JSON, bodyString);
        Request request = new Request.Builder()
                .url(FCM_MESSAGE_URL)
                .post(body)
                .addHeader("Authorization", "key=" + SERVER_KEY)
                .build();
        Response response = mClient.newCall(request).execute();
        return response.body().string();
    }

I hope Firebase will come with a better solution in future. But till then, I think this is the only way. The other way would be to send topic message or group messaging. But that was not in the scope of the question.

Update:
The JSONArray is defined like this -

JSONArray regArray = new JSONArray(regIds);

regIds is a String array of registration ids, you want to send this message to. Keep in mind that the registration ids must always be in an array, even if you want it to send to a single recipient.

Vizcacha answered 30/6, 2016 at 11:17 Comment(12)
noob i need a look at your recipients JsonArray.. Just need to check format of that array.. Thanks for the solution :)Teller
@jankigadhiya Added the explanationVizcacha
There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that you put on every client device. It can (and thus will) be taken from there and may lead to abuse of your project. I highly recommend against taking this approach, except for apps that you only put on your own devices.Filar
@FrankvanPuffelen Yes, I understand and mentioned it in the answer too. I'll edit it and make it a highlight too. I am using it just for the prototype, as creating a server at this time is not possible for me, given the time constraint.Vizcacha
@FrankvanPuffelen How about using FirebaseMessaging.getInstance().send(RemoteMessage.Builder(SENDER_ID+"@gcm.googleapis.com").setMessageId("id-1234").setData("key","value").build)); ?Aedes
@Aedes this is not working FirebaseMessaging.getInstance().send(RemoteMessage.Builder(SENDER_ID+"@gcm.goog‌​leapis.com").setMessageId("id-1234").setData("key","value").build));Gaberlunzie
@noob in this RequestBody.create(JSON, bodyString); what is JSON?Gaberlunzie
@VishalPatoliyaツ I think the json is: JSON = MediaType.parse("application/json; charset=utf-8")Goodnatured
very good answer but use root.put("to","other_user_device_token") instead of registration_ids for one to one chatBleat
Hello noob, what happens if my server key is exposed to someone ? what could a person/developer can do with it ?Boxwood
@Boxwood In best case scenario, they can just send a notification message on behalf of you. In worst case scenario, they can impersonate you and do whatever you can do in your Google cloud console/firebase console. So don't expose it.Vizcacha
@noob if you send it to only one device, what topic that you need to subscribe in order to receive the message?Chafin
F
28

Firebase has two features to send messages to devices:

  • the Notifications panel in your Firebase Console allows you to send notifications to specific devices, groups of users, or topics that users subscribed to.
  • by calling Firebase Cloud Messaging API, you can send messages with whatever targeting strategy you prefer. Calling the FCM API requires access to your Server key, which you should never expose on client devices. That's why you should always run such code on an app server.

The Firebase documentation shows this visually:

The ways to send messages to device with Firebase

Sending messages from one device directly to another device is not supported through the Firebase Cloud Messaging client-side SDKs.

Update: I wrote a blog post detailing how to send notifications between Android devices using Firebase Database, Cloud Messaging and Node.js.

Update 2: You can now also use Cloud Functions for Firebase to send messages securely, without spinning up a server. See this sample use-case to get started. If you don't want to use Cloud Functions, you can run the same logic on any trusted environment you already have, such as your development machine, or a server you control.

Filar answered 23/6, 2016 at 13:55 Comment(4)
Thanks for the answer. I understand that the server key should not be exposed to the client device, but I am just making prototypes and do not want to have an app server at this stage, so if I do expose the key and send the message using POST from the client device, will it work? I'll introduce an app server later, but do not want this now.Vizcacha
So basically an app server is needed mainly for security. What about if I don't use an app server at all just an XMPP library to send stanzas to FCM CCS and don't store my server key in my app code, but request it via some PHP script hosted on an online database when I need it. @frankvanpuffelenDepolarize
With regards to update 2, which use case relates to using Firebase Cloud Messaging with the Cloud Functions? Or were you referring to the Realtime Database chat limiting example?Edora
Why would firebase even enforces using "servers" since it is presented as a backend for client applications, why there are such restrictions for such things and Admin sdk ? we can store those sensitive informations in firestore and refresh them, get them and send notifications and messages without exposing keys to client applications!Antiperiodic
V
13

Warning There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that you put on every client device. It can (and thus will) be taken from there and may lead to abuse of your project. I highly recommend against taking this approach, except for apps that you only put on your own devices. – Frank van Puffelen

Ok, so the answer by Frank was correct that Firebase does not natively support device to device messaging. However there's one loophole in that. The Firebase server doesn't identify whether you have send the request from an actual server or are you doing it from your device.

So all you have to do is send a Post Request to Firebase's messaging server along with the Server Key. Just keep this in mind that the server key is not supposed to be on the device, but there's no other option if you want device-to-device messaging using Firebase Messaging.

I am using OkHTTP instead of default way of calling the Rest API. The code is something like this -

public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send";
OkHttpClient mClient = new OkHttpClient();
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) {

        new AsyncTask<String, String, String>() {
            @Override
            protected String doInBackground(String... params) {
                try {
                    JSONObject root = new JSONObject();
                    JSONObject notification = new JSONObject();
                    notification.put("body", body);
                    notification.put("title", title);
                    notification.put("icon", icon);

                    JSONObject data = new JSONObject();
                    data.put("message", message);
                    root.put("notification", notification);
                    root.put("data", data);
                    root.put("registration_ids", recipients);

                    String result = postToFCM(root.toString());
                    Log.d(TAG, "Result: " + result);
                    return result;
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return null;
            }

            @Override
            protected void onPostExecute(String result) {
                try {
                    JSONObject resultJson = new JSONObject(result);
                    int success, failure;
                    success = resultJson.getInt("success");
                    failure = resultJson.getInt("failure");
                    Toast.makeText(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show();
                } catch (JSONException e) {
                    e.printStackTrace();
                    Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show();
                }
            }
        }.execute();
    }

String postToFCM(String bodyString) throws IOException {
        RequestBody body = RequestBody.create(JSON, bodyString);
        Request request = new Request.Builder()
                .url(FCM_MESSAGE_URL)
                .post(body)
                .addHeader("Authorization", "key=" + SERVER_KEY)
                .build();
        Response response = mClient.newCall(request).execute();
        return response.body().string();
    }

I hope Firebase will come with a better solution in future. But till then, I think this is the only way. The other way would be to send topic message or group messaging. But that was not in the scope of the question.

Update:
The JSONArray is defined like this -

JSONArray regArray = new JSONArray(regIds);

regIds is a String array of registration ids, you want to send this message to. Keep in mind that the registration ids must always be in an array, even if you want it to send to a single recipient.

Vizcacha answered 30/6, 2016 at 11:17 Comment(12)
noob i need a look at your recipients JsonArray.. Just need to check format of that array.. Thanks for the solution :)Teller
@jankigadhiya Added the explanationVizcacha
There is a very important reason why we don't mention this approach anywhere. This exposes your server key in the APK that you put on every client device. It can (and thus will) be taken from there and may lead to abuse of your project. I highly recommend against taking this approach, except for apps that you only put on your own devices.Filar
@FrankvanPuffelen Yes, I understand and mentioned it in the answer too. I'll edit it and make it a highlight too. I am using it just for the prototype, as creating a server at this time is not possible for me, given the time constraint.Vizcacha
@FrankvanPuffelen How about using FirebaseMessaging.getInstance().send(RemoteMessage.Builder(SENDER_ID+"@gcm.googleapis.com").setMessageId("id-1234").setData("key","value").build)); ?Aedes
@Aedes this is not working FirebaseMessaging.getInstance().send(RemoteMessage.Builder(SENDER_ID+"@gcm.goog‌​leapis.com").setMessageId("id-1234").setData("key","value").build));Gaberlunzie
@noob in this RequestBody.create(JSON, bodyString); what is JSON?Gaberlunzie
@VishalPatoliyaツ I think the json is: JSON = MediaType.parse("application/json; charset=utf-8")Goodnatured
very good answer but use root.put("to","other_user_device_token") instead of registration_ids for one to one chatBleat
Hello noob, what happens if my server key is exposed to someone ? what could a person/developer can do with it ?Boxwood
@Boxwood In best case scenario, they can just send a notification message on behalf of you. In worst case scenario, they can impersonate you and do whatever you can do in your Google cloud console/firebase console. So don't expose it.Vizcacha
@noob if you send it to only one device, what topic that you need to subscribe in order to receive the message?Chafin
B
5

I have also been using direct device to device gcm messaging in my prototype. It has been working very well. We dont have any server. We exchange GCM reg id using sms/text and then communicate using GCM after that. I am putting here code related to GCM handling

**************Sending GCM Message*************

//Sends gcm message Asynchronously
public class GCM_Sender extends IntentService{
    final String API_KEY = "****************************************";

    //Empty constructor
    public GCM_Sender() {
        super("GCM_Sender");
    }

    //Processes gcm send messages
    @Override
    protected void onHandleIntent(Intent intent) {  

        Log.d("Action Service", "GCM_Sender Service Started");
        //Get message from intent
        String msg = intent.getStringExtra("msg");
        msg =  "\"" + msg + "\"";
        try{
            String ControllerRegistrationId = null;                 
            //Check registration id in db       
            if(RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().size() > 0 ) {
                String controllerRegIdArray[] = RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().get(1);
                if(controllerRegIdArray.length>0)
                    ControllerRegistrationId = controllerRegIdArray[controllerRegIdArray.length-1];

                if(!ControllerRegistrationId.equalsIgnoreCase("NULL")){
                    // 1. URL
                    URL url = new URL("https://android.googleapis.com/gcm/send");
                    // 2. Open connection
                    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
                    // 3. Specify POST method
                    urlConnection.setRequestMethod("POST");
                    // 4. Set the headers
                    urlConnection.setRequestProperty("Content-Type", "application/json");
                    urlConnection.setRequestProperty("Authorization", "key=" + API_KEY);
                    urlConnection.setDoOutput(true);
                    // 5. Add JSON data into POST request body
                    JSONObject obj = new JSONObject("{\"time_to_live\": 0,\"delay_while_idle\": true,\"data\":{\"message\":" + msg + "},\"registration_ids\":[" + ControllerRegistrationId + "]}");
                    // 6. Get connection output stream
                    OutputStreamWriter out = new OutputStreamWriter(urlConnection.getOutputStream());
                    out.write(obj.toString());
                    out.close();
                    // 6. Get the response
                    int responseCode = urlConnection.getResponseCode();

                    BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                    String inputLine;
                    StringBuffer response = new StringBuffer();
                    while ((inputLine = in.readLine()) != null){
                        response.append(inputLine);
                    }
                    in.close();
                    Log.d("GCM getResponseCode:", new Integer(responseCode).toString());
                }else{
                    Log.d("GCM_Sender:","Field REGISTRATION_TABLE is null");
                }
            }else {
                Log.d("GCM_Sender:","There is no Registration ID in DB ,please sync devices");
            }
        } catch (Exception e) {
            e.printStackTrace();
            //MessageSender.getInstance().sendMessage(msg, Commands.SMS_MESSAGE);
        } 
    }

    //Called when service is no longer alive
    @Override
    public void onDestroy() {
        super.onDestroy();
        //Do a log that GCM_Sender service has been destroyed
        Log.d("Action Service", "GCM_Sender Service Destroyed");
    }
}

**************Receiving GCM Message*************

public class GCM_Receiver extends WakefulBroadcastReceiver {
    public static final String RETRY_ACTION ="com.google.android.c2dm.intent.RETRY";
    public static final String REGISTRATION ="com.google.android.c2dm.intent.REGISTRATION";
    public SharedPreferences preferences;

    //Processes Gcm message .
    @Override
    public void onReceive(Context context, Intent intent) {
        ComponentName comp = new ComponentName(context.getPackageName(),
                GCMNotificationIntentService.class.getName());
        //Start GCMNotificationIntentService to handle gcm message asynchronously
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_OK);

        /*//Check if DatabaseService is running .
        if(!DatabaseService.isServiceRunning) {
            Intent dbService = new Intent(context,DatabaseService.class);
            context.startService(dbService);
        }*/
        //Check if action is RETRY_ACTION ,if it is then do gcm registration again .
        if(intent.getAction().equals(RETRY_ACTION)) {
            String registrationId = intent.getStringExtra("registration_id");

            if(TextUtils.isEmpty(registrationId)){
                DeviceRegistrar.getInstance().register(context);
            }else {
                //Save registration id to prefs .
                preferences = PreferenceManager.getDefaultSharedPreferences(context);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putString("BLACKBOX_REG_ID",registrationId);
                editor.commit();
            }
        } else if (intent.getAction().equals(REGISTRATION)) {
        }

    }
}

//Processes gcm messages asynchronously .
public class GCMNotificationIntentService extends IntentService{
    public static final int NOTIFICATION_ID = 1;
    private NotificationManager mNotificationManager;
    String gcmData;
    private final String TAG = "GCMNotificationIntentService";

    //Constructor with super().
    public GCMNotificationIntentService() {
        super("GcmIntentService");
    }

    //Called when startService() is called by its Client .
    //Processes gcm messages .
    @Override
    protected void onHandleIntent(Intent intent) {

        Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Started");
        Bundle extras = intent.getExtras();
        //Get instance of GoogleCloudMessaging .
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
        //Get gcm message type .
        String messageType = gcm.getMessageType(intent);

        if (!extras.isEmpty()) {
            if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR
                    .equals(messageType)) {
                sendNotification("Send error: " + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED
                    .equals(messageType)) {
                sendNotification("Deleted messages on server: "
                        + extras.toString());
            } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE
                    .equals(messageType)) {
                Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime());

                gcmData = extras.getString("message");
                Intent actionService = new Intent(getApplicationContext(),Action.class);    
                actionService.putExtra("data", gcmData);
                //start Action service .
                startService(actionService);

                //Show push notification .
                sendNotification("Action: " + gcmData);
                //Process received gcmData.

                Log.d(TAG,"Received Gcm Message from Controller : " + extras.getString("message"));
            }
        }
        GCM_Receiver.completeWakefulIntent(intent);
    }

    //Shows notification on device notification bar .
    private void sendNotification(String msg) {
        mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
        Intent notificationIntent = new Intent(this, BlackboxStarter.class);
        //Clicking on GCM notification add new layer of app.
        notificationIntent.setFlags( Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
                this).setSmallIcon(R.drawable.gcm_cloud)
                .setContentTitle("Notification from Controller")
                .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
                .setContentText(msg);
        mBuilder.setContentIntent(contentIntent);
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
        //Play default notification
        try {
            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
            Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
            r.play();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //Called when service is no longer be available .
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Destroyed");
    }

}
Boisterous answered 20/7, 2016 at 12:18 Comment(1)
Welcome to stackoverflow. You need to put actual solutions and preferably code in your answer for it to be considered as one. Otherwise you'll get downvotes and/or removal of your answer completely. If you want to share you opinion, you can comment (After reaching to reputation 50). Check the help section for more detailsVizcacha
A
0

I am late but above solutions has helped me to write down this simple answer, you can send your message directly to android devices from android application, here is the simple implementation I have done and it works great for me.

  1. compile android volley library

    compile 'com.android.volley:volley:1.0.0'
    
  2. Just copy paste this simple function ;) and your life will become smooth just like knife in butter. :D

    public static void sendPushToSingleInstance(final Context activity, final HashMap dataValue /*your data from the activity*/, final String instanceIdToken /*firebase instance token you will find in documentation that how to get this*/ ) {
    
    
    final String url = "https://fcm.googleapis.com/fcm/send";
    StringRequest myReq = new StringRequest(Request.Method.POST,url,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Toast.makeText(activity, "Bingo Success", Toast.LENGTH_SHORT).show();
                }
            },
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Toast.makeText(activity, "Oops error", Toast.LENGTH_SHORT).show();
                }
            }) {
    
        @Override
        public byte[] getBody() throws com.android.volley.AuthFailureError {
            Map<String, Object> rawParameters = new Hashtable();
            rawParameters.put("data", new JSONObject(dataValue));
            rawParameters.put("to", instanceIdToken);
            return new JSONObject(rawParameters).toString().getBytes();
        };
    
        public String getBodyContentType()
        {
            return "application/json; charset=utf-8";
        }
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<String, String>();
            headers.put("Authorization", "key="+YOUR_LEGACY_SERVER_KEY_FROM_FIREBASE_CONSOLE);
            headers.put("Content-Type","application/json");
            return headers;
        }
    
    };
    
    Volley.newRequestQueue(activity).add(myReq);
    }
    

Note If you want to send message to topics so you can change parameter instanceIdToken to something like /topics/topicName. For groups implementation is the same but you just need to take care of parameters. checkout Firebase documentation and you can pass those parameters. let me know if you face any issue.

Allopath answered 24/4, 2017 at 5:29 Comment(8)
getting error no 400 for passing wrong format in jsonHeart
please test it with simple "datavalue".Allopath
{data={"title":"Testing"}, to=diduAXILZtY:APA91bHcIBllVz_liOnF-RQa4ei} this is what I m sending, to value is trimmedHeart
please show me final result of this line in your getBody() function. "new JSONObject(rawParameters).toString()" .Allopath
@Override public byte[] getBody() throws com.android.volley.AuthFailureError { Map<String,String> rawParameters = new Hashtable<String, String>(); rawParameters.put("data", new JSONObject(dataValue).toString()); rawParameters.put("to", instanceIdToken); System.out.println(rawParameters.toString()); return new JSONObject(rawParameters).toString().getBytes(); };Heart
I have edit my answer for getBody() function. please copy that and try again. By the way what I was expecting that you will show me debugged value of this line "new JSONObject(rawParameters).toString()" . Please retry with updated answer.Allopath
Let us continue this discussion in chat.Allopath
if you send it to only one device, what topic that you need to subscribe? in order to receive the message?Chafin
B
0

According to the new documentation which was updated on October 2, 2018 you must send post request as below

https://fcm.googleapis.com/fcm/send
Content-Type:application/json
Authorization:key=AIzaSyZ-1u...0GBYzPu7Udno5aA //Server key

{
    "to": "sent device's registration token",
    "data": {
       "hello": "message from someone",
    }
}

To get device's registration token extend FirebaseMessagingService and override onNewToken(String token) For more info refer to doc https://firebase.google.com/docs/cloud-messaging/android/device-group

Bullbat answered 4/10, 2018 at 10:28 Comment(0)
C
0

According to the newest officel document, you should use the new httpv1 api, but the offical example of getAccessToken function has something wrong, and I've edited, now it works fine. I use okhttp3 to post message to the fcm server.

val client = OkHttpClient()

val obj = JSONObject()
//data
val notification = JSONObject()
notification.put("title", title)
notification.put("body", body)
//android
val android = JSONObject()
android.put("direct_boot_ok", false)
val message = JSONObject()
message.put("token", token)
message.put("notification", notification)    
obj.put("message", message)
val json = obj.toString()
val requestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
val url = "https://fcm.googleapis.com/v1/projects/your-project-id/messages:send"
val request = Request.Builder()
        .url(url)
        .post(requestBody)
        .addHeader("Authorization", "Bearer " + getAccessToken(context))
        .addHeader("Content-Type", "application/json; UTF-8")
        .build()
val call = client.newCall(request)
    call.enqueue(object : okhttp3.Callback {
        override fun onFailure(call: okhttp3.Call, e: java.io.IOException) {
            e.printStackTrace()
        }

        override fun onResponse(call: okhttp3.Call, response: Response) {
            if (response.isSuccessful) {
                response.body?.string()?.let { Log.d(TAG, it) }
            }
        }
    })


private fun getAccessToken(context:Context): String {

    val internalStorageDir = context.getExternalFilesDir(null)
    val file = File(internalStorageDir, "your-service-account.json") 
    val googleCredentials: GoogleCredentials = GoogleCredentials
        .fromStream(FileInputStream(file))
        .createScoped(SCOPES.toList())

    val token = googleCredentials.refreshAccessToken().getTokenValue()
    if(token != null){
        Log.d(TAG, "result is $token")
        return token
    }

    Log.d(TAG, "can not get token")
    return String()
}

Here is the full sample I've writted

Capacitor answered 19/7, 2024 at 0:32 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.