Is the application context always the same no matter the class? - android

I'm new in this whole Android environment and I usually have some doubts that maybe you can consider very basic knowledge and a bit stupid. I will try to do my best explaining the doubt I have and why i have it to make me understand.
I'm doing an application where you can set notifications to remind you the scholar classes you want. I have done a class that extends BroadcastReceiver so it can reset all the alarms after the device has booted. I have a database where I keep information about the alarms: the class, the time it has to be configured, etc. I retrieve all the alarms and set them to the alarmManager this way:
intent = new Intent(ctxt.getApplicationContext(), Notificacion.class);
intent.putExtra("TAG", tag);
intent.putExtra("SUBJECT", cursor2.getString(0));
intent.putExtra("AULA", cursor2.getString(1));
displayIntent = PendingIntent.getBroadcast(ctxt, Integer.parseInt(tag), intent, PendingIntent.FLAG_UPDATE_CURRENT );
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY*7, displayIntent);
Well, I guess this should work fine until here. The problem is that when you use the app and you want to set a notification, you are doing it from the class "Schedule.class" so the intent would have this context:
Intent intent = new Intent(getApplicationContext(), Notification.class);
PendingIntent pend = PendingIntent.getBroadcast(this, Integer.parseInt(tag), intent, PendingIntent.FLAG_UPDATE_CURRENT);
In the app, you can delete an alarm, and you have to call alarmManager.cancel(pend) in order to do that. So my doubt is if it will be able to cancel it.
If the contexts are different, it won't find the match with the pending intent, because it was set from the context I got in my extension of BroadCastReceiver (ctxt), and the alarm was set with the context I got from Schedule.class.
So.. is the application context always the same? I know that the context is set in order to give information to other classes about what has been going on, but I'm not sure if the Intent filter will differentiate where the context was given.
Thank you in advance!

Looking at the AlarmManager documentation for the cancel method you're using:
public void cancel (PendingIntent operation)
Added in API level 1
Remove any alarms with a matching Intent. Any
alarm, of any type, whose Intent matches this one (as defined by
filterEquals(Intent)), will be canceled.
So, the Intent.filterEquals documentation says the following:
public boolean filterEquals (Intent other)
Added in API level 1
Determine if two intents are the same for the
purposes of intent resolution (filtering). That is, if their action,
data, type, class, and categories are the same. This does not compare
any extra data included in the intents.
I can't think of any reason why the action, data, type, class, or category would be different from one explicit Intent to another (unless, obviously you went out of your way to change those things). The contexts do not appear to be in the criteria for the matching, so I think you can be fairly confident that it will be cancelled no matter which context was used to create it in the first place.

Related

Is it bad practice to store PendingIntent's in an object for later retrieval to cancel an Alarm?

I'm making an alarm clock app using PendingIntent and AlarmManager. I know in order to cancel a PendingIntent, you need to recreate it exactly. In my app, much like many other alarm clock apps, there is a list of alarms with a switch to toggle each alarm on/off. When toggled off, I currently recreate the PendingIntent and cancel it using the following:
Intent intent = new Intent(context, MyBroadcastReceiver.class);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, id, intent, 0);
alarmIntent.cancel();
The first 3 lines of the code above I believe are unnecessary. When I toggle the alarm, I have access to the custom alarm object which contains the id along with many other details of the alarm. When I'm creating a new alarm, if I store the newly created PendingIntent in the alarm object, I can have access to the PendingIntent used to create the alarm, and just retrieve it and cancel it in 1 line, like this:
this.getPendingIntent().cancel();
I wouldn't have to recreate the intent, get the id, or recreate the PendingIntent from the code shown earlier. This would ultimately save time and resources (not much but it's good practice), so I have a couple questions:
1) Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it? It seems like a straightforward answer but I haven't seen anyone do this before.
2) Is there an advantage to recreating the PendingIntent that I'm not aware of?
Thanks!
Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it?
Assuming that the underlying Intent doesn't have some massive payload (e.g., Bitmap in an extra), you should be OK.
It seems like a straightforward answer but I haven't seen anyone do this before.
https://github.com/commonsguy/cw-omnibus/tree/v8.7/AlarmManager/Simple, though it's a trivial example.
Is there an advantage to recreating the PendingIntent that I'm not aware of?
It works for cases where you do not have the PendingIntent. Your process does not live forever. If you want to use a cached PendingIntent as an optimization, that's fine, but you need to be in position to create the PendingIntent if it is needed.

