4

I've got a widget which will update itself whenever there's a configuration change (such as screen orientation), and whenever the phone is unlocked. This process involves setting onClick handlers for the buttons on my widget. This works well, however I have found that there's a usage case which causes my app to not respond to onClick events. This particular case is whenever the launcher restarts itself.

Is there a way to detect when a launcher restarts, so I can update my widget manually? Or is there another way to ensure onClick handlers are not lost?

Jim Vitek
  • 4,174
  • 3
  • 23
  • 32
Glitch
  • 2,785
  • 1
  • 27
  • 50
  • possible duplicate of [onClick intents not working after Launcher restart.](http://stackoverflow.com/questions/4909886/onclick-intents-not-working-after-launcher-restart) – Macarse Jul 11 '11 at 13:18

2 Answers2

10

Turns out I was spamming new RemoteViews() when I should have just called it once to produce the view, and then referred to that one instance when required. In my solution, I have a class variable which stores this single RemoteView instance, and a getter to access it.

Glitch
  • 2,785
  • 1
  • 27
  • 50
  • 1
    The issue seems to persist even after the fix you suggest. I think it is the same as [**Issue 28216**](http://code.google.com/p/android/issues/detail?id=28216) here. – Anirudh Ramanathan Jan 18 '13 at 14:20
  • This is pretty vague. Can you please provide more detail? "Called it once".. what does that mean? For every update iteration you create a single RemoteViews instance, or you create it once and hold on to the reference (in a service, for instance). – Sean Aitken Apr 03 '13 at 16:18
  • This worked beautifully, THANK YOU! Ofc, as usual, the Android documentation is crap. I looked in various places for a correct answer, this was the only one that worked. @CleverCoder Yes, keep hold of the single instance you create ONLY ONCE. So, in Widget's onUpdate, only create the remoteViews object if your reference is null! – Radu May 05 '13 at 17:59
  • Take note that, by using this approach to solve "launcher restarting" does come with problem. I tested using a home widget with list view. As time goes on, when more `appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, list_id)` is being triggered, the whole home widget will be very slow. My wild guess is, the "singleton" remote views will keep executing its ever grow accumulated instruction. – Cheok Yan Cheng Mar 22 '14 at 15:24
0

Proposal by @Glitch might not work for certain cases, especially app widget with ListView. This is because ListView will get very slow (Try to scroll the ListView) after appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, list_id) had been called several time.

My guess is, the single RemoteView instance will keep all its executed instruction in a list. Over the time, the instruction list will grow. Every time appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, list_id), the large instruction list will be executed all over again.

My proposed solution is as follow. However, I believe it will only work on certain device, as not all devices will receive same broadcast message during launcher restarting.

@SuppressLint("NewApi")
@Override
public void onReceive(Context context, Intent intent) {
    final String action = intent.getAction();

    if (action.equals("com.sec.android.widgetapp.APPWIDGET_RESIZE")) {
        // http://stackoverflow.com/questions/17396045/how-to-catch-widget-size-changes-on-devices-where-onappwidgetoptionschanged-not
        handleTouchWiz(context, intent);

        // Possible launcher restart.
        handleLauncherRestart(context, intent);
    } else if (action.equals("android.appwidget.action.APPWIDGET_UPDATE_OPTIONS")) {

        // Possible launcher restart.            
        handleLauncherRestart(context, intent);
    } 

    super.onReceive(context, intent);
}

private void handleLauncherRestart(Context context, Intent intent) {
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
            AppWidgetManager.INVALID_APPWIDGET_ID);
    updateAppWidget(context, appWidgetManager, appWidgetId);
}

private void handleTouchWiz(Context context, Intent intent) {
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

    int appWidgetId = intent.getIntExtra("widgetId", 0);
    int widgetSpanX = intent.getIntExtra("widgetspanx", 0);
    int widgetSpanY = intent.getIntExtra("widgetspany", 0);

    if (appWidgetId > 0 && widgetSpanX > 0 && widgetSpanY > 0) {
        Bundle newOptions = new Bundle();
        // We have to convert these numbers for future use
        // http://stackoverflow.com/questions/10008521/appwidget-size-calculation
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, widgetSpanY * 74 - 2);
            newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widgetSpanX * 74 - 2);

        } else {
            newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, widgetSpanY * 70 - 30);
            newOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, widgetSpanX * 70 - 30);
        }

        onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    }
}
Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875