Home | History | Annotate | Download | only in data
      1 package com.bumptech.glide.load.data;
      2 
      3 import android.content.ContentResolver;
      4 import android.content.Context;
      5 import android.database.Cursor;
      6 import android.net.Uri;
      7 import android.provider.MediaStore;
      8 import android.text.TextUtils;
      9 
     10 import com.bumptech.glide.Priority;
     11 
     12 import java.io.File;
     13 import java.io.FileNotFoundException;
     14 import java.io.IOException;
     15 import java.io.InputStream;
     16 
     17 /**
     18  * A DataFetcher that retrieves an {@link java.io.InputStream} for a local Uri that may or may not be for a resource
     19  * in the media store. If the local Uri is for a resource in the media store and the size requested is less than or
     20  * equal to the media store thumbnail size, preferentially attempts to fetch data for the pre-generated media store
     21  * thumbs using {@link android.provider.MediaStore.Images.Thumbnails} and
     22  * {@link android.provider.MediaStore.Video.Thumbnails}.
     23  */
     24 public class MediaStoreThumbFetcher implements DataFetcher<InputStream> {
     25     private static final int MINI_WIDTH = 512;
     26     private static final int MINI_HEIGHT = 384;
     27     private static final ThumbnailStreamOpenerFactory DEFAULT_FACTORY = new ThumbnailStreamOpenerFactory();
     28 
     29     private final Context context;
     30     private final Uri mediaStoreUri;
     31     private final DataFetcher<InputStream> defaultFetcher;
     32     private final int width;
     33     private final int height;
     34     private final ThumbnailStreamOpenerFactory factory;
     35     private InputStream inputStream;
     36 
     37     public MediaStoreThumbFetcher(Context context, Uri mediaStoreUri, DataFetcher<InputStream> defaultFetcher,
     38             int width, int height) {
     39         this(context, mediaStoreUri, defaultFetcher, width, height, DEFAULT_FACTORY);
     40     }
     41 
     42     MediaStoreThumbFetcher(Context context, Uri mediaStoreUri, DataFetcher<InputStream> defaultFetcher, int width,
     43             int height, ThumbnailStreamOpenerFactory factory) {
     44         this.context = context;
     45         this.mediaStoreUri = mediaStoreUri;
     46         this.defaultFetcher = defaultFetcher;
     47         this.width = width;
     48         this.height = height;
     49         this.factory = factory;
     50     }
     51 
     52     @Override
     53     public InputStream loadData(Priority priority) throws Exception {
     54         ThumbnailStreamOpener fetcher = factory.build(mediaStoreUri, width, height);
     55 
     56         if (fetcher != null) {
     57             inputStream = fetcher.open(context, mediaStoreUri);
     58         }
     59 
     60         if (inputStream != null) {
     61             return inputStream;
     62         } else {
     63             return defaultFetcher.loadData(priority);
     64         }
     65     }
     66 
     67     @Override
     68     public void cleanup() {
     69         if (inputStream != null) {
     70             try {
     71                 inputStream.close();
     72             } catch (IOException e) {
     73                 // Do nothing.
     74             }
     75         }
     76         defaultFetcher.cleanup();
     77     }
     78 
     79     @Override
     80     public String getId() {
     81         return mediaStoreUri.toString();
     82     }
     83 
     84     @Override
     85     public void cancel() {
     86         // Do nothing.
     87     }
     88 
     89     private static boolean isMediaStoreUri(Uri uri) {
     90         return uri != null
     91                 && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
     92                 && MediaStore.AUTHORITY.equals(uri.getAuthority());
     93     }
     94 
     95     private static boolean isMediaStoreVideo(Uri uri) {
     96         return isMediaStoreUri(uri) && uri.getPathSegments().contains("video");
     97     }
     98 
     99     static class FileService {
    100         public boolean exists(File file) {
    101             return file.exists();
    102         }
    103 
    104         public long length(File file) {
    105             return file.length();
    106         }
    107 
    108         public File get(String path) {
    109             return new File(path);
    110         }
    111     }
    112 
    113     interface ThumbnailQuery {
    114         Cursor query(Context context, Uri uri);
    115     }
    116 
    117     static class ThumbnailStreamOpener {
    118         private static final FileService DEFAULT_SERVICE = new FileService();
    119         private final FileService service;
    120         private ThumbnailQuery query;
    121 
    122         public ThumbnailStreamOpener(ThumbnailQuery query) {
    123             this(DEFAULT_SERVICE, query);
    124         }
    125 
    126         public ThumbnailStreamOpener(FileService service, ThumbnailQuery query) {
    127             this.service = service;
    128             this.query = query;
    129         }
    130 
    131         public InputStream open(Context context, Uri uri) throws FileNotFoundException {
    132             Uri thumbnailUri = null;
    133             InputStream inputStream = null;
    134 
    135             final Cursor cursor = query.query(context, uri);
    136             try {
    137                 if (cursor != null && cursor.moveToFirst()) {
    138                     String path = cursor.getString(0);
    139                     if (!TextUtils.isEmpty(path)) {
    140                         File file = service.get(path);
    141                         if (service.exists(file) && service.length(file) > 0) {
    142                             thumbnailUri = Uri.fromFile(file);
    143                         }
    144                     }
    145                 }
    146             } finally {
    147                 if (cursor != null) {
    148                     cursor.close();
    149                 }
    150             }
    151             if (thumbnailUri != null) {
    152                 inputStream = context.getContentResolver().openInputStream(thumbnailUri);
    153             }
    154             return inputStream;
    155         }
    156     }
    157 
    158     static class ImageThumbnailQuery implements ThumbnailQuery {
    159 
    160         @Override
    161         public Cursor query(Context context, Uri uri) {
    162             String id = uri.getLastPathSegment();
    163             return context.getContentResolver().query(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, new String[] {
    164                             MediaStore.Images.Thumbnails.DATA
    165                     }, MediaStore.Images.Thumbnails.IMAGE_ID + " = ? AND " + MediaStore.Images.Thumbnails.KIND + " = ?",
    166                     new String[] { id, String.valueOf(MediaStore.Images.Thumbnails.MINI_KIND) }, null);
    167         }
    168     }
    169 
    170     static class VideoThumbnailQuery implements ThumbnailQuery {
    171 
    172         @Override
    173         public Cursor query(Context context, Uri uri) {
    174             String id = uri.getLastPathSegment();
    175             return context.getContentResolver().query(MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI, new String[] {
    176                     MediaStore.Video.Thumbnails.DATA
    177             }, MediaStore.Video.Thumbnails.VIDEO_ID + " = ? AND " + MediaStore.Video.Thumbnails.KIND + " = ?",
    178                     new String[] { id, String.valueOf(MediaStore.Video.Thumbnails.MINI_KIND) }, null);
    179         }
    180     }
    181 
    182     static class ThumbnailStreamOpenerFactory {
    183 
    184         public ThumbnailStreamOpener build(Uri uri, int width, int height) {
    185             if (!isMediaStoreUri(uri) || width > MINI_WIDTH || height > MINI_HEIGHT) {
    186                 return null;
    187             } else if (isMediaStoreVideo(uri)) {
    188                 return new ThumbnailStreamOpener(new VideoThumbnailQuery());
    189             } else {
    190                 return new ThumbnailStreamOpener(new ImageThumbnailQuery());
    191             }
    192         }
    193     }
    194 }
    195