Android widget buttons stop working - android

I have an Android application with a widget, that has buttons. This code works.
The buttons on the widget stop working when something happens, such as changing the language of the phone. I use shared preferences, so if the user reinstalls the app (without uninstalling), the buttons are working again and the settings remain the set ones.
I have noticed the Intents in my AppWidgetProvider class (code beneath this analysis) are not fired appropriately.
I added a Toast message to the Call1 class instantiated from AppWidgetProvider, but it doesn't display.
My UpdateService.java is just getting the set preferences and customizing the widget's appearance, so I don't think it could possibly be related to my issue.
My Main.java file merely consists of spinners and saves shared preferences, which means I select "Computer" in a spinner, so that the "Computer" text appears on the widget. It also does not disappear when I change the language of the phone, and neither do images. Therefore, I believe UpdateService.java must be ok.
Here is the AppWidgetProvider class:
public class HelloWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
public static String ACTION_WIDGET_CONFIGURE2 = "ConfigureWidget";
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
public static String ACTION_WIDGET_RECEIVER2 = "ActionReceiverWidget";
private static final int REQUEST_CODE_FOUR = 40;
private static final int REQUEST_CODE_FIVE = 50;
private static final int REQUEST_CODE_SIX = 60;
private static final int REQUEST_CODE_SEVEN = 70;
private static final int REQUEST_CODE_EIGHT = 80;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
context.startService(new Intent(context, UpdateService.class));
//Intent widgetUpdateIntent = new Intent(context, UpdateService.class);
//context.startService(widgetUpdateIntent );
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);
//P1 starts Call1.class
Intent configIntent4 = new Intent(context, Call1.class);
configIntent4.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent4 = PendingIntent.getActivity(context, REQUEST_CODE_FOUR, configIntent4, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView01, configPendingIntent4);
//P2 starts Call2.class
Intent configIntent5 = new Intent(context, Call2.class);
configIntent5.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent5 = PendingIntent.getActivity(context, REQUEST_CODE_FIVE, configIntent5, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView02, configPendingIntent5);
//P3 starts Call3.class
Intent configIntent6 = new Intent(context, Call3.class);
configIntent6.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent6 = PendingIntent.getActivity(context, REQUEST_CODE_SIX, configIntent6, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView03, configPendingIntent6);
//P4 starts Call4.class
Intent configIntent7 = new Intent(context, Call4.class);
configIntent7.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent7 = PendingIntent.getActivity(context, REQUEST_CODE_SEVEN, configIntent7, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView04, configPendingIntent7);
//P5 starts Call5.class
Intent configIntent8 = new Intent(context, Call5.class);
configIntent8.setAction(ACTION_WIDGET_CONFIGURE);
PendingIntent configPendingIntent8 = PendingIntent.getActivity(context, REQUEST_CODE_EIGHT, configIntent8, 0);
remoteViews.setOnClickPendingIntent(R.id.ImageView05, configPendingIntent8);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action))
{
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID)
{
this.onDeleted(context, new int[] { appWidgetId });
}
}
else
{
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER))
{
String msg = "null";
try {
msg = intent.getStringExtra("msg");
} catch (NullPointerException e) {
//Log.e("Error", "msg = null");
}
}
super.onReceive(context, intent);
}
}
}
I also have an EditPreferences.java, GlobalVars.java and some other now meaningless classes. The names of the classes speak for themselves.
One other thing. I also have a Widgetmain.java:
public class WidgetMain extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.widgetmain2);
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
Edit: How about this:
When I install this app on my colleague's ZTE Blade the textviews on the widget are not loaded with the appropriate text, just with the one determined in the strings.xml.
When I reinstall the app (without uninstalling), the textviews are loaded and everything is fine. This problem doesn't emerge on my HTC Desire HD.
The textviews are load in the aforementioned UpdateService.java like this (part of the code):
RemoteViews updateViews = new RemoteViews(this.getPackageName(), R.layout.main);
updateViews.setTextViewText(R.id.widget_textview, name);
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
Even if "name" is static (e.g. String name="Something"), that textview is still not loaded at the first install.

