Search code examples
javaandroidandroid-listviewwidget

Android Widget with collection (ListView) not displaying items


I am trying to do a simple widget for my app, which displays data from an API on the widget using ListView, but the list is empty even tho Logs says that the data was downloaded successfully. Blank listView is displayed with no data in it. No errors are shown. What's the cause of it?

Resources I used when tried to solve this problem:

official documentation - https://developer.android.com/guide/topics/appwidgets#collections

similar question - How to use listview in a widget?

my AppWidgetProvider

 public class Widget extends AppWidgetProvider {
    RemoteViews views;

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

        CharSequence widgetText = context.getString(R.string.appwidget_text);
        // Construct the RemoteViews object
        views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // 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) {
            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget);
            Intent intent = new Intent(context, WidgetService.class);
            remoteViews.setRemoteAdapter(R.id.appwidget_list_view, intent);
            appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
        }
    }

WidgetFactory and WidgetService


public class WidgetFactory implements RemoteViewsService.RemoteViewsFactory{
    final Context context;
    List<SimpleCoin> list;
    List<Coin> coinList;

    public WidgetFactory(Context context, Intent intent) {
        this.context = context;
    }

    @Override
    public void onCreate() {
        //get data from database
        CoinDatabase coinDatabase = CoinDatabase.buildDatabase(context);
        list = coinDatabase.coinDao().getAllSimpleCoinsNonLive();
        coinList = new ArrayList<>();
    }

    @Override
    public void onDataSetChanged() {
        //based on data from database download content (JSON)
        if(list != null) {
            Log.d("Widget", "Coin size -> " + list.size());
            StringBuilder id = new StringBuilder("id=" + list.get(0).getId());
            for (int j = 0; j < list.size(); j++) {
                id.append(",");
                id.append(list.get(j).getId());
            }
            String finalUrl = URL + id.toString() + API_KEY;
            Observable.fromCallable(() -> {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                java.net.URL url1 = new URL(finalUrl);
                connection = (HttpURLConnection) url1.openConnection();
                connection.connect();
                InputStream stream = connection.getInputStream();
                reader = new BufferedReader(new InputStreamReader(stream));
                StringBuilder buffer = new StringBuilder();
                String line = "";
                while ((line = reader.readLine()) != null) {
                    buffer.append(line).append("\n");
                }
                connection.disconnect();
                reader.close();
                return buffer.toString();
            }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {
                }
                @Override
                public void onNext(@NonNull String s) {
                    if (s != null) {
                        try {
                            //Create new coin and update list of coins
                            JSONObject object = new JSONObject(s);
                            for (int i = 0; i < list.size(); i++) {
                                JSONObject jsonObject = object.getJSONObject("data").getJSONObject(String.valueOf(list.get(i).getId()));
                                Log.d("Widget", "Coin -> " + jsonObject.getString("name") +  jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price") + jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"));
                                Coin coin = new Coin(jsonObject.getString("name"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("price"), jsonObject.getJSONObject("quote").getJSONObject("USD").getDouble("percent_change_7d"), false);
                                coinList.add(coin);
                                getViewAt(i);
                            }

                        } catch (JSONException e) {
                            Log.d("Widget", "Coin -> " + e.toString());
                            e.printStackTrace();
                        }
                    }
                }

                @Override
                public void onError(@NonNull Throwable e) {
                }

                @Override
                public void onComplete() {
                }
            });
        }
    }

    @Override
    public void onDestroy() {}

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

    @Override
    public RemoteViews getViewAt(int i) {
        //Set ListData based on coinList
        Log.d("Widget", "Coin size getViewAt -> " + list.size() + "item num ->" + i);
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.my_widget_list_item);
        if(coinList !=  null)
        if(coinList.size() > i) {
            Log.d("Widget", "Coin position added" + coinList.get(i).getName());
            remoteViews.setTextViewText(R.id.widgetItemTaskNameLabel, coinList.get(i).getName());
        }
        return remoteViews;
    }

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

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

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

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

public class WidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new WidgetFactory(this.getApplicationContext(), intent);
    }
}

Here are my logs:


D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin -> testCoin0.00.0

D/Widget: Coin size getViewAt -> 1 item num ->0

D/Widget: Coin position added testCoin

my_widget.xml


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:tools="http://schemas.android.com/tools"
    android:background="@color/colorPrimary"
    android:padding="@dimen/widget_margin">
    <TextView
        android:id="@+id/appwidget_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_margin="8dp"
        android:background="@color/colorPrimary"
        android:contentDescription="@string/appwidget_text"
        android:text="@string/appwidget_text"
        android:textColor="#ffffff"
        android:textSize="24sp"
        android:textStyle="bold|italic" />
    <ListView
        android:id="@+id/appwidget_list_view"
        android:layout_width="match_parent"
        android:layout_margin="8dp"
        tools:listitem="@layout/my_widget_list_item"
        android:layout_height="wrap_content"
        android:background="@color/colorAccent"
        android:layout_below="@id/appwidget_text">
    </ListView>
</RelativeLayout>

my_widget_list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:paddingLeft="@dimen/widget_listview_padding_x"
    android:paddingRight="@dimen/widget_listview_padding_x"
    android:paddingStart="@dimen/widget_listview_padding_x"
    android:paddingEnd="@dimen/widget_listview_padding_x"
    android:minHeight="@dimen/widget_listview_item_height"
    android:weightSum="2"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/widgetItemTaskNameLabel"
        android:layout_width="0dp"
        android:gravity="start"
        android:layout_weight="1"
        android:textColor="@color/colorPrimary"
        android:layout_gravity="center_vertical"
        android:layout_height="wrap_content" />

</LinearLayout>


Solution

  • I found my mistake, turns out my API call shouldn't be asynchronous because as the documentation says:

    public void onDataSetChanged() -> You can do heaving lifting in here, synchronously. For example, if you need to process an image, fetch something from the network, etc., it is ok to do it here, synchronously. The widget will remain in its current state while work is being done here, so you don't need to worry about locking up the widget.

    So getting my data from Asynchronous call was the reason why data wasn't displayed