package com.bumptech.glide.request.bitmap;
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.Request;
import com.bumptech.glide.request.RequestCoordinator;
import com.bumptech.glide.request.ResourceCallback;
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 <A> The type of the model that the resource will be loaded from.
* @param <T> The type of the data that the resource will be loaded from.
* @param <Z> The type of the resource that will be loaded.
*/
public class GenericRequest<A, T, Z, R> 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<Z> transformation;
private final LoadProvider<A, T, Z, R> loadProvider;
private final int animationId;
private final RequestCoordinator requestCoordinator;
private final A model;
private Class<R> transcodeClass;
private boolean isMemoryCacheable;
private Priority priority;
private final Target<R> target;
private final RequestListener<A> 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<A, T, Z, R> loadProvider, A model, Context context, Priority priority,
Target<R> target, float sizeMultiplier, Drawable placeholderDrawable, int placeholderResourceId,
Drawable errorDrawable, int errorResourceId, RequestListener<A> requestListener, int animationId,
Animation animation, RequestCoordinator requestCoordinator, Engine engine,
Transformation<Z> transformation, Class<R> 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<InputStream, Z> cacheDecoder = loadProvider.getCacheDecoder();
ResourceDecoder<T, Z> decoder = loadProvider.getSourceDecoder();
ResourceEncoder <Z> encoder = loadProvider.getEncoder();
ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
final String id = modelLoader.getId(model);
final DataFetcher<T> 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);
}
}
}