I'm using single activity architecture in my app. Now I'm stuck with handing notifications. I'm receiving notification from firebase and when app is on background, the google play services handle such notifications great. When it's tapped it brings the app from background to foreground (it does't recreate activity / app).
I need to have the same behaviour for notification received while the app is on foreground. Therefore I override onMessageReceived() in my firebase service and create new notification here. I tried many variation of Intent's Flags passed to the notification and launchMode in manifest but it always results into activity recreation (activity has different hashcode and it's onCreate() it's called) after tapping on notification created by onMessageReceived().
Here is the code:
#Override
public void onMessageReceived(#NonNull RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Logger.d("Msg received " + remoteMessage.getNotification().getTitle());
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, FCM_CHANNEL_ID)
.setContentTitle(remoteMessage.getNotification().getTitle())
.setContentText(remoteMessage.getNotification().getBody())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(remoteMessage.getNotification().getBody()))
.build();
NotificationManagerCompat manager = NotificationManagerCompat.from(getApplicationContext());
manager.notify(FCM_NOTIFICATION_ID, notification);
}
manifest: android:launchMode="singleTask"
Any idea what to change to prevent activity recreation? (I'm testing on android 10, MainActivity extends AppCompatActivity)
Thanks.
Finally I get it work. All magic was done by adding those two lines to my code.
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
Original answer
Did you try this?
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Use the
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK flags.
This will start activity as the root of the stack.
Related
I am using an external library in our project. That library showing notification. On tapping that notification starts activity in the library.
I want to detect that activity launch from push notification to track some analytics data.
Is there any way to detect those notification taps or activity launch?
As far as I understand you activity is already launched on notification tap. For detecting activity launch you can use ActivityLifecycleCallbacks. In such case you will need to override onActivityCreated/onActivityStarted which includes created/started activity as argument. You can inject analytics component inside and send events about launched activities.
class AppLifecycleCallbacks : ActivityLifecycleCallbacks {
override fun onActivityStarted(activity: Activity) {
if (activity is MyActivity) {
//...
}
}
//...
}
Pass some extra meta data along with your pending intent generated for showing notification. And parse same in your destination activity.
Intent destination = new Intent(context, HomeActivity.class);
destination.putExtra("SOURCE","NOTIFICATION");
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, destination, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setContentTitle("Notification Title")
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentIntent(pendingIntent)
.setContentInfo("App")
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher))
.setColor(context.getColor(R.color.colorAccent))
.setLights(Color.RED, 1000, 300)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setSmallIcon(R.drawable.ic_like);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
And in activity level :
if(getIntent().getStringExtra("SOURCE").equals("NOTIFICATION")){
// launched from notification
}
If your activity is already running your intent might get delivered to :
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
Of course, yes. When you create Intent for launching your target activity you can pass arguments to the intent and when Activity starts just obtain outer arguments from intent and if these arguments came from Notification do your actions need.
I use FCM (Firebase Cloud Messaging) in my app.
I have N activities in my app
During application's lifetime it may have any stack of activities:
Activity1 ->Activity2 -> Activity3-> ... -> ActivityN
I want to achieve behavior:
Go to ActivityN
Turn application to background
Click on notification
Turn application to foreground on ActivityN
Show dialog on ActivityN
How to achieve it?
I had the same issue in that I wanted the app to just resume in its current state when the Firebase background notification arrived. Part of my solution from this answer.
In my case, I had up to four activities on the stack: A->B->C->D.
In my app's manifest, I made activity D a singleTask activity (launchMode="singleTask") and put the intent-filter in that activity.
<activity android:name=".activityD">
<intent-filter>
<action android:name=".activityD" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
I then put activity D as the "click_action" in the notification sent by Firebase Messaging.
In my case, I didn't override the onNewIntent() method in activity D since the FirebaseMessageService's onMessageReceived method will get called when the app is brought to the foreground.
Most important: Because activity D is a singleTask activity, it is only created if it's not already on the stack. Therefore, in activity D's onCreate() method, I check the contents of the Intent extras. If the extras didn't come from activity C, I pop D immediately (finish();return;) because I know it was created by the Firebase notification. That resumes the app's activity (A, B or C) when it went into the background. If the app's activity was D when it went into the background, onNewIntent() method will be called instead of onCreate() in which case I do nothing as described in step #3.
After thinking about it some more, a more general solution would be to create a "dummy" singleTask activity with the intent filter, then always pop it off the stack in the onCreate() method to resume the current activity when it was put in the background. Surely there's a better way... I just couldn't find it.
in FirebaseMessagingService class>> onMessageRecived
Intent intent = new Intent(this, ActivityN.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable._logo)
.setContentText(notification)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri);
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
In your case where you are not sure to which Activity to be open on Notification click . Then you should broadcast on Notification click .
Create a Broadcast Reaceiver.
class NotificationClickReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// here you will get Intent
// Check which activity is currently open and pass the data to it
// For passing data you can use a Another BroadcastReceiver or
}
}
Entry in manifest
<receiver
android:name=".NotificationClickReceiver"
/>
Then use pendingIntent to getBroadcast on notification click
Intent intent = new Intent(this, NotificationClickReceiver.class);
intent.putExtra("key","val");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 3, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable._logo)
.setContentText(notification)
.setContentTitle(title)
.setAutoCancel(true)
.setSound(defaultSoundUri);
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
Now you will get intent in onReceive of NotificationClickReceiver then you can figure out to which activity to pass it on.
Is it possible not launch new activity if we receive a push notification while the app is running?
My activity works with fragments and I want to do transition to a determinate fragment when the notification is received. My activity have data that I need to show the fragments. The problem is that when I receive the push notification while the app is running the method onDestroy is called and here I clear the data and then the app crash because the data are null. How can I do to not create new activity when the app receive a push notification while is running? In case the app is running I want that if you click the notification do a transition fragment, not create again the activity.
Thanks in advance.
First of all, I think that you mean "notification" to be a "message", but not android.app.Notification class.
And second, I don't think it's a best practise to raise new GUI when receiving a message, which would interrupt the user interaction. For details, please refer to: http://developer.android.com/training/notify-user/index.html and http://developer.android.com/design/patterns/notifications.html.
At last, if you really wanna do what you stated in your thread, I wonder why the data used to generate the show-data fragment is held in the activity. Try holding the data in an android.app.IntentService object, and then generate transfer the data to new activity, and then use android.app.Fragment.setArguments method to transfer the data from activity to fragment.
I think that this code will help you. This which you need is PendingIntent, it make transaction to desired activity.
/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void generateNotification(Context context, String title,
String message) {
//get the default notification sound URI
Uri uriNotificationSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
//make intent to the application. if the application is opened just maximize it.
Intent homeIntent = new Intent(context, 'your desired activity');
homeIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
homeIntent, 0);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("eCommCongress")
.setContentText(message)
.setLights(Color.GREEN, 1500, 1500)
.setSound(uriNotificationSound)
.setAutoCancel(true)
.setContentIntent(contentIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(counter, mBuilder.build());
counter++;
}
It is perfectly possible and I do such a thing in one of my apps. First, you need to declare your activity as android:launchMode="singleTop",
Then, when you build you must configure your pending intent not to fire a new instance of your activity:
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent("YOUR ACTION HERE");
intent.setClassName(this, MainActivity.class.getName());
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setContentIntent(resultPendingIntent);
Notification notification = mBuilder.build();
notification.defaults |= Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS;
notification.flags |= Notification.FLAG_SHOW_LIGHTS | Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify(idNotificacion,notification);
Now all you have to do is to override your onNewIntent inside your Activity and do whatever you want with your fragment:
#Override
protected void onNewIntent(Intent intent) {
MiLog.i(getApplicationContext(),"IntentShit","new intent received");
MiLog.i(getApplicationContext(),"IntentShit","Action: "+intent.getAction());
if(intent.getAction()!=null && intent.getAction().equals("YOUR ACTION HERE"){
//DO your stuff here
}
}
You should also take a look at this page for more info:
http://www.intridea.com/blog/2011/6/16/android-understanding-activity-launchmode
I'm able to queue and launch notifications through AlarmManager, I'm also able to launch my application when clicking the notification. Unfortunately the notification isn't removing itself when the application is launched.
Notification setup:
Intent intent = new Intent(mainActivity, NotificationPublisher.class);
intent.setAction("handle");
PendingIntent pIntent = PendingIntent.getBroadcast(mainActivity, 0, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
Notification.Builder builder = new Notification.Builder(mainActivity)
.setSmallIcon(R.drawable.phone)
.setContentTitle(title)
.setContentText(textContent)
.setAutoCancel(true)
.setContentIntent(pIntent);
return builder.getNotification();
The notification click broadcasts and hits this function:
void handle(Context context, Intent intent) {
System.out.println("handle");
Context mainContext = Extension.mainContext;
Activity mainActivity = Extension.mainActivity;
Intent launchIntent = new Intent(mainContext, mainActivity.getClass());
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(launchIntent);
}
The handle function will launch the activity but not remove the notification from the status bar. Interestingly... If I remove the call to startActivity, the notification will close.
Things I've tried:
Setting the auto cancel flag manually
Using the builder.build() instead
NotificationManger cancel(id) and/or cancelAll
Using NotificationCompat.Builder
Using a regular launch intent for the setContentIntent instead of manually calling startActivity
Based on this statement from your question:
Interestingly... If I remove the call to startActivity, the
notification will close.
I'm assuming that your Activity is just reposting the Notification.
I worked around this by launching my App 300ms after clicking the notification.
I am trying to add a button to my notification in android.
I use the addAction method in order to add an intent which supposes to open up the main activity (same as clicking the entire notification) but with an extra bundle with data.
this is what I have done so far:
notificationManager = (NotificationManager)this.getSystemService(NOTIFICATION_SERVICE);
//regular intent to view main activity
PendingIntent contentIntent = PendingIntent.getActivity(this,Constants.MAIN_ACTIVITY,
new Intent(this, MainActivity.class), 0);
//intent for viewing transaction dialog, within main activity using PURCHASE_DIALOG request code
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(Constants.LIST, (java.util.ArrayList<? extends android.os.Parcelable>) list);
PendingIntent purchaseIntent = PendingIntent.getActivity(
this, Constants.PURCHASE_DIALOG,
new Intent(this, MainActivity.class), 0, bundle);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(Constants.NOTIFICATION_TOPIC)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(watchResponse.toString()))
.setAutoCancel(true)
.addAction(R.drawable.ic_launcher, "Buy", purchaseIntent)
.setContentText(message);
mBuilder.setContentIntent(contentIntent);
notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
I simply expect that my onResume in MainActivity class will be called once clicking the extra action, and there i'll be able to get the bundle trough getIntent(), yet nothing happens when I click on it. the button is clicked, but the activity remains open and my application activity wont start.
I had a very similar issue but a very different solution. Pending intent is also not fired if you have declared <service android:enabled="false"></service> in your manifest.xml file.
Replace from android:enabled="false" to android:enabled="true"
This might not be a direct issue of the problem. But if you create the service in android studio using default template it automatically adds these properties to the service.
If this does not work there is a similar question you can find here:
Android Notification Action is not fired (PendingIntent)