summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xorg.fox.ttrss/build.gradle6
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java2
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/GalleryImageFragment.java6
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java12
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java2
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java107
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java172
-rw-r--r--org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java115
-rwxr-xr-xorg.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java63
9 files changed, 426 insertions, 59 deletions
diff --git a/org.fox.ttrss/build.gradle b/org.fox.ttrss/build.gradle
index 055dd2ad..03be018f 100755
--- a/org.fox.ttrss/build.gradle
+++ b/org.fox.ttrss/build.gradle
@@ -138,9 +138,9 @@ dependencies {
implementation 'com.squareup.okhttp3:okhttp:3.12.5'
implementation 'org.jsoup:jsoup:1.11.3'
implementation 'com.bogdwellers:pinchtozoom:0.1'
- implementation 'com.github.bumptech.glide:glide:4.11.0'
- implementation 'com.github.bumptech.glide:okhttp3-integration:4.11.0'
- implementation 'jp.wasabeef:glide-transformations:4.3.0'
+ implementation 'com.github.bumptech.glide:glide:4.14.2'
+ implementation 'com.github.bumptech.glide:okhttp3-integration:4.14.2'
+ annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
implementation 'androidx.recyclerview:recyclerview:1.4.0'
implementation 'androidx.activity:activity:1.10.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java
index eaa997f4..ced956c2 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryActivity.java
@@ -131,7 +131,7 @@ public class GalleryActivity extends CommonActivity {
Log.d(TAG, "observed items to check=" + itemsToCheck);
m_checkProgress.setMax(itemsToCheck);
- m_checkProgress.setVisibility(View.VISIBLE);
+ m_checkProgress.setVisibility(itemsToCheck > 0 ? View.VISIBLE : View.GONE);
m_checkProgress.setProgress(0);
});
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryImageFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryImageFragment.java
index d5206518..db141427 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryImageFragment.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryImageFragment.java
@@ -61,7 +61,7 @@ public class GalleryImageFragment extends GalleryBaseFragment {
// final GlideDrawableImageViewTarget glideImage = new GlideDrawableImageViewTarget(imgView);
- Glide.with(m_activity)
+ Glide.with(this)
.load(m_url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(false)
@@ -71,7 +71,7 @@ public class GalleryImageFragment extends GalleryBaseFragment {
progressBar.setVisibility(View.GONE);
errorMessage.setVisibility(View.VISIBLE);
- ActivityCompat.startPostponedEnterTransition(m_activity);
+ // ActivityCompat.startPostponedEnterTransition(m_activity);
return false;
}
@@ -81,7 +81,7 @@ public class GalleryImageFragment extends GalleryBaseFragment {
progressBar.setVisibility(View.GONE);
errorMessage.setVisibility(View.GONE);
- ActivityCompat.startPostponedEnterTransition(m_activity);
+ // ActivityCompat.startPostponedEnterTransition(m_activity);
return false;
}
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java
index 8458fc33..2f0451fb 100644
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryModel.java
@@ -79,6 +79,7 @@ public class GalleryModel extends AndroidViewModel {
m_itemsToCheck.postValue(elems.size());
int currentItem = 0;
+ boolean firstFound = false;
for (Element elem : elems) {
++currentItem;
@@ -95,6 +96,8 @@ public class GalleryModel extends AndroidViewModel {
if (src != null && src.equals(srcFirst)) {
Log.d(TAG, "first item found, vid=" + src);
+ firstFound = true;
+
GalleryEntry item = new GalleryEntry(src, GalleryEntry.GalleryEntryType.TYPE_VIDEO, poster);
checkList.add(item);
@@ -115,6 +118,8 @@ public class GalleryModel extends AndroidViewModel {
if (src != null && src.equals(srcFirst)) {
Log.d(TAG, "first item found, img=" + src);
+ firstFound = true;
+
GalleryEntry item = new GalleryEntry(src, GalleryEntry.GalleryEntryType.TYPE_IMAGE, null);
checkList.add(item);
@@ -148,6 +153,13 @@ public class GalleryModel extends AndroidViewModel {
m_checkProgress.postValue(currentItem);
}
+
+ // if we didn't find it in the document, let's add insert to the list anyway so shared transition
+ // would hopefully work
+ if (!firstFound) {
+ checkList.add(0, new GalleryEntry(srcFirst, GalleryEntry.GalleryEntryType.TYPE_IMAGE, null));
+ m_items.postValue(checkList);
+ }
});
}
}
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java
index 18d08e4f..a171fb5b 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/GalleryVideoFragment.java
@@ -56,7 +56,7 @@ public class GalleryVideoFragment extends GalleryBaseFragment {
registerForContextMenu(imgView);
- ActivityCompat.startPostponedEnterTransition(m_activity);
+ // ActivityCompat.startPostponedEnterTransition(m_activity);
view.findViewById(R.id.flavor_image_progress).setVisibility(View.VISIBLE);
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java
index 96cd245c..d5da9d6e 100755
--- a/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/HeadlinesFragment.java
@@ -64,14 +64,17 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.DrawableImageViewTarget;
import com.bumptech.glide.request.target.Target;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
+import org.fox.ttrss.glide.ProgressTarget;
import org.fox.ttrss.types.Article;
import org.fox.ttrss.types.ArticleList;
import org.fox.ttrss.types.Attachment;
@@ -87,8 +90,6 @@ import java.util.HashMap;
import java.util.TimeZone;
import java.util.stream.Collectors;
-import jp.wasabeef.glide.transformations.CropCircleTransformation;
-
public class HeadlinesFragment extends androidx.fragment.app.Fragment {
private boolean m_isLazyLoading;
@@ -617,7 +618,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
public View flavorImageOverflow;
public TextureView flavorVideoView;
public MaterialButton attachmentsView;
- //public ProgressTarget<String, GlideDrawable> flavorProgressTarget;
+ public ProgressTarget<String, Drawable> flavorProgressTarget;
int articleId;
public TextView linkHost;
@@ -659,9 +660,10 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
attachmentsView = v.findViewById(R.id.attachments);
linkHost = v.findViewById(R.id.link_host);
- /* if (flavorImageView != null && flavorImageLoadingBar != null) {
- flavorProgressTarget = new FlavorProgressTarget<>(new GlideDrawableImageViewTarget(flavorImageView), flavorImageLoadingBar);
- } */
+ if (flavorImageView != null && flavorImageLoadingBar != null) {
+ flavorProgressTarget = new FlavorProgressTarget<>(new DrawableImageViewTarget(flavorImageView),
+ flavorImageLoadingBar);
+ }
}
public void clearAnimation() {
@@ -669,7 +671,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
}
}
- /* private static class FlavorProgressTarget<Z> extends ProgressTarget<String, Z> {
+ private static class FlavorProgressTarget<Z> extends ProgressTarget<String, Z> {
private final ProgressBar progress;
public FlavorProgressTarget(Target<Z> target, ProgressBar progress) {
super(target);
@@ -694,7 +696,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
@Override protected void onDelivered() {
progress.setVisibility(View.INVISIBLE);
}
- } */
+ }
private class ArticleListAdapter extends ListAdapter<Article, ArticleViewHolder> {
public static final int VIEW_NORMAL = 0;
@@ -711,8 +713,8 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
private final TextDrawable.IBuilder m_drawableBuilder = TextDrawable.builder().round();
boolean flavorImageEnabled;
+ private final int m_screenWidth;
private final int m_screenHeight;
- private int m_lastAddedPosition;
private final ConnectivityManager m_cmgr;
@@ -740,6 +742,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
Point size = new Point();
display.getSize(size);
m_screenHeight = size.y;
+ m_screenWidth = size.x;
String headlineMode = m_prefs.getString("headline_mode", "HL_DEFAULT");
flavorImageEnabled = "HL_DEFAULT".equals(headlineMode) || "HL_COMPACT".equals(headlineMode);
@@ -1015,7 +1018,7 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
holder.flavorVideoView.setVisibility(View.GONE);
holder.flavorImageHolder.setVisibility(View.GONE);
- Glide.with(m_activity).clear(holder.flavorImageView);
+ Glide.with(HeadlinesFragment.this).clear(holder.flavorImageView);
// this is needed if our flavor image goes behind base listview element
holder.headlineHeader.setOnClickListener(v -> {
@@ -1069,66 +1072,66 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
}
holder.flavorImageView.setVisibility(View.VISIBLE);
- holder.flavorImageView.setMaxHeight((int)(m_screenHeight * 0.6f));
+
+ int maxImageSize = (int) (m_screenHeight * 0.5f);
+
+ // we also downsample below using glide to save RAM
+ // holder.flavorImageView.setMaxHeight(maxImageSize);
// only show holder if we're about to display a picture
holder.flavorImageHolder.setVisibility(View.VISIBLE);
// prevent lower listiew entries from jumping around if this row is modified
- if (m_flavorHeightsCache.containsKey(article.id)) {
+ /* if (m_flavorHeightsCache.containsKey(article.id)) {
int cachedHeight = m_flavorHeightsCache.get(article.id);
if (cachedHeight > 0) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) holder.flavorImageView.getLayoutParams();
lp.height = cachedHeight;
}
- }
+ } */
- // holder.flavorProgressTarget.setModel(article.flavorImageUri);
-
- try {
+ holder.flavorProgressTarget.setModel(article.flavorImageUri);
- Glide.with(m_activity)
- .load(article.flavorImageUri)
- .transition(DrawableTransitionOptions.withCrossFade())
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .skipMemoryCache(false)
- .listener(new RequestListener<Drawable>() {
- @Override
- public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
+ Glide.with(HeadlinesFragment.this)
+ .load(article.flavorImageUri)
+ .transition(DrawableTransitionOptions.withCrossFade())
+ .diskCacheStrategy(DiskCacheStrategy.ALL)
+ .skipMemoryCache(false)
+ .listener(new RequestListener<Drawable>() {
+ @Override
+ public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
- holder.flavorImageLoadingBar.setVisibility(View.GONE);
- holder.flavorImageView.setVisibility(View.GONE);
+ holder.flavorImageLoadingBar.setVisibility(View.GONE);
+ holder.flavorImageView.setVisibility(View.GONE);
- return false;
- }
+ return false;
+ }
- @Override
- public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
+ @Override
+ public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
- holder.flavorImageLoadingBar.setVisibility(View.GONE);
+ holder.flavorImageLoadingBar.setVisibility(View.GONE);
- if (resource.getIntrinsicWidth() > FLAVOR_IMG_MIN_SIZE && resource.getIntrinsicHeight() > FLAVOR_IMG_MIN_SIZE) {
+ if (resource.getIntrinsicWidth() > FLAVOR_IMG_MIN_SIZE && resource.getIntrinsicHeight() > FLAVOR_IMG_MIN_SIZE) {
- holder.flavorImageView.setVisibility(View.VISIBLE);
- holder.flavorImageOverflow.setVisibility(View.VISIBLE);
+ holder.flavorImageView.setVisibility(View.VISIBLE);
+ holder.flavorImageOverflow.setVisibility(View.VISIBLE);
- adjustVideoKindView(holder, article);
+ adjustVideoKindView(holder, article);
- return false;
- } else {
+ return false;
+ } else {
- holder.flavorImageOverflow.setVisibility(View.GONE);
- holder.flavorImageView.setVisibility(View.GONE);
+ holder.flavorImageOverflow.setVisibility(View.GONE);
+ holder.flavorImageView.setVisibility(View.GONE);
- return true;
- }
+ return true;
}
- })
- .into(new DrawableImageViewTarget(holder.flavorImageView));
- } catch (OutOfMemoryError e) {
- e.printStackTrace();
- }
+ }
+ })
+ .into(holder.flavorProgressTarget);
+
}
if (m_prefs.getBoolean("inline_video_player", false) && article.flavorImage != null &&
@@ -1339,12 +1342,12 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
holder.textImage.setImageDrawable(textDrawable);
} else {
- Glide.with(m_activity)
+ Glide.with(HeadlinesFragment.this)
.load(article.flavorImageUri)
.transition(DrawableTransitionOptions.withCrossFade())
.placeholder(textDrawable)
.thumbnail(0.5f)
- .transform(new CropCircleTransformation())
+ .apply(RequestOptions.circleCropTransform())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.skipMemoryCache(false)
.listener(new RequestListener<Drawable>() {
@@ -1400,12 +1403,14 @@ public class HeadlinesFragment extends androidx.fragment.app.Fragment {
intent.putExtra("content", tempContent);
- ActivityOptionsCompat options =
+ /* ActivityOptionsCompat options =
ActivityOptionsCompat.makeSceneTransitionAnimation(m_activity,
transitionView != null ? transitionView : holder.flavorImageView,
- "gallery:" + (article.flavorStreamUri != null ? article.flavorStreamUri : article.flavorImageUri));
+ "gallery:" + (article.flavorStreamUri != null ? article.flavorStreamUri : article.flavorImageUri)); */
+
+ // ActivityCompat.startActivity(m_activity, intent, options.toBundle())
- ActivityCompat.startActivity(m_activity, intent, options.toBundle());
+ startActivity(intent);
}
}
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java
new file mode 100644
index 00000000..561f9a86
--- /dev/null
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/OkHttpProgressGlideModule.java
@@ -0,0 +1,172 @@
+package org.fox.ttrss.glide;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.Registry;
+import com.bumptech.glide.annotation.GlideModule;
+import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.AppGlideModule;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import okhttp3.HttpUrl;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+import okio.BufferedSource;
+import okio.ForwardingSource;
+import okio.Okio;
+import okio.Source;
+
+@GlideModule
+public class OkHttpProgressGlideModule extends AppGlideModule {
+ @Override public void registerComponents(Context context, Glide glide, Registry registry) {
+ OkHttpClient client = new OkHttpClient.Builder()
+ .addNetworkInterceptor(createInterceptor(new DispatchingProgressListener()))
+ .build();
+
+ // registry.append() doesn't work...
+ registry.prepend(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
+ }
+
+ public static Interceptor createInterceptor(final ResponseProgressListener listener) {
+ return chain -> {
+ Request request = chain.request();
+ Response response = chain.proceed(request);
+
+ return response.newBuilder()
+ .body(new OkHttpProgressResponseBody(request.url(), response.body(), listener))
+ .build();
+ };
+ }
+
+ public interface UIProgressListener {
+ void onProgress(long bytesRead, long expectedLength);
+ /**
+ * Control how often the listener needs an update. 0% and 100% will always be dispatched.
+ * @return in percentage (0.2 = call {@link #onProgress} around every 0.2 percent of progress)
+ */
+ float getGranualityPercentage();
+ }
+
+ public static void forget(String url) {
+ DispatchingProgressListener.forget(url);
+ }
+ public static void expect(String url, UIProgressListener listener) {
+ DispatchingProgressListener.expect(url, listener);
+ }
+
+ public interface ResponseProgressListener {
+ void update(HttpUrl url, long bytesRead, long contentLength);
+ }
+
+ private static class DispatchingProgressListener implements ResponseProgressListener {
+ private static final Map<String, UIProgressListener> LISTENERS = new HashMap<>();
+ private static final Map<String, Long> PROGRESSES = new HashMap<>();
+
+ private final Handler handler;
+ DispatchingProgressListener() {
+ this.handler = new Handler(Looper.getMainLooper());
+ }
+
+ static void forget(String url) {
+ LISTENERS.remove(url);
+ PROGRESSES.remove(url);
+ }
+ static void expect(String url, UIProgressListener listener) {
+ LISTENERS.put(url, listener);
+ }
+
+ @Override public void update(HttpUrl url, final long bytesRead, final long contentLength) {
+ //System.out.printf("%s: %d/%d = %.2f%%%n", url, bytesRead, contentLength, (100f * bytesRead) / contentLength);
+
+ String key = url.toString();
+ final UIProgressListener listener = LISTENERS.get(key);
+
+ if (listener == null) {
+ return;
+ }
+ if (contentLength <= bytesRead) {
+ forget(key);
+ }
+ if (needsDispatch(key, bytesRead, contentLength, listener.getGranualityPercentage())) {
+ handler.post(() -> listener.onProgress(bytesRead, contentLength));
+ }
+ }
+
+ private boolean needsDispatch(String key, long current, long total, float granularity) {
+ if (granularity == 0 || current == 0 || total == current) {
+ return true;
+ }
+ float percent = 100f * current / total;
+ long currentProgress = (long)(percent / granularity);
+ Long lastProgress = PROGRESSES.get(key);
+ if (lastProgress == null || currentProgress != lastProgress) {
+ PROGRESSES.put(key, currentProgress);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public static class OkHttpProgressResponseBody extends ResponseBody {
+ private final HttpUrl url;
+ private final ResponseBody responseBody;
+ private final ResponseProgressListener progressListener;
+ private BufferedSource bufferedSource;
+
+ public OkHttpProgressResponseBody(HttpUrl url, ResponseBody responseBody,
+ ResponseProgressListener progressListener) {
+
+ this.url = url;
+ this.responseBody = responseBody;
+ this.progressListener = progressListener;
+ }
+
+ @Override public MediaType contentType() {
+ return responseBody.contentType();
+ }
+
+ @Override public long contentLength() {
+ return responseBody.contentLength();
+ }
+
+ @Override public BufferedSource source() {
+ if (bufferedSource == null) {
+ bufferedSource = Okio.buffer(source(responseBody.source()));
+ }
+ return bufferedSource;
+ }
+
+ private Source source(Source source) {
+ return new ForwardingSource(source) {
+ long totalBytesRead = 0L;
+ @Override public long read(Buffer sink, long byteCount) throws IOException {
+ long bytesRead = super.read(sink, byteCount);
+ long fullLength = responseBody.contentLength();
+ if (bytesRead == -1) { // this source is exhausted
+ totalBytesRead = fullLength;
+ } else {
+ totalBytesRead += bytesRead;
+ }
+ progressListener.update(url, totalBytesRead, fullLength);
+ return bytesRead;
+ }
+ };
+ }
+ }
+} \ No newline at end of file
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java
new file mode 100644
index 00000000..172dd28a
--- /dev/null
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/ProgressTarget.java
@@ -0,0 +1,115 @@
+package org.fox.ttrss.glide;
+
+
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.Resource;
+import com.bumptech.glide.request.target.Target;
+import com.bumptech.glide.request.transition.Transition;
+
+public abstract class ProgressTarget<T, Z> extends WrappingTarget<Z> implements OkHttpProgressGlideModule.UIProgressListener {
+ private T model;
+ private boolean ignoreProgress = true;
+ public ProgressTarget(Target<Z> target) {
+ this(null, target);
+ }
+ public ProgressTarget(T model, Target<Z> target) {
+ super(target);
+ this.model = model;
+ }
+
+ public final T getModel() {
+ return model;
+ }
+ public final void setModel(T model) {
+ this.model = model;
+ }
+ /**
+ * Convert a model into an Url string that is used to match up the OkHttp requests. For explicit
+ * {@link com.bumptech.glide.load.model.GlideUrl GlideUrl} loads this needs to return
+ * {@link com.bumptech.glide.load.model.GlideUrl#toStringUrl toStringUrl}. For custom models do the same as your
+ * {@link com.bumptech.glide.load.model.stream.BaseGlideUrlLoader BaseGlideUrlLoader} does.
+ * @param model return the representation of the given model, DO NOT use {@link #getModel()} inside this method.
+ * @return a stable Url representation of the model, otherwise the progress reporting won't work
+ */
+ protected String toUrlString(T model) {
+ return String.valueOf(model);
+ }
+
+ @Override public float getGranualityPercentage() {
+ return 1.0f;
+ }
+
+ @Override public void onProgress(long bytesRead, long expectedLength) {
+ if (ignoreProgress) {
+ return;
+ }
+ if (expectedLength == Long.MAX_VALUE) {
+ onConnecting();
+ } else if (bytesRead == expectedLength) {
+ onDownloaded();
+ } else {
+ onDownloading(bytesRead, expectedLength);
+ }
+ }
+
+ /**
+ * Called when the Glide load has started.
+ * At this time it is not known if the Glide will even go and use the network to fetch the image.
+ */
+ protected abstract void onConnecting();
+ /**
+ * Called when there's any progress on the download; not called when loading from cache.
+ * At this time we know how many bytes have been transferred through the wire.
+ */
+ protected abstract void onDownloading(long bytesRead, long expectedLength);
+ /**
+ * Called when the bytes downloaded reach the length reported by the server; not called when loading from cache.
+ * At this time it is fairly certain, that Glide either finished reading the stream.
+ * This means that the image was either already decoded or saved the network stream to cache.
+ * In the latter case there's more work to do: decode the image from cache and transform.
+ * These cannot be listened to for progress so it's unsure how fast they'll be, best to show indeterminate progress.
+ */
+ protected abstract void onDownloaded();
+ /**
+ * Called when the Glide load has finished either by successfully loading the image or failing to load or cancelled.
+ * In any case the best is to hide/reset any progress displays.
+ */
+ protected abstract void onDelivered();
+
+ private void start() {
+ OkHttpProgressGlideModule.expect(toUrlString(model), this);
+ ignoreProgress = false;
+ onProgress(0, Long.MAX_VALUE);
+ }
+ private void cleanup() {
+ ignoreProgress = true;
+ T model = this.model; // save in case it gets modified
+ onDelivered();
+ OkHttpProgressGlideModule.forget(toUrlString(model));
+ this.model = null;
+ }
+
+ @Override public void onLoadStarted(Drawable placeholder) {
+ super.onLoadStarted(placeholder);
+ start();
+ }
+ /** @noinspection unchecked*/
+ public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
+ cleanup();
+ super.onResourceReady(resource, (Transition)transition);
+ }
+ @Override public void onLoadFailed(Drawable errorDrawable) {
+ cleanup();
+ super.onLoadFailed(errorDrawable);
+ }
+ @Override public void onLoadCleared(Drawable placeholder) {
+ cleanup();
+ super.onLoadCleared(placeholder);
+ }
+} \ No newline at end of file
diff --git a/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java
new file mode 100755
index 00000000..314800d9
--- /dev/null
+++ b/org.fox.ttrss/src/main/java/org/fox/ttrss/glide/WrappingTarget.java
@@ -0,0 +1,63 @@
+package org.fox.ttrss.glide;
+
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.bumptech.glide.request.Request;
+import com.bumptech.glide.request.target.SizeReadyCallback;
+import com.bumptech.glide.request.target.Target;
+import com.bumptech.glide.request.transition.Transition;
+
+public class WrappingTarget<Z> implements Target<Z> {
+ protected final @NonNull Target<? super Z> target;
+ public WrappingTarget(@NonNull Target<? super Z> target) {
+ this.target = target;
+ }
+ public @NonNull Target<? super Z> getWrappedTarget() {
+ return target;
+ }
+ @Override public void getSize(SizeReadyCallback cb) {
+ target.getSize(cb);
+ }
+
+ @Override
+ public void removeCallback(@NonNull SizeReadyCallback cb) {
+
+ }
+
+ @Override public void onLoadStarted(Drawable placeholder) {
+ target.onLoadStarted(placeholder);
+ }
+ @Override public void onLoadFailed(Drawable errorDrawable) {
+ target.onLoadFailed(errorDrawable);
+ }
+
+ /** @noinspection unchecked*/
+ @Override
+ public void onResourceReady(@NonNull Z resource, @Nullable Transition<? super Z> transition) {
+ target.onResourceReady(resource, (Transition)transition);
+ }
+
+ @Override public void onLoadCleared(Drawable placeholder) {
+ target.onLoadCleared(placeholder);
+ }
+
+ @Override public Request getRequest() {
+ return target.getRequest();
+ }
+ @Override public void setRequest(Request request) {
+ target.setRequest(request);
+ }
+
+ @Override public void onStart() {
+ target.onStart();
+ }
+ @Override public void onStop() {
+ target.onStop();
+ }
+ @Override public void onDestroy() {
+ target.onDestroy();
+ }
+} \ No newline at end of file