Try to update the RemoteViews with the click listeners whenever you create new instance by "new RemoteViews". Maybe the RemoteViews are freshly loaded from the XML in some circumstances, therefor the click listeners needs to be re-assigned.

My UpdateService.java is just getting the set preferences and customizing the widget's appearance, so I don't think it could possibly be related to my issue.
It is possible it is related, in as much that you could use it to "refresh" the pending intent. I have a similar issue in my appwidget that an image button stops responding to clicks after some random run time (hours).
I found this thread:
AppWidget Button onClick stops working
And this quote:
The pending intent is "burned" after each use. You need to set it again. Or wait for the widget to get refreshed, then it happens, too, but that's probably not the desired way.
Given that the widget update time normally is set at many hours or days (mine is 86400000 milli seconds) in order to prevent the phone going out of suspend every so many minutes your widget will not often run onUpdate. It is possible that setting the pending intent ALSO in the update service will prevent the problem you describe.Each time the update service runs the pending intent is re-created.
I have today added this possible fix to my appwidget and I have to wait and see if the fix really works, but so far so good.
I added the following code in the update service' loop where it refreshes each widget:
for (int i=0; i<appWidgetIds.length; i++)
{
appWidgetId=appWidgetIds[i];
/* other stuff to do */
RemoteViews views=new RemoteViews(context.getPackageName(), R.layout.example_appwidget);
/* here you "refresh" the pending intent for the button */
Intent clickintent=new Intent("net.example.appwidget.ACTION_WIDGET_CLICK");
PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, 0);
views.setOnClickPendingIntent(R.id.example_appwidget_button, pendingIntentClick);
appWidgetManager.updateAppWidget(appWidgetId, views);
/* then tell the widget manager to update */
appWidgetManager.updateAppWidget(appWidgetId, views);
}

The problem is that you can't do a partiall update for a widget, you must set all the widget features, such as the set of PendingIntent's every time you push a new remoteView. (Partiall updates are only available for API14 and up...).
The reason your widgets are loosing their pendingIntents is that the android system saves the remoteView, and rebuilds your widget with it, in case it resets the widget (shortage of memmory, TaskManager/taskKiller in use, etc...), so you must set all the update code for the widget in the remoteView in your updateService. Otherwise, it's just won't set the pendingIntents again.
So just add the code setting the pendingIntents to the service and your problem will be solved =]

