Update WidgetList View - android

I have a problem updating my Widget ListView, I don't need it to be updated automatically i need to update It according to a button Click in my App which calls an intent services that should update data.
the widget is updated correctly for the first time to click the button only,
and in other clicks Logs shows that data changes correctly but the view is not updated.
i had tried may solution from stackoverflow but all of them didn't work.
note
i don't want to use any database of sharedpreferences
and here is my code gitHub repo for my widget classes
https://github.com/abdelmun3m/BackingApp/tree/master/app/src/main/java/com/abdelmun3m/backingapp/widget

Have you added a PendingIntent on the button that is manually updating the widget, when you create the RemoteViews? This PendingIntent will trigger an onUpdate call on your WidgetPRovider. From there you can spawn the IntentService.
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.your_widget_layout);
...
// Initialize listview here
...
Intent refreshIntent = new Intent(context,YourWidgetProvider.class);
refreshIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, REFRESH_PENDING_INTENT_ID, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.your_refresh_button_id, refreshPendingIntent);

Related

How to start a service from App Widget in Android

I am making a home screen App Widget for my Android app. In that App Widget there is a ListView. That ListView has a custom layout which has a TextView & an ImageButton. I am setting a PendingIntent on that ImageButton to start the service as follows:
Intent intent = new Intent(mContext, MyService.class);
intent.putExtra(MyConstants.MY_EXTRA, c.getString(c.getColumnIndex(MyColumns.MY_COLUMN_TITLE)));
intent.setAction(MyConstants.MY_ACTION);
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
rv.setOnClickPendingIntent(R.id.MyImageButton, pendingIntent);
in getViewAt method of RemoteViewsFactory. But on clicking the ImageButton the service does not start.
I have put an intentfilter with MY_ACTION for MyService. And also tried without it which did not make things work.
Anyone knows how to start service on click of a button inside a list view item in home screen App Widget in Android?
Everything works fine i.e. the ListView is populated perfectly from SQLite database.
From the android javadoc
public void setOnClickPendingIntent
Equivalent to calling
setOnClickListener(android.view.View.OnClickListener) to launch the
provided PendingIntent. When setting the on-click action of items
within collections (eg. ListView, StackView etc.), this method will
not work. Instead, use {#link
RemoteViews#setPendingIntentTemplate(int, PendingIntent) in
conjunction with RemoteViews#setOnClickFillInIntent(int, Intent).
In your update method of your AppWidgetProvider you have to add
final PendingIntent clickPendingIntentTemplate = PendingIntent.getService...
views.setPendingIntentTemplate(R.id.widget_list, clickPendingIntentTemplate);
and in the getViewAt method of your RemoteViewsFactory :
final Intent fillInIntent = new Intent();
fillInIntent.putExtra(...);
views.setOnClickFillInIntent(R.id.widget_list_item, fillInIntent);

Android AppWidgetManager method updateAppWidget fails to set intents, load data. And it happens randomly

My widget is comprised of 2 buttons and a listview displaying data. Most times, when the widget provider's onUpdate method is called, everything loads normally and everyone is happy.
However I've noticed sometimes after the update method is called, the widget just completely fails to load its data. The listview is empty, and all of the buttons are non-responsive. It's as if I initialized the layout into the widget, but none of the pending intents nor the adapter for list were set.
I logged everything and found that this isn't the case. The pending intents ARE created, as is the list adapter, every time, including the random time when it fails. For a long time I thought it had to do with how the list data is populated into the adapter, but seeing it work every single time, coupled with the fact that the button intents don't work as well leads me to believe the method updateAppWidget(ComponentName, RemoteViews) is what is failing. However, there are no error stacks to help me confirm this.
Here is the code which runs in a separate service called by the AppWidgetProvider's onUpdate method:
#SuppressWarnings("deprecation")
private void updateWidget(int[] ids) {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(this);
int[] widgetIds = widgetManager.getAppWidgetIds(new ComponentName(this, WidgetReceiver.class));
for (int currentWidgetId : widgetIds) {
RemoteViews widget = new RemoteViews(this.getPackageName(), R.layout.widget_layout);
Intent intent = new Intent(this, WidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, currentWidgetId);
intent.putExtra("random", randomNumber);
randomNumber++;
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
widget.setRemoteAdapter(currentWidgetId, android.R.id.list, intent);
Intent clickIntent = new Intent(this, DetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(this, 0, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setPendingIntentTemplate(android.R.id.list, pending);
Intent settingsIntent = new Intent(this, WidgetSettingsActivity.class);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent settingsPending = PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setOnClickPendingIntent(R.id.widget_settings, settingsPending);
Intent mainIntent = new Intent(this, MainActivity.class);
PendingIntent mainPending = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
widget.setOnClickPendingIntent(R.id.widget_logo, mainPending);
ComponentName widgetCompName = new ComponentName(this, WidgetReceiver.class);
widgetManager.updateAppWidget(widgetCompName, widget);
}
}
What is most frustrating about this bug is that I can't reliably recreate it. Sometimes I get (un)lucky and it shows it ugly head, other times (the majority of the time) it works perfectly.
Another thing that I thought was interesting is that I've seen the same exact problem in Facebook's widget. Due to NDA I can't show a screen of my app when it fails, but I can show a screen of Facebook's identical problem:
When Facebook's widget looks like this, it has the exact same issues as mine. No data loaded, all buttons are unresponsive.
For both Facebook and my widget, a subsequent update interval will usually make the problem go away.
As much as I appreciate that I'm not the only one who is running into this, I still don't want to release until I fix this. Has anyone here run into this, and better yet found a solution, or even a cause of the problem? Thanks for helping.
EDIT: Something interesting. I ran an experiment where I set the update interval to a full day rather than every 30 minutes. My hypothesis was that maybe the update method wasn't the cause, it was something else which was causing the widget to become blank and unresponsive.
Sure enough, after about 2 hours, I checked my phone and the widget was dead, despite no update method being called. This would lead me to believe that something else is causing this problem, NOT widgetManager.updateAppWidget(widgetCompName, widget); like I previously thought.
I know that a configuration change can cause the widget to be rebuilt, and thus it is possible that it can fail. However, I already use the Service class's onConfigurationChanged method to reload the widget if necessary. Is there another case like configuration change which can cause the widget to destroy and recreate itself?
After a lot of blood, sweat, and tears I found the solution. I am going to hold off on confirming this answer for a few days to make sure that the error doesn't pop back up, but after a lot of observation I think it is resolved.
So I was right in my edited comments in the original question. It wasn't the update method of the AppWidgetManager that caused the problem, but rather some other Android process which caused the app widget to recreate itself. Unfortunately I couldn't isolate that trigger, but I did find a solution.
In my RemoteViewsFactory class (basically the wrapper which is used to load the data set), I had a block of code that looked like this:
RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.widget_layout);
if(mItems.size() == 0)
views.setViewVisibility(R.id.widget_empty, View.VISIBLE);
else
views.setViewVisibility(R.id.widget_empty, View.GONE);
AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
ComponentName widgetCompName = new ComponentName(mContext, WidgetReceiver.class);
manager.updateAppWidget(widgetCompName, views);
Basically, if the list was empty, I showed a Loading message that took up the entire area where the listview is located. If the list wasn't empty, I'd just hide that message.
So this is what was happening: When the factory was destroyed (presumably for memory purposes) the widget itself was not. So all of the code which sets the intents for the data and stuff was not run. However, the factory was recreated, which ran that block of code which updated the app widget with the new remote views object. This remote views object didn't do any of the methods that you see in my onUpdate() method I originally posted, so all of its features didn't work.
Moral of the story: DON'T use updateAppWidget in your RemoteViewsFactory class! Now it may work if you run all the necessary lines, but I can't confirm that.

Android Widget PendingIntent Fails

There have been some similar issues discussed here, but my situation does work some of the time. I am developing a widget that when clicked should launch an activity that's part of the same package. This same activity can also be launched by a notification that may be posted. The widget updates and notification posting are done by a Service in the package. Here is the method that's called to issue the PendingIntent:
// Get pending intent for widget or notification
private PendingIntent getPendingIntent(int widgetId, int extraData) {
Intent clickIntent = new Intent(mCtx, OtdShowEvents.class);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
clickIntent.putExtra("OTDExtra", extraData);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendIntent = PendingIntent.getActivity(mCtx, 0, clickIntent,
0);
return pendIntent;
}
If I create an instance of the widget on a clean (rebooted) device, both the widget and notification launch the target activity as expected. However, if I remove the widget and create another instance, the Intent is no longer launched. Likewise, if I uninstall the widget altogether, then re-install it and create an instance, no Intent is fired off. However, if I power off and back on (leaving the widget in place), it works again when booted up.
One error that I saw along the way was from the PackageManager saying "Name not found", but indicating the package name "com.ghcssoftware.OTD.full", which is the correct name of my package!
Any ideas? And by the way, I have tried some of the PendingIntent flags such as FLAG_CANCEL_CURRENT and FLAG_UPDATE_CURRENT without affecting this behavior.
FWIW, I found that the code snippet provided in this article was exactly what I needed to figure out how to get this all working correctly, especially for multiple instances of my widget, etc.: PendingIntent in Widget + TaskKiller

appWidget layout reseting after device restart

I have a simple appWidget that displays an image. The image is selected in the configuration activity and the widget is updated via the remove view. This is done on a button push executing the code below:
Intent intent = new Intent(context, KidDialog.class);
intent.setData(selectedImage);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
widgetView.setOnClickPendingIntent(R.id.centerBrd, pendingIntent);
widgetView.setImageViewUri(R.id.widImg, selectedImage);
appWidgetManager.updateAppWidget(appWidgetId, widgetView);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
setResult(RESULT_OK, resultValue);
finish();`
As you can see, it is also seeing an PendingIntent for the onClickListener of the image. I don't think that is relevant here. Anyway, this work great. The widget does exactly what I want it to. Until I restart the device.
When I restart the widget loads with the default view from the xml in the apk. It does not keep the image update from the initial configuration. My questions is how do I get the widget to load back up after a restart with the updated view set during the configuration activity? I will also need to reset the onClick Pendingintent, but I will save that for later. I expect they are related anyway. So I am out of ideas.
I am not sure of the best answer, but when you receive the intent from the button push you could save the image name to a SharedPreference.
And when you create the widget, check the value of the sharedpreference and use that to restore the desired image..

logging to a database when user clicks on a widget

I have a widget that displays an analog clock. What I would like is for the the widget to write to a database the time when a user clicks on the widget. I've already got the databaseHelper class and have an Activity that displays a screen showing the current date and time and writes the time to a database.
I followed the tutorial here: Analog Clock Tutorial and ended up with this:
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action))
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
this.mIntent = new Intent(context, AskTheTime.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, mIntent, 0);
views.setOnClickPendingIntent(R.id.Widget, pendingIntent);
AppWidgetManager.getInstance(context).updateAppWidget(intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS), views);
}
}
The AskTheTime class extends activity and logs to the database in onCreate(). But this means that it displays only the time when the widget was started - not when it was clicked. (I guess this is because I'm starting a pendingIntent) I'm not sure If I should put the database write in another method or if I should be using Intents to do this sort of thing from widgets. Any pointers would be great! I've look at several tutorials for intents but none of them seem very clear to me.
When your widget is pressed, the OS activates the pending intent you set on the widget.
It looks like you are opening an activity AskTheTime when the widget is pressed.
So the problem is that AskTheTime might have already been created when the widget is pressed, so the onCreate() isn't called again.
What you could do is try doing your logging in onStart() or onResume() instead of, or in addition to, onCreate() inside your activity.
From the documentation regarding onStart:
Called when the activity is becoming
visible to the user.
And onResume:
Called when the activity will start
interacting with the user. At this
point your activity is at the top of
the activity stack, with user input
going to it.

Categories

Resources