package com.bumptech.glide.request; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.Log; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import com.bumptech.glide.Priority; import com.bumptech.glide.Resource; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.ResourceEncoder; import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; import com.bumptech.glide.load.engine.Engine; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.provider.LoadProvider; import com.bumptech.glide.request.target.Target; import java.io.InputStream; /** * A {@link Request} that loads a {@link Resource} into a given {@link Target}. * * @param The type of the model that the resource will be loaded from. * @param The type of the data that the resource will be loaded from. * @param The type of the resource that will be loaded. */ public class GenericRequest implements Request, Target.SizeReadyCallback, ResourceCallback { private static final String TAG = "Request"; private final int placeholderResourceId; private final int errorResourceId; private final Context context; private final Transformation transformation; private final LoadProvider loadProvider; private final int animationId; private final RequestCoordinator requestCoordinator; private final A model; private Class transcodeClass; private boolean isMemoryCacheable; private Priority priority; private final Target target; private final RequestListener requestListener; private final float sizeMultiplier; private final Engine engine; private Animation animation; private Drawable placeholderDrawable; private Drawable errorDrawable; private boolean isCancelled; private boolean isError; private boolean loadedFromMemoryCache; private Resource resource; private Engine.LoadStatus loadStatus; public GenericRequest(LoadProvider loadProvider, A model, Context context, Priority priority, Target target, float sizeMultiplier, Drawable placeholderDrawable, int placeholderResourceId, Drawable errorDrawable, int errorResourceId, RequestListener requestListener, int animationId, Animation animation, RequestCoordinator requestCoordinator, Engine engine, Transformation transformation, Class transcodeClass, boolean isMemoryCacheable) { this.loadProvider = loadProvider; this.model = model; this.context = context; this.priority = priority; this.target = target; this.sizeMultiplier = sizeMultiplier; this.placeholderDrawable = placeholderDrawable; this.placeholderResourceId = placeholderResourceId; this.errorDrawable = errorDrawable; this.errorResourceId = errorResourceId; this.requestListener = requestListener; this.animationId = animationId; this.animation = animation; this.requestCoordinator = requestCoordinator; this.engine = engine; this.transformation = transformation; this.transcodeClass = transcodeClass; this.isMemoryCacheable = isMemoryCacheable; // We allow null models by just setting an error drawable. Null models will always have empty providers, we // simply skip our sanity checks in that unusual case. if (model != null) { if (loadProvider.getCacheDecoder() == null) { throw new NullPointerException("CacheDecoder must not be null, try .cacheDecoder(ResouceDecoder)"); } if (loadProvider.getSourceDecoder() == null) { throw new NullPointerException("SourceDecoder must not be null, try .imageDecoder(ResourceDecoder) " + "and/or .videoDecoder()"); } if (loadProvider.getEncoder() == null) { throw new NullPointerException("Encoder must not be null, try .encode(ResourceEncoder)"); } if (loadProvider.getTranscoder() == null) { throw new NullPointerException("Transcoder must not be null, try .as(Class, ResourceTranscoder)"); } if (loadProvider.getModelLoader() == null) { throw new NullPointerException("ModelLoader must not be null, try .using(ModelLoader)"); } } } @Override public void run() { if (model == null) { onException(null); return; } target.getSize(this); if (!isComplete() && !isFailed()) { setPlaceHolder(); } } public void cancel() { isCancelled = true; if (loadStatus != null) { loadStatus.cancel(); loadStatus = null; } } @Override public void clear() { cancel(); setPlaceHolder(); if (resource != null) { resource.release(); resource = null; } } @Override public boolean isComplete() { return resource != null; } @Override public boolean isFailed() { return isError; } private void setPlaceHolder() { if (!canSetPlaceholder()) return; if (placeholderDrawable == null && placeholderResourceId > 0) { placeholderDrawable = context.getResources().getDrawable(placeholderResourceId); } target.setPlaceholder(placeholderDrawable); } private void setErrorPlaceholder() { if (!canSetPlaceholder()) return; if (errorDrawable == null && errorResourceId > 0) { errorDrawable = context.getResources().getDrawable(errorResourceId); } if (errorDrawable == null) { setPlaceHolder(); } else { target.setPlaceholder(errorDrawable); } } @Override public void onSizeReady(int width, int height) { if (isCancelled) { return; } width = Math.round(sizeMultiplier * width); height = Math.round(sizeMultiplier * height); ResourceDecoder cacheDecoder = loadProvider.getCacheDecoder(); ResourceDecoder decoder = loadProvider.getSourceDecoder(); ResourceEncoder encoder = loadProvider.getEncoder(); ResourceTranscoder transcoder = loadProvider.getTranscoder(); ModelLoader modelLoader = loadProvider.getModelLoader(); final String id = modelLoader.getId(model); final DataFetcher dataFetcher = modelLoader.getResourceFetcher(model, width, height); loadedFromMemoryCache = true; loadStatus = engine.load(id, width, height, cacheDecoder, dataFetcher, decoder, transformation, encoder, transcoder, priority, isMemoryCacheable, this); loadedFromMemoryCache = resource != null; } private boolean canSetImage() { return requestCoordinator == null || requestCoordinator.canSetImage(this); } private boolean canSetPlaceholder() { return requestCoordinator == null || requestCoordinator.canSetPlaceholder(this); } private boolean isAnyImageSet() { return requestCoordinator != null && requestCoordinator.isAnyRequestComplete(); } @Override public void onResourceReady(Resource resource) { if (!canSetImage()) { resource.release(); return; } if (resource == null || !transcodeClass.isAssignableFrom(resource.get().getClass())) { if (resource != null) { resource.release(); } onException(new Exception("Expected to receive an object of " + transcodeClass + " but instead got " + (resource != null ? resource.get() : null))); return; } R result = (R) resource.get(); target.onResourceReady(result); if (!loadedFromMemoryCache && !isAnyImageSet()) { if (animation == null && animationId > 0) { animation = AnimationUtils.loadAnimation(context, animationId); } if (animation != null) { target.startAnimation(animation); } } if (requestListener != null) { requestListener.onImageReady(model, target, loadedFromMemoryCache, isAnyImageSet()); } this.resource = resource; } @Override public void onException(Exception e) { // if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "load failed", e); // } isError = true; setErrorPlaceholder(); //TODO: what if this is a thumbnail request? if (requestListener != null) { requestListener.onException(e, model, target); } } }