Keep both pendingIntents with the same requestCode

I am developing an alarm application in Android. The flow is really simple, Im just creating a PendingIntent and then I call the setExact() method in the AlarmManager much like below.
Intent myIntent = new Intent(context, BroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + difference, pendingIntent);
After some tests I realized that with the above snippet I can set only one alarm because I set the requestCode of the pendingIntent to zero, and if I set another alarm with the requestCode set to 0 then it will overwrite the previous one. Is there a way to fix this without changing the requestCode? I was thinking maybe the flags can help me but I didn't find a flag that doesn't overwrite the previous pendingIntent.
I know that the obvious solution is to change 0 to another int and then keep track of all my ints, picking one that is not used. That solution would be fine if I was just starting the project, however I am already in the middle and I use as request codes predefined Enums. It is very difficult to change this mechanic and keep track of individual ints thats why I am asking if there is a way of not overwriting a pendingIntent when a new one with the same requestCode is registered. Thank you in advance.
You can make the Intents unique be setting a different ACTION on each one. Then you could still use the same requestCode and would have different PendingIntents.
You'll need to keep track of the ACTIONs you use if you want to be able to cancel the alarms later.
I am asking if there exists a certain type of flag that will be able to differentiate them
There is not, and it makes sense because this is already the purpose of the requestCode parameter.
For information, these are your options regarding flags:
You will have to change your mechanism to make it possible to have different requestCodes for Pending Intents. It may be a lot of work, but it is what you have to do.

How does android compare pending intents

Documentation for PendingIntent.FLAG_NO_CREATE reads:
Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
My question: What criteria are used to compare PendingIntents?
I'm guessing under the hood this flag uses PendingIntent.equals, but I'm not really sure what criteria that function is using. Is it using the action, requestCode, categories, extras (I'm guessing no), etc.?
Context:
I want to start an alarm with a pending intent if my alarm is not already setup. Specifically, I'm following this answer.
Intent i = new Intent(applicationContext, MyService.class);
i.setAction("myAction");
PendingIntent pi = PendingIntent.getService(applicationContext, /*requestCode*/0, i, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
AlarmManager alarmMgr = (AlarmManager)applicationContext.getSystemService(Context.AlarmService);
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.INTERVAL_HOUR, AlarmManager.INTERVAL_HOUR, pi);
}
To determine if 2 PendingIntents match, the following must be equal:
The requestCode parameter used when the PendingIntent was created
The Intent ACTION
The Intent CATEGORIES
The Intent DATA
The Intent MIMETYPE
The Intent PACKAGE
The Intent COMPONENT
Extras are not taken into consideration.
You can read more in the PendingIntent summary documentation and Intent.filterEquals().
I'm guessing under the hood this flag uses PendingIntent.equals, but
I'm not really sure what criteria that function is using. Is it using
the action, requestCode, categories, extras (I'm guessing no), etc.?
Actually the hint is in class description:
A description of an Intent and target action to perform with it.
Instances of this class are created with
getActivity(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
getService(android.content.Context,int,android.content.Intent,int);
the returned object can be handed to other applications so that they
can perform the action you described on your behalf at a later time.
By giving a PendingIntent to another application, you are granting it
the right to perform the operation you have specified as if the other
application was yourself (with the same permissions and identity). As
such, you should be careful about how you build the PendingIntent:
almost always, for example, the base Intent you supply should have the
component name explicitly set to one of your own components, to ensure
it is ultimately sent there and nowhere else.
A PendingIntent itself
is simply a reference to a token maintained by the system describing
the original data used to retrieve it. This means that, even if its
owning application's process is killed, the PendingIntent itself will
remain usable from other processes that have been given it. If the
creating application later re-retrieves the same kind of PendingIntent
(same operation, same Intent action, data, categories, and components,
and same flags), it will receive a PendingIntent representing the same
token if that is still valid, and can thus call cancel() to remove it.
Because of this behavior, it is important to know when two Intents are
considered to be the same for purposes of retrieving a PendingIntent.
A common mistake people make is to create multiple PendingIntent
objects with Intents that only vary in their "extra" contents,
expecting to get a different PendingIntent each time. This does not
happen. The parts of the Intent that are used for matching are the
same ones defined by Intent.filterEquals. If you use two Intent
objects that are equivalent as per Intent.filterEquals, then you will
get the same PendingIntent for both of them.
There are two typical ways to deal with this.
If you truly need multiple distinct
PendingIntent objects active at the same time (such as to use as two
notifications that are both shown at the same time), then you will
need to ensure there is something that is different about them to
associate them with different PendingIntents. This may be any of the
Intent attributes considered by Intent.filterEquals, or different
request code integers supplied to
getActivity(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
or getService(android.content.Context,int,android.content.Intent,int).
If you only need one PendingIntent active at a time for any of the
Intents you will use, then you can alternatively use the flags
FLAG_CANCEL_CURRENT or FLAG_UPDATE_CURRENT to either cancel or modify
whatever current PendingIntent is associated with the Intent you are
supplying.
from:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/app/PendingIntent.java#PendingIntent

android intents alarmmanager

I am trying to create intents that will be set using alarmmanager. Currently, I can do this with one intent, add extra data to it (strings, but i send them as one string with a seperator), and everything works fine and goes off at the correct time. However, when I try to send multiple intents like this, they are overwritten and only one goes off at the correct time. How can i structure my intents so that they appear different to the alarmmanager (i think they are getting deleted when filterIntent() is run).
long story short- putExtra() makes all the intents look the same still... how can i make them look different so they wont get deleted (and keep track of them in case i want to delete a specific one)
This is how I scheduled updates for my widgets. Each one allowed to be unique because they have a unique widget id number.
Intent widgetUpdate = new Intent();
widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId });
widgetUpdate.putExtra(EXTRA_POSITION,0);
widgetUpdate.putExtra(EXTRA_URL, URL);
// make this pending intent unique by adding a scheme to it
widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(URI_SCHEME + "://widget/id/"), String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(context, 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT);
// schedule the updating
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), updateRateSeconds * 1000, newPending);
According to the docs, AlarmManager checks if two Intents are equivalent using filterEquals(). Check out the docs for filterEquals() to see how it decides whether two Intents are equivalent. Also, chris324's solution is a pretty good one.

