onMessageReceived is not executing when the app is in background - java

When I open the app and receive the notification, my custom notification shows.
But when the app is in the background, it just uses the default notification setting. I'm trying to change the icon of the notification from that dot to my app logo.
My FirebaseMessagingService code is below
public static final String TG = "FCMService";
NotificationManagerCompat notificationManager;
#Override
public void onNewToken(#NonNull String s) {
Log.i(TG, "The token refreshed: " + s);
super.onNewToken(s);
}
#Override
public void onMessageReceived(#NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
if(remoteMessage.getData().size() > 0)
{
Log.d(TG, "Message data payload : " + remoteMessage.getData());
}
if(remoteMessage.getNotification() != null)
{
Log.d(TG, "Message notification body : " + remoteMessage.getNotification().getBody());
}
Log.d(TG, "From : " + remoteMessage.getFrom());
Map<String, String> s = remoteMessage.getData();
sendNotification(s.get("message"));
}
private void sendNotification(String message) {
notificationManager = NotificationManagerCompat.from(this);
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 ,intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_07c1b5098af03f95f3c3e8f7d461fb78)
.setContentTitle("Just In")
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(1, notification);
}
And my Manifest file is
<service
android:name=".MyFirebaseMessagingService"
android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>

There are two types of messages data messages and notification messages. Data messages are handled here in onMessageReceived whether
the app is in the foreground or background. Data messages are the type
traditionally used with GCM. Notification messages are only received
here in onMessageReceived when the app is in the foreground. When the
app is in the background an automatically generated notification is
displayed. When the user taps on the notification they are returned to
the app. Messages containing both notification and data payloads are
treated as notification messages. The Firebase console always sends
notification messages.
Example code :- You need to specify icon in your notification payload like this
$notification = array
(
'icon' => 'icon here',
'title' => 'title',
'body' => 'new msg',
'click_action' => 'action here'
);
Note:- you need to add these in manifest to use default notification icon like this
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/ic_launcher" />
// optional if required
<meta-data android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/notification_color" />

By the reference of Firebase, when your app is in the background, the notification is delivered to the device’s system tray. A user tap on a notification opens the app launcher by default.
Messages with both notification and data payload, when received in the background. In this case, the notification is delivered to the device’s system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.
Firebase Reference
Android Pie And Newer version has some limitations. Check this reference

Related

Android unable to receive notification when app is killed using FCM

Android able to receive notification when app is in foreground and background but when I kill it causing unable to receive notification.
I'm not sure what part was wrong.
code in AndroidManifest.xml
<service
android:name=".MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
MyFirebaseMessagingService.java
I don't create MyFirebaseIdService.java for refresh token. I use onNewToken(String mToken) in this class instead.
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onNewToken(String mToken) {
super.onNewToken(mToken);
Log.e("NEW_TOKEN",mToken);
}
Here is my onMessageReceived code.
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.d(TAG, "From: " + remoteMessage.getFrom());
RemoteMessage.Notification notification = remoteMessage.getNotification();
Map<String, String> data = remoteMessage.getData();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sendNotification(notification, data);
}
}
sendNotification code, I attempt make it to receive both data payload and notification payload but it still not work.
#RequiresApi(api = Build.VERSION_CODES.O)
private void sendNotification(RemoteMessage.Notification notification, Map<String, String> data) {
String title;
String body;
if (data != null) {
title = data.get("title");
body = data.get("body");
} else {
title = notification.getTitle();
body = notification.getBody();
}
Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "channel_id")
.setContentTitle(title)
.setContentText(body)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentIntent(pendingIntent)
.setLargeIcon(icon)
.setColor(Color.BLUE)
.setSmallIcon(R.mipmap.ic_launcher);
try {
String picture_url = data.get("picture_url");
if (picture_url != null && !"".equals(picture_url)) {
URL url = new URL(picture_url);
Bitmap bigPicture = BitmapFactory.decodeStream(url.openConnection().getInputStream());
notificationBuilder.setStyle(
new NotificationCompat.BigPictureStyle().bigPicture(bigPicture).setSummaryText(notification.getBody())
);
}
} catch (IOException e) {
e.printStackTrace();
}
notificationBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
notificationBuilder.setLights(Color.RED, 1000, 300);
NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this);
create channel code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"channel_id", "channel_name", NotificationManager.IMPORTANCE_DEFAULT
);
channel.setDescription("channel description");
channel.setShowBadge(true);
channel.canShowBadge();
channel.enableLights(true);
channel.setLightColor(Color.RED);
channel.enableVibration(true);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.setVibrationPattern(new long[]{100, 200, 300, 400, 500});
managerCompat.createNotificationChannel(channel);
}
managerCompat.notify(101,notificationBuilder.build());
}
}
You cannot receive notification when killed app for one cause:
Some manufacturers, like HUAWEI, OPPO ecc... make unavailable to push notifications while app is not running, this in order to save your battery life. Of course, important app (WhatsApp, Instagram ecc...) make exception by default. In other hand, there's a way to fix it, every app can get this permission manually via settings->battery (it changes phone by phone).
Now you have to find this permission which affords app to push notifications without running. A tip: when I've tick this one, it doesn't work; I had to unistall app via settings->app, then re-install it again

