Search code examples
androidlistviewwidgetandroid-appwidget

notifyappwidgetviewdatachanged not working on a listview on my widget


I tried lots of solutions but after weeks i have not been able to solve this issue: why "notifyappwidgetviewdatachanged" doesn't work? how can i update a listview placed on my widget? Where am i wrong?

Here are my classes.

Widget Provider:

public class Widget_Provider extends AppWidgetProvider
{
public static final String ACTION_MOSTRAORARI = "fACTION_TOAST";
public static final String EXTRA_STRING = "EXTRA_STRING";

@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)
    {
        RemoteViews views = updateWidgetListView(context, appWidgetId);

        final Intent onItemClick = new Intent(context, Widget_Provider.class);
        onItemClick.setAction(ACTION_MOSTRAORARI);
        onItemClick.setData(Uri.parse(onItemClick.toUri(Intent.URI_INTENT_SCHEME)));
        final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onItemClick, PendingIntent.FLAG_UPDATE_CURRENT);
        views.setPendingIntentTemplate(R.id.myStopList, onClickPendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}

public RemoteViews updateWidgetListView(Context context, int appWidgetId)
{
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);
    Intent svcIntent = new Intent(context, Widget_Service.class);
    svcIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
    svcIntent.setData(Uri.parse(svcIntent.toUri(Intent.URI_INTENT_SCHEME)));
    remoteViews.setRemoteAdapter(R.id.myStopList, svcIntent);
    return remoteViews;
}

@Override
public void onReceive(Context context, Intent intent)
{
    if (intent.getAction().equals(ACTION_MOSTRAORARI)) {

        if (MainUtils.isNewtworkAvailable(context)) 
        {
            String item = intent.getExtras().getString(EXTRA_STRING);
            Intent intentOrari = new Intent(context, Diag_MostraOrari.class);
            intentOrari.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            context.startActivity(intentOrari);
        }
    }

    super.onReceive(context, intent);
}

@Override
public void onDeleted(Context context, int[] appWidgetIds) {}

@Override
public void onEnabled(Context context) {}

@Override
public void onDisabled(Context context) {}
}

Widget Service:

public class Widget_Service extends RemoteViewsService
{

@Override
public RemoteViewsFactory onGetViewFactory(Intent intent)
{
    Map<String, ArrayList<String>> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

    /* ---
    * Here i fetch data from a local db and store it on "map"
    */ ---

    return (new Widget_ListProvider(this.getApplicationContext(), intent, map));
}

}

ListProvider:

public class Widget_ListProvider implements RemoteViewsFactory
{
private Map<String, ArrayList<String>> map = new HashMap<>();
private ArrayList<ListItem_Widget> listItemList = new ArrayList<>();
private Context context = null;
private int appWidgetId;

public Widget_ListProvider(Context context, Intent intent, Map<String, ArrayList<String>> map) 
{
    this.map = map;
    this.context = context;
    appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);

    populateListItem();
}

//This function populate the arraylist "listItemList" by the data stored on "map"
private void populateListItem() { [...] }

@Override
public int getCount() {
    return listItemList.size();
}

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

@Override
public RemoteViews getViewAt(int position)
{
    final RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.listitem_widget);
    ListItem_Widget listItem = listItemList.get(position);
    remoteView.setTextViewText(R.id.heading, listItem.heading);
    remoteView.setTextViewText(R.id.content, listItem.content);

    final Intent fillInIntent = new Intent();
    fillInIntent.setAction(Widget.ACTION_MOSTRAORARI);
    final Bundle bundle = new Bundle();
    bundle.putString(Widget.EXTRA_STRING, listItem.heading);
    fillInIntent.putExtras(bundle);
    remoteView.setOnClickFillInIntent(R.id.listrow, fillInIntent);

    return remoteView;
}


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

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

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

@Override
public void onCreate() {}

@Override
public void onDataSetChanged() {}

@Override
public void onDestroy() {}

}

The xml of my listview custom item contains just two textviews: "heading" and "content".

Where am i wrong? why when i call "notifyappwidgetviewdatachanged" from another activity nothing happens?

[EDIT]

That's the activity where i need to update my widget.

public class Diag_Line extends AppCompatActivity 
{
    //[...]

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        //[...]
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
        ComponentName thisAppWidget = new ComponentName(getApplicationContext().getPackageName(), Widget.class.getName());
        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
        appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);

        //"myStopList" is the id of my listview inside the widget xml.

        //[...]
    }
}

Solution

  • Well, the problem is a quite obvious: In your RemoteViewsFactory you have an empty method onDataSetChanged(). But when you're triggering notifyAppWidgetViewDataChanged(), you're getting callback in onDataSetChanged(). For better understanding check this pic. enter image description here

    If Diag_Line is a Configuration Activity just make something like this:

     appWidgetManager.updateAppWidget(appWidgetIds, views); // views is a RemoteViews that you need to build
     appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.myStopList);
    

    Also check official documentation here

    UPDATE Some hints for you. If you're working with widget, make all fetching data and fillings RemoteViews on background (IntentService is a nice approach). So just make IntentService and make logic for widget here. Don't forget to make him foreground (only for Android O), if you're triggering your updates by broadcasts. Also you can check library that makes all for you.