diff options
author | Anonymous <no-reply@google.com> | 2021-07-07 14:01:06 -0700 |
---|---|---|
committer | Jeff Davidson <jpd@google.com> | 2021-07-07 23:09:19 +0000 |
commit | 212e7004acfdce76c900fd97070e2e5e8476be20 (patch) | |
tree | cf5d56ccdb862fd9676c855f919408ef940bc019 /core/src/main/java/com/android/volley/toolbox/NetworkImageView.java | |
parent | b47af415ba74754b442c1f9f3960b29537d07e6e (diff) | |
download | volley-212e7004acfdce76c900fd97070e2e5e8476be20.tar.gz |
Import of Volley from GitHub to AOSP.android-s-beta-5android-s-beta-4android-s-beta-3android-s-beta-5android-s-beta-4
Android.bp has been updated to account for the new source directory structure.
- 0dc50bcfd021c204a9e6c9e7e6befbdfa1027247 Refactor Volley into a multi-module project. (#418) by Jeff Davidson <jpd@google.com>
- 763c86b0bc9f66a8bb499f6a8b7fd3bdc87621a8 Remove new constructors from JsonRequests which are break... by Jeff Davidson <jpd@google.com>
- 8d1b1a59e7cd1b1d3c6d8686f8831cea08f80d1f Add @NonNull annotations to Volley (#413) by Kamal Faraj <kfaraj.dev@gmail.com>
- 5ba41f8670413973f587e435598f9f1724fa26e9 Allow sending any JSON with JsonArrayRequest & JsonObject... by Paul Smith <paulsmithkc@gmail.com>
- 784cdd755392a6080e5eb0bf94bd7bf4ea31cf17 Update SNAPSHOT version after 1.2.0 release by Jeff Davidson <jpd@google.com>
- 0d6497bab417a5f78b3c8e03ea157ada0fbfbc5d Add developers stanza to Volley POM. (#400) by Jeff Davidson <jpd@google.com>
- 36274bf515a699ae5a7fe3d321206d1b803226d8 API cleanup for Async Volley stack ahead of 1.2.0 release... by Jeff Davidson <jpd@google.com>
- 03f0144843fcf9ebafe512647c1c588975429452 Update environment variable name for snapshot pushes. (#3... by Jeff Davidson <jpd@google.com>
- 3bd1975652687d2baa1b11a7f02b135edede8523 Publish SNAPSHOT builds to OSSRH instead of OJO. (#397) by Jeff Davidson <jpd@google.com>
- 0e0c3d9cfa694f8f1400a9e9abc4bc11761fdb52 Invoke RetryPolicy#retry in the blocking executor. (#393) by Jeff Davidson <jpd@google.com>
- b51831a48f06ad28f627c3624e5edb41598a2bf8 Use a consistent timebase when evaluating soft/hard TTLs.... by Jeff Davidson <jpd@google.com>
- cd0839113b100f163df1ebd04ce6d5b9e36e9863 Migrate from Travis CI to GitHub Actions. (#381) by Jeff Davidson <jpd@google.com>
- bdc0e393199ebf9e67c4e29e665252818eed4639 Clean up cache initialization in AsyncRequestQueue. (#380) by Jeff Davidson <jpd@google.com>
- 1c0ade36edde15d02844b40351ab6f80c63b71b3 Actually allow applications to provide custom executors. by Jeff Davidson <jpd@google.com>
GitOrigin-RevId: 0dc50bcfd021c204a9e6c9e7e6befbdfa1027247
Change-Id: I4b8e4098ad5c349cb83efc867273fac1d3582a34
Diffstat (limited to 'core/src/main/java/com/android/volley/toolbox/NetworkImageView.java')
-rw-r--r-- | core/src/main/java/com/android/volley/toolbox/NetworkImageView.java | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/core/src/main/java/com/android/volley/toolbox/NetworkImageView.java b/core/src/main/java/com/android/volley/toolbox/NetworkImageView.java new file mode 100644 index 0000000..a24b3e2 --- /dev/null +++ b/core/src/main/java/com/android/volley/toolbox/NetworkImageView.java @@ -0,0 +1,332 @@ +/** + * Copyright (C) 2013 The Android Open Source Project + * + * <p>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 + * + * <p>http://www.apache.org/licenses/LICENSE-2.0 + * + * <p>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.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.ViewGroup.LayoutParams; +import android.widget.ImageView; +import androidx.annotation.MainThread; +import androidx.annotation.Nullable; +import com.android.volley.VolleyError; +import com.android.volley.toolbox.ImageLoader.ImageContainer; +import com.android.volley.toolbox.ImageLoader.ImageListener; + +/** Handles fetching an image from a URL as well as the life-cycle of the associated request. */ +public class NetworkImageView extends ImageView { + /** The URL of the network image to load */ + private String mUrl; + + /** + * Resource ID of the image to be used as a placeholder until the network image is loaded. Won't + * be set at the same time as mDefaultImageDrawable or mDefaultImageBitmap. + */ + private int mDefaultImageId; + + /** + * Drawable of the image to be used as a placeholder until the network image is loaded. Won't be + * set at the same time as mDefaultImageId or mDefaultImageBitmap. + */ + @Nullable private Drawable mDefaultImageDrawable; + + /** + * Bitmap of the image to be used as a placeholder until the network image is loaded. Won't be + * set at the same time as mDefaultImageId or mDefaultImageDrawable. + */ + @Nullable private Bitmap mDefaultImageBitmap; + + /** + * Resource ID of the image to be used if the network response fails. Won't be set at the same + * time as mErrorImageDrawable or mErrorImageBitmap. + */ + private int mErrorImageId; + + /** + * Bitmap of the image to be used if the network response fails. Won't be set at the same time + * as mErrorImageId or mErrorImageBitmap. + */ + @Nullable private Drawable mErrorImageDrawable; + + /** + * Bitmap of the image to be used if the network response fails. Won't be set at the same time + * as mErrorImageId or mErrorImageDrawable. + */ + @Nullable private Bitmap mErrorImageBitmap; + + /** Local copy of the ImageLoader. */ + private ImageLoader mImageLoader; + + /** Current ImageContainer. (either in-flight or finished) */ + private ImageContainer mImageContainer; + + public NetworkImageView(Context context) { + this(context, null); + } + + public NetworkImageView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Sets URL of the image that should be loaded into this view. Note that calling this will + * immediately either set the cached image (if available) or the default image specified by + * {@link NetworkImageView#setDefaultImageResId(int)} on the view. + * + * <p>NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} or {@link + * NetworkImageView#setDefaultImageBitmap} and {@link NetworkImageView#setErrorImageResId(int)} + * or {@link NetworkImageView#setErrorImageBitmap(Bitmap)} should be called prior to calling + * this function. + * + * <p>Must be called from the main thread. + * + * @param url The URL that should be loaded into this ImageView. + * @param imageLoader ImageLoader that will be used to make the request. + */ + @MainThread + public void setImageUrl(String url, ImageLoader imageLoader) { + Threads.throwIfNotOnMainThread(); + mUrl = url; + mImageLoader = imageLoader; + // The URL has potentially changed. See if we need to load it. + loadImageIfNecessary(/* isInLayoutPass= */ false); + } + + /** + * Sets the default image resource ID to be used for this view until the attempt to load it + * completes. + * + * <p>This will clear anything set by {@link NetworkImageView#setDefaultImageBitmap} or {@link + * NetworkImageView#setDefaultImageDrawable}. + */ + public void setDefaultImageResId(int defaultImage) { + mDefaultImageBitmap = null; + mDefaultImageDrawable = null; + mDefaultImageId = defaultImage; + } + + /** + * Sets the default image drawable to be used for this view until the attempt to load it + * completes. + * + * <p>This will clear anything set by {@link NetworkImageView#setDefaultImageResId} or {@link + * NetworkImageView#setDefaultImageBitmap}. + */ + public void setDefaultImageDrawable(@Nullable Drawable defaultImageDrawable) { + mDefaultImageId = 0; + mDefaultImageBitmap = null; + mDefaultImageDrawable = defaultImageDrawable; + } + + /** + * Sets the default image bitmap to be used for this view until the attempt to load it + * completes. + * + * <p>This will clear anything set by {@link NetworkImageView#setDefaultImageResId} or {@link + * NetworkImageView#setDefaultImageDrawable}. + */ + public void setDefaultImageBitmap(Bitmap defaultImage) { + mDefaultImageId = 0; + mDefaultImageDrawable = null; + mDefaultImageBitmap = defaultImage; + } + + /** + * Sets the error image resource ID to be used for this view in the event that the image + * requested fails to load. + * + * <p>This will clear anything set by {@link NetworkImageView#setErrorImageBitmap} or {@link + * NetworkImageView#setErrorImageDrawable}. + */ + public void setErrorImageResId(int errorImage) { + mErrorImageBitmap = null; + mErrorImageDrawable = null; + mErrorImageId = errorImage; + } + + /** + * Sets the error image drawable to be used for this view in the event that the image requested + * fails to load. + * + * <p>This will clear anything set by {@link NetworkImageView#setErrorImageResId} or {@link + * NetworkImageView#setDefaultImageBitmap}. + */ + public void setErrorImageDrawable(@Nullable Drawable errorImageDrawable) { + mErrorImageId = 0; + mErrorImageBitmap = null; + mErrorImageDrawable = errorImageDrawable; + } + + /** + * Sets the error image bitmap to be used for this view in the event that the image requested + * fails to load. + * + * <p>This will clear anything set by {@link NetworkImageView#setErrorImageResId} or {@link + * NetworkImageView#setDefaultImageDrawable}. + */ + public void setErrorImageBitmap(Bitmap errorImage) { + mErrorImageId = 0; + mErrorImageDrawable = null; + mErrorImageBitmap = errorImage; + } + + /** + * Loads the image for the view if it isn't already loaded. + * + * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. + */ + void loadImageIfNecessary(final boolean isInLayoutPass) { + int width = getWidth(); + int height = getHeight(); + ScaleType scaleType = getScaleType(); + + boolean wrapWidth = false, wrapHeight = false; + if (getLayoutParams() != null) { + wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; + wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; + } + + // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content + // view, hold off on loading the image. + boolean isFullyWrapContent = wrapWidth && wrapHeight; + if (width == 0 && height == 0 && !isFullyWrapContent) { + return; + } + + // if the URL to be loaded in this view is empty, cancel any old requests and clear the + // currently loaded image. + if (TextUtils.isEmpty(mUrl)) { + if (mImageContainer != null) { + mImageContainer.cancelRequest(); + mImageContainer = null; + } + setDefaultImageOrNull(); + return; + } + + // if there was an old request in this view, check if it needs to be canceled. + if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { + if (mImageContainer.getRequestUrl().equals(mUrl)) { + // if the request is from the same URL, return. + return; + } else { + // if there is a pre-existing request, cancel it if it's fetching a different URL. + mImageContainer.cancelRequest(); + setDefaultImageOrNull(); + } + } + + // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens. + int maxWidth = wrapWidth ? 0 : width; + int maxHeight = wrapHeight ? 0 : height; + + // The pre-existing content of this view didn't match the current URL. Load the new image + // from the network. + + // update the ImageContainer to be the new bitmap container. + mImageContainer = + mImageLoader.get( + mUrl, + new ImageListener() { + @Override + public void onErrorResponse(VolleyError error) { + if (mErrorImageId != 0) { + setImageResource(mErrorImageId); + } else if (mErrorImageDrawable != null) { + setImageDrawable(mErrorImageDrawable); + } else if (mErrorImageBitmap != null) { + setImageBitmap(mErrorImageBitmap); + } + } + + @Override + public void onResponse( + final ImageContainer response, boolean isImmediate) { + // If this was an immediate response that was delivered inside of a + // layout + // pass do not set the image immediately as it will trigger a + // requestLayout + // inside of a layout. Instead, defer setting the image by posting + // back to + // the main thread. + if (isImmediate && isInLayoutPass) { + post( + new Runnable() { + @Override + public void run() { + onResponse(response, /* isImmediate= */ false); + } + }); + return; + } + + if (response.getBitmap() != null) { + setImageBitmap(response.getBitmap()); + } else if (mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } else if (mDefaultImageDrawable != null) { + setImageDrawable(mDefaultImageDrawable); + } else if (mDefaultImageBitmap != null) { + setImageBitmap(mDefaultImageBitmap); + } + } + }, + maxWidth, + maxHeight, + scaleType); + } + + private void setDefaultImageOrNull() { + if (mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } else if (mDefaultImageDrawable != null) { + setImageDrawable(mDefaultImageDrawable); + } else if (mDefaultImageBitmap != null) { + setImageBitmap(mDefaultImageBitmap); + } else { + setImageBitmap(null); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + loadImageIfNecessary(/* isInLayoutPass= */ true); + } + + @Override + protected void onDetachedFromWindow() { + if (mImageContainer != null) { + // If the view was bound to an image request, cancel it and clear + // out the image from the view. + mImageContainer.cancelRequest(); + setImageBitmap(null); + // also clear out the container so we can reload the image if necessary. + mImageContainer = null; + } + super.onDetachedFromWindow(); + } + + @Override + protected void drawableStateChanged() { + super.drawableStateChanged(); + invalidate(); + } +} |