0

Dudes,

my Android widget displays only one record from my database. I'm sure that more than one item is retrieved from the database.

WidgetProvider's onUpdate method:

public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) {
    super.onUpdate(context, appWidgetManager, appWidgetIds);

    if (items == null) {
        items = new ItemsData(new DbHelper(context));
    }

    final int N = appWidgetIds.length;

    Item[] iii = items.getItemsArray();
    Log.d(TAG, "got " + iii.length + " items from db");

    ComponentName thisWidget = new ComponentName(context, WidgetProvider.class);

    for (int i = 0; i < N; i++) {
        int appWidgetId = appWidgetIds[i];

        Log.d(TAG, "updating widget " + appWidgetId);

        RemoteViews rv = new RemoteViews(context.getPackageName(),
                R.layout.widgetlayout);

        for (Item item : iii) {

            Log.d(TAG, "updating item " + item.id + ", " + item.title);

            RemoteViews views = new RemoteViews(context.getPackageName(),
                    R.layout.row);
            views.setTextViewText(R.id.textViewShortName, item.title);
            views.setTextViewText(R.id.textViewDesc, item.description);
            views.setTextViewText(R.id.textViewDue, new StringBuilder()
                    .append(item.dueDate).toString());
            rv.addView(R.id.llWidget, views);
        }

        appWidgetManager.updateAppWidget(thisWidget, rv);

    }

}

And my layout is just:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/llWidget"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:background="#ff99ff">

    <include
        android:id="@+id/row"
        layout="@layout/row" />

</LinearLayout>

with 'row' being three TextView items, arranged to my needs.

What happens is this: When I add items to the db and widget updates, only the last item can be seen. It is placed on the top.

I've searched android widget guide, stack overflow, some other websites, tutorial from a book, but even though some have presented similar examples, I can't get it to work... Google Android widget guide shows how to use collections, but it's only for 3.0+. Learning Android book describes a widget with elements added as rows, so I followed it - but couldn't make any progress... People asked about getting data from db from within widget (eg. Android: get Widget Data from database ) and, to my knowledge, had not came across such problem. And their solutions don't work for me.

Right now I don't even know how to look for an error... Strange thing is, in LogCat I can see that "updating item X" lines.

Any ideas?

UPDATE 1. Updated method (relevant portion) after Sergey Benner's info:

    Item[] iii = items.getItemsArray();
    for (int i = 0; i < N; i++) {
        int appWidgetId = appWidgetIds[i];
        RemoteViews rv = new RemoteViews(context.getPackageName(),
                R.layout.widgetlayout);
        for (Item item : iii) {
            RemoteViews views = new RemoteViews(context.getPackageName(),
                    R.layout.row);
            views.setTextViewText(R.id.textViewShortName, item.title);
            views.setTextViewText(R.id.textViewDesc, item.description);
            views.setTextViewText(R.id.textViewDue, new StringBuilder()
                    .append(item.dueDate).toString());
            rv.addView(R.id.llWidget, views);
        }

        appWidgetManager.updateAppWidget(appWidgetId, rv);
    }

UPDATE 2.

AndroidManifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.whatnot.todoex"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:debuggable="true"
        android:name=".ToDoExApp" >

        <activity
            android:label="@string/app_name"
            android:name=".ListItemsActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <activity android:label="@string/titleAdd" android:name=".AddItemActivity" />

        <activity android:label="@string/titleRemindments" android:name=".RemindmentListActivity" />

        <activity android:label="@string/titleAddRemindment" android:name=".AddRemindmentActivity" />

        <activity android:label="@string/titleSetDate" android:name=".ChooseDateActivity" />

        <receiver android:name=".WidgetProvider" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data android:name="android.appwidget.provider"
                    android:resource="@xml/widgetprovider" />
        </receiver>

    </application>

</manifest>

widgetprovider.xml:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="294dp"
    android:minHeight="160dp"
    android:updatePeriodMillis="3000"

    android:initialLayout="@layout/widgetlayout" >

</appwidget-provider>

