Home | History | Annotate | Download | only in glide
      1 package com.bumptech.glide;
      2 
      3 import android.content.Context;
      4 import android.graphics.drawable.Drawable;
      5 import android.view.animation.Animation;
      6 import android.widget.ImageView;
      7 
      8 import com.bumptech.glide.load.Encoder;
      9 import com.bumptech.glide.load.Key;
     10 import com.bumptech.glide.load.MultiTransformation;
     11 import com.bumptech.glide.load.ResourceDecoder;
     12 import com.bumptech.glide.load.ResourceEncoder;
     13 import com.bumptech.glide.load.Transformation;
     14 import com.bumptech.glide.load.engine.DiskCacheStrategy;
     15 import com.bumptech.glide.load.resource.UnitTransformation;
     16 import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
     17 import com.bumptech.glide.manager.Lifecycle;
     18 import com.bumptech.glide.manager.RequestTracker;
     19 import com.bumptech.glide.provider.ChildLoadProvider;
     20 import com.bumptech.glide.provider.LoadProvider;
     21 import com.bumptech.glide.request.FutureTarget;
     22 import com.bumptech.glide.request.GenericRequest;
     23 import com.bumptech.glide.request.Request;
     24 import com.bumptech.glide.request.RequestCoordinator;
     25 import com.bumptech.glide.request.RequestFutureTarget;
     26 import com.bumptech.glide.request.RequestListener;
     27 import com.bumptech.glide.request.ThumbnailRequestCoordinator;
     28 import com.bumptech.glide.request.animation.GlideAnimationFactory;
     29 import com.bumptech.glide.request.animation.NoAnimation;
     30 import com.bumptech.glide.request.animation.ViewAnimationFactory;
     31 import com.bumptech.glide.request.animation.ViewPropertyAnimation;
     32 import com.bumptech.glide.request.animation.ViewPropertyAnimationFactory;
     33 import com.bumptech.glide.request.target.PreloadTarget;
     34 import com.bumptech.glide.request.target.Target;
     35 import com.bumptech.glide.signature.EmptySignature;
     36 import com.bumptech.glide.util.Util;
     37 
     38 import 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  */
     49 public 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