12

I'm developing a simple Widget to my Android app based on the Google StackWidget sample: https://android.googlesource.com/platform/development/+/master/samples/StackWidget/src/com/example/android/stackwidget/StackWidgetService.java

I'm using the Glide image library and trying to populate an ImageView on getViewAt method of StackWidgetService class that extends RemoteViewsService. My code is similar to:

Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(() ->
    Glide.with(context)
        .asBitmap()
        .load(widgetItems.get(position).image_url)
        .into(new SimpleTarget<Bitmap>(512, 512) {
            @Override
            public void onResourceReady(Bitmap bitmap, Transition transition) {
                rv.setImageViewBitmap(R.id.widget_item_image, bitmap);
            }
        })
);

What's the best way of loading images from an URL to populate a RemoteView from an Android Widget?

Elletlar
  • 3,136
  • 7
  • 32
  • 38
notGeek
  • 1,394
  • 5
  • 21
  • 40
  • 1
    Don't do it asynchronously (`onResourceReady()`). When you return the `RemoteViews`, they have to be completely filled in. – CommonsWare Dec 27 '17 at 13:59

3 Answers3

15

Glide has a construct for doing this called: AppWidgetTarget:

In Kotlin:

    val awt: AppWidgetTarget = object : AppWidgetTarget(context.applicationContext, R.id.img, remoteViews, *appWidgetIds) {
        override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
            super.onResourceReady(resource, transition)
        }
    }

    var options = RequestOptions().
            override(300, 300).placeholder(R.drawable.placeholder_img).error(R.drawable.error_img)

    Glide.with(context.applicationContext).asBitmap().load(imageUrl).apply(options).into(awt)

In Java:

   AppWidgetTarget awt = new AppWidgetTarget(context, R.id.img_dog, remoteViews, appWidgetIds) {
        @Override
        public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
            super.onResourceReady(resource, transition);
        }
    };

    RequestOptions options = new RequestOptions().
            override(300, 300).placeholder(R.drawable.placeholder_img).error(R.drawable.error_img)


    Glide.with(context.getApplicationContext())
            .asBitmap()
            .load(imageUrl)
            .apply(options)
            .into(awt);
Elletlar
  • 3,136
  • 7
  • 32
  • 38
  • You should use applicationContext to avoid having IllegalArgumentException on some devices while refreshing widget views – dipcore Apr 03 '19 at 14:45
  • I updated the Kotlin answer to use the application context in the 'with()' method. The Java answer is already using application context. Regards. – Elletlar Apr 05 '19 at 12:49
  • @dipcore Do you mean that exception -> java.lang.IllegalArgumentException: RemoteViews for widget update exceeds maximum bitmap memory usage? – Joks Oct 01 '21 at 15:17
  • No. This is a different issue. To solve the OOM, a smaller bitmap can be used or the image can be resized in Glide. [Glide — Image Resizing & Scaling](https://futurestud.io/tutorials/glide-image-resizing-scaling). – Elletlar Oct 01 '21 at 15:58
  • Unresolved reference: AppWidgetTarget – Nicholas Jela Oct 16 '21 at 11:20
  • Upgrade the Glide ver, AppWidgetTarget added in 3.5: [Glide Release Notes](https://github.com/bumptech/glide/releases?page=3) – Elletlar Jun 16 '22 at 20:18
14

Just need to do it synchronously. This seems to work fine:

    try {
        Bitmap bitmap = Glide.with(context)
                .asBitmap()
                .load(widgetItems.get(position).image_url)
                .submit(512, 512)
                .get();

        rv.setImageViewBitmap(R.id.widget_item_image, bitmap);
    } catch (Exception e) {
        e.printStackTrace();
    }
notGeek
  • 1,394
  • 5
  • 21
  • 40
0

Another example in java.

  static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                            int appWidgetId) {
    String url = "https://firebasestorage.googleapis.com/v0/b/telling-299f3.appspot.com/o/POSTS%2Fcropped1099087212.jpg?alt=media&token=6a3bca19-6483-484d-a7e3-64c25a8a77a8";

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

    Intent intent = new Intent(context, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

    AppWidgetTarget awt = new AppWidgetTarget(context, R.id.widget_post_image, views, appWidgetId) {
        @Override
        public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
            super.onResourceReady(resource, transition);
        }
    };

    RequestOptions options = new RequestOptions().
            override(300, 300).placeholder(R.drawable.post_placeholder).error(R.drawable.post_placeholder);


    Glide.with(context.getApplicationContext())
            .asBitmap()
            .load(url)
            .apply(options)
            .into(awt);
    //views.setImageViewBitmap(R.id.widget_post_image, bitmap);

    views.setOnClickPendingIntent(R.id.widget_post_image, pendingIntent);

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

my app_widget.xml file contains

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#09C"
android:padding="@dimen/widget_margin">

<ImageView
    android:id="@+id/widget_post_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:src="@drawable/placeholder_image_one" />
</LinearLayout>
Bukunmi
  • 2,504
  • 1
  • 18
  • 15