Search code examples
androidlistviewandroid-widgetremoteview

Same widgets with different ListView data


I have a simple AppWidget that has ListView.
The widget have simple config Activity, where you select which set of data you want to display. That info (int id) is stored into SharedPreferences and later get from SP in RemoteViewsFactory.
All is working perfectly, until I add another Widget to the screen. The new one doesn't load different data if selected, but rather takes the one from previous Widget.
With little debugging I think that my onUpdate method is't being called, but I cannot find the problem here.

WidgetConfig.java

public class IngredientsWidgetConfigureActivity extends Activity implements RecipesAdapter.RecipesAdapterOnClickHandler {


int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
RecipesAdapter mAdapter;


public IngredientsWidgetConfigureActivity(){
    super();
}

//save recipeId into SharedPrefs
static void saveSelectedRecipe (Context context, int appWidgetId, int selectedRecipe){
    SharedPreferences.Editor prefs = context.getSharedPreferences(AppConstants.PREFS_NAME, 0).edit();
    prefs.putInt(AppConstants.PREF_PREFIX_KEY + appWidgetId, selectedRecipe);
    prefs.apply();
}


@Override
protected void onCreate(@Nullable Bundle icicle) {
    super.onCreate(icicle);

    setResult(RESULT_CANCELED);

    setContentView(R.layout.ingredients_widget_configure);

    //setup RecyclerView
    ...

    Intent intent = getIntent();
    Bundle extras = intent.getExtras();

    if (extras != null){
        mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
    }
    if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID){
        finish();
        return;
    }

}

@Override
public void onClick(Recipe recipe) {
    final Context context = IngredientsWidgetConfigureActivity.this;

    // When the button is clicked, store the recipe Id
    int selectedRecipe = recipe.id;
    saveSelectedRecipe(context, mAppWidgetId, selectedRecipe);

    // It is the responsibility of the configuration activity to update the app widget
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    IngredientsWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);

    // Make sure we pass back the original appWidgetId
    Intent resultValue = new Intent();
    resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
    setResult(RESULT_OK, resultValue);
    finish();
}

WidgetProvider

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

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

    Intent intent = new Intent(context, WidgetRemoteViewsService.class);
    intent.putExtra(AppConstants.APP_WIDGET_ID_KEY, appWidgetId);
    views.setRemoteAdapter(R.id.widget_list_view, intent);


    //TODO: recipes are not changing in different widgets

    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views);
}


@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);


    }

}

And finally my
RemoteViewsFactory.java

public WidgetRemoteViewsFactory (Context context, Intent intent){
    mContext = context;
    appWidgetId = intent.getIntExtra(AppConstants.APP_WIDGET_ID_KEY, 1);
}

@Override
public void onCreate() {

}

@Override
public void onDataSetChanged() {

    final long identityToken = Binder.clearCallingIdentity();

//get id from SharedPref
    RecipeDatabase mDb = RecipeDatabase.getInstance(mContext);
    SharedPreferences prefs = mContext.getSharedPreferences(AppConstants.PREFS_NAME, 0);
    recipeId = prefs.getInt(AppConstants.PREF_PREFIX_KEY + appWidgetId, 9999);


    recipe = mDb.recipeDAO().loadRecipe(recipeId);


    Binder.restoreCallingIdentity(identityToken);


}

@Override
public void onDestroy() {

}

@Override
public int getCount() {
    if (recipe == null) {
        return 0;
    } else {
        return recipe.ingredients.size();
    }
}

@Override
public RemoteViews getViewAt(int position) {
    if (position > recipe.ingredients.size()){
        return null;
    }


    RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget_list_item);
    remoteViews.setTextViewText(R.id.widget_text, recipe.ingredients.get(position).getIngredient());

    return remoteViews;
}

@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

  • Solved... I had to put new data into the Intent in updateAppWidget so the Factory loads new instance instead of reusing new one.
    So I am sending new random value data with every widget, so it updates properly.
    updateAppWidget snippet:

    //send intent and data to Adapter
        Intent intent = new Intent(context, WidgetRemoteViewsService.class);
        intent.putExtra(AppConstants.APP_WIDGET_ID_KEY, appWidgetId);
    
        //set random data to initialize new Factory
        Random rnd = new Random();
        intent.setData(Uri.fromParts("content", String.valueOf(rnd.nextInt()), null));
    
        views.setRemoteAdapter(R.id.widget_list_view, intent);
    

    Thanks to @TheFedex87