I think the PendingIntents may need a flag passed to them, maybe try changing:
PendingIntent.getActivity(context, REQUEST_CODE, configIntent, 0);
to:
PendingIntent.getActivity(context, REQUEST_CODE, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
From the PendingIntent documentation, I think code '0' is undefined. In this case FLAG_UPDATE_CURRENT would work best, as you probably want to update the Intent every time the button is clicked.

Given all the information you gave, I'd say your update method is not triggered properly when the preferences are changed.
I expect after so much tests, you have verified your Manifest file contains:
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
Have you confirmed onUpdate ever runs? It seems to me that if reinstalling the application without deinstalling solves your issues, it might be because it forces an update call.
After careful check, it turns out that ScanPlayGames has a point: the official documentation's example uses super.onUpdate(). Note that it uses it at the end of the method, but several examples on Internet state you're better served using it at the start of your method.

I've had that problem for long time. My widget has button #(onUpdate). The widget has a service for updates. The button on the widget stop working when something happens, like: changing the font, etc..
When i re-install the app, the button works again. Finally, I realized that i never called onUpdate in my Service class.
Calling onUpdate from the service class fixed the problem.

If someone still has this problem try setting the attribute android:updatePeriodMillis in your AppWidgetProviderInfo;
The operating system can kill the pending intent for various reasons and your buttons can stop to work. When you set this attribute, you are telling Android when it should call the onUpdate method in the AppWidgetProvider, so all pending intents will be re-created.
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
...
android:updatePeriodMillis="3600000">
</appwidget-provider>

Related

AppWidget refresh Uri for RemoteViews

I have created an Appwidget that displays an image file (test.png) that is provided to it's RemoteViews via Uri.
In onUpdate i run a service that changes the content of the file. I have also set an onClickListener for the image that will call onUpdate.
-If I create an instance of the AppWidget it displays the most recently changed version of the Uri file.
-If I click the widget, my service makes the approporaite changes to the file (which I can verify with a file explorer), but it does not update the image displayed in the AppWidget.
-(and most importantly)If I delete the AppWidget and create a new one, It displays the current/correct version of the image file.
I'm aware that my service may be taking too long to take effect on the first pass, but it should display the most recent image on the next onClick/call of onUpdate.
As it stands now, the AppWidget only displays the version of the image file that exists on the first call of onUpdate.
Question:
What is the proper way to refresh the RemoteView content of an Appwidget, am I missing something in my Approach here?
thanks for your time!
Update:
I have tried calling the AppWidgetManager.notifyAppWidgetViewDataChanged() method from AppWidgetProvider.onReceive(), and still not change to the RemoteViews content after onUpdate().
public class CCWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,int[] appWidgetIds)
{
// Get all ids
ComponentName thisWidget = new ComponentName(context,CCWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout04);
/*
* it's here that I run a service that changes the content of the file /test/test.png
*/
RelativeLayout RL_widget = new RelativeLayout(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
RL_widget = (RelativeLayout)inflater.inflate(R.layout.widget_main, null);
Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/test/test.png");
remoteViews.setImageViewUri(R.id.IV_widget_image,uri);
Intent intent = new Intent(context, CCWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
//PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.IV_widget_image, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
There are various things that I have found can make widgets hard.
A. onUpdate isn't really an update mechanism
Contrary to how it sounds, onUpdate is only called in two situations:
When the widget is created.
Whenever the update cycle time (updateMillis defined in the xml definition file file for the widget) elapses.
Key point: onUpdate is never called at other times. (As far as I have ever seen in practice).
If you want the widget to update at another time, it is necessary to create a separate mechanism with knowledge of the widget and the capacity to be triggered. Typically this would be a Service which you start in the onUpdate routine. This might look like:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds )
{
// start the service in case it ain't started yet, this also force a widget update to ensure correct status on them
Intent intent = new Intent(context, MyService.class);
intent.putExtra('service.startCode', /*A number to identify what is going on*/);
context.startService(intent);
// Everything else is triggered from the service!!
}
The service then sets the content sof the widget, and updates them as necessary, either through internal timekeeping or through the use of the Broadcast mechanism.
B. You can't really update a bit of the widget
It might seem logical to create a remoteViews when you create the widget and then update that, or the Views in it, when things change. In my experience this doesn't work predictably. When you want to change anything in a widget, create a new remoteViews, fill it out correctly and then assign it to the widget.
I ran into some device dependency with the way RemoteView was handling URIs, and found my way to a solution like this:
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
Uri uri = Uri.parse("file://"+Environment.getExternalStorageDirectory().getPath()+"/test/test.png");
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.crap);
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
remoteViews.setImageViewBitmap(R.id.IV_widget_image, bitmap);
It also eliminated the need to cycle RemoteViews, as in my other answer.
Certainly not elegant, or memory efficient, but I found that if I duplicated my RemoteViews
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout01);
RemoteViews remoteViews2 = new RemoteViews(context.getPackageName(),R.layout.widget_layout02);
duplicate the code surrounding them, and swap them intermittently between image updates (kind of like a buffer), resetting the RemoteViews each time forces an update on their content!
Works for me for now, please feel free chime in with a more correct solution.

