diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPhotoViewerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPhotoViewerFragment.java index 04e0dbcc0e2d..dfa592f139d3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPhotoViewerFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderPhotoViewerFragment.java @@ -88,7 +88,7 @@ private void showImage() { if (isAdded() && !TextUtils.isEmpty(mImageUrl)) { // use max of width/height so image is cached the same regardless of orientation Rect pt = DisplayUtils.getWindowSize(requireActivity()); - int hiResWidth = Math.max(pt.height(), pt.width()); + int hiResWidth = (int) (Math.max(pt.height(), pt.width()) * 0.8); // don't use AT media proxy here mPhotoView.setImageUrl(mImageUrl, hiResWidth, mIsPrivate, false, mPhotoViewListener); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderPhotoView.java b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderPhotoView.java index 8bf58a596d74..100ba53723c8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderPhotoView.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/ReaderPhotoView.java @@ -17,6 +17,8 @@ import com.github.chrisbanes.photoview.PhotoViewAttacher; import org.wordpress.android.R; +import org.wordpress.android.analytics.AnalyticsTracker; +import org.wordpress.android.analytics.AnalyticsTracker.Stat; import org.wordpress.android.ui.reader.utils.ReaderUtils; import org.wordpress.android.util.AppLog; import org.wordpress.android.util.PhotonUtils; @@ -24,15 +26,22 @@ import org.wordpress.android.util.image.ImageManager.RequestListener; import org.wordpress.android.util.image.ImageType; +import java.util.HashMap; +import java.util.Map; + /** * used by ReaderPhotoViewerActivity to show full-width images - based on Volley's ImageView * but adds pinch/zoom and the ability to first load a lo-res version of the image */ public class ReaderPhotoView extends RelativeLayout { + private static final String FAILED_IMAGE = "failed_image"; + public interface PhotoViewListener { void onTapPhotoView(); } + private String mImageUrl; + private PhotoViewListener mPhotoViewListener; private String mLoResImageUrl; private String mHiResImageUrl; @@ -75,6 +84,8 @@ public void setImageUrl(String imageUrl, boolean isPrivate, boolean isPrivateAtSite, PhotoViewListener listener) { + mImageUrl = imageUrl; + int loResWidth = (int) (hiResWidth * 0.10f); mLoResImageUrl = ReaderUtils .getResizedImageUrl(imageUrl, loResWidth, 0, isPrivate, isPrivateAtSite, PhotonUtils.Quality.LOW); @@ -110,16 +121,28 @@ private void loadImage() { mImageManager .loadWithResultListener(mImageView, ImageType.IMAGE, mHiResImageUrl, ScaleType.CENTER, mLoResImageUrl, - new RequestListener() { + new RequestListener<>() { @Override public void onLoadFailed(@Nullable Exception e, @Nullable Object model) { if (e != null) { AppLog.e(AppLog.T.READER, e); - } - boolean lowResNotLoadedYet = isLoading(); - if (lowResNotLoadedYet) { - hideProgress(); - showError(); + // Check if the stack trace contains TimeoutError before trying fallback + if (containsTimeoutInStackTrace(e)) { + AppLog.d(AppLog.T.READER, "Timeout detected in stack trace, loading fallback image"); + mImageView.post( + () -> loadFallbackImage() + ); + // Track the error to better understand the root of the issue + Map properties = new HashMap<>(); + properties.put(FAILED_IMAGE, mHiResImageUrl); + AnalyticsTracker.track(Stat.IMAGE_LOADING_TIMEOUT, properties); + } else { + boolean lowResNotLoadedYet = isLoading(); + if (lowResNotLoadedYet) { + hideProgress(); + showError(); + } + } } } @@ -130,6 +153,47 @@ public void onResourceReady(@NonNull Drawable resource, @Nullable Object model) }); } + private boolean containsTimeoutInStackTrace(@Nullable Exception e) { + if (e == null) { + return false; + } + + // Convert stack trace to string and check for TimeoutError + java.io.StringWriter sw = new java.io.StringWriter(); + java.io.PrintWriter pw = new java.io.PrintWriter(sw); + e.printStackTrace(pw); + String stackTrace = sw.toString(); + + return stackTrace.contains("TimeoutError"); + } + + private void loadFallbackImage() { + if (!hasLayout()) { + return; + } + // Load the original image URL + mImageManager + .loadWithResultListener(mImageView, ImageType.IMAGE, mImageUrl, ScaleType.CENTER, mLoResImageUrl, + new RequestListener<>() { + @Override + public void onLoadFailed(@Nullable Exception e, @Nullable Object model) { + if (e != null) { + AppLog.e(AppLog.T.READER, e); + } + boolean lowResNotLoadedYet = isLoading(); + if (lowResNotLoadedYet) { + hideProgress(); + showError(); + } + } + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Object model) { + handleResponse(); + } + }); + } + private void handleResponse() { hideProgress(); hideError(); diff --git a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java index 97f1170b357d..c063dd6e0160 100644 --- a/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java +++ b/libs/analytics/src/main/java/org/wordpress/android/analytics/AnalyticsTracker.java @@ -1173,7 +1173,8 @@ public enum Stat { BACKGROUND_REST_AUTODISCOVERY_FAILED, WP_ANDROID_APPLICATION_PASSWORD_LOGIN, JP_ANDROID_APPLICATION_PASSWORD_LOGIN, - APPLICATION_PASSWORD_SET_OFF; + APPLICATION_PASSWORD_SET_OFF, + IMAGE_LOADING_TIMEOUT; /* * Please set the event name in the enum only if the new Stat's name in lower case does not match it. * In that case you also need to add the event in the `AnalyticsTrackerNosaraTest.specialNames` map.