How do I detect if device is charging from background

I have searched thick and thin through the Developer notes for android and am unable to find out how to perform a specific action when a device is plugged in, and perform another action when unplugged.
I have attempted to set a Broadcast Receiver like below however it will not run:
<receiver android:name=".PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
I understand that from API 26 and up, you can no longer receive some broadcasts registered in the manifest, and must register them dynamically.
I need to have this run in the background but can't figure out how? This article updated in 2019 (after api 26) implies that I should be able to do it.
The charging status can change as easily as a device can be plugged in, so it's important to monitor the charging state for changes and alter your refresh rate accordingly.
The BatteryManager broadcasts an action whenever the device is connected or disconnected from power. It's important to receive these events even while your app isn't running particularly as these events should impact how often you start your app in order to initiate a background update so you should register a BroadcastReceiver in your manifest to listen for both events by defining the ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED within an intent filter.
My end goal is to call the broadcast receiver whenever device is plugged in or unplugged.
How would I go about implementing this?
The best way to accomplish this is by having a service run in the background so that you can receive the broadcast from android without the user using the app.
Start by registering a service in your manifest file.
<Service android:name=".serviceName"/>
Now create your class with the same name as registered in the manifest above, this class must extend Service.
public class BackgroundService extends Service {
private static final int NOTIF_ID = 1;
private static final String NOTIF_CHANNEL_ID = "AppNameBackgroundService";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
//Add broadcast receiver for plugging in and out here
ChargeDetection chargeDetector = new ChargeDetection(); //This is the name of the class at the bottom of this code.
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
this.registerReceiver(chargeDetector, filter);
startForeground();
return super.onStartCommand(intent, flags, startId);
}
private void startForeground() {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
startForeground(NOTIF_ID, new NotificationCompat.Builder(this,
NOTIF_CHANNEL_ID) // don't forget create a notification channel first
.setOngoing(true)
.setSmallIcon(R.mipmap.final_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText("Background service is running")
.setContentIntent(pendingIntent)
.build());
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
NOTIF_CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
class ChargeDetection extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Now check if user is charging here. This will only run when device is either plugged in or unplugged.
}
}
}
You need to register this receiver from code
You could run a WorkManager to run once every 15 minutes (the minimum is once every 15 minutes)
register the receiver, check if it's charging
unregister the receiver

Is it possible to launch a specific activity with only data payload FCM notification?

Using Firebase Cloud Function, I sent automatically a notification when a user reply to another one. When the notification is sent, the user can open it and an activity showing the conversation should be displayed. If my using only data as payload like below, it is not possible to perform a click_action and open the corresponding activity. Defining my payload like this (and get data in onMessageReceived) does not work:
const payload = {
data : {
post : xxx,
comment : xxx,
from : xxx,
to : xxxx,
action_click : "open_activity_B"
}
};
Is there a way to achieve what I want without adding notification?
Thanks for your help
Edit:
Some more informations to explain you why I'm doing this like that. I want to create a notification as below (in foreground & background):
I achieved that only by using the method I provided you but without the click_action. If i use notification payload, I cannot display large icon (avatar at the end of the notification).
Furthermore, in foreground my icon app is displayed in the notification but in background, I have a default icon...
My code below:
if (remoteMessage.getData().size() > 0) {
post_id = remoteMessage.getData().get("post");
comment_id = remoteMessage.getData().get("comment");
originatorUid = remoteMessage.getData().get("originatorUid");
image = remoteMessage.getData().get("image");
if (/* Check if data needs to be processed by long running
job */ true) {
// For long-running tasks (10 seconds or more) use
Firebase Job Dispatcher.
scheduleJob();
} else {
// Handle message within 10 seconds
handleNow();
}
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
title = remoteMessage.getNotification().getTitle();
body = remoteMessage.getNotification().getBody();
clickaction = remoteMessage.getNotification().getClickAction();
icon = remoteMessage.getNotification().getIcon();
}
Then in onMessageReceived:
Intent intent=new Intent(clickaction);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("post_id", post_id);
intent.putExtra("comment_id", comment_id);
intent.putExtra("originatorUid", originatorUid);
pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_ONE_SHOT);
String channelId = getString(R.string.app_name);
Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.myicon)
.setLargeIcon(image)
.setContentTitle(title)
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
As per official documentaion:
Messages with both notification and data payload, both background and
foreground. In this case, the notification is delivered to the device’s system tray, and the data payload is delivered in the extras
of the intent of your launcher Activity.
The launcher activity is specified in the AndroidManifest.xml file using category LAUNCHER as specified below:
<activity
android:name="com.example.MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
You can change the default behaviour overriding and specify another activity. In your message notification data, add a new property named click_action with the value of an action string. Then give it an intent filter in the AndroidManifest.xml file that matches the action, like in the example below:
{
"to": "tgFvOPQLccSe:EDE90N.........5Tg",
"notification": {
"title": "My Message",
"body": "Hello Kmel!",
"click_action": "com.example.MY_NEW_ACTIVITY"
},
"data": {
"score": "111"
}
}
Define the intent filter like this:
<activity android:name=".MyFcmNotificationActivity">
<intent-filter>
<action android:name="com.example.MY_NEW_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
But remember, the data payload is not delivered to the activity when the message is received, is delivered when the user clicks on the notification.