Home widget - Is it inefficient for UI event handler registration be done multiple times through onUpdate

So far, all the AppWidgetProvider code example I had seen, they placed UI event handler register code in onUpdate.
However, isn't this is some how inefficient? As I thought UI event handler registration just need to be done 1 time.
onUpdate will always be triggered repeatably.
Is there any more efficient way?
public class MyWidgetProvider extends AppWidgetProvider {
private static final String ACTION_CLICK = "ACTION_CLICK";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
...
// Register an onClickListener
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.update, pendingIntent);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
Let me put it in this way.
Remote views are not the real views, but rather a set of rules, which is used to create real views. Then you call updateAppWidget(), then this set of rules gets sent to Android, and Android creates new views by applying these rules. OnClickPendingIntent is one of those rules. If it's there, then it gets applied and you have a listener set.
If you create a new instance of remoteViews, then you have to provide a OnClickPendingIntent to this instance too. If you don't, then there will be no listener registered and you won't get a callback. In this regards, you do not set listener twice or multiple times at the same instance - because you always re-create remoteViews too - and this is optimal.
Android might apply additional optimization by reusing already existing views, if remote views were not changed. But this is something out of your control.

Android widget won't switch layouts more than once

I'm adding a widget to an old app which I'm updating from a service I'm using to poll for data in the background (on an alarm). I update the widget every time the service gets a result. This is currently working correctly.
// Called from inside my service when it has results
private void updateWidget(List<Earthquake> earthquakes) {
AppWidgetManager manager = AppWidgetManager.getInstance(this);
int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(this, WhatsShakingWidgetProvider.class));
if (appWidgetIds == null || appWidgetIds.length == 0)
return;
Earthquake earthquake = earthquakes.get(0);
RemoteViews views = new RemoteViews(getPackageName(), R.layout.widget_detail);
// Update views
views.setTextViewText(R.id.widget_detail_latest_magnitude, earthquake.getFormattedMagnitude());
// etc...
// Update each widget
for(int appWidgetId : appWidgetIds) {
manager.updateAppWidget(appWidgetId, views);
}
}
This polling service is optional; it can be turned on or off in the app's settings.
If the service is off when the user adds the widget, the widget_error layout is shown, as expected. The user can tap on the widget to enter the settings and turn the background updates on. When they do this (turn the setting on or off), I broadcast ACTION_APPWIDGET_UPDATE. The widget enters onUpdate correctly, and is updated correctly by the service the next time it runs (I've set it up so the widget triggers a service call in onUpdate - see below).
The widget does not correctly display the widget_error layout when the service becomes disabled after being enabled - it leaves the old layout in place, even though all the disabled-case code is run.
This is the code that gets called when the user toggles the setting (Source):
// If our user has widgets, we should update those - let the widget do the updating depending on the prefs, though.
Intent intent = new Intent(this, WhatsShakingWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// Use an array and EXTRA_APPWIDGET_IDS instead of AppWidgetManager.EXTRA_APPWIDGET_ID,
// since it seems the onUpdate() is only fired on that:
int[] ids = { R.xml.widget_info };
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids);
sendBroadcast(intent);
And this is the code in onUpdate which should be updating the widgets, but isn't:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean backgroundUpdatesEnabled = prefs.getBoolean(PreferenceActivity.KEY_PREF_ALLOW_BG_NOTIFICATIONS,
DefaultPrefs.BG_NOTIFICATIONS_ENABLED);
if (!backgroundUpdatesEnabled) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_error);
// Update click to take to preferences
Intent intent = new Intent(context, PreferenceActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_error_parent_container, pendingIntent);
// Update each widget
appWidgetManager.updateAppWidget(appWidgetIds, views);
} else {
// Let's get some data for the user! Service does the work of updating the views.
WakefulIntentService.sendWakefulWork(context, GeonetService.class);
}
}
There are no errors logged in Logcat. Stepping through this, I correctly enter each part of the if when expected (that is, if the user turned the setting off, then I create RemoteViews views as widget_error, otherwise I start the service).
Why does the widget_error layout display correctly the first time through onUpdate, but not when the user enables, then disables, the background update setting?
I've tried wrapping this in a RelativeLayout and setting the visibility of the error message/the content, but that exhibited the same behaviour - I couldn't get the error message to show back up after initially hiding it.
I ended up duplicating the code in two places (the preferences activity and the widget provider) and it worked. The only variable appears to be the Context object.
It appears that for some reason the Context instance you get in the AppWidgetProvider (that is, in onUpdate) only works the first time - or, doesn't work when I send the broadcast myself. I'm not sure why.
I pulled my duplicated code out to a separate class and just pass in the Context instance I have available, whether it's the Service, an Activity, or the AppWidgetProvider (which is a BroadcastReceiver). This correctly updates the widget, and I can call it from anywhere I have a Context.
Source is available here.

