aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/java/com/android/volley/toolbox/ImageRequest.java
diff options
context:
space:
mode:
authorAnonymous <no-reply@google.com>2021-07-09 17:40:10 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-07-09 17:40:10 +0000
commitfd5d34f4fc6ffdf0b9c31381ffee78cf73f007d3 (patch)
treecf5d56ccdb862fd9676c855f919408ef940bc019 /core/src/main/java/com/android/volley/toolbox/ImageRequest.java
parent904ab87ee280c7545a908a8ea10a833385a7cf3f (diff)
parentba6983e134f8f64ced7921f506862a7fcee59d19 (diff)
downloadvolley-fd5d34f4fc6ffdf0b9c31381ffee78cf73f007d3.tar.gz
Import of Volley from GitHub to AOSP. am: 212e7004ac am: f77c53c3a3 am: f33c2e45dc am: ba6983e134t_frc_odp_330442040t_frc_odp_330442000t_frc_con_330443020t_frc_cbr_330443000t_frc_ase_330444010t_frc_art_330443060t_frc_adb_330444000frc_340821000frc_340819280frc_340819220frc_340819190frc_340819030frc_340819020frc_340819010frc_340818170frc_340818110android-vts-14.0_r4android-vts-14.0_r3android-vts-14.0_r2android-vts-14.0_r1android-u-beta-1-gplandroid-security-14.0.0_r9android-security-14.0.0_r8android-security-14.0.0_r7android-security-14.0.0_r6android-security-14.0.0_r5android-security-14.0.0_r4android-security-14.0.0_r3android-security-14.0.0_r2android-security-14.0.0_r10android-security-14.0.0_r1android-platform-14.0.0_r9android-platform-14.0.0_r8android-platform-14.0.0_r7android-platform-14.0.0_r6android-platform-14.0.0_r5android-platform-14.0.0_r4android-platform-14.0.0_r3android-platform-14.0.0_r2android-platform-14.0.0_r1android-cts-14.0_r4android-cts-14.0_r3android-cts-14.0_r2android-cts-14.0_r1android-14.0.0_r9android-14.0.0_r8android-14.0.0_r7android-14.0.0_r6android-14.0.0_r54android-14.0.0_r53android-14.0.0_r52android-14.0.0_r51android-14.0.0_r50android-14.0.0_r5android-14.0.0_r45android-14.0.0_r44android-14.0.0_r43android-14.0.0_r42android-14.0.0_r41android-14.0.0_r40android-14.0.0_r4android-14.0.0_r39android-14.0.0_r38android-14.0.0_r37android-14.0.0_r36android-14.0.0_r35android-14.0.0_r34android-14.0.0_r33android-14.0.0_r32android-14.0.0_r31android-14.0.0_r30android-14.0.0_r3android-14.0.0_r29android-14.0.0_r28android-14.0.0_r27android-14.0.0_r26android-14.0.0_r25android-14.0.0_r24android-14.0.0_r23android-14.0.0_r22android-14.0.0_r21android-14.0.0_r20android-14.0.0_r2android-14.0.0_r19android-14.0.0_r18android-14.0.0_r17android-14.0.0_r16android-14.0.0_r15android-14.0.0_r14android-14.0.0_r13android-14.0.0_r12android-14.0.0_r11android-14.0.0_r10android-14.0.0_r1android-13.0.0_r83android-13.0.0_r82android-13.0.0_r81android-13.0.0_r80android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r74android-13.0.0_r73android-13.0.0_r72android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r66android-13.0.0_r65android-13.0.0_r64android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r60android-13.0.0_r59android-13.0.0_r58android-13.0.0_r57android-13.0.0_r56android-13.0.0_r55android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android-13.0.0_r49android-13.0.0_r48android-13.0.0_r47android-13.0.0_r46android-13.0.0_r45android-13.0.0_r44android-13.0.0_r43android-13.0.0_r42android-13.0.0_r41android-13.0.0_r40android-13.0.0_r39android-13.0.0_r38android-13.0.0_r37android-13.0.0_r36android-13.0.0_r35android-13.0.0_r34android-13.0.0_r33android-13.0.0_r32android-13.0.0_r30android-13.0.0_r29android-13.0.0_r28android-13.0.0_r27android-13.0.0_r24android-13.0.0_r23android-13.0.0_r22android-13.0.0_r21android-13.0.0_r20android-13.0.0_r19android-13.0.0_r18android-13.0.0_r17android-13.0.0_r16aml_uwb_341710010aml_uwb_341513070aml_uwb_341511050aml_uwb_341310300aml_uwb_341310030aml_uwb_341111010aml_uwb_341011000aml_uwb_331910010aml_uwb_331820070aml_uwb_331613010aml_uwb_331611010aml_uwb_331410010aml_uwb_331310030aml_uwb_331115000aml_uwb_331015040aml_uwb_330810010aml_tz5_341510070aml_tz5_341510050aml_tz5_341510010aml_tz4_332714070aml_tz4_332714050aml_tz4_332714010aml_tz4_331910000aml_tz4_331314030aml_tz4_331314020aml_tz4_331314010aml_tz4_331012050aml_tz4_331012040aml_tz4_331012000aml_rkp_341810000aml_rkp_341510000aml_rkp_341311000aml_rkp_341114000aml_rkp_341015010aml_rkp_341012000aml_odp_341812000aml_odp_341717000aml_odp_341610000aml_hef_341811030aml_hef_341717050aml_hef_341613000aml_hef_341512030aml_hef_341415040aml_hef_341311010aml_hef_341114030aml_go_wif_330911000aml_go_uwb_330912000aml_go_tz4_330912000aml_go_tet_330914010aml_go_swc_330913000aml_go_sta_330911000aml_go_sdk_330810000aml_go_sch_330911000aml_go_res_330912000aml_go_per_330912000aml_go_odp_330913000aml_go_odp_330912000aml_go_neu_330912000aml_go_net_330913000aml_go_mpr_330912000aml_go_ase_330913000aml_go_ads_330915100aml_go_ads_330915000aml_go_ads_330913000aml_go_adb_330913000aml_cfg_341510000aml_ase_341810000aml_ase_341510000aml_ase_341410000aml_ase_341310010aml_ase_341113000aml_ase_340913000aml_ase_331311020aml_ase_331112000aml_ase_331011020aml_ads_341826300aml_ads_341826060aml_ads_341720000aml_ads_341615050aml_ads_341517040aml_ads_341413000aml_ads_341316030aml_ads_341131050aml_ads_341027030aml_ads_340915050aml_ads_331920180aml_ads_331814200aml_ads_331710270aml_ads_331611190aml_ads_331511020aml_ads_331418080aml_ads_331131000main-16k-with-phonesmain-16kandroid14-tests-releaseandroid14-security-releaseandroid14-s2-releaseandroid14-s1-releaseandroid14-releaseandroid14-qpr3-s2-releaseandroid14-qpr3-releaseandroid14-qpr2-s5-releaseandroid14-qpr2-s4-releaseandroid14-qpr2-s3-releaseandroid14-qpr2-s2-releaseandroid14-qpr2-s1-releaseandroid14-qpr2-releaseandroid14-qpr1-s2-releaseandroid14-qpr1-releaseandroid14-platform-releaseandroid14-mainline-uwb-releaseandroid14-mainline-healthfitness-releaseandroid14-mainline-appsearch-releaseandroid14-mainline-adservices-releaseandroid14-gsiandroid14-devandroid14-d2-s5-releaseandroid14-d2-s4-releaseandroid14-d2-s3-releaseandroid14-d2-s2-releaseandroid14-d2-s1-releaseandroid14-d2-releaseandroid14-d1-s7-releaseandroid14-d1-s6-releaseandroid14-d1-s5-releaseandroid14-d1-s4-releaseandroid14-d1-s3-releaseandroid14-d1-s2-releaseandroid14-d1-s1-releaseandroid14-d1-releaseandroid13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-releaseandroid13-qpr3-c-s8-releaseandroid13-qpr3-c-s7-releaseandroid13-qpr3-c-s6-releaseandroid13-qpr3-c-s5-releaseandroid13-qpr3-c-s4-releaseandroid13-qpr3-c-s3-releaseandroid13-qpr3-c-s2-releaseandroid13-qpr3-c-s12-releaseandroid13-qpr3-c-s11-releaseandroid13-qpr3-c-s10-releaseandroid13-qpr3-c-s1-releaseandroid13-qpr2-s9-releaseandroid13-qpr2-s8-releaseandroid13-qpr2-s7-releaseandroid13-qpr2-s6-releaseandroid13-qpr2-s5-releaseandroid13-qpr2-s3-releaseandroid13-qpr2-s2-releaseandroid13-qpr2-s12-releaseandroid13-qpr2-s11-releaseandroid13-qpr2-s10-releaseandroid13-qpr2-s1-releaseandroid13-qpr2-releaseandroid13-qpr2-b-s1-releaseandroid13-qpr1-s8-releaseandroid13-qpr1-s7-releaseandroid13-qpr1-s6-releaseandroid13-qpr1-s5-releaseandroid13-qpr1-s4-releaseandroid13-qpr1-s3-releaseandroid13-qpr1-s2-releaseandroid13-qpr1-s1-releaseandroid13-qpr1-releaseandroid13-mainline-uwb-releaseandroid13-mainline-tzdata4-releaseandroid13-mainline-go-wifi-releaseandroid13-mainline-go-uwb-releaseandroid13-mainline-go-tzdata4-releaseandroid13-mainline-go-tethering-releaseandroid13-mainline-go-sdkext-releaseandroid13-mainline-go-scheduling-releaseandroid13-mainline-go-resolv-releaseandroid13-mainline-go-permission-releaseandroid13-mainline-go-os-statsd-releaseandroid13-mainline-go-odp-releaseandroid13-mainline-go-neuralnetworks-releaseandroid13-mainline-go-networking-releaseandroid13-mainline-go-mediaprovider-releaseandroid13-mainline-go-media-swcodec-releaseandroid13-mainline-go-appsearch-releaseandroid13-mainline-go-adservices-releaseandroid13-mainline-go-adbd-releaseandroid13-mainline-appsearch-releaseandroid13-mainline-adservices-releaseandroid13-frc-odp-releaseandroid13-frc-conscrypt-releaseandroid13-frc-cellbroadcast-releaseandroid13-frc-art-releaseandroid13-frc-adbd-releaseandroid13-devandroid13-d4-s2-releaseandroid13-d4-s1-releaseandroid13-d4-releaseandroid13-d3-s1-releaseandroid13-d2-releaseaml_tz5_341510010aml_tz4_332714010aml_rkp_341810000aml_odp_341812000aml_hef_341811030aml_ase_341810000aml_ads_341826300aml_ads_341826060
Original change: https://android-review.googlesource.com/c/platform/external/volley/+/1758419 Change-Id: I7191d1e2ebb6c416909593386dbbd3481241bf8c
Diffstat (limited to 'core/src/main/java/com/android/volley/toolbox/ImageRequest.java')
-rw-r--r--core/src/main/java/com/android/volley/toolbox/ImageRequest.java283
1 files changed, 283 insertions, 0 deletions
diff --git a/core/src/main/java/com/android/volley/toolbox/ImageRequest.java b/core/src/main/java/com/android/volley/toolbox/ImageRequest.java
new file mode 100644
index 0000000..32b5aa3
--- /dev/null
+++ b/core/src/main/java/com/android/volley/toolbox/ImageRequest.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.volley.toolbox;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.widget.ImageView.ScaleType;
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import com.android.volley.DefaultRetryPolicy;
+import com.android.volley.NetworkResponse;
+import com.android.volley.ParseError;
+import com.android.volley.Request;
+import com.android.volley.Response;
+import com.android.volley.VolleyLog;
+
+/** A canned request for getting an image at a given URL and calling back with a decoded Bitmap. */
+public class ImageRequest extends Request<Bitmap> {
+ /** Socket timeout in milliseconds for image requests */
+ public static final int DEFAULT_IMAGE_TIMEOUT_MS = 1000;
+
+ /** Default number of retries for image requests */
+ public static final int DEFAULT_IMAGE_MAX_RETRIES = 2;
+
+ /** Default backoff multiplier for image requests */
+ public static final float DEFAULT_IMAGE_BACKOFF_MULT = 2f;
+
+ /** Lock to guard mListener as it is cleared on cancel() and read on delivery. */
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ @Nullable
+ private Response.Listener<Bitmap> mListener;
+
+ private final Config mDecodeConfig;
+ private final int mMaxWidth;
+ private final int mMaxHeight;
+ private final ScaleType mScaleType;
+
+ /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
+ private static final Object sDecodeLock = new Object();
+
+ /**
+ * Creates a new image request, decoding to a maximum specified width and height. If both width
+ * and height are zero, the image will be decoded to its natural size. If one of the two is
+ * nonzero, that dimension will be clamped and the other one will be set to preserve the image's
+ * aspect ratio. If both width and height are nonzero, the image will be decoded to be fit in
+ * the rectangle of dimensions width x height while keeping its aspect ratio.
+ *
+ * @param url URL of the image
+ * @param listener Listener to receive the decoded bitmap
+ * @param maxWidth Maximum width to decode this bitmap to, or zero for none
+ * @param maxHeight Maximum height to decode this bitmap to, or zero for none
+ * @param scaleType The ImageViews ScaleType used to calculate the needed image size.
+ * @param decodeConfig Format to decode the bitmap to
+ * @param errorListener Error listener, or null to ignore errors
+ */
+ public ImageRequest(
+ String url,
+ Response.Listener<Bitmap> listener,
+ int maxWidth,
+ int maxHeight,
+ ScaleType scaleType,
+ Config decodeConfig,
+ @Nullable Response.ErrorListener errorListener) {
+ super(Method.GET, url, errorListener);
+ setRetryPolicy(
+ new DefaultRetryPolicy(
+ DEFAULT_IMAGE_TIMEOUT_MS,
+ DEFAULT_IMAGE_MAX_RETRIES,
+ DEFAULT_IMAGE_BACKOFF_MULT));
+ mListener = listener;
+ mDecodeConfig = decodeConfig;
+ mMaxWidth = maxWidth;
+ mMaxHeight = maxHeight;
+ mScaleType = scaleType;
+ }
+
+ /**
+ * For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to the
+ * normal constructor with {@code ScaleType.CENTER_INSIDE}.
+ */
+ @Deprecated
+ public ImageRequest(
+ String url,
+ Response.Listener<Bitmap> listener,
+ int maxWidth,
+ int maxHeight,
+ Config decodeConfig,
+ Response.ErrorListener errorListener) {
+ this(
+ url,
+ listener,
+ maxWidth,
+ maxHeight,
+ ScaleType.CENTER_INSIDE,
+ decodeConfig,
+ errorListener);
+ }
+
+ @Override
+ public Priority getPriority() {
+ return Priority.LOW;
+ }
+
+ /**
+ * Scales one side of a rectangle to fit aspect ratio.
+ *
+ * @param maxPrimary Maximum size of the primary dimension (i.e. width for max width), or zero
+ * to maintain aspect ratio with secondary dimension
+ * @param maxSecondary Maximum size of the secondary dimension, or zero to maintain aspect ratio
+ * with primary dimension
+ * @param actualPrimary Actual size of the primary dimension
+ * @param actualSecondary Actual size of the secondary dimension
+ * @param scaleType The ScaleType used to calculate the needed image size.
+ */
+ private static int getResizedDimension(
+ int maxPrimary,
+ int maxSecondary,
+ int actualPrimary,
+ int actualSecondary,
+ ScaleType scaleType) {
+
+ // If no dominant value at all, just return the actual.
+ if ((maxPrimary == 0) && (maxSecondary == 0)) {
+ return actualPrimary;
+ }
+
+ // If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
+ if (scaleType == ScaleType.FIT_XY) {
+ if (maxPrimary == 0) {
+ return actualPrimary;
+ }
+ return maxPrimary;
+ }
+
+ // If primary is unspecified, scale primary to match secondary's scaling ratio.
+ if (maxPrimary == 0) {
+ double ratio = (double) maxSecondary / (double) actualSecondary;
+ return (int) (actualPrimary * ratio);
+ }
+
+ if (maxSecondary == 0) {
+ return maxPrimary;
+ }
+
+ double ratio = (double) actualSecondary / (double) actualPrimary;
+ int resized = maxPrimary;
+
+ // If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
+ if (scaleType == ScaleType.CENTER_CROP) {
+ if ((resized * ratio) < maxSecondary) {
+ resized = (int) (maxSecondary / ratio);
+ }
+ return resized;
+ }
+
+ if ((resized * ratio) > maxSecondary) {
+ resized = (int) (maxSecondary / ratio);
+ }
+ return resized;
+ }
+
+ @Override
+ protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
+ // Serialize all decode on a global lock to reduce concurrent heap usage.
+ synchronized (sDecodeLock) {
+ try {
+ return doParse(response);
+ } catch (OutOfMemoryError e) {
+ VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
+ return Response.error(new ParseError(e));
+ }
+ }
+ }
+
+ /** The real guts of parseNetworkResponse. Broken out for readability. */
+ private Response<Bitmap> doParse(NetworkResponse response) {
+ byte[] data = response.data;
+ BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
+ Bitmap bitmap = null;
+ if (mMaxWidth == 0 && mMaxHeight == 0) {
+ decodeOptions.inPreferredConfig = mDecodeConfig;
+ bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
+ } else {
+ // If we have to resize this image, first get the natural bounds.
+ decodeOptions.inJustDecodeBounds = true;
+ BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
+ int actualWidth = decodeOptions.outWidth;
+ int actualHeight = decodeOptions.outHeight;
+
+ // Then compute the dimensions we would ideally like to decode to.
+ int desiredWidth =
+ getResizedDimension(
+ mMaxWidth, mMaxHeight, actualWidth, actualHeight, mScaleType);
+ int desiredHeight =
+ getResizedDimension(
+ mMaxHeight, mMaxWidth, actualHeight, actualWidth, mScaleType);
+
+ // Decode to the nearest power of two scaling factor.
+ decodeOptions.inJustDecodeBounds = false;
+ // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
+ // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
+ decodeOptions.inSampleSize =
+ findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
+ Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
+
+ // If necessary, scale down to the maximal acceptable size.
+ if (tempBitmap != null
+ && (tempBitmap.getWidth() > desiredWidth
+ || tempBitmap.getHeight() > desiredHeight)) {
+ bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);
+ tempBitmap.recycle();
+ } else {
+ bitmap = tempBitmap;
+ }
+ }
+
+ if (bitmap == null) {
+ return Response.error(new ParseError(response));
+ } else {
+ return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
+ }
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ synchronized (mLock) {
+ mListener = null;
+ }
+ }
+
+ @Override
+ protected void deliverResponse(Bitmap response) {
+ Response.Listener<Bitmap> listener;
+ synchronized (mLock) {
+ listener = mListener;
+ }
+ if (listener != null) {
+ listener.onResponse(response);
+ }
+ }
+
+ /**
+ * Returns the largest power-of-two divisor for use in downscaling a bitmap that will not result
+ * in the scaling past the desired dimensions.
+ *
+ * @param actualWidth Actual width of the bitmap
+ * @param actualHeight Actual height of the bitmap
+ * @param desiredWidth Desired width of the bitmap
+ * @param desiredHeight Desired height of the bitmap
+ */
+ @VisibleForTesting
+ static int findBestSampleSize(
+ int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
+ double wr = (double) actualWidth / desiredWidth;
+ double hr = (double) actualHeight / desiredHeight;
+ double ratio = Math.min(wr, hr);
+ float n = 1.0f;
+ while ((n * 2) <= ratio) {
+ n *= 2;
+ }
+
+ return (int) n;
+ }
+}