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