boztalay
boztalay

Reputation: 562

Android Widget with ImageView is Invisible Until I Start Moving It

I've got a widget with an ImageView, whose source I'm setting with a URI to an image on the user's device. I set it with RemoteViews.setImageViewUri() in the onUpdate of the widget's provider. The strangest thing is happening: I can't see the widget or the image while the widget is stationary on my home screen, but when I pick it up and start moving it, the image appears!

Here are some screen shots illustrating this:

enter image description here enter image description here

Here's the layout for the widget:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/picture_widget_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/widget_margin" >

    <ImageView
        android:id="@+id/the_picture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="@string/image_content_description"
        android:scaleType="centerCrop" />

</RelativeLayout>

And here's my AppWidgetProvider:

public class WidgetProvider extends AppWidgetProvider {

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

        updateAllWidgets(context, appWidgetManager, appWidgetIds);
    }

    private void updateAllWidgets(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        PuppyFramePersistenceManager persistenceManager = new PuppyFramePersistenceManager(context);

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

            RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.puppyframe_widget);
            Intent configIntent = new Intent(context, AlbumsActivity.class);

            Uri.withAppendedPath(Uri.parse("pw" + i + "://widget/id/"), String.valueOf(appWidgetId));
            configIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

            PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
            remoteViews.setOnClickPendingIntent(R.id.picture_widget_parent, configPendingIntent);

            String currentAlbumId = persistenceManager.getCurrentAlbumIdForAppWidgetId(appWidgetId);
            if(currentAlbumId != null) {
                Uri imageUri = Uri.parse(persistenceManager.getAlbumWithId(currentAlbumId).getImagePaths().get(0));
                remoteViews.setImageViewUri(R.id.the_picture, imageUri);
            }

            appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
        }
    }
}

This is my AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.boztalay.puppyframe"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity android:name="com.boztalay.puppyframe.configuration.albums.AlbumsActivity" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>

        <activity android:name="com.boztalay.puppyframe.configuration.editalbum.EditAlbumActivity" />

        <receiver android:name="com.boztalay.puppyframe.widget.PuppyFrameWidgetProvider" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/puppyframe_info" />
        </receiver>

        <service android:name="com.boztalay.puppyframe.widget.ScreenOnService"></service>
    </application>

</manifest>

And lastly, this is my widget provider info:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:updatePeriodMillis="21600000"
    android:initialLayout="@layout/puppyframe_widget"
    android:configure="com.boztalay.puppyframe.configuration.albums.AlbumsActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>

Thanks!

Upvotes: 1

Views: 1411

Answers (2)

boztalay
boztalay

Reputation: 562

The issue ultimately has to do with the size of the images. With the very large images that the phone's camera takes, it would cause this bug. But with smaller ones on the phone, they appeared just fine. What I had to do was actually resize and cache the images that are too big, which I deemed to be 75% of the phone's screen's resolution.

Upvotes: 0

Dan Hulme
Dan Hulme

Reputation: 15290

The way the AppWidgetManager interacts with your AppWidgetProvider is not as documented. The documentation claims that if you have a configure activity, onUpdate is not called.

In fact, onUpdate is called before the configure activity starts. It's not called when the activity finishes. Looking at your onUpdate, it appears that if the appwidget isn't configured yet, it leaves the ImageView blank. Even without seeing your configure activity, I guess that what's going on is this:

  • AppWidgetManager creates the new widget and calls onUpdate. Because the new widget is not yet configured, you don't set the ImageView to an image.
  • The configure activity runs and sets RESULT_OK. The widget is now configured, but hasn't been updated again, so the ImageView is still blank.
  • When you resize or move the widget, the AppWidgetManager calls onUpdate again. This time, your code reads the widget configuration and sets the ImageView. Now the widget shows the chosen image.

The answer is that when your configure activity finishes, it should send an intent to your AppWidgetProvider to have it update again. This isn't a panacea, though, as there are other bugs in AppWidgetProvider. For example, if you do an orientation change while the configure activity is running, restarting it, the AppWidgetProvider hides the appwidget from the screen, even after you finish and return RESULT_OK. Changing the orientation again while the launcher is showing (after the configure activity has finished) makes the appwidget appear.

The reason I know all this is that I've had the same problems writing a similar app, which may be helpful to you if you're struggling with this functionality. Showr displays images on the home screen or lock screen. The images can be chosen from other apps on the device (such as the Gallery), downloaded from a URL, or updated from an RSS or Atom feed. It even scales the images to an appropriate size and can crop them to fit the appwidget. It's a free download from Google Play.

Upvotes: 1

Related Questions