I have a follow on question to the question answered here: How to send parameters from a notification-click to an activity?
So, I have 3 activities, A, B, and C. An alarm (timer) starts when the user gets to activity C. After X amount of time, a notification appears in the status bar. Below is how I launch the notification. "MainActivity.class" is activity A. However, launching the notification in this way restarts the application in whatever activity the user was in when the closed it. So, if they were in activity C when the app closed and they click on the notification, the app will resume to activity C.
Intent notificationIntent = new Intent( this, MainActivity.class );
notificationIntent.setAction( Intent.ACTION_MAIN );
notificationIntent.addCategory( Intent.CATEGORY_LAUNCHER );
notificationIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED );
notificationIntent.putExtra( NotifyService.NOTIFICATION_INTENT, true );
PendingIntent contentIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT );
Notification notification = new Notification.Builder( this )
.setContentTitle( title )
.setContentText( text )
.setTicker( ticker )
.setSmallIcon( R.drawable.ic_launcher )
.setWhen( time )
.setContentIntent( contentIntent )
.setAutoCancel( true )
.setDefaults( Notification.DEFAULT_ALL )
.build();
I have this in the manifest for activities A, B, and C: android:launchMode="singleTop". I also have the following code in each activity.
#Override
public void onNewIntent( Intent intent )
{
super.onNewIntent( intent );
Bundle extras = intent.getExtras();
if( extras != null )
{
Boolean startedFromNotification = extras.getBoolean( NotifyService.NOTIFICATION_INTENT );
if( startedFromNotification )
{
// Do something
}
}
}
Now, if the user was in activity A when they closed the app, then onNewIntent gets called in that activity when they click on the notification (working as expected). But, if they were in activity B or C, not only does onNewIntent in those activities not get called, but the extra I put in there (NotifyService.NOTIFICATION_INTENT) is also missing.
Is there anyway to pass the notification intent extras to the app when it resumes to activity B or C?
I was lead to an answer via this question: FLAG_ACTIVITY_NEW_TASK not behaving as expected when used in PendingIntent
First, I changed this line:
Intent notificationIntent = new Intent( this, MainActivity.class );
To:
Intent notificationIntent = new Intent( this, NotificationActivity.class );
I made NotificationActivity.class a placeholder activity that will let other activities know when the app is being resumed from a notification. These lines of code were removed:
notificationIntent.setAction( Intent.ACTION_MAIN );
notificationIntent.addCategory( Intent.CATEGORY_LAUNCHER );
notificationIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED );
notificationIntent.putExtra( NotifyService.NOTIFICATION_INTENT, true );
And, this line:
PendingIntent contentIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT );
Was changed to:
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT );
In the onCreate() method of NotificationActivity, I added these lines to the bottom:
STARTED_FROM_NOTIFICATION = true;
finish();
Where STARTED_FROM_NOTIFICATION is in a common source file. Then, in each activity in the onStart() method:
if( STARTED_FROM_NOTIFICATION == true )
{
STARTED_FROM_NOTIFICATION = false;
// Do something
}
I also removed android:launchMode="singleTop" from all activities.
Using this method, the NotificationActivity will be placed on top of the existing stack of activities, but will be finished before it is displayed. After it is finished, onStart() for the activity below it will be called and operation works as desired.
Related
I have a helper method that sets up and returns a notification that user's credentials are expired and he needs to log back in. Upon pressing this notification, I expect that he gets a login activity without being able to go back to the home screen.
I was trying to use all the flags to force the stack to be cleared (to erase history of activities), but I can still press back on the login activity and go back to the home screen (like I was never logged out). So I tried to set some extra info for the login activity to recognize if back button can be pressed or not. Nothing works: history is available and none of the extra info that I pass is recognized. Can anybody help me understand where my problem is?
Here is the helper method (in the UiUtils class):
public static Notification getInvalidTokenNotification(Context context)
{
SharePrefHelper.setRememberMe(false);
Intent loginIntent = new Intent(context, LoginActivity.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
loginIntent.putExtra("CAN_GO_BACK", false);
loginIntent.putExtra("TEST_VALUE", 12);
// Action on click:
PendingIntent loginPendingIntent = PendingIntent.getActivity(
context,
0,
loginIntent,
0
);
return new Notification.Builder(context)
.setContentTitle(context.getString(R.string.notification_token_invalid_title))
.setContentText(context.getString(R.string.notification_token_invalid_content))
.setSmallIcon(R.drawable.logo_wenow)
.setContentIntent(loginPendingIntent)
.setAutoCancel(true)
.build();
}
Here is the code to show notification:
#OnClick(R.id.debug_notification_test)
public void onClickTestNotificationBtn()
{
// Show notification:
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(UiUtils.NOTIFICATION_ID_INVALID_TOKEN, UiUtils.getInvalidTokenNotification(getApplicationContext()));
}
// Logout user:
SharePrefHelper.delUser();
}
Use PendingIntent.FLAG_UPDATE_CURRENT as the last parameter of this call:
PendingIntent loginPendingIntent = PendingIntent.getActivity(
context,
0,
loginIntent,
0
);
This will ensure that you replace the extras every time you create the Notification
I have an activity with launchMode set to singleTask:
<activity
android:name="com.blah.blah.MyActivity"
android:launchMode="singleTask">
</activity>
I have an ongoing notification with a PendingIntent that launches that activity:
Intent activityIntent = new Intent( this,
MyActivity.class );
TaskStackBuilder stackBuilder = TaskStackBuilder.create( this );
stackBuilder.addParentStack( MyActivity.class );
stackBuilder.addNextIntent( activityIntent );
PendingIntent resultingActivityPendingIntent = stackBuilder.getPendingIntent( REQUEST_CODE,
PendingIntent.FLAG_UPDATE_CURRENT );
...
m_notificationBuilder.setContentIntent( resultingActivityPendingIntent );
startForeground( ONGOING_NOTIFICATION_ID,
m_notificationBuilder.build() );
When I am interacting with an existing MyActivity, then I hit Home and restart MyActivity via the launcher, MyActivity's onNewIntent() gets called as expected.
The problem is that when I am interacting with an existing MyActivity, and I click on the ongoing notification, a new MyActivity is created via onCreate(), and the existing one is destroyed via onDestroy(). I expected that MyActivity's onNewIntent() would be called instead. Why doesn't this happen?
I have tried these answers without success:
Intent - if activity is running, bring it to front, else start a new one (from notification)
How to make notification resume and not recreate activity?
FLAG_ACTIVITY_REORDER_TO_FRONT ignored (in this case the existing instance is not destroyed when the notification is clicked)
Your problem is due to the use of TaskStackBuilder. This code is causing your problem:
Intent activityIntent = new Intent( this,
MyActivity.class );
TaskStackBuilder stackBuilder = TaskStackBuilder.create( this );
stackBuilder.addParentStack( MyActivity.class );
stackBuilder.addNextIntent( activityIntent );
PendingIntent resultingActivityPendingIntent =
stackBuilder.getPendingIntent(REQUEST_CODE,
PendingIntent.FLAG_UPDATE_CURRENT );
When you use TaskStackBuilder this way, it sets additional flags in the generated Intents that cause the task to be reset (all Activities in the task are destroyed) before your Activity gets launched.
Instead use:
Intent activityIntent = new Intent( this,
MyActivity.class );
PendingIntent resultingActivityPendingIntent =
PendingIntent.getActivity(this, REQUEST_CODE,
activityIntent, PendingIntent.FLAG_UPDATE_CURRENT );
I am trying to send information from notification to invoked activity, while from my activity I got null.
The code for notification is:
private void showNotification() {
Intent resultIntent = new Intent(this, MainActivity.class);
if (D)
Log.d(TAG, "Id: " + Id);
resultIntent.putExtra("ineedid", deviceId);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MeterActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
// Bundle tmp = resultIntent.getExtras();
// if (tmp == null) {
// Log.d(TAG, "tmp bundle is null");
// } else {
// long id = tmp.getLong("ineedid", -1);
// Log.d(TAG, "tmp id : " + id);
// }
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
BLEMessengerService.this)
.setSmallIcon(R.drawable.ic_action_search)
.setContentTitle("Event tracker")
.setContentText("Events received").setOngoing(true)
.setContentIntent(resultPendingIntent)
.setWhen(System.currentTimeMillis());
int mId = R.string.service_notification_start_service;
mNM.notify(mId, mBuilder.getNotification());
}
Code for get information from intent in main activity;
Bundle extras = getIntent().getExtras();
if (extras != null) {
long deviceID = getIntent().getLongExtra("ineedid",
-1);
if (ID == -1) {
if (D)
Log.i(TAG_D, "Wrong Id received.");
finish();
} else {
device = dataSource.getDeviceByID(deviceID);
if (D)
Log.i(TAG_D, "Get the id.");
}
} else {
if (D)
Log.d(TAG_D, "Bundle is null");
finish();
}
I have verified before the notification get notified, bundle is not null, and it has id in extras.
While, when I tried to fetch it from intent, it's gone. Help.
in PendingIntent use this flag PendingIntent.FLAG_UPDATE_CURRENT it's work for me
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
For me, in addition to setting Intent.FLAG_ACTIVITY_SINGLE_TOP , I had to add a unique action to the intent:
Intent resultIntent = new Intent(caller, NotificationActivity.class);
String xId = getNextUniqueId();
resultIntent.putExtra("x_id", xId);
resultIntent.setAction(xId);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
.. without the setAction(..), Extras is null on the Intent received by my NotificationActivity.
This post helps explain it: https://stackoverflow.com/a/3128271/2162226
When an activity is launched(when it is first started ) or relaunched(when it's brought to the top of the stack) the getIntent().getExtra() won't give null. But when the activity is present on the top of the stack then on starting that activity with the use of PendingIntent would not relaunch the activity(onCreate() won't get called) instead onResume() would be called. And getIntent().getExtra() would return the value which is associated with the intent that started the activity(which is null).
In order to update the intent do the following steps:
1). Set FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP flags in your intent.
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
2). Override onNewIntent() in the activity where getIntent().getExtra() is called. This method is called by FLAG_ACTIVITY_SINGLE_TOP
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
For more info: See this doc
I just got the answer,
add line: resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
NOTICE: if you add it as resultIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
It won't work.
I also tried other flags like, "FLAG_ACTIVITY_NEW_TASK" and "FLAG_ACTIVITY_RESET_TASK_IF_NEEDED". neither works here.
For me, my Samsung device kept removing the extras from the intent and sometimes even crashed the app. Tried all the combinations but the actual reason for this behaviour was not explicitly adding PendingIntent.FLAG_IMMUTABLE flag to the pending intent.
Although, this is a requirement for Android 12 and above.
val pendingIntent = PendingIntent.getActivity(
this, 0,
getIntent(),
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
I was working in a local notifications plugin for Unity when I encountered the same problem -> I launched the app, scheduled the local notification, sent the app to background, local notification appeared and, when I clicked on it, app was resumed and getIntent().getExtras() was null.
In my case, the problem was I was trying to get the extras at the wrong intent. Try to print the intents with a simple toString, at the creation moment and when you get it, to ensure that the received is what you expect.
Also take a look to the onNewIntent method.
If you are using the implicit intent to build a PendingIntent, you may get this problem.
previous approach with the issue.
val intent = Intent()
intent.action = Intent.ACTION_VIEW
intent.data = Uri.parse("deeplink or applink here")
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.putExtra("key", Parcelable)
PendingIntent.getActivity(context, abs(Random.nextInt()), intent, PendingIntent.FLAG_UPDATE_CURRENT)
based on implicit Intent tried multiple approaches listed above not work for me.
here is the approach solved my problem. change the Intent from implicit one to an explicit one.
// only diff
val intent = Intent(context, YourIntentDestinationActivity::class.java)
// only diff
intent.action = Intent.ACTION_VIEW
intent.data = Uri.parse("deeplink or applink here")
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
intent.putExtra("key", Parcelable)
PendingIntent.getActivity(context, abs(Random.nextInt()), intent, PendingIntent.FLAG_UPDATE_CURRENT)
hope this works for you.
This seem to be a common problem and I went through all the related questions I could find already:
Activity isn't picking up new intent, Why extra data (integer) is not sent in android notification intent?, Notification passes old Intent Extras, Can't put extras for an intent in notification, android pending intent notification problem; but still cannot figure this out.
Problem is the same. I set a notification with a PendingIntent carrying some extra information and I don't get it on the other side.
Here is the code for generating the notification:
Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.notification_ticker), System.currentTimeMillis());
notification.flags = Notification.FLAG_AUTO_CANCEL | Notification.FLAG_ONLY_ALERT_ONCE;
Intent start_test = new Intent(this, MyActivity.class);
start_test.putExtra("start_test", true);
start_test.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), start_test, PendingIntent.FLAG_CANCEL_CURRENT);
notification.setLatestEventInfo(this, getResources().getString(R.string.notification_title), getResources().getString(R.string.notification_content, expired), pi);
nm.notify(NOTIFICATION_ID, notification);
And on the other side:
boolean start_test=getIntent().getBooleanExtra("start_test", false);
The bundle is actually not there (getExtras() returns null).
I tried the different flags for PendingIntent.getActivity (FLAG_UPDATE_CURRENT, FLAG_CANCEL_CURRENT, FLAG_ONE_SHOT), none of those helped.
As shown in the code, I use getCurrentMillis to make sure the requestID changes....
Also found that in MyActivity, onCreate and onNewIntent are not getting called. only onResume is. Even though the FLAG_ACTIVITY_NEW_TASK is set... ?
Am I missing something very simple ?
Setting FLAG_ACTIVITY_NEW_TASK for the notification Intent will cause the following:
If the activity is not already running in a task, a new task will be started and the Intent will be delivered to the activity in onCreate()
However, if the activity is already running in a task, that task will be brought to the foreground. That's all. The Intent will not be delivered and onNewIntent() will not be called.
If you want the Intent to actually be delivered you need to specify:
start_test.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
It makes no difference whether the launchMode of the activity is singleTop or not, you still must specify Intent.FLAG_ACTIVITY_SINGLE_TOP in the Intent.
Note: If the activity is already running in a task and the activity is not on top of the activity stack in that task, the task will be brought to the foreground. That's all. The Intent will not be delivered. The only way to make this happen would be to add Intent.FLAG_ACTIVITY_CLEAR_TOP to the other 2 flags, but this may not be what you want to happen (depends on your specific scenario).
See my (still) open issue on Google code at http://code.google.com/p/android/issues/detail?id=17137
I just added the PendingIntent.FLAG_UPDATE_CURRENT flag to my pending intent, and it started working for me (skipped all flags for Intent).
Example Code:
PendingIntent pendIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
If not needed then avoid setting your activity in manifest as a single task. omit this line or change it to singleTop if you can:
android:launchMode="singleTaskā
If you must have a single task then in pending intent set flags to :
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
And PendingIntent.FLAG_CANCEL_CURRENT.
resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_CLEAR_TASK);
pIntent = PendingIntent.getActivity(context, 0, resultIntent, PendingIntent.FLAG_CANCEL_CURRENT );
If you have a single task - Reset your extras in your activity inside onNewIntent() by setIntent(intent);
protected void onNewIntent(Intent intent) {
setIntent(intent);
...
}
You can send extras in intent following way
PendingIntent contentIntent ;
Intent intent = new Intent(this,TestActivity.class);
intent.putExtra("extra","Test");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(ArticleDetailedActivity.class);
contentIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
To Get Intent extra value in Test Activity class you need to write following code :
Intent intent = getIntent();
String extra = intent.getStringExtra("extra") ;
Simpler approach: the real problem is that the Intent, being to the same target, gets replaced. I've solved this by just created a new service, a "NotificationDismissalService". The only intent going to that service is the setDeleteIntent item. Because it's a unique service, the parameters do not get replaced. The body of NotificationDismissalService (and it would really be one such dismissal service per unique intent type) is a simple implementation of "onStartCommand" that sends an intent to the preferred service (reading/writing the correct parameters). Because that's an actual send of a service, without a pendingintent in between, it works just fine.
When app is not running or is in background and user clicks on Notification in tray, you can get Intent extra and check the keys:
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("RC","MainActivity.onCreate() called");
if (getIntent().getExtras() != null){
Bundle extra = getIntent().getExtras();
Log.d("RC", "MainActivity.onCreate() : keys count = " + extra.keySet().size());
for ( String key : extra.keySet()) {
Log.d("RC","MainActivity.onCreate() : key = " + key + " = " + extra.getString(key));
}
}
}
This will show:
D/RC: MainActivity.onCreate() called
D/RC: MainActivity.onCreate() : keys count = 8
D/RC: MainActivity.onCreate() : key = google.delivered_priority = high
D/RC: MainActivity.onCreate() : key = google.sent_time = null
D/RC: MainActivity.onCreate() : key = google.ttl = null
D/RC: MainActivity.onCreate() : key = google.original_priority = high
D/RC: MainActivity.onCreate() : key = from = 631131412302
D/RC: MainActivity.onCreate() : key = google.message_id = 0:1627370460932539%952b1da9952b1da9
D/RC: MainActivity.onCreate() : key = gcm.n.analytics_data = null
D/RC: MainActivity.onCreate() : key = collapse_key = com.reflectcode.demo.cloudmessaging
I have used something like this
Intent myIntent = new Intent(context, DoSomething.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
context,
0,
myIntent,
Intent.FLAG_ACTIVITY_NEW_TASK);
Check out the full example here
How do i get the result of an activity started from NotificationManager?
In other words i need to get the resultCode from a PendingIntent.
public void test(Context context){
Notification notification = new Notification(null, "text", System.currentTimeMillis());
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.example.com"));
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
notification.setLatestEventInfo(context, "text", "text", pendingIntent);
notificationManager.notify(0, notification);
}
I want to be notified when the browser's activity ends.
OBS: This code is outside an activity, that's why it recieves the
context as parameter
Let's assume you have activity A that sets PendingIntent. This PendingIntent calls activity B. You want receive result of B.
You can do this by introducing proxy activity: A -> PendingIntent -> ProxyActivity --> startActivityForResult --> B. This way you'll receive result from B into your ProxyActivity activity.
Note that you should call startActivityForResult() in ProxyActivity.onCreate().
Let's see in simply way ,
IN Activity A :
intet.putStringExtra("from notification") ;
In Activity B:
if (getintent().getStringExtra("from notification") !=null ){
//TODO do what u want !
}