1package com.bumptech.glide;
2
3import android.annotation.TargetApi;
4import android.app.Activity;
5import android.content.ComponentCallbacks2;
6import android.content.Context;
7import android.graphics.Bitmap;
8import android.graphics.drawable.BitmapDrawable;
9import android.graphics.drawable.Drawable;
10import android.net.Uri;
11import android.os.ParcelFileDescriptor;
12import android.support.v4.app.Fragment;
13import android.support.v4.app.FragmentActivity;
14import android.util.Log;
15import android.view.View;
16import android.widget.ImageView;
17import com.android.volley.RequestQueue;
18import com.bumptech.glide.load.engine.Engine;
19import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
20import com.bumptech.glide.load.engine.cache.DiskCache;
21import com.bumptech.glide.load.engine.cache.MemoryCache;
22import com.bumptech.glide.load.model.GenericLoaderFactory;
23import com.bumptech.glide.load.model.GlideUrl;
24import com.bumptech.glide.load.model.ImageVideoWrapper;
25import com.bumptech.glide.load.model.ModelLoader;
26import com.bumptech.glide.load.model.ModelLoaderFactory;
27import com.bumptech.glide.load.model.file_descriptor.FileDescriptorFileLoader;
28import com.bumptech.glide.load.model.file_descriptor.FileDescriptorModelLoader;
29import com.bumptech.glide.load.model.file_descriptor.FileDescriptorResourceLoader;
30import com.bumptech.glide.load.model.file_descriptor.FileDescriptorStringLoader;
31import com.bumptech.glide.load.model.file_descriptor.FileDescriptorUriLoader;
32import com.bumptech.glide.load.model.stream.StreamFileLoader;
33import com.bumptech.glide.load.model.stream.StreamModelLoader;
34import com.bumptech.glide.load.model.stream.StreamResourceLoader;
35import com.bumptech.glide.load.model.stream.StreamStringLoader;
36import com.bumptech.glide.load.model.stream.StreamUriLoader;
37import com.bumptech.glide.load.model.stream.StreamUrlLoader;
38import com.bumptech.glide.load.resource.bitmap.CenterCrop;
39import com.bumptech.glide.load.resource.bitmap.FileDescriptorBitmapDataLoadProvider;
40import com.bumptech.glide.load.resource.bitmap.FitCenter;
41import com.bumptech.glide.load.resource.bitmap.ImageVideoDataLoadProvider;
42import com.bumptech.glide.load.resource.bitmap.StreamBitmapDataLoadProvider;
43import com.bumptech.glide.load.resource.gif.GifData;
44import com.bumptech.glide.load.resource.gif.GifDataLoadProvider;
45import com.bumptech.glide.load.resource.gif.GifDrawable;
46import com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapper;
47import com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperTransformation;
48import com.bumptech.glide.load.resource.gifbitmap.ImageVideoGifDataLoadProvider;
49import com.bumptech.glide.load.resource.transcode.BitmapDrawableTranscoder;
50import com.bumptech.glide.load.resource.transcode.GifBitmapWrapperDrawableTranscoder;
51import com.bumptech.glide.load.resource.transcode.GifDataDrawableTranscoder;
52import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
53import com.bumptech.glide.load.resource.transcode.TranscoderFactory;
54import com.bumptech.glide.manager.RequestManagerRetriever;
55import com.bumptech.glide.provider.DataLoadProviderFactory;
56import com.bumptech.glide.request.GlideAnimation;
57import com.bumptech.glide.request.Request;
58import com.bumptech.glide.request.target.ImageViewTargetFactory;
59import com.bumptech.glide.request.target.Target;
60import com.bumptech.glide.request.target.ViewTarget;
61import com.bumptech.glide.volley.VolleyUrlLoader;
62
63import java.io.File;
64import java.io.InputStream;
65import java.net.URL;
66
67/**
68 * A singleton to present a simple static interface for building requests with {@link BitmapRequestBuilder} and maintaining
69 * an {@link Engine}, {@link BitmapPool}, {@link DiskCache} and {@link MemoryCache}.
70 *
71 * <p>
72 * Note - This class is not thread safe.
73 * </p>
74 */
75public class Glide {
76    // 250 MB
77    static final int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
78
79    private static final String DEFAULT_DISK_CACHE_DIR = "image_manager_disk_cache";
80    private static final String TAG = "Glide";
81    private static Glide GLIDE;
82
83    private final GenericLoaderFactory loaderFactory = new GenericLoaderFactory();
84    private final RequestQueue requestQueue;
85    private final Engine engine;
86    private final BitmapPool bitmapPool;
87    private final MemoryCache memoryCache;
88    private final ImageViewTargetFactory imageViewTargetFactory = new ImageViewTargetFactory();
89    private final TranscoderFactory transcoderFactory = new TranscoderFactory();
90    private final DataLoadProviderFactory dataLoadProviderFactory;
91    private final CenterCrop bitmapCenterCrop;
92    private final GifBitmapWrapperTransformation drawableCenterCrop;
93    private final FitCenter bitmapFitCenter;
94    private final GifBitmapWrapperTransformation drawableFitCenter;
95
96    /**
97     * Try to get the external cache directory if available and default to the internal. Use a default name for the
98     * cache directory if no name is provided
99     *
100     * @param context A context
101     * @return A File representing the default disk cache directory
102     */
103    public static File getPhotoCacheDir(Context context) {
104        return getPhotoCacheDir(context, DEFAULT_DISK_CACHE_DIR);
105    }
106
107    /**
108     * Try to get the external cache directory if available and default to the internal. Use a default name for the
109     * cache directory if no name is provided
110     *
111     * @param context A context
112     * @param cacheName The name of the subdirectory in which to store the cache
113     * @return A File representing the default disk cache directory
114     */
115    @SuppressWarnings("ResultOfMethodCallIgnored")
116    public static File getPhotoCacheDir(Context context, String cacheName) {
117        File cacheDir = context.getCacheDir();
118        if (cacheDir != null) {
119            File result = new File(cacheDir, cacheName);
120            result.mkdirs();
121            return result;
122        }
123        if (Log.isLoggable(TAG, Log.ERROR)) {
124            Log.e(TAG, "default disk cache dir is null");
125        }
126        return null;
127    }
128
129    /**
130     * Get the singleton.
131     *
132     * @return the singleton
133     */
134    public static Glide get(Context context) {
135        if (GLIDE == null) {
136            GLIDE = new GlideBuilder(context).createGlide();
137        }
138
139        return GLIDE;
140    }
141
142    /**
143     * Returns false if the {@link Glide} singleton has not yet been created and can therefore be setup using
144     * {@link #setup(GlideBuilder)}.
145     *
146     * @see #setup(GlideBuilder)
147     */
148    public static boolean isSetup() {
149        return GLIDE != null;
150    }
151
152    /**
153     * Creates the {@link Glide} singleton using the given builder. Can be used to set options like cache sizes and
154     * locations.
155     *
156     * @see #isSetup()
157     *
158     * @param builder The builder.
159     * @throws IllegalArgumentException if the Glide singleton has already been created.
160     */
161    public static void setup(GlideBuilder builder) {
162        if (isSetup()) {
163            throw new IllegalArgumentException("Glide is already setup, check with isSetup() first");
164        }
165
166        GLIDE = builder.createGlide();
167    }
168
169    static void tearDown() {
170        GLIDE = null;
171    }
172
173    Glide(Engine engine, RequestQueue requestQueue, MemoryCache memoryCache, BitmapPool bitmapPool,
174            Context context) {
175        this.engine = engine;
176        this.requestQueue = requestQueue;
177        this.bitmapPool = bitmapPool;
178        this.memoryCache = memoryCache;
179
180        dataLoadProviderFactory = new DataLoadProviderFactory();
181
182        dataLoadProviderFactory.register(InputStream.class, Bitmap.class, new StreamBitmapDataLoadProvider(bitmapPool));
183        dataLoadProviderFactory.register(ParcelFileDescriptor.class, Bitmap.class,
184                new FileDescriptorBitmapDataLoadProvider(bitmapPool));
185
186        ImageVideoDataLoadProvider imageVideoDataLoadProvider = new ImageVideoDataLoadProvider(bitmapPool);
187        dataLoadProviderFactory.register(ImageVideoWrapper.class, Bitmap.class, imageVideoDataLoadProvider);
188
189        GifDataLoadProvider gifDataLoadProvider = new GifDataLoadProvider(context, bitmapPool);
190        dataLoadProviderFactory.register(InputStream.class, GifData.class, gifDataLoadProvider);
191
192        dataLoadProviderFactory.register(ImageVideoWrapper.class, GifBitmapWrapper.class,
193                new ImageVideoGifDataLoadProvider(imageVideoDataLoadProvider, gifDataLoadProvider));
194
195        register(File.class, ParcelFileDescriptor.class, new FileDescriptorFileLoader.Factory());
196        register(File.class, InputStream.class, new StreamFileLoader.Factory());
197        register(Integer.class, ParcelFileDescriptor.class, new FileDescriptorResourceLoader.Factory());
198        register(Integer.class, InputStream.class, new StreamResourceLoader.Factory());
199        register(String.class, ParcelFileDescriptor.class, new FileDescriptorStringLoader.Factory());
200        register(String.class, InputStream.class, new StreamStringLoader.Factory());
201        register(Uri.class, ParcelFileDescriptor.class, new FileDescriptorUriLoader.Factory());
202        register(Uri.class, InputStream.class, new StreamUriLoader.Factory());
203        register(URL.class, InputStream.class, new StreamUrlLoader.Factory());
204        register(GlideUrl.class, InputStream.class, new VolleyUrlLoader.Factory(requestQueue));
205
206        transcoderFactory.register(Bitmap.class, BitmapDrawable.class,
207                new BitmapDrawableTranscoder(context.getResources(), bitmapPool));
208        transcoderFactory.register(GifBitmapWrapper.class, Drawable.class,
209                new GifBitmapWrapperDrawableTranscoder(new BitmapDrawableTranscoder(context.getResources(), bitmapPool),
210                        new GifDataDrawableTranscoder()));
211        transcoderFactory.register(GifData.class, GifDrawable.class, new GifDataDrawableTranscoder());
212
213        bitmapCenterCrop = new CenterCrop(bitmapPool);
214        drawableCenterCrop = new GifBitmapWrapperTransformation(bitmapCenterCrop);
215
216        bitmapFitCenter = new FitCenter(bitmapPool);
217        drawableFitCenter = new GifBitmapWrapperTransformation(bitmapFitCenter);
218    }
219
220    public BitmapPool getBitmapPool() {
221        return bitmapPool;
222    }
223
224    <Z, R> ResourceTranscoder<Z, R> buildTranscoder(Class<Z> decodedClass, Class<R> transcodedClass) {
225        return transcoderFactory.get(decodedClass, transcodedClass);
226    }
227
228    <T, Z> DataLoadProvider<T, Z> buildDataProvider(Class<T> dataClass, Class<Z> decodedClass) {
229        return dataLoadProviderFactory.get(dataClass, decodedClass);
230    }
231
232    <R> Target<R> buildImageViewTarget(ImageView imageView, Class<R> transcodedClass) {
233        return imageViewTargetFactory.buildTarget(imageView, transcodedClass);
234    }
235
236    Engine getEngine() {
237        return engine;
238    }
239
240    CenterCrop getBitmapCenterCrop() {
241        return bitmapCenterCrop;
242    }
243
244    FitCenter getBitmapFitCenter() {
245        return bitmapFitCenter;
246    }
247
248    GifBitmapWrapperTransformation getDrawableCenterCrop() {
249        return drawableCenterCrop;
250    }
251
252    GifBitmapWrapperTransformation getDrawableFitCenter() {
253        return drawableFitCenter;
254    }
255
256    private GenericLoaderFactory getLoaderFactory() {
257        return loaderFactory;
258    }
259
260    /**
261     * Returns the {@link RequestQueue} Glide is using to fetch images over http/https.
262     */
263    public RequestQueue getRequestQueue() {
264        return requestQueue;
265    }
266
267    /**
268     * Clears as much memory as possible.
269     *
270     * @see ComponentCallbacks2#onLowMemory()
271     */
272    public void clearMemory() {
273        bitmapPool.clearMemory();
274        memoryCache.clearMemory();
275    }
276
277    /**
278     * Clears some memory with the exact amount depending on the given level.
279     *
280     * @see ComponentCallbacks2#onTrimMemory(int)
281     */
282    public void trimMemory(int level) {
283        bitmapPool.trimMemory(level);
284        memoryCache.trimMemory(level);
285    }
286
287    /**
288     * Adjusts Glide's current and maximum memory usage based on the given {@link MemoryCategory}.
289     *
290     * <p>
291     *     The default {@link MemoryCategory} is {@link MemoryCategory#NORMAL}. {@link MemoryCategory#HIGH} increases
292     *     Glide's maximum memory usage by up to 50% and {@link MemoryCategory#LOW} decreases Glide's maximum memory
293     *     usage by 50%. This method should be used to temporarily increase or decrease memory useage for a single
294     *     Activity or part of the app. Use {@link GlideBuilder#setMemoryCache(MemoryCache)} to set a permanent
295     *     memory size if you want to change the default.
296     * </p>
297     */
298    public void setMemoryCategory(MemoryCategory memoryCategory) {
299        memoryCache.setSizeMultiplier(memoryCategory.getMultiplier());
300        bitmapPool.setSizeMultiplier(memoryCategory.getMultiplier());
301    }
302
303    /**
304     * Cancel any pending loads Glide may have for the target and free any resources (such as {@link Bitmap}s) that may
305     * have been loaded for the target so they may be reused.
306     *
307     * @param target The Target to cancel loads for.
308     */
309    public static void clear(Target target) {
310        Request request = target.getRequest();
311        if (request!= null) {
312            request.clear();
313        }
314    }
315
316    /**
317     * Cancel any pending loads Glide may have for the view and free any resources that may have been loaded for the
318     * view.
319     *
320     * <p>
321     *     Note that this will only work if {@link View#setTag(Object)} is not called on this view outside of Glide.
322     * </p>
323     *
324     * @see #clear(Target).
325     *
326     * @param view The view to cancel loads and free resources for.
327     * @throws IllegalArgumentException if an object other than Glide's metadata is set as the view's tag.
328     */
329    public static void clear(View view) {
330        Target viewTarget = new ClearTarget(view);
331        clear(viewTarget);
332    }
333
334    /**
335     * Use the given factory to build a {@link ModelLoader} for models of the given class. Generally the best use of
336     * this method is to replace one of the default factories or add an implementation for other similar low level
337     * models. Typically the {@link RequestManager#using(StreamModelLoader)} or
338     * {@link RequestManager#using(FileDescriptorModelLoader)} syntax is preferred because it directly links the model
339     * with the ModelLoader being used to load it. Any factory replaced by the given factory will have its
340     * {@link ModelLoaderFactory#teardown()}} method called.
341     *
342     * <p>
343     *     Note - If a factory already exists for the given class, it will be replaced. If that factory is not being
344     *     used for any other model class, {@link ModelLoaderFactory#teardown()}
345     *     will be called.
346     * </p>
347     *
348     * <p>
349     *     Note - The factory must not be an anonymous inner class of an Activity or another object that cannot be
350     *     retained statically.
351     * </p>
352     *
353     * @see RequestManager#using(FileDescriptorModelLoader)
354     * @see RequestManager#using(StreamModelLoader)
355     *
356     * @param modelClass The model class.
357     * @param resourceClass The resource class the model loader will translate the model type into.
358     * @param factory The factory to use.
359     * @param <T> The type of the model.
360     * @param <Y> the type of the resource.
361     */
362    public <T, Y> void register(Class<T> modelClass, Class<Y> resourceClass, ModelLoaderFactory<T, Y> factory) {
363        ModelLoaderFactory<T, Y> removed = loaderFactory.register(modelClass, resourceClass, factory);
364        if (removed != null) {
365            removed.teardown();
366        }
367    }
368
369    /**
370     * Removes any {@link ModelLoaderFactory} registered for the given model and resource classes if one exists. If a
371     * {@link ModelLoaderFactory} is removed, its {@link ModelLoaderFactory#teardown()}} method will be called.
372     *
373     * @param modelClass The model class.
374     * @param resourceClass The resource class.
375     * @param <T> The type of the model.
376     * @param <Y> The type of the resource.
377     */
378    public <T, Y> void unregister(Class<T> modelClass, Class<Y> resourceClass) {
379        ModelLoaderFactory<T, Y> removed = loaderFactory.unregister(modelClass, resourceClass);
380        if (removed != null) {
381            removed.teardown();
382        }
383    }
384
385    /**
386     * Build a {@link ModelLoader} for the given model class using registered {@link ModelLoaderFactory}s.
387     *
388     * @see  #buildModelLoader(Object, Class, Context)
389     * @see  #buildStreamModelLoader(Class, Context)
390     * @see  #buildFileDescriptorModelLoader(Class, Context)
391     *
392     * @param modelClass The class to get a {@link ModelLoader} for.
393     * @param resourceClass The resource class to get a {@link ModelLoader} for.
394     * @param context Any context.
395     * @param <T> The type of the model.
396     * @param <Y> The type of the resource.
397     * @return A new {@link ModelLoader} for the given model class.
398     */
399    public static <T, Y> ModelLoader<T, Y> buildModelLoader(Class<T> modelClass, Class<Y> resourceClass,
400            Context context) {
401        return Glide.get(context).getLoaderFactory().buildModelLoader(modelClass, resourceClass, context);
402    }
403
404    /**
405     * A convenience method to build a {@link ModelLoader} for a given model object using registered
406     * {@link ModelLoaderFactory}s.
407     *
408     * @see #buildModelLoader(Class, Class, Context)
409     *
410     * @param model A non null model object whose class we will get a {@link ModelLoader} for.
411     * @param resourceClass The resource class to get a {@link ModelLoader} for.
412     * @param context Any context.
413     * @param <T> The type of the model.
414     * @param <Y> The type of the resource.
415     * @return A new {@link ModelLoader} for the given model and resource classes, or null if model is null.
416     */
417    @SuppressWarnings("unchecked")
418    public static <T, Y> ModelLoader<T, Y> buildModelLoader(T model, Class<Y> resourceClass, Context context) {
419        if (model == null) {
420            if (Log.isLoggable(TAG, Log.DEBUG)) {
421                Log.d(TAG, "Unable to load null model, setting placeholder only");
422            }
423            return null;
424        }
425        return buildModelLoader((Class<T>) model.getClass(), resourceClass, context);
426    }
427
428    /**
429     * A method to build a {@link ModelLoader} for the given model that produces {@link InputStream}s using a registered
430     * factory.
431     *
432     * @see #buildModelLoader(Class, Class, android.content.Context)
433     */
434    public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(Class<T> modelClass, Context context) {
435        return buildModelLoader(modelClass, InputStream.class, context);
436    }
437
438    /**
439     * A method to build a {@link ModelLoader} for the given model that produces {@link InputStream}s using a registered
440     * factory.
441     *
442     * @see #buildModelLoader(Object, Class, Context)
443     */
444    public static <T> ModelLoader<T, InputStream> buildStreamModelLoader(T model, Context context) {
445        return buildModelLoader(model, InputStream.class, context);
446    }
447
448    /**
449     * A method to build a {@link ModelLoader} for the given model class that produces
450     * {@link ParcelFileDescriptor}s using a registered factory.
451     *
452     * @see #buildModelLoader(Class, Class, android.content.Context)
453     */
454    public static <T> ModelLoader<T, ParcelFileDescriptor> buildFileDescriptorModelLoader(Class<T> modelClass,
455            Context context) {
456        return buildModelLoader(modelClass, ParcelFileDescriptor.class, context);
457    }
458
459    /**
460     * A method to build a {@link ModelLoader} for the given model class that produces
461     * {@link ParcelFileDescriptor}s using a registered factory.
462     *
463     * @see #buildModelLoader(Object, Class, android.content.Context)
464     */
465    public static <T> ModelLoader<T, ParcelFileDescriptor> buildFileDescriptorModelLoader(T model, Context context) {
466        return buildModelLoader(model, ParcelFileDescriptor.class, context);
467    }
468
469    /**
470     * Begin a load with Glide by passing in a context.
471     *
472     * @param context Any context, will not be retained.
473     * @return A model request to pass in the object representing the image to be loaded.
474     */
475    public static RequestManager with(Context context) {
476        return RequestManagerRetriever.get(context);
477    }
478
479    public static RequestManager with(Activity activity) {
480        return RequestManagerRetriever.get(activity);
481    }
482
483    public static RequestManager with(FragmentActivity activity) {
484        return RequestManagerRetriever.get(activity);
485    }
486
487    @TargetApi(11)
488    public static RequestManager with(android.app.Fragment fragment) {
489        return RequestManagerRetriever.get(fragment);
490    }
491
492    public static RequestManager with(Fragment fragment) {
493        return RequestManagerRetriever.get(fragment);
494    }
495
496    private static class ClearTarget extends ViewTarget<View, Object> {
497        public ClearTarget(View view) {
498            super(view);
499        }
500
501        @Override
502        public void onResourceReady(Object resource, GlideAnimation<Object> glideAnimation) { }
503
504        @Override
505        public void setPlaceholder(Drawable placeholder) { }
506    }
507}
508