I wrote a simple Android App that show a custom Notification like this:
Context context = getApplicationContext();
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification( R.drawable.icon, title, System.currentTimeMillis());
Intent notificationIntent = new Intent( context, this.getClass());
notificationIntent.putExtra("com.mysecure.lastpage", "SECURECODE");
PendingIntent pendingIntent = PendingIntent.getActivity( context , 0, notificationIntent, 0);
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
notification.contentView = new RemoteViews(context.getPackageName(), R.layout.notifypbar);
notification.contentIntent = pendingIntent;
notification.contentView.setTextViewText(R.id.notifypb_status_text, text);
notification.contentView.setProgressBar(R.id.notifypb_status_progress, 100, (int)(100*progress), false);
manager.notify(104, notification);
This piece of code is called ONLY ONCE in my application and it displays a notification with a progress bar (all correctly).
Now, when a user clicks on this notification my application handles the onResume event.
public void onResume()
{
super.onResume();
// TODO: Extras è SEMPRE NULL!!! impossibile!
Intent callingintent = getIntent();
Bundle extras = callingintent.getExtras();
but extras is always NULL!
I've tried any combination of:
notificationIntent.putExtra("com.mysecure.lastpage", "SECURECODE");
or
Bundle extra = new Bundle();
extra.putString(key, value);
notificationIntent.putExtra(extra);
but getIntent().getExtras() returns always NULL.
This is the scenario:
The method getIntent() returns the FIRST intent than launch activity.
So, when the activity is CLOSED (terminated) and the user clicks on the notification, it will run a new instance of the activity and getIntent() works as expected (Extras is not null).
But if the activity is "sleeping" (it is in the background) and the user clicks on the notification, getIntent() always returns the very FIRST intent that started the activity and NOT the notification intent.
So to catch the notification intent while the application is running, simply use this
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
and then override onNewIntent(Intent newintent).
So when an application first runs, getIntent() can be used and when application is resumed from sleeping, onNewIntent works.
Just Write this code above your on top of your Resume() method. This is all it takes. This refreshes intent - I don't really know, but it works.
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
Problem: You are sending the same request code for your pending intens. Change this.
Solution: Set global variable int UNIQUE_INT_PER_CALL =0 and when you create pendingIntent call like below.
PendingIntent contentIntent = PendingIntent.getActivity(context, UNIQUE_INT_PER_CALL, notificationIntent, 0);
UNIQUE_INT_PER_CALL++; // to increment.
Since it seems your activity is already running, I think you need to specify FLAG_UPDATE_CURRENT, otherwise the getIntent() call will return the previous one. See this answer.
Look at Shared Preferences for passing and retrieving persistent key/value pairs.
Related
My MainActicity starts RefreshService with a Intent which has a boolean extra called isNextWeek.
My RefreshService makes a Notification which starts my MainActivity when the user clicks on it.
this looks like this:
Log.d("Refresh", "RefreshService got: isNextWeek: " + String.valueOf(isNextWeek));
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra(MainActivity.IS_NEXT_WEEK, isNextWeek);
Log.d("Refresh", "RefreshService put in Intent: isNextWeek: " + String.valueOf(notificationIntent.getBooleanExtra(MainActivity.IS_NEXT_WEEK,false)));
pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
builder = new NotificationCompat.Builder(this).setContentTitle("Title").setContentText("ContentText").setSmallIcon(R.drawable.ic_notification).setContentIntent(pendingIntent);
notification = builder.build();
// Hide the notification after its selected
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(NOTIFICATION_REFRESH, notification);
As you can see the notificationIntent should have the booleanextra IS_NEXT_WEEK with the value of isNextWeek which is put in the PendingIntent.
When I click now this Notification I always get false as value of isNextWeek
This is the way I get the value in the MainActivity:
isNextWeek = getIntent().getBooleanExtra(IS_NEXT_WEEK, false);
Log:
08-04 00:19:32.500 13367-13367/de.MayerhoferSimon.Vertretungsplan D/Refresh: MainActivity sent: isNextWeek: true
08-04 00:19:32.510 13367-13573/de.MayerhoferSimon.Vertretungsplan D/Refresh: RefreshService got: isNextWeek: true
08-04 00:19:32.510 13367-13573/de.MayerhoferSimon.Vertretungsplan D/Refresh: RefreshService put in Intent: isNextWeek: true
08-04 00:19:41.990 13367-13367/de.MayerhoferSimon.Vertretungsplan D/Refresh: MainActivity.onCreate got: isNextWeek: false
When I directly start the MainActivity with an Intent with the ìsNextValue` like this:
Intent i = new Intent(this, MainActivity.class);
i.putExtra(IS_NEXT_WEEK, isNextWeek);
finish();
startActivity(i);
everything works fine and I get true when isNextWeek is true.
What do I make wrong that there is always a false value?
UPDATE
this solves the problem:
https://stackoverflow.com/a/18049676/2180161
Quote:
My suspicion is that, since the only thing changing in the Intent is
the extras, the PendingIntent.getActivity(...) factory method is
simply re-using the old intent as an optimization.
In RefreshService, try:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);
See:
http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_CANCEL_CURRENT
UPDATE 2
See answer below why it is better to use PendingIntent.FLAG_UPDATE_CURRENT.
Using PendingIntent.FLAG_CANCEL_CURRENT not a good solution because of inefficient use of memory. Instead use PendingIntent.FLAG_UPDATE_CURRENT.
Use also Intent.FLAG_ACTIVITY_SINGLE_TOP (the activity will not be launched if it is already running at the top of the history stack).
Intent resultIntent = new Intent(this, FragmentPagerSupportActivity.class).
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
resultIntent.putExtra(FragmentPagerSupportActivity.PAGE_NUMBER_KEY, pageNumber);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
Then:
#Override
protected void onCreate(Bundle savedInstanceState) {
try {
super.onCreate(savedInstanceState);
int startPageNumber;
if ( savedInstanceState != null)
{
startPageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY);
//so on
It should work now.
If you still have not expected behaviour, try to implement void onNewIntent(Intent intent) event handler, that way you can access the new intent that was called for the activity (which is not the same as just calling getIntent(), this will always return the first Intent that launched your activity.
#Override
protected void onNewIntent(Intent intent) {
int startPageNumber;
if (intent != null) {
startPageNumber = intent.getExtras().getInt(PAGE_NUMBER_KEY);
} else {
startPageNumber = 0;
}
}
I think you need to update the Intent when you receive a new one by overriding onNewIntent(Intent) in your Activity. Add the following to your Activity:
#Override
public void onNewIntent(Intent newIntent) {
this.setIntent(newIntent);
// Now getIntent() returns the updated Intent
isNextWeek = getIntent().getBooleanExtra(IS_NEXT_WEEK, false);
}
Edit:
This is needed only if your Activity has already been started when the intent is received. If your activity is started (and not just resumed) by the intent, then the problem is elsewhere and my suggestion may not fix it.
Following code should work:-
int icon = R.drawable.icon;
String message = "hello";
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification(icon, message, when);
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.putExtra("isNexWeek", true);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, title, message, pIntent);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
In MainActivity onCreate:
if (getIntent().getExtras() != null && getIntent().getExtras().containsKey("isNextWeek")) {
boolean isNextWeek = getIntent().getExtras().getBoolean("isNextWeek");
}
So the actual reason is that the PendingIntent will cache the previous intent if the intents only differ in their extras. In my situation no combination of PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_CANCEL_CURRENT solves this as either the old intent will be replaced or the new one will stay the same as the initial one.
You need to ensure that Android cannot cache the Intents behind the PendingIntent. The solution for me is to make them differ in their data attribute.
so in the original posters code you would need to ensure that the data attribute is unique per each combination of extras that you are attaching.
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra(MainActivity.IS_NEXT_WEEK, isNextWeek);
notificationIntent.setData(Uri.parse("myapp://nextWeek/" + (isNextWeek ? "1" : "0"))
Alternatively you could probably also just add a uuid to the data uri (however, if you have lots and lots of notifications, it might be nice to cache them
I am using an Activitiy with various Tabs on it. From a different part of the application, Notifications are created to tell the user that something has changed. I now managed to Call the Activity, when the user clicks on the Notification. But how can i determine wheter a Activity is created the "normal" way during runtime or by clicking on the notification?
(Depending on the notification clicked, i want to forward to another tab instead of showing the main Tab.)
Intent intent = new Intent(ctx, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, intent, 0);
// TODO: Replace with .Build() for api >= 16
Notification noti = new Notification.Builder(ctx)
.setContentTitle("Notification"
.setContentText(this.getName())
.setSmallIcon(R.drawable.icon)
.setContentIntent(pendingIntent)
.setDefaults(
Notification.DEFAULT_SOUND
| Notification.DEFAULT_LIGHTS)
.setAutoCancel(true)
.getNotification();
NotificationManager notificationManager = (NotificationManager) ctx
.getSystemService(Context.NOTIFICATION_SERVICE);
// Hide the notification after its selected
notificationManager.notify(this.getId(), noti);
This successfully calls my MainActivity. But is there some Method that is called when the Activity is triggered by the pendingIntent?
Thought about to define something like this in the Main Activity:
onTriggeredByNotification(Notification noti){
//determinte tab, depending on Notification.
}
Pass a boolean value from notification and check for the same in the onCreate method of the activity.
Intent intent = new Intent(ctx, MainActivity.class);
intent.putExtra("fromNotification", true);
...
if (getIntent().getExtras() != null) {
Bundle b = getIntent().getExtras();
boolean cameFromNotification = b.getBoolean("fromNotification");
}
You can try this in your Notification
Intent intent=new Intent();
intent.setAction("Activity1");
In the Activity override onNewIntent() method and get action so you can determine the activity is called or not.
Better than using the reserved action field of your intent as specified by #ricintech, you could use an extra parameter in your pending intent and detect it in your onCreate method and in your onNewIntent metod inside your activity.
I have developed an application to download a video file and store it in the SD card. In the process I also update the progress and status of the download as a status bar notification using the NotificationManager.
My class called the DownloadTask.java extends the AsyncTask. So here I update the progress using the onProgressUpdate() method where in I use the NotificationManager for the purpose. Everything works like a charm except, on completion of download I want to click the notification to open the specific video file. So this is what i have done:
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
int icon = android.R.drawable.stat_sys_download_done;
long when = System.currentTimeMillis();
mNotification = new Notification(icon, "", when);
mContentTitle_complete = mContext.getString(R.string.download_complete);
notificationIntent = new Intent(mContext,OpenDownloadedVideo.class);
notificationIntent.putExtra("fileName", file);
mContentIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
mNotification.setLatestEventInfo(mContext, file, mContentTitle_complete, mContentIntent);
mNotification.flags = Notification.FLAG_AUTO_CANCEL;
mNotificationManager.notify(NOTIFICATION_ID, mNotification);
Note that the fileName and NOTIFICATION_ID are unique in my case.
The Activity OpenDownloadedVideo.java opens the file by:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
fileName = getIntent().getExtras().getString("fileName");
Intent i = new Intent(Intent.ACTION_VIEW);
File videoFileToPlay = new File(Environment.getExternalStorageDirectory()+"/MyFolder"+"/"+fileName);
i.setDataAndType(Uri.fromFile(videoFileToPlay), "video/*");
startActivity(i);
finish();
} catch(Exception e) {
//
}
}
So when I download a video for the first time and click on the notification the appropriate video file will be opened. However next time when I download another video, and click on the notification the first file which was downloaded will be opened again.
This is because getIntent inside OpenDownloadedVideo returns the first Intent created and not the latest. How can I correct this?
Also, please note that the problem scenario exists when I download more than one video, e.g. if I download five different video files and there are five notifications in the status bar. The same file will be opened each time a notification is clicked.
/**
* Override super.onNewIntent() so that calls to getIntent() will return the
* latest intent that was used to start this Activity rather than the first
* intent.
*/
#Override
public void onNewIntent(Intent intent){
super.onNewIntent(intent); // Propagate.
setIntent(intent); // Passing the new intent to setIntent() means this new intent will be the one returned whenever getIntent() is called.
}
actually you just need create PendingIntent with PendingIntent.FLAG_UPDATE_CURRENT ,like this:
mContentIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
From the Android Activity.onNewIntent() documentation
Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent.
So when you get the new Intent, you need to explicitly set it as the activity intent.
#Alex and #Codinguser, thank you for your replies. Much appreciated. However I found a different answer that worked for me. When creating a PendingIntent for the Intent pass a unique value to it. In my case I was doing this:
mContentIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
but now I'm using NOTIFICATION_ID since it was unique. I've changed the above call to:
mContentIntent = PendingIntent.getActivity(mContext, NOTIFICATION_ID,
notificationIntent, 0);
That's all and it works.
I found some information on it in the question
Mulitple Instances of Pending Intent
What if you make your OpenDownloadedVideo activity SINGLE_TOP and override onNewIntent?
in the activity you're intent is launching.
Add (override) the following function:
#Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
this.setIntent(intent);
}
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
My application receives a C2DM message and sends a status bad notification with the C2DM message. So far so good.
When the user clicks on the notification, an activity is called, passing the C2DM message as a variable.
Now, the first time it works smoothly, the second time the variable passed is not refreshed. It's always the first variable passed.
Am I missing something?
Here are the snipperts:
C2DM Notification
Intent notificationIntent = new Intent(context, BMBPad.class);
notificationIntent.putExtra("seqid", message);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
This is how I read the variable in the Activity called by the Intent.
extra = this.getIntent().getExtras();
seqidi = extra.getString("seqid");
Anyone any idea why that happens?
You need to use the flag PendingIntent.FLAG_UPDATE_CURRENT
In your case:
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Please also have a look here: Android PendingIntent
You can try to append this snippet in the Activity called by the Intent.
/**
* Override super.onNewIntent() to let getIntent() fetch the latest intent
* that was used to start this Activity rather than the first intent.
*/
#Override
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
setIntent(intent);
}
override onNewIntent() method, get your variable like this:
#Override
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
seqid = intent.getStringExtra("seqid","");
}
because start the activity again will trigger onNewIntent() method.