row.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textViewShortName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textViewDesc"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_weight="0.80"
            android:textAppearance="?android:attr/textAppearanceSmall" />

        <TextView
            android:id="@+id/textViewDue"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:textAppearance="?android:attr/textAppearanceSmall" />
    </LinearLayout>

</LinearLayout>

and updated onUpdate method at the beginning of this post. There is not much else in the WidgetProvider: an empty constructor, onEnabled with just super.onEnabled.. Also: I've put whole widget layout and row xmls.

UPDATE 3. Now, each time an item is changed i run this method:

private void updateWidget(Item item) {
    Log.d(TAG, "updateWidget(). Sending broadcast to widget.");

    Intent intent = new Intent(WidgetProvider.UPDATE);
    intent.putExtra(WidgetProvider.UPDATE_ITEM, item.id);
    sendBroadcast(intent);
}

My widget's onReceived looks like this now:

public void onReceive(Context context, Intent intent) {

    if (intent.getAction().equals(UPDATE)) {
        Log.d(TAG, "onReceived detected new update");
        AppWidgetManager appWidgetManager = AppWidgetManager
                .getInstance(context); // 13
        this.onUpdate(context, appWidgetManager, appWidgetManager
                .getAppWidgetIds(new ComponentName(context,
                        WidgetProvider.class)),
                intent.getExtras().getLong(WidgetProvider.UPDATE_ITEM));
    } else {
        super.onReceive(context, intent);
    }
}

And a special, modified version of onUpdate is like this:

public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds, long itemId) {
    super.onUpdate(context, appWidgetManager, appWidgetIds);

    if (items == null) {
        items = ((ToDoExApp) ((ContextWrapper) context).getBaseContext()).itemsData;
    }

    Item item = items.getItem2(itemId);
    if (item == null) {
        Log.d(TAG, "Item with id = " + itemId + " not found.");
        return;
    }

    final int N = appWidgetIds.length;
    for (int i = 0; i < N; i++) {
        int appWidgetId = appWidgetIds[i];

        Log.d(TAG, "updating widget " + appWidgetId);

        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.row);
        views.setTextViewText(R.id.textViewShortName, item.title);
        views.setTextViewText(R.id.textViewDesc, item.description);
        views.setTextViewText(R.id.textViewDue,
                new StringBuilder().append(item.dueDate).toString());
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

}

Now, whenever I change, add an item to the database, information about this is broadcasted to the widget. Then it is received and processed. However, only this item is displayed on the widget. Yamba application (from Learning Android) has something very similar - each time an update is found, it is being added to the widget. The only serious difference that I see is that Yamba uses content resolver and I use database...

Community
  • 1
  • 1
Genkaku
  • 191
  • 1
  • 2
  • 7

1 Answers1

0

I used the following to add items to my widget as as sample:

    for(Server srv:mylist){
      newView = new RemoteViews(context.getPackageName(), R.layout.widget_row);
      newView.setTextViewText(R.id.servername,srv.getServerName());
      newView.setTextViewText(R.id.serverstatus,srv.getServerStatus());
      remoteViews.addView(R.id.widget,newView);
    }

this basically adds a "row" newView to the remoteViews

edit:

Running widget on 2.2. The widget can be stretched depending on your launcher of course.

EDIT:

try and use the following in the onUpdate()


    ComponentName thisWidget = new ComponentName(context, MyWidget.class);
    Item[] iii = items.getItemsArray();
    RemoteViews rv = new RemoteViews(context.getPackageName(),
            R.layout.widgetlayout);
    for (Item item : iii) {
        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.row);
        views.setTextViewText(R.id.textViewShortName, item.title);
        views.setTextViewText(R.id.textViewDesc, item.description);
        views.setTextViewText(R.id.textViewDue, new StringBuilder()
                .append(item.dueDate).toString());
        rv.addView(R.id.llWidget, views);
    }

    appWidgetManager.updateAppWidget(thisWidget, rv);