Passing arguments to a BroadcastReceiver through an Intent in Android

I have an application, which sets an alarm using AlarmManager, which starts another Activity when it goes off. The AlarmManager takes a PendingIntent and spawns a BroadcastReceiver class when the specified time comes. I'm wondering whether there is any way that I can pass arguments to this BroadcastReceiver through the Intent object which goes into PendingIntent?
Basically what I'd like to do is something like this:
Intent my_intent = new Intent(this, BroadcastService.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, my_intent, 0);
my_intent.putExtra("arg1", arg1);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (1000), pendingIntent);
and I'd like to be able to retrieve arg1 in the BroadcastReceiver's onReceive(Context, Intent) method. I figured that the local variable my_intent would be the second parameter passed on to onReceive by the PendingIntent, but apparently that's not quite right. Is it possible to pass parameters between an Activity and a BroadcastReceiver in this fashion (using Intent.putExtra()) or should I use a ContentProvider instead?
Thanks!
Iva
I had a similar problem, but I was already populating the Intent first before wrapping it in a PendingIntent. But the answer to my problem was, as pointed out above, that I needed to use the PendingIntent.FLAG_UPDATE_CURRENT flag. Once I set the flag, it worked! I hope this helps others.
-Jeff
int code=1;
Intent i2 = new Intent(StartAlarm);
i2.putExtra("_id",code);
class test extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent arg1) {
int i=arg1.getIntExtra("_id",-1);
}
}
I have an application, which sets an
alarm using AlarmManager, which starts
another Activity when it goes off.
That is bad form. Do not pop up activities unannounced like this without a very good reason (e.g., an incoming phone call). What if the user is in the middle of doing something, like TXTing or playing a game or trying to tap numbers for a phone menu?
Is it possible to pass parameters
between an Activity and a
BroadcastReceiver in this fashion
(using Intent.putExtra())
Yes. However, bear in mind that you will want to use PendingIntent.FLAG_UPDATE_CURRENT when you create your PendingIntent, to ensure that any new extras you supply on the Intent actually get used.
Yes, I think it is possible to pass any data of basic Java type and Serializable/Parceable types in the extras of an Intent wrapped around a PendingIntent and then retrieve them using the Intent instance passed to the onReceive of the Broadcastreceiver. Your approach looks okay to me.
What is the problem/error that you are getting? Is "arg1" instance Serializable?

Categories

Resources