1

I have a calendar widget and I'm updating it myself everyday at 12:01AM as well when the size is changed so I don't have a need for automatic update. I also set the updatePeriodMillis to 0. The problem I'm having is that the App Widget calls onAppWidgetOptionsChanged():

  • Every time the screen unlocks
  • Every time you're brought home

It wouldn't be an issue for me, but the problem is you can see for a split second the App Widget updating. Anyone know of any way I can prevent that from happening?

For reference here is part of my WidgetProvider class

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

    if (action != null) {
        if (action.equals(ACTION_REFRESH) || action.equals(ACTION_NEXT) || action.equals(ACTION_PREVIOUS)) {
            context.sendBroadcast(new Intent(action));
        }
    }

    Bundle extra = intent.getExtras();
    if (extra != null && extra.containsKey(KEY_APP_WIDGET_ID)) {
        updateAppWidget(context, AppWidgetManager.getInstance(context), extra.getInt(KEY_APP_WIDGET_ID));
    }
    super.onReceive(context, intent);
}


@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
    Intent broadcastIntent = new Intent(ACTION_REFRESH);
    broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
    context.sendBroadcast(broadcastIntent);
    updateAppWidget(context, appWidgetManager, appWidgetId);
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}

private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
        int appWidgetId) {

    RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);

    Intent intent = new Intent(context, WidgetService.class);
    views.setRemoteAdapter(R.id.widget_calendar_container, intent);

    // Buttons on widget click handlers
    views.setOnClickPendingIntent(R.id.widget_refresh, getPendingSelfIntent(context, ACTION_REFRESH, appWidgetId));
    views.setOnClickPendingIntent(R.id.widget_next, getPendingSelfIntent(context, ACTION_NEXT, appWidgetId));
    views.setOnClickPendingIntent(R.id.widget_previous, getPendingSelfIntent(context, ACTION_PREVIOUS, appWidgetId));

    // Widget Container click handler
    Intent homeIntent = new Intent(Intent.ACTION_MAIN);
    homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    homeIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
    homeIntent.setComponent(new ComponentName(context.getPackageName(), Home.class.getName()));
    PendingIntent homePendingIntent = PendingIntent.getActivity(context, 0, homeIntent, 0);
    views.setPendingIntentTemplate(R.id.widget_calendar_container, homePendingIntent);


    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views);
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_calendar_container);
}
Pants
  • 2,563
  • 1
  • 20
  • 34
  • Override the onUpdate so that it does nothing. Would help if you included your entire class for testing – Kwright02 Sep 02 '18 at 23:52
  • @Kwright02 thanks for that. I debugged it further, and I see that `onAppWidgetOptionsChanged()` gets called when the screen awakes or when the user goes home... – Pants Sep 03 '18 at 00:00
  • Awesome glad I could help. Please re-comment on here if there's further issues or that didn't solve it. If it did solve it, please provide an answer to your own question so others know how to solve this issue if they have it. – Kwright02 Sep 03 '18 at 00:02
  • No it did not solve it, I now know however where the issue actuality is. @Kwright02 – Pants Sep 03 '18 at 00:03
  • awesome just make sure to post your solution : – Kwright02 Sep 03 '18 at 00:05
  • 1
    Note that your code may have an infinite loop. You have a `BroadcastReceiver` that re-broadcasts a just-received `Intent` action, when that action is one of three values (e.g., `ACTION_REFRESH`). Your receiver obviously can receive such actions, so it will receive the broadcast, send the broadcast, receive the broadcast that it just sent, send that broadcast again, and so on, AFAICT. That will not help performance. Beyond that, see if you can boost the performance of `WidgetService`. – CommonsWare Sep 03 '18 at 00:05
  • @CommonsWare thanks for the tip. I am now using `LocalBroadCastManager` to communicate between activities. I can already see the performance improvement. However, onAppWidgetOptionsChanged() still gets called unnecessarily when the screen is awaken. – Pants Sep 03 '18 at 00:39

1 Answers1

0

I prefer another answer, but this is what I got so far.

@Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
        int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
        if (width != WidgetRemoteViewsFactory.width) {
            Intent broadcastIntent = new Intent(ACTION_REFRESH);
            broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
            LocalBroadcastManager.getInstance(context).sendBroadcast(broadcastIntent);
            updateAppWidget(context, appWidgetManager, appWidgetId);
            super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        }
    }

I made the field I am interested in width static on WidgetRemoteViewsFactory and I am only broadcasting when that field changes. I can't seem to prevent onAppWidgetOptionsChanged() from being called because AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED is being broadcasted and received every time the screen is awaken.

Android Documentation says

Called in response to the AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED broadcast when this widget has been layed out at a new size. - AppWidgetProvider#onAppWidgetOptionsChanged

But that action is being broadcasted every time the screen awakens.

If anyone has a better solution, please feel free to post it. There has to be a better solution or this is a bug on Android's side.

Pants
  • 2,563
  • 1
  • 20
  • 34
  • "I made the field I am interested in width static on WidgetRemoteViewsFactory and I am only broadcasting when that field changes" -- most of the time, `width` will be `0`, as you will have a fresh process, so `static` values will be back to defaults. "I can't seem to prevent onAppWidgetOptionsChanged() from being called" -- when and how frequently is is called is up to a combination of the home screen implementation, Google (via the Android source code), and possibly the device manufacturer. Your performance problem would appear to be in `WidgetService`. – CommonsWare Sep 03 '18 at 11:00
  • @CommonsWare what do you recommend then? I have fixed the performance to tht best of my abilities by broadcasting only locally with `LocalBroadcastManager`, I query the database `onDataSetChanged` only when necessary and I still see four a split second the App Widget refreshing. – Pants Sep 03 '18 at 13:34
  • What exactly do you mean by "I still see four a split second the App Widget refreshing"? – CommonsWare Sep 03 '18 at 13:35
  • The other performance issue I can imagine is rendering the widget itself. It is a custom View in which I load into an Imageview like this https://stackoverflow.com/a/4138451/6003161 – Pants Sep 03 '18 at 13:36
  • @CommonsWare the App Widget is blank for a fraction of a second when the screen awakens, then it loads correctly. Maybe for 100 milliseconds. – Pants Sep 03 '18 at 13:38
  • Given your answer, I assume that if you do not call `updateAppWidget()`, you do not see this effect. I suggest that you run some experiments to determine exactly where the delay is coming from. First, have it publish a trivial `RemoteViews` with just a `TextView`. Then, have it publish a single `ImageView` with a resource as content. Then, have it publish a single `ImageView` with your rendered `View` as content. Then, have it publish whatever `AdapterView` that you are using, with simple `TextViews` for items. And so on. As you ramp up the complexity, see where the 100ms delay creeps in. – CommonsWare Sep 03 '18 at 13:48