I have successfully developed a chat app using java and firebase and I am stuck at sending and receiving chat notifications when chatting with a friend who has install my app. i have tried using FCM but to no avail. I have written the code and when i send a message, the friend doesn't receive a notification whether app is in foreground or background as well as no errors are found. please help me out. Below is the code for that class that extends the FirebaseMessagingService.
#SuppressLint("MissingFirebaseInstanceTokenRefresh")
public class NotifyFirebaseMessaging extends FirebaseMessagingService {
#Override
public void onMessageReceived(#NonNull #NotNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
String sent = remoteMessage.getData().get("sent");
String user = remoteMessage.getData().get("user");
SharedPreferences preferences = getSharedPreferences("PREFS", MODE_PRIVATE);
String currentUser = preferences.getString("currentuser", "none");
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if(firebaseUser != null && sent.equals(firebaseUser.getUid())) {
if (!currentUser.equals(user)) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
sendOreoNotification(remoteMessage);
}
else{
sendNotification(remoteMessage);
}
}
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
private void sendOreoNotification(RemoteMessage remoteMessage){
String user = remoteMessage.getData().get("user");
String icon = remoteMessage.getData().get("icon");
String title = remoteMessage.getData().get("title");
String body = remoteMessage.getData().get("body");
RemoteMessage.Notification notification = remoteMessage.getNotification();
assert user != null;
int j = Integer.parseInt(user.replaceAll("[\\D]", ""));
Intent intent = new Intent(this, ChatActivity.class);
Bundle bundle = new Bundle();
bundle.putString("userid", user);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, j, intent,PendingIntent.FLAG_ONE_SHOT);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
OreoNotification oreoNotification = new OreoNotification(this);
Notification.Builder builder = oreoNotification.getOreoNotification(title,body,pendingIntent,defaultSound,icon);
int i = 0;
if(j>0){
i = j;
}
oreoNotification.getManager().notify(i, builder.build());
}
private void sendNotification(RemoteMessage remoteMessage) {
String user = remoteMessage.getData().get("user");
String icon = remoteMessage.getData().get("icon");
String title = remoteMessage.getData().get("title");
String body = remoteMessage.getData().get("body");
RemoteMessage.Notification notification = remoteMessage.getNotification();
assert user != null;
int j = Integer.parseInt(user.replaceAll("[\\D]", ""));
Intent intent = new Intent(this, ChatActivity.class);
Bundle bundle = new Bundle();
bundle.putString("userid", user);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, j, intent,PendingIntent.FLAG_ONE_SHOT);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setSmallIcon(Integer.parseInt(icon))
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setSound(defaultSound)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
int i = 0;
if(j>0){
i = j;
}
notificationManager.notify(i, builder.build());
}
}
I think you may be confused about what your sendNotification does. As far as I can tell, the code you shared only receives a message from Firebase Cloud Messaging and then displays it in the notification tray of the local device.
None of the code sends a message to Firebase Cloud Messaging, which is actually only supported from trusted environments (such as your development machine, a server you control, or Cloud Functions/Cloud Run) and not from within your client-side application code.
You can also send a message from within the Firebase console, which is great for testing receipt and for targeted messaging campaigns.
To learn more about this, I recommend checking out:
How to send one to one message using Firebase Messaging
How to send Device to device notification by using FCM without using XMPP or any other script.?
Related
I am building an app that should remind the user about upcoming events that they set (basically a reminder). The issue i'm running into is pushing notifications to the user's phone (only on API 26+) when the app has not been utilized for a while (about 15 minutes or more); notifications are not displayed at all.
I read up on this and realized that App Standby and Doze mode may prevent my app from pushing such notifications; my notifications are received as intended by the user on phones running API 25 and below. To solve this I tried using AlarmManager.setExactAndAllowWhileIdle() but the issue persists.
class TaskNotifications {
private AlarmManager alarmManager;
private Context c;
TaskNotifications(Context context) {
this.c = context;
this.alarmManager = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
}
void setReminder(Context context, Task task) {
if (VERSION.SDK_INT < Build.VERSION_CODES.O) {
long reminderMilliseconds = task.getReminderMilliseconds();
if (reminderMilliseconds > Calendar.getInstance().getTimeInMillis() && !task.isDone()) {
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("ID", task.getID());
intent.putExtra("TITLE", task.getTitle());
intent.putExtra("DETAILS", task.getDetails());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, task.getID(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (VERSION.SDK_INT >= 23) {
this.alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, reminderMilliseconds, pendingIntent);
} else
this.alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderMilliseconds, pendingIntent);
}
}
}
void cancelReminder(Task task) {
if (VERSION.SDK_INT < Build.VERSION_CODES.O) {
this.alarmManager.cancel(PendingIntent.getBroadcast(this.c, task.getID(),
new Intent(this.c, NotificationReceiver.class), PendingIntent.FLAG_CANCEL_CURRENT));
}
}
}
public class NotificationReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Intent startIntent = new Intent(context, NotificationJobIntentService.class);
startIntent.putExtra("ID", intent.getIntExtra("ID", -1));
startIntent.putExtra("TITLE", intent.getStringExtra("TITLE"));
startIntent.putExtra("DETAILS", intent.getStringExtra("DETAILS"));
JobIntentService.enqueueWork(context, NotificationJobIntentService.class, intent.getIntExtra("ID", -1), startIntent);
}
}
public class NotificationJobIntentService extends JobIntentService {
private String CHANNEL_ID = getResources().getString(R.string.channel_name);
protected void onHandleWork(#NonNull Intent intent) {
createNotificationChannel(NotificationJobIntentService.this);
int NOTIFICATION_ID = intent.getIntExtra("ID", -1);
String GROUP = "NOTIFICATION_GROUP";
String title = intent.getStringExtra("TITLE");
if (title.isEmpty())
title = getResources().getString(R.string.no_title);
String details = intent.getStringExtra("DETAILS");
if (details.isEmpty())
details = getResources().getString(R.string.no_details);
Intent openAppIntent = new Intent(NotificationJobIntentService.this, MainActivity.class);
TaskStackBuilder create = TaskStackBuilder.create(this);
create.addNextIntentWithParentStack(openAppIntent);
NotificationCompat.Builder builder = new NotificationCompat.Builder(NotificationJobIntentService.this, this.CHANNEL_ID)
.setContentTitle(title)
.setContentText(details)
.setSmallIcon(R.drawable.baseline_alarm_black_18)
.setPriority(NotificationCompat.PRIORITY_MAX)
.setContentIntent(create.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT))
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setGroup(GROUP)
.setAutoCancel(true)
.setColor(Color.argb(100, 0, 87, 75))
.setVibrate(new long[]{1000, 1000})
.setLights(Color.GREEN, PathInterpolatorCompat.MAX_NUM_POINTS, PathInterpolatorCompat.MAX_NUM_POINTS)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM));
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, builder.build());
}
private void createNotificationChannel(Context context) {
if (VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence string = context.getString(R.string.channel_name);
String description = context.getString(R.string.channel_description);
NotificationChannel notificationChannel = new NotificationChannel(this.CHANNEL_ID, string, NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription(description);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.GREEN);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{1000, 1000});
(context.getSystemService(NotificationManager.class)).createNotificationChannel(notificationChannel);
}
}
}
Is there a reliable way for me to send exact/somewhat exact notifications to my users' phones running API 26+? Or is there an error in my code that I did not notice?
I was unable to make the notification system work on API 26+ devices, however, I used the Android Calendar Provider Reminders to add the event to the users calendar and then set a reminder through the default calendar...not what I originally wanted, but it is a band-aid solution.
If anybody can still solve the problem as intended, please let me know.
Code below:
if (task.getEventID() > 0) {
//Remove existing events for this task
ContentResolver cr = c.getContentResolver();
int iNumRowsDeleted;
Uri eventUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, task.getEventID());
iNumRowsDeleted = cr.delete(eventUri, null, null);
Log.i("removeEvent()", "Deleted " + iNumRowsDeleted + " calendar entry.");
}
try {
//Add an event
ContentResolver cr = context.getContentResolver();
ContentValues values = new ContentValues();
values.put(CalendarContract.Events.DTSTART, task.getCal().getTimeInMillis());
values.put(CalendarContract.Events.DTEND, task.getCal().getTimeInMillis()+60*60*1000);//Each task a duration of 60 minutes
values.put(CalendarContract.Events.TITLE, task.getTitle() + " - " + task.getDetails());
values.put(CalendarContract.Events.CALENDAR_ID, getPrimaryCalendar());
values.put(CalendarContract.Events.EVENT_TIMEZONE, Calendar.getInstance().getTimeZone().getID());
Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);
// Save the eventId into the Task object for possible future delete.
task.setEventID(Integer.parseInt(uri.getLastPathSegment()));
Log.i("addEvent()","The event id is " + task.getEventID());
// Add a reminder
ContentValues valuesR = new ContentValues();
valuesR.put(CalendarContract.Reminders.MINUTES, (task.getCal().getTimeInMillis() - reminderMilliseconds)/(1000*60));
valuesR.put(CalendarContract.Reminders.EVENT_ID, task.getEventID());
valuesR.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT); /*The alarm method, as set on the server. METHOD_DEFAULT, METHOD_ALERT, METHOD_EMAIL, METHOD_SMS and METHOD_ALARM are possible values; the device will only process METHOD_DEFAULT and METHOD_ALERT reminders (the other types are simply stored so we can send the same reminder info back to the server when we make changes).*/
Uri uriR = cr.insert(CalendarContract.Reminders.CONTENT_URI, valuesR);
Cursor c = CalendarContract.Reminders.query(cr, task.getEventID(), new String[]{CalendarContract.Reminders.MINUTES});
if (c.moveToFirst()) {
Log.i("setReminder()",task.toString());
Log.i("setReminder()","calendar has reminder at " + c.getInt(c.getColumnIndex(CalendarContract.Reminders.MINUTES)));
}
c.close();
} catch (Exception e) {
e.printStackTrace();
}
My Android application gets firebase notifications. And I need to localize this notifications depends on application language on the client side, not on server side.
If application is in foreground I use onMessageReceived() from FirebaseMessagingService and push my own localized notification. But if application is in background onMessageReceived() doesn't called.
In this case I use my own class extended BroadcastReceiver. onReceive(Context context, Intent intent) method catches notification, I localize it and push. Everything goes good, but in the end I get 2 push notifications: my own localized and firebase.
How can I get rid of this firebase notification and get only my own?
public class FirebaseDataReceiver extends BroadcastReceiver {
Context context;
PendingIntent pendingIntent;
public void onReceive(Context context, Intent intent) {
this.context = context;
Bundle dataBundle = intent.getExtras();
String title = "";
String body = "";
String type = "";
String objectId = "";
if (dataBundle != null) {
type = dataBundle.getString("type");
objectId = dataBundle.getString("objectId");
title = NotificationUtils.getNotificationTitle(context, dataBundle);
body = NotificationUtils.getNotificationBody(context, dataBundle);
}
Intent newIntent = new Intent(context, TutorialActivity_.class);
newIntent.putExtra("target", "notification");
newIntent.putExtra("type", type);
newIntent.putExtra("objectId", objectId);
newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent = PendingIntent.getActivity(context,
0,
newIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new Notification.Builder(context)
.setContentTitle(title)
.setContentText(body)
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_ALL)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.splash_mini)
.build();
deleteLastNotification();
NotificationManagerCompat.from(context).notify(0, notification);
}
}
You should really use Data notifications from the server. Normal message notifications can't achieve this behaviour you're looking for. Check out the docs here:https://firebase.google.com/docs/cloud-messaging/concept-options
So your request to Firebase from the server should look something like this:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
Instead of:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"notification":{
"title":"Portugal vs. Denmark",
"body":"great match!"
}
}
}
My MyFirebaseMessagingService.
public class MyFirebaseMessagingService
extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.d(TAG, "FRCM:"+ remoteMessage.getFrom());
/*Check if the message contains data*/
if(remoteMessage.getData().size() > 0){
Log.d(TAG,"Message data: "+ remoteMessage.getData());
}
/*Check if the message contains notification*/
if(remoteMessage.getNotification() != null){
Log.d(TAG,"Message body: "+ remoteMessage.getNotification().getBody());
sendNotification(remoteMessage.getNotification().getBody());
}
}
/*Display Notification Body*/
private void sendNotification(String body) {
Intent intent = new Intent(this, Home.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0/*Request code*/, intent, PendingIntent.FLAG_ONE_SHOT);
/*Set sound of Notification*/
Uri notificationSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notifiBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_event_note_black_24dp)
.setContentTitle("Firebase Cloud Messaging")
.setContentText(body)
.setAutoCancel(true)
.setSound(notificationSound)
.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0/*ID of notification*/, notifiBuilder.build());
intent = new Intent("myAction");
intent.putExtra("title", title);
intent.putExtra("message", message);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
and My Activity Messaging is.
public class Mess{
}
You send messages from your phone using FCM. You need to make a POST to https://fcm.googleapis.com/fcm/send api with payload that you want to send, and you server key found in Firebase Console project or You can Use POSTMAN google chrome extension
As an exameple of payload sending to a single user with to param:
{ "data": {
"score": "5x1",
"time": "15:10"
},
"to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}
Also, to param can be used for topics "to": "topics/yourTopic"
In data you can send whatever you want, message is received in onMessageReceived() service from Firebase.
More details you can found in Firebase documentation.
For sending data to another activity
private void sendNotification(String body) {
Intent intent = new Intent(this, Home.class);
here you can set intent
intent.putExtra("word", body);
and to read from activity use
b = getIntent().getExtras();
String passed_from = b.getString("word");
I am working on an android app where I need to get incoming message from whatsapp or any other chatting app and then reply to that message using my app.
I am able to extract message and other data from notification using NotificationListener Service, but I don't know how to reply to that notification from my app.
I got all the information using the following method of NotificationListener Class.
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
Intent intent = new Intent(this, ChatHeadService.class);
startService(intent);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
String key = sbn.getKey(); // notification key
PendingIntent intent1 = sbn.getNotification().contentIntent; // pending intent
String pack = sbn.getPackageName(); // package name of app
Bundle noti = sbn.getNotification().extras; // extra bundle of notification
String name = noti.getString("android.title"); // name of the sender
Bitmap img = sbn.getNotification().largeIcon; // the icon of the sender user
CharSequence[] textlines = noti.getCharSequenceArray("android.textLines");
if (textlines != null)
Log.i("textlines", textlines.toString());
Now I need to reply to the above notification!
I would like to put two buttons on my notifications from the status bar. Of course they do not appear until the user touches to expand them. I have created the custom layout for my notification using RemoteViews but am unsure if it's possible to obtain a reference to them because of my current code structure.
#Override
public void onMessage(Context context, Intent intent) {
Log.w("C2DMReceiver",
"Message Received, this is the message with no payload");
Bundle extras = intent.getExtras();
if (extras != null) {
String[] payload = new String[3];
payload[0] = (String) extras.get("payload");
payload[1] = (String) extras.get("payload2");
SharedPreferences sharedP = Prefs.get(this);
boolean inApp = sharedP.getBoolean("currentlyInApp", true);
if (!inApp) {
createNotification(context, payload);
}
}
}
public void createNotification(Context context, String[] payload) {
SharedPreferences sharedP = Prefs.get(context);
boolean needsToLogin = sharedP
.getBoolean("loginFromNotification", true);
Log.w("C2DMReceiver", "createNotification called");
NotificationManager notificationmanager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, WebViewActivity.class);
Intent notificationIntent2 = new Intent(this, UniteActivity.class);
PendingIntent pIntent;
if (needsToLogin) {
pIntent = PendingIntent.getActivity(this, 0, notificationIntent2,
PendingIntent.FLAG_CANCEL_CURRENT);
} else {
pIntent = PendingIntent.getActivity(this, 0, notificationIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
// Compatibility builder
NotificationCompat.Builder notification = new NotificationCompat.Builder(
context);
RemoteViews remote = new RemoteViews(getPackageName(),R.layout.notification);
//Button okButton = (Button) findViewById(R.layout.notification);
notification.setAutoCancel(false);
notification.setContent(remote);
notification.setContentIntent(pIntent);
notification.setWhen(System.currentTimeMillis());
notification.setTicker(payload[0]);
notification.setSmallIcon(R.drawable.default1);
notification.setContentTitle(payload[1]);
notification.setContentText(payload[0]);
long duration[] = { 100, 300, 100 };
notification.setVibrate(duration);
notificationmanager.notify(0, notification.getNotification());
}
onMessage is a method pulled from the Google C2DM library where the notifications are generated by intents received from google. Without a view, how can I obtain a reference to my buttons using findViewById()? or some other means
I think you are looking for the method:
RemoteViews.setOnClickPendingIntent(int, android.app.PendingIntent)
So, if you add...
remote.setOnClickPendingIntent(R.id.button, pIntent);
...it should work.