1package com.bumptech.glide; 2 3import android.app.Activity; 4import android.app.Fragment; 5import android.content.Context; 6import android.net.Uri; 7import android.os.ParcelFileDescriptor; 8import android.provider.MediaStore; 9import android.support.v4.app.FragmentActivity; 10import com.bumptech.glide.load.ResourceDecoder; 11import com.bumptech.glide.load.ResourceEncoder; 12import com.bumptech.glide.load.model.ModelLoader; 13import com.bumptech.glide.load.model.ModelLoaderFactory; 14import com.bumptech.glide.load.model.file_descriptor.FileDescriptorModelLoader; 15import com.bumptech.glide.load.model.stream.MediaStoreStreamLoader; 16import com.bumptech.glide.load.model.stream.StreamByteArrayLoader; 17import com.bumptech.glide.load.model.stream.StreamFileLoader; 18import com.bumptech.glide.load.model.stream.StreamModelLoader; 19import com.bumptech.glide.load.model.stream.StreamResourceLoader; 20import com.bumptech.glide.load.model.stream.StreamStringLoader; 21import com.bumptech.glide.load.model.stream.StreamUriLoader; 22import com.bumptech.glide.manager.ConnectivityMonitor; 23import com.bumptech.glide.manager.ConnectivityMonitorFactory; 24import com.bumptech.glide.manager.RequestTracker; 25import com.bumptech.glide.volley.VolleyUrlLoader; 26 27import java.io.File; 28import java.io.InputStream; 29import java.net.URL; 30import java.util.UUID; 31 32/** 33 * A class for managing and starting requests for Glide. Can use activity, fragment and connectivity lifecycle events to 34 * intelligently stop, start, and restart requests. Retrieve either by instantiating a new object, or to take advantage 35 * built in Activity and Fragment lifecycle handling, use the static Glide.load methods with your Fragment or Activity. 36 * 37 * @see Glide#with(Activity) 38 * @see Glide#with(FragmentActivity) 39 * @see Glide#with(Fragment) 40 * @see Glide#with(android.support.v4.app.Fragment) 41 * @see Glide#with(Context) 42 */ 43public class RequestManager { 44 private final ConnectivityMonitor connectivityMonitor; 45 private final Context context; 46 private final RequestTracker requestTracker; 47 private final Glide glide; 48 private final OptionsApplier optionsApplier; 49 private DefaultOptions options; 50 51 public RequestManager(Context context) { 52 this(context, new RequestTracker(), new ConnectivityMonitorFactory()); 53 } 54 55 RequestManager(Context context, RequestTracker requestTracker, ConnectivityMonitorFactory factory) { 56 this.context = context; 57 this.requestTracker = requestTracker; 58 this.connectivityMonitor = factory.build(context, new RequestManagerConnectivityListener(requestTracker)); 59 connectivityMonitor.register(); 60 this.glide = Glide.get(context); 61 this.optionsApplier = new OptionsApplier(); 62 } 63 64 public interface DefaultOptions { 65 public <T> void apply(T model, GenericRequestBuilder<T, ?, ?, ?> requestBuilder); 66 } 67 68 public void setDefaultOptions(DefaultOptions options) { 69 this.options = options; 70 } 71 72 /** 73 * Cancels any in progress loads, but does not clear resources of completed loads. 74 */ 75 public void pauseRequests() { 76 requestTracker.pauseRequests(); 77 } 78 79 /** 80 * Restarts any loads that have not yet completed. 81 */ 82 public void resumeRequests() { 83 requestTracker.resumeRequests(); 84 } 85 86 /** 87 * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE 88 * permission is present) and restarts failed or paused requests. 89 */ 90 public void onStart() { 91 // onStart might not be called because this object may be created after the fragment/activity's onStart method. 92 connectivityMonitor.register(); 93 94 requestTracker.resumeRequests(); 95 } 96 97 /** 98 * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE 99 * permission is present) and pauses in progress loads. 100 */ 101 public void onStop() { 102 connectivityMonitor.unregister(); 103 requestTracker.pauseRequests(); 104 } 105 106 /** 107 * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed 108 * requests. 109 */ 110 public void onDestroy() { 111 requestTracker.clearRequests(); 112 } 113 114 /** 115 * Use the given generic model loader to load the given generic data class. 116 * <p> 117 * Note that in most cases you will also need to specify an {@link ResourceDecoder} and an 118 * {@link ResourceEncoder} for the load to complete successfully. 119 * </p> 120 * @param modelLoader The {@link ModelLoader} class to use to load the model. 121 * @param dataClass The type of data the {@link ModelLoader} will load. 122 * @param <A> The type of the model to be loaded. 123 * @param <T> The type of the data to be loaded from the mode. 124 * @return A {@link GenericModelRequest} to set options for the load and ultimately the target to load the model 125 * into. 126 */ 127 public <A, T> GenericModelRequest<A, T> using(ModelLoader<A, T> modelLoader, Class<T> dataClass) { 128 return new GenericModelRequest<A, T>(modelLoader, dataClass); 129 } 130 131 /** 132 * Set the {@link ModelLoader} to use for for a new load where the model loader translates from a model to an 133 * {@link InputStream} resource for loading images. 134 * 135 * @param modelLoader The model loader to use. 136 * @param <T> The type of the model. 137 * @return A new {@link ImageModelRequest}. 138 */ 139 public <T> ImageModelRequest<T> using(final StreamModelLoader<T> modelLoader) { 140 return new ImageModelRequest<T>(modelLoader); 141 } 142 143 /** 144 * A convenience method to use a {@link StreamByteArrayLoader} to decode an image from a byte array. 145 * 146 * @param modelLoader The byte array loader. 147 * @return A new {@link ImageModelRequest}. 148 */ 149 public ImageModelRequest<byte[]> using(StreamByteArrayLoader modelLoader) { 150 return new ImageModelRequest<byte[]>(modelLoader); 151 } 152 153 /** 154 * Set the {@link ModelLoader} to use for a new load where the model loader translates from a model to an 155 * {@link ParcelFileDescriptor} resource for loading video thumbnails. 156 * 157 * @param modelLoader The model loader to use. 158 * @param <T> The type of the model. 159 * @return A new {@link VideoModelRequest}. 160 */ 161 public <T> VideoModelRequest<T> using(final FileDescriptorModelLoader<T> modelLoader) { 162 return new VideoModelRequest<T>(modelLoader); 163 } 164 165 /** 166 * Use the {@link ModelLoaderFactory} currently registered for {@link String} to load the image represented by 167 * the given {@link String}. Defaults to {@link StreamStringLoader.Factory} and {@link StreamStringLoader} to 168 * load the given model. 169 * 170 * @see #using(StreamModelLoader) 171 * 172 * @param string The string representing the image. Must be either a path, or a uri handled by 173 * {@link StreamUriLoader} 174 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 175 * into. 176 */ 177 public DrawableTypeRequest<String> load(String string) { 178 return loadGeneric(string); 179 } 180 181 /** 182 * Use the {@link ModelLoaderFactory} currently registered for {@link Uri} to load the image at the given uri. 183 * Defaults to {@link StreamUriLoader.Factory} and {@link StreamUriLoader}. 184 * 185 * @see #using(StreamModelLoader) 186 * 187 * @param uri The uri representing the image. Must be a uri handled by {@link StreamUriLoader} 188 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 189 * into. 190 */ 191 public DrawableTypeRequest<Uri> load(Uri uri) { 192 return loadGeneric(uri); 193 } 194 195 /** 196 * Use {@link MediaStore.Images.Thumbnails} and {@link MediaStore.Video.Thumbnails} to retrieve pre-generated 197 * thumbnails for the given uri. Falls back to the registered {@link ModelLoaderFactory} registered for {@link Uri}s 198 * if the given uri is not a media store uri or if no pre-generated thumbnail exists for the given uri. In addition, 199 * mixes the given mimeType, dateModified, and orientation into the cache key to detect and invalidate thumbnails 200 * if content is changed locally. 201 * 202 * @param uri The uri representing the media. 203 * @param mimeType The mime type of the media store media. Ok to default to empty string "". See 204 * {@link MediaStore.Images.ImageColumns#MIME_TYPE} or {@link MediaStore.Video.VideoColumns#MIME_TYPE}. 205 * @param dateModified The date modified time of the media store media. Ok to default to 0. See 206 * {@link MediaStore.Images.ImageColumns#DATE_MODIFIED} or {@link MediaStore.Video.VideoColumns#DATE_MODIFIED}. 207 * @param orientation The orientation of the media store media. Ok to default to 0. See 208 * {@link MediaStore.Images.ImageColumns#ORIENTATION}. 209 * @return A new {@link DrawableRequestBuilder} to set options for the load and ultimately the target to load the 210 * uri into. 211 */ 212 public DrawableTypeRequest<Uri> loadFromMediaStore(Uri uri, String mimeType, long dateModified, int orientation) { 213 ModelLoader<Uri, InputStream> genericStreamLoader = Glide.buildStreamModelLoader(uri, context); 214 ModelLoader<Uri, InputStream> mediaStoreLoader = new MediaStoreStreamLoader(context, genericStreamLoader, 215 mimeType, dateModified, orientation); 216 ModelLoader<Uri, ParcelFileDescriptor> fileDescriptorModelLoader = Glide.buildFileDescriptorModelLoader(uri, 217 context); 218 return optionsApplier.apply(uri, new DrawableTypeRequest<Uri>(uri, mediaStoreLoader, fileDescriptorModelLoader, 219 context, glide, requestTracker, optionsApplier)); 220 } 221 222 /** 223 * Use the {@link ModelLoaderFactory} currently registered for {@link File} to load the image represented by the 224 * given {@link File}. Defaults to {@link StreamFileLoader.Factory} and {@link StreamFileLoader} to load the 225 * given model. 226 * 227 * @see #using(StreamModelLoader) 228 * 229 * @param file The File containing the image 230 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 231 * into. 232 */ 233 public DrawableTypeRequest<File> load(File file) { 234 return loadGeneric(file); 235 } 236 237 /** 238 * Use the {@link ModelLoaderFactory} currently registered for {@link Integer} to load the image represented by 239 * the given {@link Integer} resource id. Defaults to {@link StreamResourceLoader.Factory} and 240 * {@link StreamResourceLoader} to load the given model. 241 * 242 * @see #using(StreamModelLoader) 243 * 244 * @param resourceId the id of the resource containing the image 245 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 246 * into. 247 */ 248 public DrawableTypeRequest<Integer> load(Integer resourceId) { 249 return loadGeneric(resourceId); 250 } 251 252 /** 253 * Use the {@link ModelLoaderFactory} currently registered for the given model type to load the image 254 * represented by the given model. 255 * 256 * @param model The model to load. 257 * @param <T> The type of the model to load. 258 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 259 * into. 260 */ 261 @SuppressWarnings("unused") 262 public <T> DrawableTypeRequest<T> loadFromImage(T model) { 263 return loadGeneric(model); 264 } 265 266 /** 267 * Use the {@link ModelLoaderFactory} currently registered for {@link URL} to load the image represented by the 268 * given {@link URL}. Defaults to {@link VolleyUrlLoader.Factory} and {@link VolleyUrlLoader} to load the given 269 * model. 270 * 271 * @see #using(StreamModelLoader) 272 * 273 * @param url The URL representing the image. 274 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the model 275 * into. 276 */ 277 public DrawableTypeRequest<URL> loadFromImage(URL url) { 278 return loadGeneric(url); 279 } 280 281 /** 282 * Use a new {@link StreamByteArrayLoader} to load an image from the given model. 283 * 284 * @see #loadFromImage(byte[]) 285 * 286 * @param model The data to load. 287 * @param id A unique id that identifies the image represented by the model suitable for use as a cache key 288 * (url, filepath etc). If there is no suitable id, use {@link #loadFromImage(byte[])} instaed. 289 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 290 * into. 291 */ 292 public DrawableTypeRequest<byte[]> loadFromImage(byte[] model, final String id) { 293 final StreamByteArrayLoader loader = new StreamByteArrayLoader(id); 294 return optionsApplier.apply(model, 295 new DrawableTypeRequest<byte[]>(model, loader, null, context, glide, requestTracker, optionsApplier)); 296 } 297 298 /** 299 * Use a new {@link StreamByteArrayLoader} to load an image from the given model. Suitable when there is no 300 * simple id that represents the given data. 301 * 302 * @param model the data to load. 303 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 304 * into. 305 */ 306 public DrawableTypeRequest<byte[]> loadFromImage(byte[] model) { 307 return loadFromImage(model, UUID.randomUUID() 308 .toString()); 309 } 310 311 /** 312 * Use the {@link ModelLoaderFactory} currently registered for the given model type for 313 * {@link ParcelFileDescriptor}s to load a thumbnail for the video represented by the given model. 314 * 315 * @param model The model to load. 316 * @param <T> The type of the model to load. 317 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 318 * into. 319 */ 320 @SuppressWarnings("unused") 321 public <T> DrawableTypeRequest<T> loadFromVideo(T model) { 322 return loadGeneric(model); 323 } 324 325 /** 326 * Use the {@link ModelLoaderFactory}s currently registered for the given model type for 327 * {@link InputStream}s and {@link ParcelFileDescriptor}s to load a thumbnail from either the image or the video 328 * represented by the given model. 329 * 330 * @param model The model the load. 331 * @param <T> The type of the model to load. 332 * @return A {@link DrawableTypeRequest} to set options for the load and ultimately the target to load the image 333 * into. 334 */ 335 public <T> DrawableTypeRequest<T> load(T model) { 336 return loadGeneric(model); 337 } 338 339 private <T> DrawableTypeRequest<T> loadGeneric(T model) { 340 ModelLoader<T, InputStream> streamModelLoader = Glide.buildStreamModelLoader(model, context); 341 ModelLoader<T, ParcelFileDescriptor> fileDescriptorModelLoader = 342 Glide.buildFileDescriptorModelLoader(model, context); 343 if (model != null && streamModelLoader == null && fileDescriptorModelLoader == null) { 344 throw new IllegalArgumentException("Unknown type " + model + ". You must provide a Model of a type for" 345 + " which there is a registered ModelLoader, if you are using a custom model, you must first call" 346 + " Glide#register with a ModelLoaderFactory for your custom model class"); 347 } 348 return optionsApplier.apply(model, new DrawableTypeRequest<T>(model, streamModelLoader, 349 fileDescriptorModelLoader, context, glide, requestTracker, optionsApplier)); 350 } 351 352 /** 353 * A helper class for building requests with custom {@link ModelLoader}s that translate models to 354 * {@link ParcelFileDescriptor} resources for loading video thumbnails. 355 * 356 * @param <T> The type of the model. 357 */ 358 public class VideoModelRequest<T> { 359 private final ModelLoader<T, ParcelFileDescriptor> loader; 360 361 private VideoModelRequest(ModelLoader<T, ParcelFileDescriptor> loader) { 362 this.loader = loader; 363 } 364 365 public BitmapTypeRequest<T> loadFromVideo(T model) { 366 return optionsApplier.apply(model, new BitmapTypeRequest<T>(context, model, null, loader, glide, 367 requestTracker, optionsApplier)); 368 } 369 } 370 371 /** 372 * A helper class for building requests with custom {@link ModelLoader}s that translate models to 373 * {@link InputStream} resources for loading images. 374 * 375 * @param <T> The type of the model. 376 */ 377 public class ImageModelRequest<T> { 378 private final ModelLoader<T, InputStream> loader; 379 380 private ImageModelRequest(ModelLoader<T, InputStream> loader) { 381 this.loader = loader; 382 } 383 384 public DrawableTypeRequest<T> load(T model) { 385 return optionsApplier.apply(model, new DrawableTypeRequest<T>(model, loader, null, context, glide, 386 requestTracker, optionsApplier)); 387 } 388 } 389 390 class OptionsApplier { 391 392 public <A, X extends GenericRequestBuilder<A, ?, ?, ?>> X apply(A model, X builder) { 393 if (options != null) { 394 options.apply(model, builder); 395 } 396 return builder; 397 } 398 399 } 400 401 /** 402 * A helper class for building requests with custom {@link ModelLoader}s that translate models to 403 * {@link InputStream} resources for loading images. 404 * 405 * @param <T> The type of the model. 406 */ 407 public class GenericModelRequest<A, T> { 408 private final ModelLoader<A, T> modelLoader; 409 private final Class<T> dataClass; 410 411 private GenericModelRequest(ModelLoader<A, T> modelLoader, Class<T> dataClass) { 412 this.modelLoader = modelLoader; 413 this.dataClass = dataClass; 414 } 415 416 public GenericTypeRequest load(A model) { 417 return new GenericTypeRequest(model, modelLoader, dataClass); 418 } 419 420 public class GenericTypeRequest { 421 private final A model; 422 private final ModelLoader<A, T> modelLoader; 423 private final Class<T> dataClass; 424 425 private GenericTypeRequest(A model, ModelLoader<A, T> modelLoader, Class<T> dataClass) { 426 this.model = model; 427 this.modelLoader = modelLoader; 428 this.dataClass = dataClass; 429 } 430 431 public <Z> GenericTranscodeRequest<A, T, Z> as(Class<Z> resourceClass) { 432 return optionsApplier.apply(model, new GenericTranscodeRequest<A, T, Z>(context, glide, model, 433 modelLoader, dataClass, resourceClass, requestTracker, optionsApplier)); 434 } 435 } 436 } 437 438 private static class RequestManagerConnectivityListener implements ConnectivityMonitor.ConnectivityListener { 439 private RequestTracker requestTracker; 440 441 public RequestManagerConnectivityListener(RequestTracker requestTracker) { 442 this.requestTracker = requestTracker; 443 } 444 445 @Override 446 public void onConnectivityChanged(boolean isConnected) { 447 if (isConnected) { 448 requestTracker.restartRequests(); 449 } 450 } 451 } 452} 453