I still dont understand what's the point in this

        for (int i = 0; i < N; i++) {
             int appWidgetId = appWidgetIds[i];

WORKING EXAMPLE: you might still want to implement a service


public class StatusWidget extends AppWidgetProvider {
     LayoutInflater  inflater;
    int[] appWidgetIds;
    AppWidgetManager appWidgetManager;
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
                         int[] appWidgetIds) {
        this.appWidgetIds=appWidgetIds;
        this.appWidgetManager=appWidgetManager;
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new MyTime(context, appWidgetManager), 1, 30000); //30 seconds timeout
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            final int appWidgetId = intent.getExtras().getInt(
                                AppWidgetManager.EXTRA_APPWIDGET_ID,
                                AppWidgetManager.INVALID_APPWIDGET_ID);

            if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
                this.onDeleted(context, new int[]{appWidgetId});
            }
        } else {
            super.onReceive(context, intent);
        }
    }

    private class MyTime extends TimerTask {
        RemoteViews remoteViews;
        AppWidgetManager appWidgetManager;
        ComponentName thisWidget;
        Context context;
        public MyTime(Context context, AppWidgetManager appWidgetManager) {
            this.context = context;
            this.appWidgetManager = appWidgetManager;
            remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
            thisWidget = new ComponentName(context, StatusWidget.class);
        }

        @Override
        public void run() {
            remoteViews.removeAllViews(R.id.widget);
             Date date = new Date();
            DateFormat format = SimpleDateFormat.getTimeInstance(
            SimpleDateFormat.LONG, Locale.getDefault());        
            InputStream is = null;
            ImageAdapter myServers =null;
               List<Server> mylist =null;
            try {
                is = new ByteArrayInputStream(Utils.fetch("http://mystatusserver/status", null).toByteArray());
            } catch (Exception cte) {
            }

            try {
                String l = Utils.readInputStream(is);
             List<Server> list= Utils.parseServers(l);
             mylist=   Utils.loadMyServerList(context, list);
            } catch (Exception e) {
                e.printStackTrace();
            }
             RemoteViews newView =null;
            for(Server srv:mylist){
              newView = new RemoteViews(context.getPackageName(), R.layout.widget_row);
              newView.setTextViewText(R.id.servername,srv.getServerName());
              newView.setTextViewText(R.id.serverstatus,srv.getServerStatus());
              remoteViews.addView(R.id.widget,newView);
            }
            newView = new RemoteViews(context.getPackageName(), R.layout.datetime);
            newView.setTextViewText(R.id.time,"Last Updated At: "+format.format(date));
            remoteViews.addView(R.id.widget,newView);
           appWidgetManager.updateAppWidget(thisWidget, remoteViews);

        }
    }

}

hope it helps abit

Sergey Benner
  • 4,421
  • 2
  • 22
  • 29
  • Thanks, however for some reason I can no longer see any item on the widget. The widget is just blank. My widget layout is just a LinearLayout with 'include row'. And I do develop for Android 2.2. – Genkaku Feb 12 '12 at 08:40
  • you have to put it into your question please. it's impossible to read. – Sergey Benner Feb 12 '12 at 12:55
  • Unfortunately not a single row is displayed in the widget :( – Genkaku Feb 12 '12 at 13:24
  • Did you debug it? have you registered the udpate it in the Manifest file correctly? how your `appwidget-provider` looks like? show everything. your manifest how you registered it. and show you widget then. – Sergey Benner Feb 12 '12 at 13:29
  • I've posted additions. I've no ideas on where to start debugging. LogCat shows me that items are being retrieved from the db and processed in the loop. – Genkaku Feb 12 '12 at 13:59
  • you might want to read this http://stackoverflow.com/questions/2078122/android-widget-not-updating and I suggest you implement a service or a thread which will update your widget more often. this `android:updatePeriodMillis="3000"` will not work. – Sergey Benner Feb 12 '12 at 16:00
  • thanks for the info about widget updating frequency. I've modified my code and updated the question. I've tried to post one item to the widget at a time. However this approach has failed me. – Genkaku Feb 16 '12 at 10:50