Clarification on the docs for PendingIntent.FLAG_CANCEL_CURRENT - android

From the documentation of Pending Intent FLAG_CANCEL_CURRENT in Android:
by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it. If this assurance is not an issue, consider FLAG_UPDATE_CURRENT
Can anyone explain what this line means?

Once you create a new PendingIntent with FLAG_CANCEL_CURRENT, anything holding a previous PendingIntent for the same Intent will no longer be able to execute that original PendingIntent.
For example, suppose we have this:
Intent i=new Intent(this, Foo.class);
i.putExtra("key", 1);
PendingIntent pi=PendingIntent.getActivity(this, 0, i, 0);
and we use that PendingIntent with, say, a Notification.
Later on, we execute:
Intent i=new Intent(this, Foo.class);
i.putExtra("key", 2);
PendingIntent pi2=PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);
At this point, the PendingIntent created originally (pi) is no longer valid, and whatever we use pi2 for will see the updated extra value (2).
If, instead, we did:
Intent i=new Intent(this, Foo.class);
i.putExtra("key", 2);
PendingIntent pi2=PendingIntent.getActivity(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
At this point, pi and pi2 both represent the same PendingIntent, and both will see the updated extra value (2).
Or, if we did:
Intent i=new Intent(this, Foo.class);
i.putExtra("key", 2);
PendingIntent pi2=PendingIntent.getActivity(this, 0, i, 0);
At this point, pi and pi2 still represent the same PendingIntent, but the extras are unchanged, as getActivity() returns the original PendingIntent without applying the new extras.
Most times, FLAG_UPDATE_CURRENT is a fine answer when you are trying to replace extras inside a PendingIntent.

Related

Intent extras are lost with a broadcast PendingIntent and AlarmManager.setAlarmClock()

I create a PendingIntent like this:
Intent intent = new Intent(context, AlarmReceiver.class);
intent.setAction("Foobar");
intent.putExtra(EXTRA_ALARM, alarm);
intent.putExtra(EXTRA_TRYTWO, tryTwo);
intent.putExtra(EXTRA_BEGAN_TIME, beganTime);
return PendingIntent.getBroadcast(
context, (int) alarm.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT
);
The alarm variable is a Parcelable. I schedule the PendingIntent like this:
PendingIntent alarmModifyPendingIntent = PendingIntent.getActivity(
context, 0, editIntent, PendingIntent.FLAG_CANCEL_CURRENT
);
am.setAlarmClock(
new AlarmManager.AlarmClockInfo(time, alarmModifyPendingIntent), pendingIntent
);
Where the variable pendingIntent is created as shown above.
The AlarmReceiver object receives an Intent in onReceive at the correct time. However, this Intent does not contain the extras that I have set. For instance intent.getParcelableExtra(EXTRA_ALARM) returns null.
This problem occurs with Android 7.0 (API level 24) at least, using an LG G5.
Using FLAG_CANCEL_CURRENT or FLAG_ONE_SHOT does not work either.
The alarm variable is a Parcelable.
It is not safe to put a custom Parcelable in an Intent that is delivered to another process. This is particularly true with AlarmManager on Android 7.0.
You need to replace that Parcelable with something else, such as a byte[], where you manually convert your Parcelable to/from that byte[].

Android Pending Intent to start service

So I have a service, in the onCreate() I setup 3 pending intents to start the same service, each having different extra data to specify the action. I'm creating a notification, and I want to use one pending intent as the click action, one as the dismiss action, and the third is for an alarm.
Intent iCancel = new Intent(this, BootService.class);
Intent iAlarm = new Intent(this, BootService.class);
Intent iDismiss = new Intent(this, BootService.class);
// each will have actions
iAlarm.putExtra(INTENT_ACTION, INTENT_ALARM_FIRED);
iCancel.putExtra(INTENT_ACTION, INTENT_CANCEL);
iDismiss.putExtra(INTENT_ACTION, INTENT_DISMISS);
PendingIntent piCancel = PendingIntent.getService(
this,
0,
iCancel,
Intent.FILL_IN_DATA);
mPiAlarm = PendingIntent.getService(this, 0, iAlarm, Intent.FILL_IN_DATA);
PendingIntent piDismiss = PendingIntent.getService(this, 0, iDismiss, Intent.FILL_IN_DATA);
mNotifyBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_about)
.setContentTitle(getString(R.string.app_name))
.setContentIntent(piCancel)
.setDeleteIntent(piDismiss);
The problem is all pending intents seem that have the same intent extra data, so when onStartCommand is launched no matter whether the notification was clicked or dismissed or neither, constant INTENT_CANCEL is received from intent.getIntExtra(INTENT_ACTION)
I believe it has something to do with the flags used in PendingIntent.getService(), i'm confused about which to use. I've tried using PendingIntent.FLAG_CANCEL_CURRENT, and UPDATE_CURRENT, neither seem to fix the issue but the result is different, I receive constant INTENT_ALARM_FIRED for every action.
How can I get each pending intent to have its own intent extra data?
Solution
I discovered an exact warning about this scenario right in the PendingIntent doc. http://developer.android.com/reference/android/app/PendingIntent.html
just changed my request codes and it works
This has to do with Intents being considered the same. See PendingIntent for more information.
One way to get around this would be to just vary the requestCode for each Intent. (The 2nd parameter in the getService call)

