Related
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.
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:
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.
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.
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");
}
}
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
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.
compile android volley library
compile 'com.android.volley:volley:1.0.0'
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.
I am using Android Firebase Auth Ui for providing Phone Number based sign in option in an android app. I am trying to provide an additional option to signed in users to switch their signed in phone number to another number keeping the same user account.
But as per Firebase Docs for Phone number there are no options to change the signed in number.
There are options for linking different auth providers like email, google or Facebook login etc to same account. But there is no way mentioned about how to change the phone number or email id keeping the same user id.
Is there a workaround or method by which we can achieve this?
An API exists for updating the phone number of a current user: FirebaseUser#updatePhoneNumber(PhoneAuthCredential credential)
I also had this challenge to update user phone number and when I go on documentation I got something by using I have done this task.
you can go for documentation by click here
Now the method you can use : - for java android project.
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential( "+91-98298XXXX2", "OTP_CODE" );
// Update Mobile Number...
firebaseAuth.getCurrentUser().updatePhoneNumber(phoneAuthCredential)
.addOnCompleteListener(new OnCompleteListener <Void>() {
#Override
public void onComplete(#NonNull Task <Void> task) {
if (task.isSuccessful()) {
// Update Successfully
} else {
// Failed
}
}
}
);
val options = PhoneAuthOptions.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(100L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(activity) // Activity (for callback binding)
.setCallbacks(returnCallBack()) // OnVerificationStateChangedCallbacks
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
private fun returnCallBack() = object : PhoneAuthProvider
.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
FirebaseAuth.getCurrentUser()?.updatePhoneNumber(credential)
}
override fun onVerificationFailed(e: FirebaseException) {
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
Log.e("phone", e.toString())
}
override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
//You need this to pass as a parameter for the update method call.
vericationSent = verificationId
}
}
fun confirmChange(code: String, context: Context?) {
if(code.contains(Regex(onlyNumber))) {
Log.d("codeSent" , "Right code : $code")
FirebaseAuth.getCurrentUser()
?.updatePhoneNumber(PhoneAuthProvider.getCredential(vericationSent, code))
?.addOnCompleteListener {task ->
//it worked if you reach here.
}?.addOnFailureListener {
//Show the error to user
}
}
vericationSent = EMPTY
} else {
Log.d("codeSent" , "wrong code : $code")
}
}
Try this
//Send otp to phone number
String verificationId;
private void startLoginFirebase(){
PhoneAuthProvider.getInstance(firebaseAuth).verifyPhoneNumber(phone, 90L, TimeUnit.SECONDS, PhoneAuthActivity.this, new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(#NonNull String s, #NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
verificationId = s;
updatePhoneNum();
}
#Override
public void onCodeAutoRetrievalTimeOut(#NonNull String s) {
super.onCodeAutoRetrievalTimeOut(s);
}
#Override
public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
}
#Override
public void onVerificationFailed(#NonNull FirebaseException e) {
processFurther(e.getLocalizedMessage().toString(), 0);
}
});
}
//Verify Otp
private void updatePhoneNum(){
PhoneAuthCredential phoneAuthCredential = PhoneAuthProvider.getCredential(verificationId, otp);
firebaseAuth.getCurrentUser().updatePhoneNumber(phoneAuthCredential).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
}
});
}
Apparently - according to the project maintainers - FirebaseUI-Android doesn't support this feature, and it looks like they have no plans of doing it any time soon :(
After searching the docs I could not find any info on how to send device to device messages using FCM without the use of an external server.
For example, if I was creating a chat application I would need to send push notifications to users about unread messages since they won't be online all the time and I can't have a persistent service in the background that would always be connected to the real time database because that would be too resource heavy.
So how would I send a push notification to a user "A" when a certain user "B" sends him/her a chat message? Do I need an external server for this or can it be done with just Firebase servers?
UPDATE: It is now possible to use firebase cloud functions as the server for handling push notifications. Check out their documentation here
============
According to the docs you must implement a server for handling push notifications in device to device communication.
Before you can write client apps that use Firebase Cloud Messaging, you must have an app server that meets the following criteria:
...
You'll need to decide which FCM connection server protocol(s) you want to use to enable your app server to interact with FCM connection servers. Note that if you want to use upstream messaging from your client applications, you must use XMPP. For a more detailed discussion of this, see Choosing an FCM Connection Server Protocol.
If you only need to send basic notifications to your users from the server. You can use their serverless solution, Firebase Notifications.
See a comparison here between FCM and Firebase Notifications:
https://firebase.google.com/support/faq/#messaging-difference
Making a HTTP POST request with the link https://fcm.googleapis.com/fcm/send with required header and data helped me. In the below code snippet
Constants.LEGACY_SERVER_KEY is a local class variable, you can find this at your Firebase Project Settings->Cloud Messaging->Legacy Server key. You need to pass device registration token i.e. regToken in below code snippet referenced HERE.
At last you need okhttp library dependency in order to get this snippet work.
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
private void sendNotification(final String regToken) {
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
OkHttpClient client = new OkHttpClient();
JSONObject json=new JSONObject();
JSONObject dataJson=new JSONObject();
dataJson.put("body","Hi this is sent from device to device");
dataJson.put("title","dummy title");
json.put("notification",dataJson);
json.put("to",regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization","key="+Constants.LEGACY_SERVER_KEY)
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
}catch (Exception e){
//Log.d(TAG,e+"");
}
return null;
}
}.execute();
}
further if you want to send message to a particular topic, replace regToken in json like this
json.put("to","/topics/foo-bar")
and don't forget to add INTERNET permission in your AndroidManifest.xml.
IMPORTANT : - Using above code means your server key resides in the client application. That is dangerous as someone can dig into your application and get the server key to send malicious notifications to your users.
You can do it using Volly Jsonobject request....
follow this Steps first:
1 copy legacy server key and store it as Legacy_SERVER_KEY
Legacy Server key
you can see in picture how to get
2 You need Volley dependency
compile 'com.mcxiaoke.volley:library:1.0.19'
Code for send Push:-
private void sendFCMPush() {
String Legacy_SERVER_KEY = YOUR_Legacy_SERVER_KEY;
String msg = "this is test message,.,,.,.";
String title = "my title";
String token = FCM_RECEIVER_TOKEN;
JSONObject obj = null;
JSONObject objData = null;
JSONObject dataobjData = null;
try {
obj = new JSONObject();
objData = new JSONObject();
objData.put("body", msg);
objData.put("title", title);
objData.put("sound", "default");
objData.put("icon", "icon_name"); // icon_name image must be there in drawable
objData.put("tag", token);
objData.put("priority", "high");
dataobjData = new JSONObject();
dataobjData.put("text", msg);
dataobjData.put("title", title);
obj.put("to", token);
//obj.put("priority", "high");
obj.put("notification", objData);
obj.put("data", dataobjData);
Log.e("!_#rj#_##_PASS:>", obj.toString());
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, Constants.FCM_PUSH_URL, obj,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e("!_##_SUCESS", response + "");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("!_##_Errors--", error + "");
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization", "key=" + Legacy_SERVER_KEY);
params.put("Content-Type", "application/json");
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
int socketTimeout = 1000 * 60;// 60 seconds
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
requestQueue.add(jsObjRequest);
}
Just Call sendFCMPush();
1) subscribe an identical topic name, for example:
ClientA.subcribe("to/topic_users_channel")
ClientB.subcribe("to/topic_users_channel")
2) send messages inside the application
GoogleFirebase : How-to send topic messages
Yes, it's possible to do it without any server. You can create a device group client side and then you exchange messages in the group. However there are limitations:
You have to use the same Google account on the devices
You can't send high priority messages
Reference: Firebase doc See the section "Managing device groups on Android client apps"
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
I have made cloud function which is trigger when new message is added in database
It is node.js code
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin'); admin.initializeApp();
exports.sendNotification = functions.database.ref('/conversations/{chatLocation}/{messageLocation}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
const toIDUser = original.toID;
const isGroupChat = original.isGroupChat;
if (isGroupChat) {
const tokenss = admin.database().ref(`/users/${toIDUser}/tokens`).once('value').then(function(snapshot) {
// Handle Promise
const tokenOfGroup = snapshot.val()
// get tokens from the database at particular location get values
const valuess = Object.keys(tokenOfGroup).map(k => tokenOfGroup[k]);
//console.log(' ____________ddd((999999ddd_________________ ' + valuess );
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(valuess, payload);
}, function(error) {
console.error(error);
});
return ;
} else {
// get token from the database at particular location
const tokenss = admin.database().ref(`/users/${toIDUser}/credentials`).once('value').then(function(snapshot) {
// Handle Promise
// The Promise was "fulfilled" (it succeeded).
const credentials = snapshot.val()
// console.log('snapshot ......snapshot.val().name****^^^^^^^^^^^^kensPromise****** :- ', credentials.name);
//console.log('snapshot.....****snapshot.val().token****^^^^^^^^^^^^kensPromise****** :- ', credentials.token);
const deviceToken = credentials.token;
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(deviceToken, payload);
}, function(error) {
console.error(error);
});
}
return ;
});
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
From the relevant page on Google Cloud Functions:
Developers can use Cloud Functions to keep users engaged and up to
date with relevant information about an app. Consider, for example, an
app that allows users to follow one another's activities in the app.
In such an app, a function triggered by Realtime Database writes to
store new followers could create Firebase Cloud Messaging (FCM)
notifications to let the appropriate users know that they have gained
new followers.
Example:
The function triggers on writes to the Realtime Database path where followers are stored.
The function composes a message to send via FCM.
FCM sends the notification message to the user's device.
Here is a demo project for sending device-to-device push notifications with Firebase and Google Cloud Functions.
You can use firebase realtime database to do so. You can create data structure for storing chats and add observers for the conversation threads for both users. It still does device - server - device architecture, but in this case there is no additional server on the developers' part. This uses the firebase servers. You can check out a tutorial here (ignore the UI part, although, that is also a good starting point for chat UI frameworks).
Firebase Realtime Chat
If you have fcm(gcm) token of the device to whom you want to send notification. It's just a post request to send the notification.
https://github.com/prashanthd/google-services/blob/master/android/gcm/gcmsender/src/main/java/gcm/play/android/samples/com/gcmsender/GcmSender.java
In my case I use retrofit with this class Message:
public class Message {
private String to;
private String collapseKey;
private Notification notification;
private Data data;
public Message(String to, String collapseKey, Notification notification, Data data) {
this.to = to;
this.collapseKey = collapseKey;
this.notification = notification;
this.data = data;
}
}
Data
public class Data {
private String body;
private String title;
private String key1;
private String key2;
public Data(String body, String title, String key1, String key2) {
this.body = body;
this.title = title;
this.key1 = key1;
this.key2 = key2;
}
}
Notification
public class Notification {
private String body;
private String title;
public Notification(String body, String title) {
this.body = body;
this.title = title;
}
}
this the call
private void sentToNotification() {
String to = "YOUR_TOKEN";
String collapseKey = "";
Notification notification = new Notification("Hello bro", "title23");
Data data = new Data("Hello2", "title2", "key1", "key2");
Message notificationTask = new Message(to, collapseKey, notification, data);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
ServiceAPI api = new retrofit.create(ServiceAPI.class);
Call<Message> call = api .sendMessage("key=YOUR_KEY", notificationTask);
call.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.d("TAG", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("TAG", t.getMessage());
}
});
}
our ServiceAPi
public interface ServiceAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Header("Authorization") String token, #Body Message message);
}
You can use Retrofit. Subscribe devices to topic news. Send notification from one device to other.
public void onClick(View view) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key=legacy server key from FB console"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Notification title","Notification body");
Call<Message> call2 = firebaseAPI.sendMessage(new Message("topic or deviceID", notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, Response<Message> response) {
Log.d("Response ", "onResponse");
t1.setText("Notification sent");
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.d("Response ", "onFailure");
t1.setText("Notification failure");
}
});
}
POJOs
public class Message {
String to;
NotifyData notification;
public Message(String to, NotifyData notification) {
this.to = to;
this.notification = notification;
}
}
and
public class NotifyData {
String title;
String body;
public NotifyData(String title, String body ) {
this.title = title;
this.body = body;
}
}
and FirebaseAPI
public interface FirebaseAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Body Message message);
}
Here is walk around how to get notifications without second server apart from the Firebase one. So we use Firebase only, without additional server.
At the mobile app code, we create its own notifications function by Android libraries like here, not using Firebase libraries like here, without Firebase Cloud messaging.
Here is an example with Kotlin:
private fun notification() {
createNotificationChannel()
val intent = Intent(this, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val notificationBuilder = NotificationCompat.Builder(this, "yuh_channel_id")
.setSmallIcon(R.drawable.ic_send)
.setContentText("yuh")
.setContentText("yuh")
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notificationBuilder.build())
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(0, notificationBuilder.build())
}
}
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "yuh_channel"
val descriptionText = "yuh_description"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val CHANNEL_ID = "yuh_channel_id"
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
In the Firebase database, create collection "pending notifications". Documents should contain user name (to send notification to) and source name (where should user go upon tapping the notification).
In the app code, implement option for adding new records to the Pending Notifications collection. E. g. if user A sends message to user B, then the document with the id of user B (who will be notified) is created in the collection.
In the app code, set up background (when the app is not visible to the user) service. Like here. In the background service, set up a listener for changes in the "Notifications Pending" collection. When the new record with the user id comes to the collection, call the notification function created in the paragrath 1 supra and delete the consequent record from the collection.
So I had an idea here. See: If the FCM, as well as the GCM, has a endpoit to http request where we can send a post json with our message data, including the token (s) of devices that we want this message to be delivered.
So why not send a post to Firebase server with this notification to be delivered to user B? you understand ?
So, you send the message and chat with a call post to ensure delivery of the notification if the user is with your app in the background. I am also in need of it soon, I will test later. What do you say about?
Simplest way :
void sendFCMPush(String msg,String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key="+Const.FIREBASE_LEGACY_SERVER_KEY); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Chatting", msg);
Call<Message> call2 = firebaseAPI.sendMessage(new Message(token, notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.e("## SUCCES #E$#", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("E$ FAILURE E$#", t.getMessage());
}
});
}
Create Class to make Object:
public class Message {
String to;
NotifyData data;
public Message(String to, NotifyData data) {
this.to = to;
this.data = data;
}
}
Create Class to make Object:
public class Notification {
String title;
String message;
enter code here`enter code here`
public Notification(String title, String message) {
this.title = title;
this.message = message;
}
}
After searching the docs I could not find any info on how to send device to device messages using FCM without the use of an external server.
For example, if I was creating a chat application I would need to send push notifications to users about unread messages since they won't be online all the time and I can't have a persistent service in the background that would always be connected to the real time database because that would be too resource heavy.
So how would I send a push notification to a user "A" when a certain user "B" sends him/her a chat message? Do I need an external server for this or can it be done with just Firebase servers?
UPDATE: It is now possible to use firebase cloud functions as the server for handling push notifications. Check out their documentation here
============
According to the docs you must implement a server for handling push notifications in device to device communication.
Before you can write client apps that use Firebase Cloud Messaging, you must have an app server that meets the following criteria:
...
You'll need to decide which FCM connection server protocol(s) you want to use to enable your app server to interact with FCM connection servers. Note that if you want to use upstream messaging from your client applications, you must use XMPP. For a more detailed discussion of this, see Choosing an FCM Connection Server Protocol.
If you only need to send basic notifications to your users from the server. You can use their serverless solution, Firebase Notifications.
See a comparison here between FCM and Firebase Notifications:
https://firebase.google.com/support/faq/#messaging-difference
Making a HTTP POST request with the link https://fcm.googleapis.com/fcm/send with required header and data helped me. In the below code snippet
Constants.LEGACY_SERVER_KEY is a local class variable, you can find this at your Firebase Project Settings->Cloud Messaging->Legacy Server key. You need to pass device registration token i.e. regToken in below code snippet referenced HERE.
At last you need okhttp library dependency in order to get this snippet work.
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
private void sendNotification(final String regToken) {
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
OkHttpClient client = new OkHttpClient();
JSONObject json=new JSONObject();
JSONObject dataJson=new JSONObject();
dataJson.put("body","Hi this is sent from device to device");
dataJson.put("title","dummy title");
json.put("notification",dataJson);
json.put("to",regToken);
RequestBody body = RequestBody.create(JSON, json.toString());
Request request = new Request.Builder()
.header("Authorization","key="+Constants.LEGACY_SERVER_KEY)
.url("https://fcm.googleapis.com/fcm/send")
.post(body)
.build();
Response response = client.newCall(request).execute();
String finalResponse = response.body().string();
}catch (Exception e){
//Log.d(TAG,e+"");
}
return null;
}
}.execute();
}
further if you want to send message to a particular topic, replace regToken in json like this
json.put("to","/topics/foo-bar")
and don't forget to add INTERNET permission in your AndroidManifest.xml.
IMPORTANT : - Using above code means your server key resides in the client application. That is dangerous as someone can dig into your application and get the server key to send malicious notifications to your users.
You can do it using Volly Jsonobject request....
follow this Steps first:
1 copy legacy server key and store it as Legacy_SERVER_KEY
Legacy Server key
you can see in picture how to get
2 You need Volley dependency
compile 'com.mcxiaoke.volley:library:1.0.19'
Code for send Push:-
private void sendFCMPush() {
String Legacy_SERVER_KEY = YOUR_Legacy_SERVER_KEY;
String msg = "this is test message,.,,.,.";
String title = "my title";
String token = FCM_RECEIVER_TOKEN;
JSONObject obj = null;
JSONObject objData = null;
JSONObject dataobjData = null;
try {
obj = new JSONObject();
objData = new JSONObject();
objData.put("body", msg);
objData.put("title", title);
objData.put("sound", "default");
objData.put("icon", "icon_name"); // icon_name image must be there in drawable
objData.put("tag", token);
objData.put("priority", "high");
dataobjData = new JSONObject();
dataobjData.put("text", msg);
dataobjData.put("title", title);
obj.put("to", token);
//obj.put("priority", "high");
obj.put("notification", objData);
obj.put("data", dataobjData);
Log.e("!_#rj#_##_PASS:>", obj.toString());
} catch (JSONException e) {
e.printStackTrace();
}
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.POST, Constants.FCM_PUSH_URL, obj,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e("!_##_SUCESS", response + "");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("!_##_Errors--", error + "");
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Authorization", "key=" + Legacy_SERVER_KEY);
params.put("Content-Type", "application/json");
return params;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
int socketTimeout = 1000 * 60;// 60 seconds
RetryPolicy policy = new DefaultRetryPolicy(socketTimeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
jsObjRequest.setRetryPolicy(policy);
requestQueue.add(jsObjRequest);
}
Just Call sendFCMPush();
1) subscribe an identical topic name, for example:
ClientA.subcribe("to/topic_users_channel")
ClientB.subcribe("to/topic_users_channel")
2) send messages inside the application
GoogleFirebase : How-to send topic messages
Yes, it's possible to do it without any server. You can create a device group client side and then you exchange messages in the group. However there are limitations:
You have to use the same Google account on the devices
You can't send high priority messages
Reference: Firebase doc See the section "Managing device groups on Android client apps"
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
I have made cloud function which is trigger when new message is added in database
It is node.js code
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin'); admin.initializeApp();
exports.sendNotification = functions.database.ref('/conversations/{chatLocation}/{messageLocation}')
.onCreate((snapshot, context) => {
// Grab the current value of what was written to the Realtime Database.
const original = snapshot.val();
const toIDUser = original.toID;
const isGroupChat = original.isGroupChat;
if (isGroupChat) {
const tokenss = admin.database().ref(`/users/${toIDUser}/tokens`).once('value').then(function(snapshot) {
// Handle Promise
const tokenOfGroup = snapshot.val()
// get tokens from the database at particular location get values
const valuess = Object.keys(tokenOfGroup).map(k => tokenOfGroup[k]);
//console.log(' ____________ddd((999999ddd_________________ ' + valuess );
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(valuess, payload);
}, function(error) {
console.error(error);
});
return ;
} else {
// get token from the database at particular location
const tokenss = admin.database().ref(`/users/${toIDUser}/credentials`).once('value').then(function(snapshot) {
// Handle Promise
// The Promise was "fulfilled" (it succeeded).
const credentials = snapshot.val()
// console.log('snapshot ......snapshot.val().name****^^^^^^^^^^^^kensPromise****** :- ', credentials.name);
//console.log('snapshot.....****snapshot.val().token****^^^^^^^^^^^^kensPromise****** :- ', credentials.token);
const deviceToken = credentials.token;
const payload = {
notification: {
title: original.senderName + " :- ",
body: original.content
}
};
return admin.messaging().sendToDevice(deviceToken, payload);
}, function(error) {
console.error(error);
});
}
return ;
});
Google Cloud Functions make it now possible send push notifications from device-to-device without an app server.
From the relevant page on Google Cloud Functions:
Developers can use Cloud Functions to keep users engaged and up to
date with relevant information about an app. Consider, for example, an
app that allows users to follow one another's activities in the app.
In such an app, a function triggered by Realtime Database writes to
store new followers could create Firebase Cloud Messaging (FCM)
notifications to let the appropriate users know that they have gained
new followers.
Example:
The function triggers on writes to the Realtime Database path where followers are stored.
The function composes a message to send via FCM.
FCM sends the notification message to the user's device.
Here is a demo project for sending device-to-device push notifications with Firebase and Google Cloud Functions.
You can use firebase realtime database to do so. You can create data structure for storing chats and add observers for the conversation threads for both users. It still does device - server - device architecture, but in this case there is no additional server on the developers' part. This uses the firebase servers. You can check out a tutorial here (ignore the UI part, although, that is also a good starting point for chat UI frameworks).
Firebase Realtime Chat
If you have fcm(gcm) token of the device to whom you want to send notification. It's just a post request to send the notification.
https://github.com/prashanthd/google-services/blob/master/android/gcm/gcmsender/src/main/java/gcm/play/android/samples/com/gcmsender/GcmSender.java
In my case I use retrofit with this class Message:
public class Message {
private String to;
private String collapseKey;
private Notification notification;
private Data data;
public Message(String to, String collapseKey, Notification notification, Data data) {
this.to = to;
this.collapseKey = collapseKey;
this.notification = notification;
this.data = data;
}
}
Data
public class Data {
private String body;
private String title;
private String key1;
private String key2;
public Data(String body, String title, String key1, String key2) {
this.body = body;
this.title = title;
this.key1 = key1;
this.key2 = key2;
}
}
Notification
public class Notification {
private String body;
private String title;
public Notification(String body, String title) {
this.body = body;
this.title = title;
}
}
this the call
private void sentToNotification() {
String to = "YOUR_TOKEN";
String collapseKey = "";
Notification notification = new Notification("Hello bro", "title23");
Data data = new Data("Hello2", "title2", "key1", "key2");
Message notificationTask = new Message(to, collapseKey, notification, data);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
ServiceAPI api = new retrofit.create(ServiceAPI.class);
Call<Message> call = api .sendMessage("key=YOUR_KEY", notificationTask);
call.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.d("TAG", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("TAG", t.getMessage());
}
});
}
our ServiceAPi
public interface ServiceAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Header("Authorization") String token, #Body Message message);
}
You can use Retrofit. Subscribe devices to topic news. Send notification from one device to other.
public void onClick(View view) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key=legacy server key from FB console"); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Notification title","Notification body");
Call<Message> call2 = firebaseAPI.sendMessage(new Message("topic or deviceID", notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, Response<Message> response) {
Log.d("Response ", "onResponse");
t1.setText("Notification sent");
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.d("Response ", "onFailure");
t1.setText("Notification failure");
}
});
}
POJOs
public class Message {
String to;
NotifyData notification;
public Message(String to, NotifyData notification) {
this.to = to;
this.notification = notification;
}
}
and
public class NotifyData {
String title;
String body;
public NotifyData(String title, String body ) {
this.title = title;
this.body = body;
}
}
and FirebaseAPI
public interface FirebaseAPI {
#POST("/fcm/send")
Call<Message> sendMessage(#Body Message message);
}
Here is walk around how to get notifications without second server apart from the Firebase one. So we use Firebase only, without additional server.
At the mobile app code, we create its own notifications function by Android libraries like here, not using Firebase libraries like here, without Firebase Cloud messaging.
Here is an example with Kotlin:
private fun notification() {
createNotificationChannel()
val intent = Intent(this, LoginActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
val notificationBuilder = NotificationCompat.Builder(this, "yuh_channel_id")
.setSmallIcon(R.drawable.ic_send)
.setContentText("yuh")
.setContentText("yuh")
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(0, notificationBuilder.build())
with(NotificationManagerCompat.from(this)) {
// notificationId is a unique int for each notification that you must define
notify(0, notificationBuilder.build())
}
}
private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "yuh_channel"
val descriptionText = "yuh_description"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val CHANNEL_ID = "yuh_channel_id"
val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
description = descriptionText
}
// Register the channel with the system
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
In the Firebase database, create collection "pending notifications". Documents should contain user name (to send notification to) and source name (where should user go upon tapping the notification).
In the app code, implement option for adding new records to the Pending Notifications collection. E. g. if user A sends message to user B, then the document with the id of user B (who will be notified) is created in the collection.
In the app code, set up background (when the app is not visible to the user) service. Like here. In the background service, set up a listener for changes in the "Notifications Pending" collection. When the new record with the user id comes to the collection, call the notification function created in the paragrath 1 supra and delete the consequent record from the collection.
So I had an idea here. See: If the FCM, as well as the GCM, has a endpoit to http request where we can send a post json with our message data, including the token (s) of devices that we want this message to be delivered.
So why not send a post to Firebase server with this notification to be delivered to user B? you understand ?
So, you send the message and chat with a call post to ensure delivery of the notification if the user is with your app in the background. I am also in need of it soon, I will test later. What do you say about?
Simplest way :
void sendFCMPush(String msg,String token) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "key="+Const.FIREBASE_LEGACY_SERVER_KEY); // <-- this is the important line
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
httpClient.addInterceptor(logging);
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://fcm.googleapis.com/")//url of FCM message server
.client(client)
.addConverterFactory(GsonConverterFactory.create())//use for convert JSON file into object
.build();
// prepare call in Retrofit 2.0
FirebaseAPI firebaseAPI = retrofit.create(FirebaseAPI.class);
//for messaging server
NotifyData notifydata = new NotifyData("Chatting", msg);
Call<Message> call2 = firebaseAPI.sendMessage(new Message(token, notifydata));
call2.enqueue(new Callback<Message>() {
#Override
public void onResponse(Call<Message> call, retrofit2.Response<Message> response) {
Log.e("## SUCCES #E$#", response.body().toString());
}
#Override
public void onFailure(Call<Message> call, Throwable t) {
Log.e("E$ FAILURE E$#", t.getMessage());
}
});
}
Create Class to make Object:
public class Message {
String to;
NotifyData data;
public Message(String to, NotifyData data) {
this.to = to;
this.data = data;
}
}
Create Class to make Object:
public class Notification {
String title;
String message;
enter code here`enter code here`
public Notification(String title, String message) {
this.title = title;
this.message = message;
}
}
I am trying the sign in a user with the silent signin api.
First time a user opens the app iam singin in the user using Auth.GoogleSignInApi.getSignInResultFromIntent(data); where the user is prompted to choose a signin email.
Next time a need a renewed token, i try to get a token in the background, by using the Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
code is as follows:
public void createNewToken(String serverKey, Context context) {
this.context = context;
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(serverKey)
.requestEmail()
.requestProfile()
.build();
GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(context)
.addOnConnectionFailedListener(this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.addScope(new Scope(Scopes.PROFILE))
.addScope(new Scope(Scopes.EMAIL))
.build();
OptionalPendingResult<GoogleSignInResult> pendingResult = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (pendingResult.isDone()) {
handleResult(pendingResult.get());
} else {
pendingResult.setResultCallback(new ResultCallback<GoogleSignInResult>() {
#Override
public void onResult(#NonNull GoogleSignInResult result) {
handleResult(result);
}
});
}
}
However, the problem is i dont get any callback, either from isDone or in the callback(). no error are given except from this:
D/GoogleSignInApiImpl: getSavedSignInResultIfEligible
D/GoogleSignInApiImpl: trySilentSignIn
Am i dusing the api wrongly or why dosnt this work? thx for any feedback!
Try adding ".enableAutoManage(...)" when building the Google API client: developers.google.com/identity/sign-in/android
If you don't want to use enableAutoManage, because you are not in a FragmentActivity for example, you can call manually googleApiClient.connect() and disconnect.