My app creates many widgets of the same type, but with different content. When the user clicks on one widget, it is important to know the widgetId, so I know the content to update.
My way seems to be totaly wrong, because my PendingIntent gets overridden. So I always get the same EXTRA_APPWIDGET_ID instead of n different ones.
Here is my code, the part when I set the PendingIntent for my OnClick.
// edit intent
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.base_info_widget);
Intent editIntent = new Intent(context,MainActivity.class);
editIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_CLEAR_TOP);
editIntent.putExtra(GlobalVariables.COMMAND_KEY_ACTION,GlobalVariables.COMMAND_VALUE_EDIT);
editIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,appWidgetId);
Log.d(MY_TAG, "putExtra EXTRA_APPWIDGET_ID="+ Integer.toString(appWidgetId));
PendingIntent pendingEditIntent = PendingIntent.getActivity(context, 0, editIntent, PendingIntent.FLAG_CANCEL_CURRENT);
views.setOnClickPendingIntent(R.id.layout_wrapper, pendingEditIntent);
I hope my question was clear. How can I solve that problem?
Make your intent unique:
editIntent.setData(Uri.parse(editIntent.toUri(Intent.URI_INTENT_SCHEME)));
That's why you get always same id on all widgets
Related
In my project i have tried out some possibilities and limitations of PendingIntent
So i have created some notification, that opens an activity by tapping
Intent intent = new Intent(this, NotificationActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra(Constants.INTENT_ITEM_NAME, "ItemName");
intent.putExtra(Constants.INTENT_ITEM_ID, itemId);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
// properties set
;
notificationManager.notify(new Random().nextInt(10), notificationBuilder.build());
at begin was everithing good, until i have placed other item to show in NotificationActivity by tap on notification.
Here starts problems: in NotificationActivity was still showed previous item.
After some search it could be solved with:
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
BUT: if i have in same time many notifications, then i always see only last item, PendingIntent was created for.
Second parameter of getActivity is requestCode. So when i update solution to
PendingIntent pendingIntent = PendingIntent.getActivity(this, new Random().nextInt(1000), intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then also many items are supported, and if for some reason the random makes me same numbers, the content in NotificationActivity will be the same.
So to 99% the solution is good, yes? NO
Again to first approach
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
If i set new extras for intent -> old extras will be readed.
In other words: by new Random().nextInt(1000) somewhere in the system of android device will be stored till 1000 PendingIntents for one of activities i have forever
So here are the questions:
Where all this PendingIntents stored?
How to clear it?
With How to clear it, i mean a way to remove all currently maked PendingIntents with some Random as requestCode, so that in clear way with only one notification at time i could use
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
with different items
PendingIntents are stored in non-persistent storage. When the device restarts, they are all gone.
Also, if you create a PendingIntent and put it in a Notification, once the Notification is gone (dismissed, opened, etc.) the PendingIntent is no longer in use and will be deleted.
Generally you should not use random numbers for the requestCode as this is no guarantee of uniqueness. You need to find a way to make your PendingIntent unique if you want to have many of them existing in parallel.
I've got some weird behavior and I can only assume is because of the Pending intents I am using.
Scenario
I have a widget (4x1) which has 4 buttons. Within onUpdate of the widget, I add an pending intent for each button. My intents fires a Service with a bundeled parameter and depending on this parameter starts something. I attach intents as this:
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
Bundle b = new Bundle();
b.putString("myVariable", someVariable);
Intent intent = new Intent(context, AppStarterService.class);
intent.putExtras(b);
PendingIntent pendingIntent = PendingIntent.getService(context, buttopnPosition, intent, 0);
views.setOnClickPendingIntent(R.id.btnOne, pendingIntent);
The problem
The code works just fine, until the user decides to update the content of the button. Then, a new Pending Intent is done. So, when I press again the button, sometimes it still executes the old intent and not the new one. I don't know how to explain this better. Let's say for my first intent the parameter is "TestOne", after my update, the new intent has parameter "TestX". When the user clicks on the button, on my service I get in intent extras still "TestOne" instead "TestX". So, my guess is that somehow, I need to cancel the previous intent, when the widget button content changes.
Is this the issue ? Am I doing something wrong ? How do I cancel the old intent, I need to recreate it and then cancel it ?
Thank you for your time.
I you keep having this problem even with FLAG_UPDATE_CURRENT, try defining a different requestCode each time, with something like this:
private static int request = 0;
...
PendingIntent pendingIntent = PendingIntent.getService(context, request++, intent, 0);
So each time a new PendingIntent is created, a new requestCode is used, at least during class life.
I hope it helps.
I think you want to set the flag http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_UPDATE_CURRENT as the last parameter to PendingIntent.getService
How do I reopen the configuration activity for a widget after it has been added to the homescreen?
The following code from a Google search does not work because the widget id in the extra does not carry through to the activity:
String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
Intent configIntent = new Intent(context, Configuration.class);
configIntent.setAction(ACTION_WIDGET_CONFIGURE);
configIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
views.setOnClickPendingIntent(R.id.editButton, configPendingIntent);
OK, I'm a year too late, but I was just looking for the same answer as you and noticed a missing flag in your code, which was the answer to a different problem I had a couple of days ago concerning pending intents, extras and widgets. Try changing this line of your code:
PendingIntent configPendingIntent
= PendingIntent.getActivity(context, 0, configIntent, 0);
to this:
PendingIntent configPendingIntent
= PendingIntent.getActivity(context, 0, configIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent will reuse existing cached intents that match new ones and the "match" does not consider extras, so new extras can be ignored. Adding FLAG_UPDATE_CURRENT makes PendingIntent write the extras into the cached intent (or something like that).
Anyway, the above worked for me just now (though I also used AppWidgetManager.ACTION_APPWIDGET_CONFIGURE as the action name, if that makes any difference).
Unfortunately you don't: the widget spec as it currently exists does not support reconfiguration of widgets through the widget configuration activity.
What you can do is launch your widget configuration activity and track the specific widget ID manually: once the new settings are in place you can then update the widget manually using the appropiate manager method. You won't be able to use the same code path as a new widget does, however.
I have a WidgetProvider and an Configure Activity
When the Widget is started it starts with the configure activity and I set it up by making a custom call to the widgetprovider
(which you will notice is from the sdk tutorial examples)
// Push widget update to surface with newly set prefix
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
AwarenessWidget.updateAppWidget(context, appWidgetManager,
mAppWidgetId, position);
// Make sure we pass back the original appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
I pass the Widget ID to the function.... inside the widget I create a Intent like this:
Intent configIntent = new Intent(context, Configure.class);
configIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent pendingIntent = PendingIntent.getActivity
(context, 0, configIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.MainImage,pendingIntent);
views.setImageViewResource(R.id.MainImage, lv_images[version]);
appWidgetManager.updateAppWidget(appWidgetId, views);
I am always referencing the widget ID and even add it as a extra on the intent
but when I get two of these widgets on the home screen the widget ID is always referencing the last placed widget ID
I had a similar problem. Just add this to your config activity, where you set your PendingIntent:
Uri data = Uri.withAppendedPath(
Uri.parse(URI_SCHEME + "://widget/id/")
,String.valueOf(appWidgetId));
intent.setData(data);
The variable URI_SCHEME is a String, and can be whatever you'd like.. ie - "ABCD" This causes each widget to have a unique PendingIntent.
Here is a more in-depth explanation of why your code doesn't work and how to fix it. From the Android SDK Documentation:
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.
Note that specifying differing "extra" contents isn't enough for the PendingIntents to be considered unique, but setting a unique URI with setData is. That is why Snailer's URI solution "magically" fixes the problem.
The documentation also offers a different (arguably simpler) solution to the problem. Instead of creating a custom URI just set a unique requestCode when you call getActivity:
PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Source: http://developer.android.com/reference/android/app/PendingIntent.html
In my testing, using the setData(...) on the PendingIntent doesn't fix the issue on a Verizon Thunderbolt running Android 4.0.4. It works on my other test devices and emulator.
I tested the use of the requestCode instead, and it works in all cases. I just set the requestCode to be the widget ID:
pendingIntent = PendingIntent.getService(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
I have a alarm thing going on in my app and it launches a notification that then when pressed launched an activity.
The problem is that when I create more than one alarm then the activity launched from the notification gets the same extras as the first one. I think the problem is either with the intent i put in the pending intent or in the pending intent itself. I think I might need to put a flag on one of these but I dont know which one.
Intent showIntent =new Intent(context, notificationreceiver.class);
showIntent.putExtra("details", alarmname);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
showIntent, 0);
notification.setLatestEventInfo(context, "The event is imminent",
alarmname, contentIntent);
And the receiver of the notification
Bundle b = getIntent().getExtras();
String eventname = b.getString("details");
details.setText(eventname);
The "details" extra is the same to every the next time a notification happens instead of having the different value.
Until I set the intents I am sure that the correct value goes to the "details" so its a problem of getting the first intent everytime i press any notification.
How can I make it to launch the correct intents?
Hope I was as clear as i could
Thanks!
The way I solved that problem was by assigning a unique requestCode when you get the PendingIntent:
PendingIntent.getActivity(context, requestCode, showIntent, 0);
By doing so you are registering with the system different/unique intent instances.
Tip: A good way of making the requestCode unique would be by passing to it the current system time.
int requestID = (int) System.currentTimeMillis();
The problem is that when I create more
than one alarm then the activity
launched from the notification gets
the same extras as the first one.
Correct.
How can I make it to launch the
correct intents?
That depends on whether you have two alarms that will be registered at once, or not.
If not, you can use FLAG_ONE_SHOT or one of the other PendingIntent flags to have your second PendingIntent use the newer extras.
If, however, you will have two alarms registered at once, with different Intent extras, you will need to make the two Intents be more materially different, such that filterEquals() returns false when comparing the two. For example, you could call setData() or setAction() and provide different values for each Intent.
I had this issue in my app and just generated a random number to over come overriding notifications intent:
int random= new Random().nextInt();
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
random,
PendingIntent.FLAG_UPDATE_CURRENT
);
I followed the solution provided by U-ramos and this worked for me
int requestID = (int) System.currentTimeMillis();
PendingIntent pendingIntent = PendingIntent.getActivity(this, requestID, showIntent, 0);
another solution:
use the PendingIntent.FLAG_UPDATE_CURRENT like this:
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,i, PendingIntent.FLAG_UPDATE_CURRENT);
this worked for me