Working example of Recognizer PendingIntent flow

I have the following code setup to launch the voice recognizer with a pendingintent to launch another activity:
Intent voiceActivityIntent = new Intent (MainActivity.this, VoiceActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity (MainActivity.this, 0,
voiceActivityIntent, PendingIntent.FLAG_ONE_SHOT);
Intent intent = new Intent (RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
.putExtra (RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
.putExtra (RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pendingIntent);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
The documentation states that startActivity can't be used with RecognizerIntent.ACTION_RECOGNIZE_SPEECH, and using startActivityForResult simply returns the result to the current activity (MainActivity) which is not desired.
I've tried:
pendingIntent.send ();
but this simply take me to VoiceActivity.class without executing the recognizer.
I'm currently testing on the Android Wear Round API 21 emulator.
just add the following to your code:
// this intent wraps voice recognition intent
PendingIntent pendingIntVoice = PendingIntent.getActivity(context, 0, intent, 0);
pendingIntVoice.send();
In other words, with pendingIntent.send (); you're not calling the Voice Recognizer Intent, BUT the Intent that should be automatically called when voice recognition ends (infact you set it with putExtra (RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pendingIntent) line)
I wanted something similar, here's what I came up with and it works. This is inside a Service:
//IntentHandlerService extends IntentService
//The intent I want sent back to me after voice recognition is complete...
Intent inputTextIntent = new Intent(this, IntentHandlerService.class);
//...gets wrapped in this PendingIntent...
PendingIntent pendingIntent = PendingIntent.getService(this, 0, inputTextIntent, PendingIntent.FLAG_ONE_SHOT);
//...and added to the intent aimed for the recognizer...
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pendingIntent);
//...which is the one you start.
startActivity(intent);

Passing a value with PendingIntent

I am using PendingIntent. I need to pass a value. Intent uses putExtra. Is there an equivalent for PendingIntent? If yes, please provide sample example. Thanks in advance.
I am using:
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0);
Just put the extras in the original intent, i.e.
Intent i = new Intent(context, MainActivity.class);
i.putExtra("key1", "the answer");
i.putExtra("key2", 42);
...
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, i, 0);
The "inner" intent is the one your Activity will actually receive. Check the documentation for PendingIntent.getActivity().
Then, in MainActivity.onCreate():
Intent intent = getIntent();
String strValue = intent.getStringExtra("key1");
int intValue = intent.getIntExtra("key2");
...

Android: can I put 2 Pendingintents on the same view?

I have an App Widget.
In the App Widget I try to set 2 Pendingintests on the same Viev:
//FIRST PENDINGINTENT
Intent i1 = new Intent(getApplicationContext(), AppWidget.class);
i1.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
i1.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pi = PendingIntent.getBroadcast(
getApplicationContext(), 0, i1,
PendingIntent.FLAG_UPDATE_CURRENT);
//SECONDPENDINGINTENT
Intent i11 = new Intent(getApplicationContext(), WakeUp.class);
PendingIntent pi1 = PendingIntent.getActivity(
getApplicationContext(), 0, i11,0);
//I SET THE PENDINGINTENT ON THE VIEW
updateViews.setOnClickPendingIntent(R.id.background, pi1);
updateViews.setOnClickPendingIntent(R.id.background, pi);
As you can see I set 2 Pendingintents (pi and pi1) on the SAME view R.id.background.
The Pendingintent pi works as it shuould.
The Pendingintent pi1 has NO EFFECT.
Please any help very much appreciated
This is not possible. Any View in the RemoteViews can have only one PendingIntent for setOnClickPendingIntent(). If you call setOnClickPendingIntent() twice, the last one in wins.
Hence, please call it just once, and have WakeUp call sendBroadcast() to accomplish your second operation.
Also, please replace getApplicationContext() with this, as you do not need the application context in any of this code.

Categories

Resources