Search code examples
javaandroidandroid-widget

App widget with list not updating - why so?


I have a widget with two texts contained in a listview (for the purpose of allowing long text scroll). At the moment when the app is running when I click the "refresh widget" button in the widget it works. However, when the app is not running, the title of the widget is being updated while the listview isn't updated.

Here is my code

MyAppWidget.java

public class MyAppWidget extends AppWidgetProvider {
  private static final String RELOAD_CLICKED = "MyAppWidget#btnReload";

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId, getClass());
        }
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        super.onReceive(context, intent);
        if (RELOAD_CLICKED.equals(intent.getAction())) {
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
            updateAppWidget(context, appWidgetManager, appWidgetId, getClass());
        } 
    }


    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId, Class<?> cls) {

        WidgetViewModel viewModel = DbManager.getInstance(context).getRandomWidget();

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget);
        views.setTextViewText(R.id.txtTitle, viewModel.title);
        Intent intent = new Intent(context, WidgetService.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        String texts[] = new String[] {viewModel.widgetText1, widgetText2};
        intent.putExtra("texts", texts);
        views.setRemoteAdapter(R.id.widgetListTexts, intent);
        views.setOnClickPendingIntent(R.id.btnReload,
                getPendingSelfIntent(context, RELOAD_CLICKED, appWidgetId, cls));
        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);

        Intent updateIntent = new Intent(WidgetRemoteViewsFactory.UPDATE_WIDGET_ACTION);
        updateIntent.putExtra("texts", texts);
        context.sendBroadcast(updateIntent);
    }

    private static PendingIntent getPendingSelfIntent(Context context, String action,
                                                      int appWidgetId, Class<?> cls) {
        Intent intent = new Intent(context, cls);
        intent.setAction(action);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        return PendingIntent.getBroadcast(context, 0, intent, 0);
    }
}

WidgetRemoteViewsFactory.java

public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

    public static final String UPDATE_WIDGET_ACTION = "WidgetRemoteViewsFactory#update";

    private final Context context;
    private final int appWidgetId;

    private String[] texts;
    private BroadcastReceiver receiver;

    public WidgetRemoteViewsFactory(Context context, Intent intent) {
        this.context = context;
        texts = intent.getStringArrayExtra("texts");
        appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);

        context.registerReceiver(receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                texts = intent.getStringArrayExtra("texts");
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
                appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetListTexts);
            }
        }, new IntentFilter(UPDATE_WIDGET_ACTION));
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public RemoteViews getLoadingView() {
        return null;
    }

    @Override
    public RemoteViews getViewAt(int position) {
        RemoteViews remoteView = new RemoteViews(context.getPackageName(),
                position == 0 ?
                        R.layout.app_widget_textview1
                        : R.layout.app_widget_textview2);

        remoteView.setTextViewText(
                position == 0 ?
                        R.id.widgetText1
                        : R.id.widgetText2,
                texts[position]);

        return remoteView;
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public void onCreate() {
    }

    @Override
    public void onDataSetChanged() {
    }

    @Override
    public void onDestroy() {
        context.unregisterReceiver(receiver);
    }
}

and WidgetService.java:

public class WidgetService extends RemoteViewsService {

    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WidgetRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}

Solution

  • Fixed by remove the broadcasts and just saving the last dataset in updateAppWidget() and after calling appWidgetManager.updateAppWidget call also appWidgetManager.notifyAppWidgetViewDataChanged to update the list. In the factory just load the last dataset from file.