How Do I Properly Update AppWidget?

I'm a noob to android and I'm having issues updating an appwidget. It's a news widget that displays different text every 20 secs. I have no problem getting the text to switch & display properly when the widget is initialized. However, after every 30 min update of the widget my widgetID int array retains the int that existed prior to the update. So after each update the widget shows old data and new data. Is there a to clear the widget ID int array of old data during the update process. Any help is greatly appreciated.
My Code:
allWidgetIds2 = appWidgetManager.getAppWidgetIds(thisWidget);
Method for switching text in widget. This works fine initially, but after update shows old data along with new data...
public void updateStory() {
tickerheadline = RssReader.rssheadline.get(storyCounter);
tickerstory = RssReader.rssstory.get(storyCounter);
remoteViews.setTextViewText(R.id.headline, tickerheadline );
remoteViews.setTextViewText(R.id.story, tickerstory );
if (storyCounter==RssReader.rssheadline.size()-1){
storyCounter = 0;
storyCounter++;
}else{
storyCounter++;
}
appWidgetManager.updateAppWidget(allWidgetIds, remoteViews);
//Log.e("Widget", allWidgetIds.toString());
mHandler.postDelayed(new Runnable() {
public void run() {
updateStory();
} } ,20000); }
}
EDIT
public void updateStory() {
//Added
appWidgetManager = AppWidgetManager.getInstance(this.getApplicationContext());
ComponentName thisWidget = new ComponentName(getApplicationContext(),MyWidgetProvider.class);
remoteViews = new RemoteViews(this.getApplicationContext().getPackageName(),R.layout.widget1);
tickerheadline = RssReader.rssheadline.get(storyCounter);
tickerstory = RssReader.rssstory.get(storyCounter);
remoteViews.setTextViewText(R.id.headline, tickerheadline );
remoteViews.setTextViewText(R.id.story, tickerstory );
if (storyCounter==RssReader.rssheadline.size()-1){
storyCounter = 0;
storyCounter++;
}else{
storyCounter++;
}
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
//appWidgetManager.updateAppWidget(allWidgetIds, remoteViews);
//Log.e("Widget", allWidgetIds.toString());
mHandler.postDelayed(new Runnable() {
public void run() {
updateStory();
} } ,20000); }
}
Although you can be very clever with AppWidgets in android in terms of updating and so in, the easiest way to get going is simply to rebuild the widget contents every time you refresh. As you're only refreshing every 30 minutes, there's not much reason to worry about cpu usage.
So, I would suggest doing a standard rebuild each time you trigger an update using the standard approach. The code you have above, whilst it looks correct, doesn't actually force an update, much cleaner to start from scratch. Once that's working, then if you can make it more lightweight, do tell me how!
// You need to provide:
// myContext - the Context it is being run in.
// R.layout.widget - the xml layout of the widget
// All the content to put in it (duh!)
// Widget.class - the class for the widget
RemoteViews rv= new RemoteViews(myContext.getPackageName(), R.layout.widget);
rv.setXXXXXXX // as per normal using most recent data set.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(myContext);
// If you know the App Widget Id use this:
appWidgetManager.updateAppWidget(appWidgetId, rv);
// Or to update all your widgets with the same contents:
ComponentName projectWidget = new ComponentName(myContext, Widget.class);
appWidgetManager.updateAppWidget(projectWidget, rv);
Often the easiest approach (in my view) is to wrap this up in a WidgetUpdater.class which you either call from a service or directly using a time alarm call. Using onUpdate is generally a bad idea because it force the phone into full power mode and isn't very controllable. I've never managed to make it work. Much better to do something like:
Intent myIntent = // As per your onUpdate Code
alarmPendingIntent = PendingIntent.getService(context, 0, myIntent, PendingIntent.FLAG_ONE_SHOT);
// ... using the alarm manager mechanism.
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdateTimeMillis, alarmPendingIntent);

