Android widget buttons stop working
Asked Answered
W

7

13

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.

Winou answered 13/7, 2011 at 20:9 Comment(4)
I opened a bounty on this question since i have not made any progress in weeks.Winou
How come you never call super.onUpdate() in your update function?Collinsworth
I call it, I just somehow missed it from the code. I tried it as the first line as well as the last line of the onUpdate function: super.onUpdate(context, appWidgetManager, appWidgetIds);Winou
Ok, sorry. I read your solution more closely. What I don't understand is what you call your "Update Service" loop. What exactly is your service doing? See, I don't need a service in my widget. Should I just set a timer to periodically check whether the widget needs refreshing?Whitehorse
D
6

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);
}
Dhammapada answered 18/10, 2011 at 22:46 Comment(4)
After a day of testing I can verify that the fix I suggested above indeed works. I am not sure if this should be considered a work around, because I don't know if the problem is caused by a bug or a "feature".Dhammapada
I have had the above solution in place for more than a month now, also in apps available in the android market and it's a satisfactory solution for me.Dhammapada
Help me please!: #29598991Cornelison
What did you mean by "refresh" (which line specifically refreshes it)? Why did you write appWidgetManager.updateAppWidget(appWidgetId, views); twice?Disinherit
B
6

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.

Bogart answered 7/11, 2011 at 9:10 Comment(1)
I found that remote views need passing of pending intent every time I change widget. Otherwise it is removed on updateGrath
K
2

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 =]

Krenn answered 24/5, 2014 at 11:19 Comment(0)
G
0

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.

Giantism answered 9/8, 2011 at 23:16 Comment(0)
A
0

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.

Auschwitz answered 6/9, 2011 at 16:18 Comment(2)
1. Yes, the Manifest contains that line. 2. OnUpdate must run since imageviews are working in the widget. Or what do u mean? 3. I have super.onUpdate() in the code, I just missed it out from here.Winou
About 2., I tend to log messages almost every line in such situations, just to be sure, and also step into code if at all possible. I reasoned something else might make the images work. I'd still log if I were you. Apart from that, I'm at a loss. Hope you solve it!Auschwitz
R
0

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.

Rabon answered 17/6, 2012 at 22:30 Comment(0)
S
0

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>
Shemeka answered 22/4, 2019 at 20:6 Comment(1)
Welcome to StackOverflow! Please provide a specific answer backed with references. If you have a suggestion or need clarification leave a comment. We have guidelines on How to write a good answer.Minny

© 2022 - 2024 — McMap. All rights reserved.