From 35f6d47c6caa0cbde5467370255f6976a6ebbd71 Mon Sep 17 00:00:00 2001 From: Andrei Date: Sun, 19 Apr 2020 12:32:11 -0700 Subject: [PATCH] Android widget image loading --- .../src/com/newsblur/util/ImageLoader.java | 152 ++++++------------ .../widget/WidgetRemoteViewsFactory.java | 25 ++- 2 files changed, 65 insertions(+), 112 deletions(-) diff --git a/clients/android/NewsBlur/src/com/newsblur/util/ImageLoader.java b/clients/android/NewsBlur/src/com/newsblur/util/ImageLoader.java index 1586852fd..b3bc23005 100644 --- a/clients/android/NewsBlur/src/com/newsblur/util/ImageLoader.java +++ b/clients/android/NewsBlur/src/com/newsblur/util/ImageLoader.java @@ -8,7 +8,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.app.Activity; -import android.appwidget.AppWidgetManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -74,21 +73,34 @@ public class ImageLoader { } } - public WidgetPhotoToLoad displayWidgetImage(int widgetId, AppWidgetManager appWidgetManager, String url, RemoteViews remoteViews, int imageViewId) { - if (url == null) { - remoteViews.setImageViewResource(imageViewId, emptyRID); - return null; + /** + * Synchronous background call coming from app widget on home screen + */ + public void displayWidgetImage(String url, int imageViewId, int maxDimPX, RemoteViews remoteViews) { + url = buildUrlIfNeeded(url); + + // try from memory + Bitmap bitmap = memoryCache.get(url); + if (bitmap != null) { + remoteViews.setImageViewBitmap(imageViewId, bitmap); + remoteViews.setViewVisibility(imageViewId, View.VISIBLE); + return; } - if (url.startsWith("/")) { - url = APIConstants.buildUrl(url); + // try from disk + bitmap = getImageFromDisk(url, maxDimPX, false, 0f); + if (bitmap == null) { + // try for network + bitmap = getImageFromNetwork(url, maxDimPX,false, 0f); } - WidgetPhotoToLoad widgetPhotoToLoad = new WidgetPhotoToLoad(widgetId, appWidgetManager, url, remoteViews, imageViewId); -// PhotoToLoad photoToLoad = new PhotoToLoad(url, imageView, roundRadius, cropSquare, maxDimPX, allowDelay); - - executorService.submit(new WidgetPhotoLoader(widgetPhotoToLoad)); - return widgetPhotoToLoad; + if (bitmap != null) { + memoryCache.put(url, bitmap); + remoteViews.setImageViewBitmap(imageViewId, bitmap); + remoteViews.setViewVisibility(imageViewId, View.VISIBLE); + } else { + remoteViews.setViewVisibility(imageViewId, View.GONE); + } } public PhotoToLoad displayImage(String url, ImageView imageView, float roundRadius, boolean cropSquare, int maxDimPX, boolean allowDelay) { @@ -97,9 +109,7 @@ public class ImageLoader { return null; } - if (url.startsWith("/")) { - url = APIConstants.buildUrl(url); - } + url = buildUrlIfNeeded(url); imageViewMappings.put(imageView, url); PhotoToLoad photoToLoad = new PhotoToLoad(url, imageView, roundRadius, cropSquare, maxDimPX, allowDelay); @@ -108,21 +118,6 @@ public class ImageLoader { return photoToLoad; } - public static class WidgetPhotoToLoad { - public int widgetId; - public AppWidgetManager appWidgetManager; - public String url; - public RemoteViews remoteViews; - public int imageViewId; - public WidgetPhotoToLoad(int widgetId, final AppWidgetManager appWidgetManager, final String url, final RemoteViews remoteViews, final int imageViewId){ - WidgetPhotoToLoad.this.widgetId = widgetId; - WidgetPhotoToLoad.this.appWidgetManager = appWidgetManager; - WidgetPhotoToLoad.this.url = url; - WidgetPhotoToLoad.this.remoteViews = remoteViews; - WidgetPhotoToLoad.this.imageViewId = imageViewId; - } - } - public class PhotoToLoad { public String url; public ImageView imageView; @@ -142,71 +137,6 @@ public class ImageLoader { } } - private class WidgetPhotoLoader implements Runnable { - WidgetPhotoToLoad widgetPhotoToLoad; - - public WidgetPhotoLoader(WidgetPhotoToLoad widgetPhotoToLoad) { - this.widgetPhotoToLoad = widgetPhotoToLoad; - } - - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE); -// if (photoToLoad.cancel) return; - - // try from memory - Bitmap bitmap = memoryCache.get(widgetPhotoToLoad.url); - - if (bitmap != null) { - widgetPhotoToLoad.remoteViews.setImageViewBitmap(widgetPhotoToLoad.imageViewId, bitmap); -// widgetPhotoToLoad.appWidgetManager.updateAppWidget(widgetPhotoToLoad.widgetId, widgetPhotoToLoad.remoteViews); - return; - } - - // this not only sets a theoretical cap on how frequently we will churn against memory, storage, CPU, - // and the UI handler, it also ensures that if the loader gets very behind (as happens during fast - // scrolling, the caller has a few cycles to raise the cancellation flag, saving many resources. -// if (photoToLoad.allowDelay) { -// try { -// Thread.sleep(20); -// } catch (InterruptedException ie) { -// return; -// } -// } - -// if (photoToLoad.cancel) return; - - // ensure this imageview even still wants this image -// if (!isUrlMapped(photoToLoad.imageView, photoToLoad.url)) return; - - // callers frequently might botch this due to lazy view measuring -// if (photoToLoad.maxDimPX < 1) { -// photoToLoad.maxDimPX = Integer.MAX_VALUE; -// } - - // try from disk - File f = fileCache.getCachedFile(widgetPhotoToLoad.url); - // the only reliable way to check a cached file is to try decoding it. the util method will - // return null if it fails -// bitmap = UIUtils.decodeImage(f, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); - bitmap = UIUtils.decodeImage(f, 50, false, 4f); - // try for network - if (bitmap == null) { -// if (widgetPhotoToLoad.cancel) return; - fileCache.cacheFile(widgetPhotoToLoad.url); - f = fileCache.getCachedFile(widgetPhotoToLoad.url); -// bitmap = UIUtils.decodeImage(f, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); - bitmap = UIUtils.decodeImage(f, 50, false, 4f); - } - - if (bitmap != null) { - memoryCache.put(widgetPhotoToLoad.url, bitmap); - } - widgetPhotoToLoad.remoteViews.setImageViewBitmap(widgetPhotoToLoad.imageViewId, bitmap); -// setViewImage(bitmap, photoToLoad); - } - } - private class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; @@ -249,16 +179,11 @@ public class ImageLoader { } // try from disk - File f = fileCache.getCachedFile(photoToLoad.url); - // the only reliable way to check a cached file is to try decoding it. the util method will - // return null if it fails - bitmap = UIUtils.decodeImage(f, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); - // try for network + bitmap = getImageFromDisk(photoToLoad.url, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); if (bitmap == null) { + // try for network if (photoToLoad.cancel) return; - fileCache.cacheFile(photoToLoad.url); - f = fileCache.getCachedFile(photoToLoad.url); - bitmap = UIUtils.decodeImage(f, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); + bitmap = getImageFromNetwork(photoToLoad.url, photoToLoad.maxDimPX, photoToLoad.cropSquare, photoToLoad.roundRadius); } if (bitmap != null) { @@ -326,4 +251,23 @@ public class ImageLoader { return true; } -} + private String buildUrlIfNeeded(String url) { + if (url.startsWith("/")) { + url = APIConstants.buildUrl(url); + } + return url; + } + + private Bitmap getImageFromDisk(String url, int maxDimPX, boolean cropSquare, float roundRadius) { + // the only reliable way to check a cached file is to try decoding it. the util method will + // return null if it fails + File f = fileCache.getCachedFile(url); + return UIUtils.decodeImage(f, maxDimPX, cropSquare, roundRadius); + } + + private Bitmap getImageFromNetwork(String url, int maxDimPX, boolean cropSquare, float roundRadius) { + fileCache.cacheFile(url); + File f = fileCache.getCachedFile(url); + return UIUtils.decodeImage(f, maxDimPX, cropSquare, roundRadius); + } +} \ No newline at end of file diff --git a/clients/android/NewsBlur/src/com/newsblur/widget/WidgetRemoteViewsFactory.java b/clients/android/NewsBlur/src/com/newsblur/widget/WidgetRemoteViewsFactory.java index 131901456..5f57bb7cf 100644 --- a/clients/android/NewsBlur/src/com/newsblur/widget/WidgetRemoteViewsFactory.java +++ b/clients/android/NewsBlur/src/com/newsblur/widget/WidgetRemoteViewsFactory.java @@ -9,7 +9,8 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.Loader; -import android.util.Log; +import android.text.TextUtils; +import android.view.View; import android.widget.RemoteViews; import android.widget.RemoteViewsService; @@ -17,6 +18,7 @@ import com.newsblur.R; import com.newsblur.domain.Story; import com.newsblur.util.FeedSet; import com.newsblur.util.FeedUtils; +import com.newsblur.util.Log; import com.newsblur.util.PrefsUtils; import com.newsblur.util.StoryUtils; import com.newsblur.util.ThumbnailStyle; @@ -36,8 +38,8 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF private int appWidgetId; private boolean skipCursorUpdate; - public WidgetRemoteViewsFactory(Context context, Intent intent) { - Log.d(TAG, "Constructor"); + WidgetRemoteViewsFactory(Context context, Intent intent) { + com.newsblur.util.Log.d(TAG, "Constructor"); this.context = context; appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); @@ -101,7 +103,7 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF */ @Override public RemoteViews getViewAt(int position) { - Log.d(TAG, "getViewAt " + position); + com.newsblur.util.Log.d(TAG, "getViewAt " + position); Story story = storyItems.get(position); WidgetRemoteViews rv = new WidgetRemoteViews(context.getPackageName(), R.layout.view_widget_story_item); @@ -110,9 +112,12 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF rv.setTextViewText(R.id.story_item_author, story.authors); rv.setTextViewText(R.id.story_item_feedtitle, story.extern_feedTitle); - FeedUtils.iconLoader.displayWidgetImage(appWidgetId, AppWidgetManager.getInstance(context), story.extern_faviconUrl, rv, R.id.story_item_feedicon); - if (PrefsUtils.getThumbnailStyle(context) != ThumbnailStyle.OFF && story.thumbnailUrl != null) { -// FeedUtils.thumbnailLoader.displayWidgetImage(appWidgetId, AppWidgetManager.getInstance(context), story.thumbnailUrl, rv, R.id.story_item_thumbnail); + // image dimensions same as R.layout.view_widget_story_item + FeedUtils.iconLoader.displayWidgetImage(story.extern_faviconUrl, R.id.story_item_feedicon, UIUtils.dp2px(context, 18), rv); + if (PrefsUtils.getThumbnailStyle(context) != ThumbnailStyle.OFF && !TextUtils.isEmpty(story.thumbnailUrl)) { + FeedUtils.thumbnailLoader.displayWidgetImage(story.thumbnailUrl, R.id.story_item_thumbnail, UIUtils.dp2px(context, 64), rv); + } else { + rv.setViewVisibility(R.id.story_item_thumbnail, View.GONE); } //TODO: authors and dates don't get along @@ -200,7 +205,11 @@ public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsF */ @Override public void onDestroy() { - Log.d(TAG, "onDestroy"); + com.newsblur.util.Log.d(TAG, "onDestroy"); + if (cursor != null) { + cursor.close(); + } + PrefsUtils.removeWidgetFeed(context, appWidgetId); } /**