Self-updating every 10 seconds widget Handler.postDelayed problem

I'm trying to make work a self-updating functionality in an Android widget, something simple as changing two TextViews of it every 10 seconds. The ideal solution would be to make it similar to the genie widget (news & weather). And so far, it works okay: it updates every 10 seconds via Handler.postDelayed's Runnable.
Runnable updateWidgetText = new Runnable()
{
#Override
public void run() {
if (screenEnabled) {
AppWidgetManager gm = AppWidgetManager.getInstance(ctx);
ComponentName thisWidget = new ComponentName(ctx,AnimeUndergroundWidget.class);
int index = (int)(REFRESH_COUNT % WIDGET_NEWS_TO_SHOW);
Noticia n = AUnder.single().getNoticiaByIndex(index);
if (n!=null)
{
last = n;
RemoteViews views = new RemoteViews(ctx.getPackageName(),R.layout.auwidget);
views.setTextViewText(R.id.widget_textview, n.getTitulo());
views.setTextViewText(R.id.widget_textototal, n.getTexto().replace("\n", ""));
Intent clickintent = new Intent(INTENT_GO_TO_NEW);
PendingIntent pendingIntentClick = PendingIntent.getBroadcast(ctx, 0, clickintent, 0);
views.setOnClickPendingIntent(R.id.widget_fondo_titulo, pendingIntentClick);
Intent click2intent = new Intent(INTENT_GO_TO_NEW);
PendingIntent pendingIntentClick2 = PendingIntent.getBroadcast(ctx, 0, click2intent, 0);
views.setOnClickPendingIntent(R.id.widget_fondo_Texto, pendingIntentClick2);
gm.updateAppWidget(thisWidget, views);
}
REFRESH_COUNT++;
}
handler.removeCallbacks(this);
handler.postDelayed(this, WIDGET_REFRESH_TIME*1000);
}
};
The runnable is initially launched in the onUpdate method of my Widget class:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
ctx = context;
context.startService(new Intent(context, UpdateWidgetService.class));
handler.postDelayed(updateWidgetText,WIDGET_REFRESH_TIME*1000);
// here goes some more code, updating the views and refreshing click intents
}
I put this in case someone finds it useful.
But let me go straight to the point: when I get the phone out of sleep (turn on the screen), the widget goes crazy and starts changing the TextViews with a fastforward-like effect.
I assume it is because there are some postDelayed events in queue, or may be in the updateAppWidget queue.
I've already tried a workaround using the code shown in here: http://thinkandroid.wordpress.com/2010/01/24/handling-screen-off-and-screen-on-intents/
You can see it in the first snippet in my code, I check a boolean variable that has stored screen state to avoid using postDelayed when the screen is turned off. But that doesn't seem to fix it.
This problem is driving me crazy for one week now, so I ask out of despair: is there any way to do this properly?
Your postDelayed call isn't inside your if (screenEnabled) block, so it will continue to post the events even when the screenEnabled is false

Categories

Resources