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