1package com.bumptech.glide;
2
3import android.content.Context;
4import android.graphics.drawable.Drawable;
5import android.view.animation.Animation;
6import android.widget.ImageView;
7
8import com.bumptech.glide.load.Encoder;
9import com.bumptech.glide.load.Key;
10import com.bumptech.glide.load.MultiTransformation;
11import com.bumptech.glide.load.ResourceDecoder;
12import com.bumptech.glide.load.ResourceEncoder;
13import com.bumptech.glide.load.Transformation;
14import com.bumptech.glide.load.engine.DiskCacheStrategy;
15import com.bumptech.glide.load.resource.UnitTransformation;
16import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
17import com.bumptech.glide.manager.Lifecycle;
18import com.bumptech.glide.manager.RequestTracker;
19import com.bumptech.glide.provider.ChildLoadProvider;
20import com.bumptech.glide.provider.LoadProvider;
21import com.bumptech.glide.request.FutureTarget;
22import com.bumptech.glide.request.GenericRequest;
23import com.bumptech.glide.request.Request;
24import com.bumptech.glide.request.RequestCoordinator;
25import com.bumptech.glide.request.RequestFutureTarget;
26import com.bumptech.glide.request.RequestListener;
27import com.bumptech.glide.request.ThumbnailRequestCoordinator;
28import com.bumptech.glide.request.animation.GlideAnimationFactory;
29import com.bumptech.glide.request.animation.NoAnimation;
30import com.bumptech.glide.request.animation.ViewAnimationFactory;
31import com.bumptech.glide.request.animation.ViewPropertyAnimation;
32import com.bumptech.glide.request.animation.ViewPropertyAnimationFactory;
33import com.bumptech.glide.request.target.PreloadTarget;
34import com.bumptech.glide.request.target.Target;
35import com.bumptech.glide.signature.EmptySignature;
36import com.bumptech.glide.util.Util;
37
38import java.io.File;
39
40/**
41 * A generic class that can handle setting options and staring loads for generic resource types.
42 *
43 * @param  The type of model representing the resource.
44 * @param  The data type that the resource {@link com.bumptech.glide.load.model.ModelLoader} will provide that
45 *                  can be decoded by the {@link com.bumptech.glide.load.ResourceDecoder}.
46 * @param  The type of the resource that will be loaded.
47 * @param  The type of resource the decoded resource will be transcoded to.
48 */
49public class GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> implements Cloneable {
50    protected final Class<ModelType> modelClass;
51    protected final Context context;
52    protected final Glide glide;
53    protected final Class<TranscodeType> transcodeClass;
54    protected final RequestTracker requestTracker;
55    protected final Lifecycle lifecycle;
56    private ChildLoadProvider<ModelType, DataType, ResourceType, TranscodeType> loadProvider;
57
58    private ModelType model;
59    private Key signature = EmptySignature.obtain();
60    // model may occasionally be null, so to enforce that load() was called, set a boolean rather than relying on model
61    // not to be null.
62    private boolean isModelSet;
63    private int placeholderId;
64    private int errorId;
65    private RequestListener<? super ModelType, TranscodeType> requestListener;
66    private Float thumbSizeMultiplier;
67    private GenericRequestBuilder<?, ?, ?, TranscodeType> thumbnailRequestBuilder;
68    private Float sizeMultiplier = 1f;
69    private Drawable placeholderDrawable;
70    private Drawable errorPlaceholder;
71    private Priority priority = null;
72    private boolean isCacheable = true;
73    private GlideAnimationFactory<TranscodeType> animationFactory = NoAnimation.getFactory();
74    private int overrideHeight = -1;
75    private int overrideWidth = -1;
76    private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.RESULT;
77    private Transformation<ResourceType> transformation = UnitTransformation.get();
78    private boolean isTransformationSet;
79
80    GenericRequestBuilder(LoadProvider<ModelType, DataType, ResourceType, TranscodeType> loadProvider,
81            Class<TranscodeType> transcodeClass, GenericRequestBuilder<ModelType, ?, ?, ?> other) {
82        this(other.context, other.modelClass, loadProvider, transcodeClass, other.glide, other.requestTracker,
83                other.lifecycle);
84        this.model = other.model;
85        this.isModelSet = other.isModelSet;
86        this.signature = other.signature;
87        this.diskCacheStrategy = other.diskCacheStrategy;
88        this.isCacheable = other.isCacheable;
89    }
90
91    GenericRequestBuilder(Context context, Class<ModelType> modelClass,
92            LoadProvider<ModelType, DataType, ResourceType, TranscodeType> loadProvider,
93            Class<TranscodeType> transcodeClass, Glide glide, RequestTracker requestTracker, Lifecycle lifecycle) {
94        this.context = context;
95        this.modelClass = modelClass;
96        this.transcodeClass = transcodeClass;
97        this.glide = glide;
98        this.requestTracker = requestTracker;
99        this.lifecycle = lifecycle;
100        this.loadProvider = loadProvider != null
101                ? new ChildLoadProvider<ModelType, DataType, ResourceType, TranscodeType>(loadProvider) : null;
102
103        if (context == null) {
104            throw new NullPointerException("Context can't be null");
105        }
106        if (modelClass != null && loadProvider == null) {
107            throw new NullPointerException("LoadProvider must not be null");
108        }
109    }
110
111    /**
112     * Loads and displays the resource retrieved by the given thumbnail request if it finishes before this request.
113     * Best used for loading thumbnail resources that are smaller and will be loaded more quickly than the full size
114     * resource. There are no guarantees about the order in which the requests will actually finish. However, if the
115     * thumb request completes after the full request, the thumb resource will never replace the full resource.
116     *
117     * @see #thumbnail(float)
118     *
119     * <p>
120     *     Recursive calls to thumbnail are supported.
121     * </p>
122     *
123     * @param thumbnailRequest The request to use to load the thumbnail.
124     * @return This request builder.
125     */
126    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> thumbnail(
127            GenericRequestBuilder<?, ?, ?, TranscodeType> thumbnailRequest) {
128        this.thumbnailRequestBuilder = thumbnailRequest;
129
130        return this;
131    }
132
133    /**
134     * Loads a resource in an identical manner to this request except with the dimensions of the target multiplied
135     * by the given size multiplier. If the thumbnail load completes before the fullsize load, the thumbnail will
136     * be shown. If the thumbnail load completes afer the fullsize load, the thumbnail will not be shown.
137     *
138     * <p>
139     *     Note - The thumbnail resource will be smaller than the size requested so the target (or {@link ImageView})
140     *     must be able to scale the thumbnail appropriately. See {@link android.widget.ImageView.ScaleType}.
141     * </p>
142     *
143     * <p>
144     *     Almost all options will be copied from the original load, including the
145     *     {@link com.bumptech.glide.load.model.ModelLoader}, {@link com.bumptech.glide.load.ResourceDecoder}, and
146     *     {@link Transformation}s. However, {@link #placeholder(int)} and {@link #error(int)},
147     *     and {@link #listener(RequestListener)} will only be used on the fullsize load and will not be copied for
148     *     the thumbnail load.
149     * </p>
150     *
151     * <p>
152     *     Recursive calls to thumbnail are supported.
153     * </p>
154     *
155     * @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the thumbnail.
156     * @return This request builder.
157     */
158    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> thumbnail(
159            float sizeMultiplier) {
160        if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
161            throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
162        }
163        this.thumbSizeMultiplier = sizeMultiplier;
164
165        return this;
166    }
167
168    /**
169     * Applies a multiplier to the {@link Target}'s size before loading the resource. Useful for loading thumbnails
170     * or trying to avoid loading huge resources (particularly {@link android.graphics.Bitmap}s on devices with overly
171     * dense screens.
172     *
173     * @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the resource.
174     * @return This request builder.
175     */
176    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> sizeMultiplier(
177            float sizeMultiplier) {
178        if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
179            throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
180        }
181        this.sizeMultiplier = sizeMultiplier;
182
183        return this;
184    }
185
186    /**
187     * Sets the {@link com.bumptech.glide.load.ResourceDecoder} to use to load the resource from the original data.
188     * By default, this decoder will only be used if the final transformed resource is not in the disk cache.
189     *
190     * @see #cacheDecoder(com.bumptech.glide.load.ResourceDecoder)
191     * @see com.bumptech.glide.load.engine.DiskCacheStrategy
192     *
193     * @param decoder The {@link com.bumptech.glide.load.ResourceDecoder} to use to decode the resource.
194     * @return This request builder.
195     */
196    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> decoder(
197            ResourceDecoder<DataType, ResourceType> decoder) {
198        // loadProvider will be null if model is null, in which case we're not going to load anything so it's ok to
199        // ignore the decoder.
200        if (loadProvider != null) {
201            loadProvider.setSourceDecoder(decoder);
202        }
203
204        return this;
205    }
206
207    /**
208     * Sets the {@link com.bumptech.glide.load.ResourceDecoder} to use to load the resource from the disk cache. By
209     * default, this decoder will only be used if the final transformed resource is already in the disk cache.
210     *
211     * @see #decoder(com.bumptech.glide.load.ResourceDecoder)
212     * @see com.bumptech.glide.load.engine.DiskCacheStrategy
213     *
214     * @param cacheDecoder The decoder to use.
215     * @return This request builder.
216     */
217    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> cacheDecoder(
218            ResourceDecoder<File, ResourceType> cacheDecoder) {
219        // loadProvider will be null if model is null, in which case we're not going to load anything so it's ok to
220        // ignore the decoder.
221        if (loadProvider != null) {
222            loadProvider.setCacheDecoder(cacheDecoder);
223        }
224
225        return this;
226    }
227
228    /**
229     * Sets the source encoder to use to encode the data retrieved by this request directly into cache. The returned
230     * resource will then be decoded from the cached data.
231     *
232     * @see com.bumptech.glide.load.engine.DiskCacheStrategy
233     *
234     * @param sourceEncoder The encoder to use.
235     * @return This request builder.
236     */
237    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> sourceEncoder(
238            Encoder<DataType> sourceEncoder) {
239        if (loadProvider != null) {
240            loadProvider.setSourceEncoder(sourceEncoder);
241        }
242
243        return this;
244    }
245
246    /**
247     * Sets the {@link com.bumptech.glide.load.engine.DiskCacheStrategy} to use for this load. Defaults to
248     * {@link com.bumptech.glide.load.engine.DiskCacheStrategy#RESULT}.
249     *
250     * <p>
251     *     For most applications {@link com.bumptech.glide.load.engine.DiskCacheStrategy#RESULT} is ideal.
252     *     Applications that use the same resource multiple times in multiple sizes and are willing to trade off some
253     *     speed and disk space in return for lower bandwidth usage may want to consider using
254     *     {@link com.bumptech.glide.load.engine.DiskCacheStrategy#SOURCE} or
255     *     {@link com.bumptech.glide.load.engine.DiskCacheStrategy#RESULT}. Any download only operations should
256     *     typically use {@link com.bumptech.glide.load.engine.DiskCacheStrategy#SOURCE}.
257     * </p>
258     *
259     * @param strategy The strategy to use.
260     * @return This request builder.
261     */
262    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType>  diskCacheStrategy(
263            DiskCacheStrategy strategy) {
264        this.diskCacheStrategy = strategy;
265
266        return this;
267    }
268
269    /**
270     * Sets the {@link com.bumptech.glide.load.Encoder} to use to encode the original data directly to cache. Will only
271     * be used if the original data is not already in cache and if the
272     * {@link com.bumptech.glide.load.engine.DiskCacheStrategy} is set to
273     * {@link com.bumptech.glide.load.engine.DiskCacheStrategy#SOURCE} or
274     * {@link com.bumptech.glide.load.engine.DiskCacheStrategy#ALL}.
275     *
276     * @see #sourceEncoder(com.bumptech.glide.load.Encoder)
277     * @see com.bumptech.glide.load.engine.DiskCacheStrategy
278     *
279     * @param encoder The encoder to use.
280     * @return This request builder.
281     */
282    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> encoder(
283            ResourceEncoder<ResourceType> encoder) {
284        // loadProvider will be null if model is null, in which case we're not going to load anything so it's ok to
285        // ignore the encoder.
286        if (loadProvider != null) {
287            loadProvider.setEncoder(encoder);
288        }
289
290        return this;
291    }
292
293    /**
294     * Sets the priority for this load.
295     *
296     * @param priority A priority.
297     * @return This request builder.
298     */
299    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> priority(
300            Priority priority) {
301        this.priority = priority;
302
303        return this;
304    }
305
306    /**
307     * Transform resources with the given {@link Transformation}s. Replaces any existing transformation or
308     * transformations.
309     *
310     * @param transformations the transformations to apply in order.
311     * @return This request builder.
312     */
313    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transform(
314            Transformation<ResourceType>... transformations) {
315        isTransformationSet = true;
316        if (transformations.length == 1) {
317            transformation = transformations[0];
318        } else {
319            transformation = new MultiTransformation<ResourceType>(transformations);
320        }
321
322        return this;
323    }
324
325    /**
326     * Removes the current {@link com.bumptech.glide.load.Transformation}.
327     *
328     * @return This request builder.
329     */
330    @SuppressWarnings("unchecked")
331    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> dontTransform() {
332        Transformation<ResourceType> transformation = UnitTransformation.get();
333        return transform(transformation);
334    }
335
336    /**
337     * Sets the {@link com.bumptech.glide.load.resource.transcode.ResourceTranscoder} to use for this load.
338     *
339     * @see com.bumptech.glide.load.resource.transcode.UnitTranscoder
340     * @see com.bumptech.glide.load.resource.transcode.GlideBitmapDrawableTranscoder
341     * @see com.bumptech.glide.load.resource.transcode.GifBitmapWrapperDrawableTranscoder
342     *
343     * @param transcoder The transcoder to use.
344     * @return This request builder.
345     */
346    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> transcoder(
347            ResourceTranscoder<ResourceType, TranscodeType> transcoder) {
348        if (loadProvider != null) {
349            loadProvider.setTranscoder(transcoder);
350        }
351
352        return this;
353    }
354
355    /**
356     * Removes any existing animation set on the builder. Will be overridden by subsequent calls that set an animation.
357     * @return This request builder.
358     */
359    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> dontAnimate() {
360        GlideAnimationFactory<TranscodeType> animation = NoAnimation.getFactory();
361        return animate(animation);
362    }
363
364    /**
365     * Sets an animation to run on the wrapped target when an resource load finishes. Will only be run if the resource
366     * was loaded asynchronously (ie was not in the memory cache)
367     *
368     * @param animationId The resource id of the animation to run
369     * @return This request builder.
370     */
371    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(int animationId) {
372        return animate(new ViewAnimationFactory<TranscodeType>(context, animationId));
373    }
374
375    /**
376     * Sets an animation to run on the wrapped target when a resource load finishes. Will only be run if the resource
377     * was loaded asynchronously (ie was not in the memory cache)
378     *
379     * @see #animate(int)
380     * @see #animate(com.bumptech.glide.request.animation.ViewPropertyAnimation.Animator)
381     *
382     * @deprecated If this builder is used for multiple loads, using this method will result in multiple view's being
383     * asked to start an animation using a single {@link android.view.animation.Animation} object which results in
384     * views animating repeatedly. Use {@link #animate(int)} or
385     * {@link #animate(com.bumptech.glide.request.animation.ViewPropertyAnimation.Animator)}. Scheduled to be removed in
386     * Glide 4.0.
387     * @param animation The animation to run
388     * @return This request builder.
389     */
390    @Deprecated
391    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(Animation animation) {
392        return animate(new ViewAnimationFactory<TranscodeType>(animation));
393    }
394
395    /**
396     * Sets an animator to run a {@link android.view.ViewPropertyAnimator} on a view that the target may be wrapping
397     * when a resource load finishes. Will only be run if the load was loaded asynchronously (ie was not in the
398     * memory cache).
399     *
400     * @param animator The {@link com.bumptech.glide.request.animation.ViewPropertyAnimation.Animator} to run.
401     * @return This request builder.
402     */
403    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(
404            ViewPropertyAnimation.Animator animator) {
405        return animate(new ViewPropertyAnimationFactory<TranscodeType>(animator));
406    }
407
408    GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> animate(
409            GlideAnimationFactory<TranscodeType> animationFactory) {
410        if (animationFactory == null) {
411            throw new NullPointerException("Animation factory must not be null!");
412        }
413        this.animationFactory = animationFactory;
414
415        return this;
416    }
417
418    /**
419     * Sets an Android resource id for a {@link android.graphics.drawable.Drawable} resourceto display while a resource
420     * is loading.
421     *
422     * @param resourceId The id of the resource to use as a placeholder
423     * @return This request builder.
424     */
425    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> placeholder(
426            int resourceId) {
427        this.placeholderId = resourceId;
428
429        return this;
430    }
431
432    /**
433     * Sets an {@link android.graphics.drawable.Drawable} to display while a resource is loading.
434     *
435     * @param drawable The drawable to display as a placeholder.
436     * @return This request builder.
437     */
438    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> placeholder(
439            Drawable drawable) {
440        this.placeholderDrawable = drawable;
441
442        return this;
443    }
444
445    /**
446     * Sets a resource to display if a load fails.
447     *
448     * @param resourceId The id of the resource to use as a placeholder.
449     * @return This request builder.
450     */
451    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> error(
452            int resourceId) {
453        this.errorId = resourceId;
454
455        return this;
456    }
457
458    /**
459     * Sets a {@link Drawable} to display if a load fails.
460     *
461     * @param drawable The drawable to display.
462     * @return This request builder.
463     */
464    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> error(
465            Drawable drawable) {
466        this.errorPlaceholder = drawable;
467
468        return this;
469    }
470
471    /**
472     * Sets a RequestBuilder listener to monitor the resource load. It's best to create a single instance of an
473     * exception handler per type of request (usually activity/fragment) rather than pass one in per request to
474     * avoid some redundant object allocation.
475     *
476     * @param requestListener The request listener to use.
477     * @return This request builder.
478     */
479    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> listener(
480            RequestListener<? super ModelType, TranscodeType> requestListener) {
481        this.requestListener = requestListener;
482
483        return this;
484    }
485
486    /**
487     * Allows the loaded resource to skip the memory cache.
488     *
489     * <p>
490     *     Note - this is not a guarantee. If a request is already pending for this resource and that request is not
491     *     also skipping the memory cache, the resource will be cached in memory.
492     * </p>
493     *
494     * @param skip True to allow the resource to skip the memory cache.
495     * @return This request builder.
496     */
497    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> skipMemoryCache(boolean skip) {
498        this.isCacheable = !skip;
499
500        return this;
501    }
502
503    /**
504     * Overrides the {@link Target}'s width and height with the given values. This is useful almost exclusively for
505     * thumbnails, and should only be used when you both need a very specific sized image and when it is impossible or
506     * impractical to return that size from {@link Target#getSize(com.bumptech.glide.request.target.SizeReadyCallback)}.
507     *
508     * @param width The width in pixels to use to load the resource.
509     * @param height The height in pixels to use to load the resource.
510     * @return This request builder.
511     */
512    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> override(int width, int height) {
513        if (width <= 0) {
514            throw new IllegalArgumentException("Width must be > 0");
515        }
516        if (height <= 0) {
517            throw new IllegalArgumentException("Height must be > 0");
518        }
519        this.overrideWidth = width;
520        this.overrideHeight = height;
521
522        return this;
523    }
524
525    /**
526     * Sets some additional data to be mixed in to the memory and disk cache keys allowing the caller more control over
527     * when cached data is invalidated.
528     *
529     * <p>
530     *     Note - The signature does not replace the cache key, it is purely additive.
531     * </p>
532     *
533     * @see com.bumptech.glide.signature.StringSignature
534     *
535     * @param signature A unique non-null {@link com.bumptech.glide.load.Key} representing the current state of the
536     *                  model that will be mixed in to the cache key.
537     * @return This request builder.
538     */
539    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> signature(Key signature) {
540        if (signature == null) {
541            throw new NullPointerException("Signature must not be null");
542        }
543        this.signature = signature;
544        return this;
545    }
546
547    /**
548     * Sets the specific model to load data for.
549     *
550     * <p>
551     *      This method must be called at least once before {@link #into(com.bumptech.glide.request.target.Target)} is
552     *      called.
553     * </p>
554     *
555     * @param model The model to load data for, or null.
556     * @return This request builder.
557     */
558    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> load(ModelType model) {
559        this.model = model;
560        isModelSet = true;
561        return this;
562    }
563
564    /**
565     * Returns a copy of this request builder with all of the options set so far on this builder.
566     *
567     * <p>
568     *     This method returns a "deep" copy in that all non-immutable arguments are copied such that changes to one
569     *     builder will not affect the other builder. However, in addition to immutable arguments, the current model
570     *     is not copied copied so changes to the model will affect both builders.
571     * </p>
572     */
573    @SuppressWarnings("unchecked")
574    @Override
575    public GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> clone() {
576        try {
577            GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType> clone =
578                    (GenericRequestBuilder<ModelType, DataType, ResourceType, TranscodeType>) super.clone();
579            clone.loadProvider = loadProvider != null ? loadProvider.clone() : null;
580            return clone;
581        } catch (CloneNotSupportedException e) {
582            throw new RuntimeException(e);
583        }
584    }
585
586    /**
587     * Set the target the resource will be loaded into.
588     *
589     * @see Glide#clear(com.bumptech.glide.request.target.Target)
590     *
591     * @param target The target to load the resource into.
592     * @return The given target.
593     */
594    public <Y extends Target<TranscodeType>> Y into(Y target) {
595        Util.assertMainThread();
596        if (target == null) {
597            throw new IllegalArgumentException("You must pass in a non null Target");
598        }
599        if (!isModelSet) {
600            throw new IllegalArgumentException("You must first set a model (try #load())");
601        }
602
603        Request previous = target.getRequest();
604
605        if (previous != null) {
606            previous.clear();
607            requestTracker.removeRequest(previous);
608            previous.recycle();
609        }
610
611        Request request = buildRequest(target);
612        target.setRequest(request);
613        lifecycle.addListener(target);
614        requestTracker.runRequest(request);
615
616        return target;
617    }
618
619    /**
620     * Sets the {@link ImageView} the resource will be loaded into, cancels any existing loads into the view, and frees
621     * any resources Glide may have previously loaded into the view so they may be reused.
622     *
623     * @see Glide#clear(android.view.View)
624     *
625     * @param view The view to cancel previous loads for and load the new resource into.
626     * @return The {@link com.bumptech.glide.request.target.Target} used to wrap the given {@link ImageView}.
627     */
628    public Target<TranscodeType> into(ImageView view) {
629        Util.assertMainThread();
630        if (view == null) {
631            throw new IllegalArgumentException("You must pass in a non null View");
632        }
633
634        if (!isTransformationSet && view.getScaleType() != null) {
635            switch (view.getScaleType()) {
636                case CENTER_CROP:
637                    applyCenterCrop();
638                    break;
639                case FIT_CENTER:
640                case FIT_START:
641                case FIT_END:
642                    applyFitCenter();
643                    break;
644                //$CASES-OMITTED$
645                default:
646                    // silently ignore
647                    break;
648            }
649        }
650
651        return into(glide.buildImageViewTarget(view, transcodeClass));
652    }
653
654    /**
655     * Returns a future that can be used to do a blocking get on a background thread.
656     *
657     * @param width The desired width in pixels (note this will be overriden by {@link #override(int, int)} if
658     *              previously called).
659     * @param height The desired height in pixels (note this will be overriden by {@link #override(int, int)}}
660     *               if previously called).
661     *
662     * @see Glide#clear(com.bumptech.glide.request.FutureTarget)
663     *
664     * @return An {@link com.bumptech.glide.request.FutureTarget} that can be used to obtain the
665     *         resource in a blocking manner.
666     */
667    public FutureTarget<TranscodeType> into(int width, int height) {
668        final RequestFutureTarget<ModelType, TranscodeType> target =
669                new RequestFutureTarget<ModelType, TranscodeType>(glide.getMainHandler(), width, height);
670
671        // TODO: Currently all loads must be started on the main thread...
672        glide.getMainHandler().post(new Runnable() {
673            @Override
674            public void run() {
675                if (!target.isCancelled()) {
676                    into(target);
677                }
678            }
679        });
680
681        return target;
682    }
683
684    /**
685     * Preloads the resource into the cache using the given width and height.
686     *
687     * <p>
688     *     Pre-loading is useful for making sure that resources you are going to to want in the near future are
689     *     available quickly.
690     * </p>
691     *
692     * @see com.bumptech.glide.ListPreloader
693     */
694    public Target<TranscodeType> preload(int width, int height) {
695        final PreloadTarget<TranscodeType> target = PreloadTarget.obtain(width, height);
696        return into(target);
697    }
698
699    void applyCenterCrop() {
700        // To be implemented by subclasses when possible.
701    }
702
703    void applyFitCenter() {
704        // To be implemented by subclasses when possible.
705    }
706
707    private Priority getThumbnailPriority() {
708        final Priority result;
709        if (priority == Priority.LOW) {
710            result = Priority.NORMAL;
711        } else if (priority == Priority.NORMAL) {
712            result = Priority.HIGH;
713        } else {
714            result = Priority.IMMEDIATE;
715        }
716        return result;
717    }
718
719    private Request buildRequest(Target<TranscodeType> target) {
720        if (priority == null) {
721            priority = Priority.NORMAL;
722        }
723        return buildRequestRecursive(target, null);
724    }
725
726    private Request buildRequestRecursive(Target<TranscodeType> target, ThumbnailRequestCoordinator parentCoordinator) {
727        if (thumbnailRequestBuilder != null) {
728            // Recursive case: contains a potentially recursive thumbnail request builder.
729            if (thumbnailRequestBuilder.animationFactory.equals(NoAnimation.getFactory())) {
730                thumbnailRequestBuilder.animationFactory = animationFactory;
731            }
732
733            if (thumbnailRequestBuilder.priority == null) {
734                thumbnailRequestBuilder.priority = getThumbnailPriority();
735            }
736
737            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
738            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
739            // Recursively generate thumbnail requests.
740            Request thumbRequest = thumbnailRequestBuilder.buildRequestRecursive(target, coordinator);
741            coordinator.setRequests(fullRequest, thumbRequest);
742            return coordinator;
743        } else if (thumbSizeMultiplier != null) {
744            // Base case: thumbnail multiplier generates a thumbnail request, but cannot recurse.
745            ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);
746            Request fullRequest = obtainRequest(target, sizeMultiplier, priority, coordinator);
747            Request thumbnailRequest = obtainRequest(target, thumbSizeMultiplier, getThumbnailPriority(), coordinator);
748            coordinator.setRequests(fullRequest, thumbnailRequest);
749            return coordinator;
750        } else {
751            // Base case: no thumbnail.
752            return obtainRequest(target, sizeMultiplier, priority, parentCoordinator);
753        }
754    }
755
756    private Request obtainRequest(Target<TranscodeType> target, float sizeMultiplier, Priority priority,
757            RequestCoordinator requestCoordinator) {
758        return GenericRequest.obtain(
759                loadProvider,
760                model,
761                signature,
762                context,
763                priority,
764                target,
765                sizeMultiplier,
766                placeholderDrawable,
767                placeholderId,
768                errorPlaceholder,
769                errorId,
770                requestListener,
771                requestCoordinator,
772                glide.getEngine(),
773                transformation,
774                transcodeClass,
775                isCacheable,
776                animationFactory,
777                overrideWidth,
778                overrideHeight,
779                diskCacheStrategy);
780    }
781}
782