androidwidgetremoteview

Replace RemoteViewsFactory on receiving a broadcast in widget provider


I have an app with a widget showing a listview. I want to update the list based on the recipe that user opens in the app.

To do that first I send a broadcast with an extra integer when activity is open.

Then in widget provider I am setting remote adapter with an intent containing integer received from broadcast.

Now I would expect new RemoteViewsFactory to be created each time so I can extract an integer from an intent and load different list based on this number.

The problem is this only happens when an app is first open, every other time only onDataSetChanged() in MyWidgetRemoteViewsFactory is called so I cannot get the recipe number to update data correctly. The list in widget never gets updated.

How to force widget to recreate RemoteViewsFactory? Based on the other topic on stackoverflow I have tried passing null in appWidgetManager.updateAppWidget(appWidgetId1, null) - this didn't work.

MainListActivity.java - send a broadcast

@Override
public void onItemClickListener(int itemID) {

    Intent recipeIntent = new Intent(this, BakingWidgetProvider.class);
    recipeIntent.setAction(BakingWidgetProvider.UPDATE_WIDGET_RECIPE);
    recipeIntent.putExtra(StepsListActivity.EXTRA_RECIPE_ID, itemID);
    sendBroadcast(recipeIntent);

    Intent intent = new Intent(MainListActivity.this, StepsListActivity.class);
    intent.putExtra(StepsListActivity.EXTRA_RECIPE_ID, itemID);
    startActivity(intent);

}

BakingWidgetProvider.java - Receive broadcast

@Override
public void onReceive(Context context, Intent intent) {

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);


    if (intent.getAction().equals(UPDATE_WIDGET_RECIPE)) {

        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.baking_widget);

        int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        int viewIndex = intent.getIntExtra(StepsListActivity.EXTRA_RECIPE_ID, 0);

        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, BakingWidgetProvider.class));

        for (int appWidgetId1 : appWidgetIds) {
             //trying to pass null to clear the data ?
            appWidgetManager.updateAppWidget(appWidgetId1, null);
        }

        Intent intent2 = new Intent(context, MyWidgetRemoteViewsService.class);
        Bundle bundle = new Bundle();
        bundle.putInt(StepsListActivity.EXTRA_RECIPE_ID,viewIndex );
        intent2.putExtras(bundle);

        remoteViews.setRemoteAdapter(R.id.list_view, intent2);

        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.list_view);

        //empty view
        remoteViews.setEmptyView(R.id.list_view, R.id.empty_view);

        for (int appWidgetId1 : appWidgetIds) {

           appWidgetManager.updateAppWidget(appWidgetId1, remoteViews);
        }

    }
    super.onReceive(context, intent);
}

MyWidgetRemoteViewsService.java - get an integer from an intent to update data properly

public class MyWidgetRemoteViewsService extends RemoteViewsService {

private static final String MyOnClick = "myOnClickTag";

@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {

    return new MyWidgetRemoteViewsFactory(this.getApplicationContext(), intent);
}

class MyWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

    List<Recipe> recipeList;
    private Context mContext;
    private int mRecipeOpen;

    public MyWidgetRemoteViewsFactory(Context context, Intent intent) {

        mRecipeOpen =  intent.getExtras().getInt(StepsListActivity.EXTRA_RECIPE_ID);
        mContext = context;

    }
    @Override
    public void onCreate() {
    }

    @Override
    public void onDataSetChanged() {
        recipeList = AppDatabase.getInstance(getApplicationContext()).recipeDao().getAll();
    }

    @Override
    public void onDestroy() {
    }

    @Override
    public int getCount() {
        return recipeList.get(mRecipeOpen).ingredients.size();
    }

    @Override
    public RemoteViews getViewAt(int position) {

        RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item);

        rv.setTextViewText(R.id.quantity, recipeList.get(mRecipeOpen).ingredients.get(position).getQuantity());
        rv.setTextViewText(R.id.measurement, recipeList.get(mRecipeOpen).ingredients.get(position).getMeasure());
        rv.setTextViewText(R.id.ingredient, recipeList.get(mRecipeOpen).ingredients.get(position).getIngredient());

        return rv;
    }

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

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

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

    }

    @Override
    public boolean hasStableIds() {
        return true;

    }

}

}


Solution

  • In the end I had to change my approach.

    I have placed MyWidgetRemoteViewsFactory in a separate class and extended BroadcastReceiver. Here in onReceive method I can retrieve the broadcast with the required integer that is sent from BakingWidgetProvider.