Push notifications are not being sent from Firebase to Android

I have added firebase dependencies and included google-services.json. I am able to use Firebase database successfully. But Push notifications are not being sent from fireabse console.
Please let me know what is wrong here.
I have following classes
public class FirebaseIDService extends FirebaseInstanceIdService {
private static final String TAG = "FirebaseIDService";
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(refreshedToken);
}
/**
* Persist token to third-party servers.
* <p>
* Modify this method to associate the user's FCM InstanceID token with any server-side account
* maintained by your application.
*
* #param token The new token.
*/
private void sendRegistrationToServer(String token) {
// Add custom implementation, as needed.
}
}
And Messaging Service
public class GalleryMessagingService extends FirebaseMessagingService {
private static final String TAG = "FCM Service";
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
send();
}
private void send() {
Intent resultIntent = new Intent(this, ImageGridActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("My notification")
.setContentText("First Notification");
mBuilder.setContentIntent(resultPendingIntent);
// Sets an ID for the notification
int mNotificationId = 001;
NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotifyMgr.notify(mNotificationId, mBuilder.build());
}
I have added following in Manifest.xml as well
<service android:name="com.doublenine.apps.ui.GalleryMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name="com.doublenine.apps.ui.FirebaseIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
-->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/ic_launcher" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
notification message. for more. -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#android:color/holo_green_dark" />
FirebaseIDService class looks incomplete as I don't know what to write in sendRegistrationToServer()

Android: writing to realm database from notification

I am building an app and ran into a problem that I couldn't find an answer to.
I have a realm database that I use to store some very simple information. Now what I want to do is every day at a set time prompt the user with a notification with a few buttons on it. And depending on what button the user clicks, I want to write a different value to the realm database. PREFERABLY without opening the app. Is this possible?
Thanks in advance!
You can do it in this way.
First, create a Notification with actions on it.
Intent intentLike = new Intent("MY_ACTION");
intentLike.putExtra("KEY","LIKE");
PendingIntent likePendingIntent = PendingIntent.getBroadcast(context,0,intentLike,PendingIntent.FLAG_UPDATE_CURRENT);
Intent intentShare = new Intent("MY_ACTION");
intentShare.putExtra("KEY","SHARE");
PendingIntent sharePendingIntent = PendingIntent.getBroadcast(context,1,intentShare,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.addAction(R.drawable.notification_action_like, "Like", likePendingIntent)
.addAction(R.drawable.notification_action_share, "Share", sharePendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
Now create a BroadcastReceiver class to receive values.
public class LikeShareReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String receivedValue = intent.getExtra("KEY");
if (receivedValue.equals("LIKE")) {
//update like in Realm database.
}else if (receivedValue.equals("SHARE")) {
//update share in Realm database.
}
}
}
Add this BroadcastReceiver in the manifest file.
<receiver android:enabled="true" android:name="LikeShareReceiver">
<intent-filter>
<action android:name="MY_ACTION" />
</intent-filter>
</receiver>
How this will work?
When a user clicks on an action button it will trigger a broadcast with an intent having values in it. BroadcastReceiver will receive this broadcast and update the database accordingly.
Note:addAction() method will only work with api level >=4.1

Categories