How to use Intent correctly - android

I'm using Intents to save data and recover them in others places of my application. I have used them in others places, but now, it's not working as I hope.
public class GCMIntentService extends GCMBaseIntentService {
public GCMIntentService() {
super(ConstantsGCM.GCM_SENDER_ID);
}
#Override
protected void onMessage(Context context, Intent intent) {
...
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager notManager = (NotificationManager) context.getSystemService(ns);
String room = intent.getExtras().getString(ConstantsGCM.GCM_ROOM);
Intent notIntent;
PendingIntent contIntent;
Notification notif;
notif = new Notification(icon, textStatus, time);
notIntent = new Intent(contexto,RoomsActivity2.class);
Bundle b2 = new Bundle();
b2.putString(ConstantsRooms.ROOM, room);
notIntent.putExtras(b2);
contIntent = PendingIntent.getActivity(contexto, 0, notIntent, 0);
notif.setLatestEventInfo(contexto, tittle, description, contIntent);
notif.flags |= Notification.FLAG_AUTO_CANCEL;
notManager.notify((int)(Math.random()*1000), notif);
This code is executed when a notification is coming. When I click this notification, it's executed the Activity RoomsActivities2.class. Here, I just call this code:
public String getMessageString(String cod){
String result = "";
bundle = getIntent().getExtras();
if (bundle != null){
result = bundle.getString(cod);
}
return result;
}
But, I didn't get the last data saved in the Intent. What's it wrong? I guess that I'm not using it correctly. Why can't I get the data from the activity?
What I think that it's happening it's:
The application gets a lot of notifications, the first one works right. But, if I keep getting more notifications, the data isn't override and I always get the first one although when I debug the code, I'm setting the another data.

OK, it's been a while since I've worked with pending intents, but I remember two things:
replace:
contIntent = PendingIntent.getActivity(contexto, 0, notIntent, 0);
with:
contIntent = PendingIntent.getActivity(contexto, 0, notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
That will keep the bundle around.
BUT that flag will override any existing pending intents with the newest one, you might not want that.
If you have multiple pending intents from the same context with the same intent (but different bundles!), you can use the 2nd parameter.
contIntent = PendingIntent.getActivity(contexto, requestCode, notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
as long as every pending intent has a unique requestCode, and even though the google dev's docs say the parameter isn't used, it actually does serve a purpose for identifying pending intent and allows duplicates with different bundles.

Related

Updating multiple local notifications with SQL

Hello currently I have setup a application which allows the user to set an alarm. When this alarms reaches it designated time it will then send a notification to the user. Now currently each time this notification is activated it sends a static message e.g. "You have a notification". I want to be able to change this notifications title to the alarms name which I have stored inside of SQLOPENHELPER when the user made the alarm.
I have also given each alarm a specific ID which is the current time in milliseconds and then also stored this inside of my SQL. Now I know you require a new ID for each notification but how would I do this and relate it back to my SQL?
I have included a small diagram just to clarify what I'm doing and my current alarm and broadcast receiver...
Diagram................
Set alarm.....
// id - relates to the custom id given to that alarm
// receiver - is our intent
pendingIntent = PendingIntent.getBroadcast(v.getContext(), id, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, myCalendar.getTimeInMillis(), pendingIntent);
// Intent array list is used to store multiple alarms
intentArrayList.add(pendingIntent);
Broadcast receiver...
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
Intent moveActivity = new Intent(context, AlarmActivity.class);
moveActivity.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Works with moveActivity to move the user back to main application.
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, moveActivity, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setContentIntent(pendingIntent)
.setContentTitle("title")
.setContentText("text")
.setSmallIcon(R.mipmap.ic_launcher)
.setAutoCancel(true);
notificationManager.notify(0, builder.build());
// Notification ID has been left at 0 because I don't know what to do as this point and after trying to figure it out for 2 days I thought I should ask.
//// UPDATE
Main Activity
I have implemented a put extra just under my intent Array List
receiver.putExtra("title", id);
Broadcast
Bundle bundle = intent.getExtras();
if (bundle != null) {
pass = bundle.getString("title");
//I have put my notification in this section
}
I tested it by putting "pass" which is the String for retrieving the put extra into the title of my notification and it comes up blank?
Update your receiver intent as below to pass the ALARM_ID:
Intent receiver = new Intent(getApplicationContext(), Receiver.class);
receiver.putExtra("KEY_ALARM_ID", ALARM_ID); // ALARM_ID = id
In PendingIntent.getBroadcast(), Use ALARM_ID as requestCode.
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, receiver, PendingIntent.FLAG_UPDATE_CURRENT );
Always use different requestCode for different notification to get
correct result.
In notify() method, use ALARM_ID as id:
notificationManager.notify(ALARM_ID, builder.build());
Notification id should be unique within your application. If a
notification with the same id has already been posted by your
application and has not yet been canceled, it will be replaced by the
updated information.
In your BroadcastReceiver class onReceive() method get the ALARM_ID:
#Override
public void onReceive(Context context, Intent intent)
{
int alarmId= intent.getExtras().getInt("KEY_ALARM_ID");
// Do something with alarmId
}
Hope this will solve your problem.
// Main activity
receiver.putExtra("title", id);
pendingIntent = PendingIntent.getBroadcast(v.getContext(), id, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, myCalendar.getTimeInMillis(), pendingIntent);
intentArrayList.add(pendingIntent);
Broadcast
Bundle bundle = intent.getExtras();
int pass = bundle.getInt("title" , 1);
Thanks psKink you gave me the kick I needed

PendingIntent does not send Intent extras

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

Can not initiate activity from GCMIntentService.class

I have a GCMIntentService class, in which i get some messages returned from my server. I want to be able to start a certain activity when a certain message arrives at my app. For example if in my onMessage() method (onMethod() is the method, and the first place, in the app, that receives the messages from the server) arrives the string = "tomatoe", i want to start a specific activity. The way i know right now to start an activity, is this:
Intent resactivity = new Intent(getApplicationContext(), ResponseActivity.class);
resactivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(resactivity);
The problem is that GCMIntentService, is not a class that extends activity, and i believe that i can't use this code for this purpose. Is there some way to initiate an activity from a class that is not an activity, by creating an intent inside that class?
The problem is that GCMIntentService, is not a class that extends activity, and i believe that i can't use this code for this purpose.
GCMIntentService inherits from Context, which is where startActivity() is defined.
Bear in mind, though, that your users may attack you with pitchforks or machine guns for popping up activities at random points in time, perhaps in the middle of something else that they are doing. Please make this behavior configurable, or else make very very certain that your users will appreciate these interruptions.
I post the method I usually use to start an activity when a notification arrives in my apps. Take a look to all the configurations and remove those you are not interested in:
#Override
protected void onMessage(Context context, Intent intent) {
String app_name = context.getString(R.string.app_name);
String message = intent.getStringExtra("payload");
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager notificationManager = (NotificationManager) context.getSystemService(ns);
int icono = R.drawable.ic_stat_notify;
long time = System.currentTimeMillis();
Notification notification = new Notification(icono, app_name, time);
notification.defaults |= Notification.DEFAULT_SOUND;
Intent notificationIntent = new Intent(context, ResponseActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.when = System.currentTimeMillis();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.setLatestEventInfo(context, app_name, message, pendingIntent);
notificationManager.notify(0, notification);
}
You should be able to do it as such:
getApplication().startActivity(resactivity);

Android getIntent() returns the first intent

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);
}

Sending different parameters to pendingIntent through notification

My target: download files in parallel, and when file downloads complete, I get notifications.
Those notifications are suppose to launch an activity when you click on it which gets unique parameters through putExtra.
The problem is that I can't have different values in each launch of that activity.
Each time the activity, which is being launched through the notification bar, launches it destroys the extra of the ones which haven't been launched yet (the ones that still appear on the notification bar).
How do I keep all my notifications with their own parameters?
Here is my code:
if (messagesManager == null)
{
messagesManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
notification = new Notification(R.drawable.icon, message, System.currentTimeMillis());
// for launch activity
Intent intent = new Intent(context, DialogActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("fileName", fileName); //- this is where i put my extra's!!
intent.putExtra("onSdcard", onSdcard);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, "DownloadManager", message, contentIntent);
notification.flags = notification.FLAG_AUTO_CANCEL;
int noticeId = generateNotificationId(requestId);
messagesManager.notify(noticeId, notification);
Now this is the dialog activity:
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Intent i = getIntent();
boolean onSdcard = i.getBooleanExtra("onSdcard", true);
String fileName = i.getStringExtra("fileName");
...
}
I tried to use this technique as it was written in another post here, but it didn't work.
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Please check the linked duplicate,
you need two things
you need to add an action string
the action string must be unique, probably a timestamp added to it would be good

Categories

Resources