blob: 9841070f8939f425c4548a5be980d85c868d35a0 [file] [log] [blame]
Angus Kongbd260692013-08-07 14:52:56 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera.data;
18
Sascha Haeberlingb7639c62013-09-09 14:42:43 -070019import android.app.Activity;
Angus Kongbd260692013-08-07 14:52:56 -070020import android.content.ContentResolver;
21import android.content.Context;
22import android.database.Cursor;
23import android.graphics.Bitmap;
24import android.graphics.BitmapFactory;
Angus Kongbd260692013-08-07 14:52:56 -070025import android.graphics.drawable.Drawable;
Sam Juddde3e9ab2014-03-17 13:07:22 -070026import android.media.CamcorderProfile;
Angus Kongbd260692013-08-07 14:52:56 -070027import android.net.Uri;
Angus Kong8a2350a2013-12-16 15:02:34 -080028import android.os.Bundle;
Angus Kongbd260692013-08-07 14:52:56 -070029import android.provider.MediaStore;
Sam Judd43bf03f2014-03-17 11:27:03 -070030import android.view.LayoutInflater;
Angus Kong2bca2102014-03-11 16:27:30 -070031import android.view.Gravity;
Angus Kongbd260692013-08-07 14:52:56 -070032import android.view.View;
Angus Kongbd260692013-08-07 14:52:56 -070033import android.widget.ImageView;
34
Sam Juddaeed91f2014-03-13 11:40:10 -070035import com.android.camera.Storage;
Angus Kong2bca2102014-03-11 16:27:30 -070036import com.android.camera.debug.Log;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070037import com.android.camera.util.CameraUtil;
Angus Kongbd260692013-08-07 14:52:56 -070038import com.android.camera2.R;
Sam Judd4021c892014-03-17 12:57:50 -070039import com.bumptech.glide.Glide;
40import com.bumptech.glide.presenter.target.ImageViewTarget;
41import com.bumptech.glide.presenter.target.Target;
Angus Kongbd260692013-08-07 14:52:56 -070042
43import java.io.File;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070044import java.text.DateFormat;
Sam Juddaeed91f2014-03-13 11:40:10 -070045import java.util.ArrayList;
Angus Kongbd260692013-08-07 14:52:56 -070046import java.util.Date;
Sam Juddaeed91f2014-03-13 11:40:10 -070047import java.util.List;
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -070048import java.util.Locale;
Angus Kongbd260692013-08-07 14:52:56 -070049
50/**
51 * A base class for all the local media files. The bitmap is loaded in
Sascha Haeberlingb0a59292013-08-19 14:02:19 -070052 * background thread. Subclasses should implement their own background loading
53 * thread by sub-classing BitmapLoadTask and overriding doInBackground() to
54 * return a bitmap.
Angus Kongbd260692013-08-07 14:52:56 -070055 */
56public abstract class LocalMediaData implements LocalData {
Sam Juddaeed91f2014-03-13 11:40:10 -070057 /** The minimum id to use to query for all media at a given media store uri */
58 static final int QUERY_ALL_MEDIA_ID = -1;
59 private static final String CAMERA_PATH = Storage.DIRECTORY + "%";
60 private static final String SELECT_BY_PATH = MediaStore.MediaColumns.DATA + " LIKE ?";
61
Angus Kong32509872013-10-02 16:53:40 -070062 protected final long mContentId;
ztenghuia16e7b52013-08-23 11:47:56 -070063 protected final String mTitle;
64 protected final String mMimeType;
65 protected final long mDateTakenInSeconds;
66 protected final long mDateModifiedInSeconds;
67 protected final String mPath;
Angus Kongbd260692013-08-07 14:52:56 -070068 // width and height should be adjusted according to orientation.
ztenghuia16e7b52013-08-23 11:47:56 -070069 protected final int mWidth;
70 protected final int mHeight;
71 protected final long mSizeInBytes;
72 protected final double mLatitude;
73 protected final double mLongitude;
Angus Kong8a2350a2013-12-16 15:02:34 -080074 protected final Bundle mMetaData;
Angus Kongbd260692013-08-07 14:52:56 -070075
Angus Kongc27d21b2013-08-12 15:03:45 -070076 /**
Sascha Haeberlingb0a59292013-08-19 14:02:19 -070077 * Used for thumbnail loading optimization. True if this data has a
78 * corresponding visible view.
Angus Kongc27d21b2013-08-12 15:03:45 -070079 */
Angus Kongbd260692013-08-07 14:52:56 -070080 protected Boolean mUsing = false;
81
Sascha Haeberling280fd3e2013-11-21 13:52:15 -080082 public LocalMediaData(long contentId, String title, String mimeType,
ztenghuia16e7b52013-08-23 11:47:56 -070083 long dateTakenInSeconds, long dateModifiedInSeconds, String path,
84 int width, int height, long sizeInBytes, double latitude,
85 double longitude) {
Angus Kong32509872013-10-02 16:53:40 -070086 mContentId = contentId;
Sam Judd43bf03f2014-03-17 11:27:03 -070087 mTitle = title;
88 mMimeType = mimeType;
ztenghuia16e7b52013-08-23 11:47:56 -070089 mDateTakenInSeconds = dateTakenInSeconds;
90 mDateModifiedInSeconds = dateModifiedInSeconds;
Sam Judd43bf03f2014-03-17 11:27:03 -070091 mPath = path;
ztenghuia16e7b52013-08-23 11:47:56 -070092 mWidth = width;
93 mHeight = height;
94 mSizeInBytes = sizeInBytes;
95 mLatitude = latitude;
96 mLongitude = longitude;
Angus Kong8a2350a2013-12-16 15:02:34 -080097 mMetaData = new Bundle();
ztenghuia16e7b52013-08-23 11:47:56 -070098 }
99
Sam Juddaeed91f2014-03-13 11:40:10 -0700100 private interface CursorToLocalData {
101 public LocalData build(Cursor cursor);
102 }
103
104 private static List<LocalData> queryLocalMediaData(ContentResolver contentResolver,
105 Uri contentUri, String[] projection, long minimumId, String orderBy,
106 CursorToLocalData builder) {
107 String selection = SELECT_BY_PATH + " AND " + MediaStore.MediaColumns._ID + " > ?";
108 String[] selectionArgs = new String[] { CAMERA_PATH, Long.toString(minimumId) };
109
110 Cursor cursor = contentResolver.query(contentUri, projection,
111 selection, selectionArgs, orderBy);
112 List<LocalData> result = new ArrayList<LocalData>();
113 if (cursor != null) {
114 while (cursor.moveToNext()) {
115 LocalData data = builder.build(cursor);
116 if (data != null) {
117 result.add(data);
118 } else {
119 final int dataIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
Angus Kong2bca2102014-03-11 16:27:30 -0700120 Log.e(TAG, "Error loading data:" + cursor.getString(dataIndex));
Sam Juddaeed91f2014-03-13 11:40:10 -0700121 }
122 }
Sam Juddde3e9ab2014-03-17 13:07:22 -0700123
124 cursor.close();
Sam Juddaeed91f2014-03-13 11:40:10 -0700125 }
126 return result;
127 }
128
Angus Kongbd260692013-08-07 14:52:56 -0700129 @Override
130 public long getDateTaken() {
ztenghuia16e7b52013-08-23 11:47:56 -0700131 return mDateTakenInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -0700132 }
133
134 @Override
135 public long getDateModified() {
ztenghuia16e7b52013-08-23 11:47:56 -0700136 return mDateModifiedInSeconds;
137 }
138
139 @Override
Angus Kong32509872013-10-02 16:53:40 -0700140 public long getContentId() {
141 return mContentId;
Angus Kongbd260692013-08-07 14:52:56 -0700142 }
143
144 @Override
145 public String getTitle() {
Sam Judd4021c892014-03-17 12:57:50 -0700146 return mTitle;
Angus Kongbd260692013-08-07 14:52:56 -0700147 }
148
149 @Override
150 public int getWidth() {
ztenghuia16e7b52013-08-23 11:47:56 -0700151 return mWidth;
Angus Kongbd260692013-08-07 14:52:56 -0700152 }
153
154 @Override
155 public int getHeight() {
ztenghuia16e7b52013-08-23 11:47:56 -0700156 return mHeight;
Angus Kongbd260692013-08-07 14:52:56 -0700157 }
158
159 @Override
Angus Kongc195e7a2014-02-20 16:56:37 -0800160 public int getRotation() {
Doris Liu87fc5e12013-09-16 13:38:24 -0700161 return 0;
162 }
163
164 @Override
Angus Kongbd260692013-08-07 14:52:56 -0700165 public String getPath() {
ztenghuia16e7b52013-08-23 11:47:56 -0700166 return mPath;
167 }
168
169 @Override
170 public long getSizeInBytes() {
171 return mSizeInBytes;
Angus Kongbd260692013-08-07 14:52:56 -0700172 }
173
174 @Override
175 public boolean isUIActionSupported(int action) {
176 return false;
177 }
178
179 @Override
180 public boolean isDataActionSupported(int action) {
181 return false;
182 }
183
184 @Override
Angus Kong8a2350a2013-12-16 15:02:34 -0800185 public boolean delete(Context context) {
ztenghuia16e7b52013-08-23 11:47:56 -0700186 File f = new File(mPath);
Angus Kongbd260692013-08-07 14:52:56 -0700187 return f.delete();
188 }
189
190 @Override
Angus Kongbd260692013-08-07 14:52:56 -0700191 public void onFullScreen(boolean fullScreen) {
192 // do nothing.
193 }
194
195 @Override
196 public boolean canSwipeInFullScreen() {
197 return true;
198 }
199
Angus Kong8a2350a2013-12-16 15:02:34 -0800200 protected ImageView fillImageView(Context context, ImageView v,
Sam Judd4021c892014-03-17 12:57:50 -0700201 int decodeWidth, int decodeHeight, int placeHolderResourceId,
Sascha Haeberling14ff6c82013-12-13 13:29:58 -0800202 LocalDataAdapter adapter, boolean isInProgress) {
Sam Judd4021c892014-03-17 12:57:50 -0700203
204 SizedImageViewTarget target = (SizedImageViewTarget) v.getTag();
205 if (target != null) {
206 target = new SizedImageViewTarget(v);
207 v.setTag(target);
Andy Huiberscaca8c72013-12-13 15:53:43 -0800208 }
209
Sam Judd4021c892014-03-17 12:57:50 -0700210 Glide.with(context)
211 .load(mPath)
212 .fitCenter()
213 .placeholder(placeHolderResourceId)
214 .into(target);
Angus Kongbd260692013-08-07 14:52:56 -0700215
Angus Kongbd260692013-08-07 14:52:56 -0700216 return v;
217 }
218
219 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700220 public View getView(Context context, View recycled, int decodeWidth, int decodeHeight,
221 int placeHolderResourceId, LocalDataAdapter adapter, boolean isInProgress) {
Sam Judd43bf03f2014-03-17 11:27:03 -0700222 final ImageView imageView;
223 if (recycled != null) {
224 imageView = (ImageView) recycled;
225 } else {
226 imageView = new ImageView(context);
227 }
228
229 return fillImageView(context, imageView, decodeWidth, decodeHeight,
Sam Judd4021c892014-03-17 12:57:50 -0700230 placeHolderResourceId, adapter, isInProgress);
Angus Kongbd260692013-08-07 14:52:56 -0700231 }
232
233 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700234 public void loadFullImage(Context context, int width, int height, View view,
235 LocalDataAdapter adapter) {
Andy Huiberscaca8c72013-12-13 15:53:43 -0800236 // Default is do nothing.
237 // Can be implemented by sub-classes.
238 }
239
240 @Override
Angus Kongbd260692013-08-07 14:52:56 -0700241 public void prepare() {
242 synchronized (mUsing) {
243 mUsing = true;
244 }
245 }
246
247 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700248 public void recycle(View view) {
Angus Kongbd260692013-08-07 14:52:56 -0700249 synchronized (mUsing) {
250 mUsing = false;
251 }
252 }
253
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700254 @Override
255 public double[] getLatLong() {
ztenghuia16e7b52013-08-23 11:47:56 -0700256 if (mLatitude == 0 && mLongitude == 0) {
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700257 return null;
258 }
259 return new double[] {
ztenghuia16e7b52013-08-23 11:47:56 -0700260 mLatitude, mLongitude
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700261 };
262 }
263
Angus Kongbd260692013-08-07 14:52:56 -0700264 protected boolean isUsing() {
265 synchronized (mUsing) {
266 return mUsing;
267 }
268 }
269
270 @Override
Sascha Haeberling88ef7662013-08-15 17:19:22 -0700271 public String getMimeType() {
ztenghuia16e7b52013-08-23 11:47:56 -0700272 return mMimeType;
Sascha Haeberling88ef7662013-08-15 17:19:22 -0700273 }
274
275 @Override
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700276 public MediaDetails getMediaDetails(Context context) {
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -0700277 DateFormat dateFormatter = DateFormat.getDateTimeInstance();
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700278 MediaDetails mediaDetails = new MediaDetails();
ztenghuia16e7b52013-08-23 11:47:56 -0700279 mediaDetails.addDetail(MediaDetails.INDEX_TITLE, mTitle);
280 mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, mWidth);
281 mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, mHeight);
282 mediaDetails.addDetail(MediaDetails.INDEX_PATH, mPath);
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700283 mediaDetails.addDetail(MediaDetails.INDEX_DATETIME,
ztenghuia16e7b52013-08-23 11:47:56 -0700284 dateFormatter.format(new Date(mDateModifiedInSeconds * 1000)));
285 if (mSizeInBytes > 0) {
286 mediaDetails.addDetail(MediaDetails.INDEX_SIZE, mSizeInBytes);
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700287 }
ztenghuia16e7b52013-08-23 11:47:56 -0700288 if (mLatitude != 0 && mLongitude != 0) {
289 String locationString = String.format(Locale.getDefault(), "%f, %f", mLatitude,
290 mLongitude);
Sascha Haeberlinge2fb9942013-08-22 13:49:46 -0700291 mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, locationString);
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700292 }
293 return mediaDetails;
294 }
295
296 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700297 public abstract int getViewType();
Angus Kongbd260692013-08-07 14:52:56 -0700298
Angus Kong8a2350a2013-12-16 15:02:34 -0800299 @Override
300 public Bundle getMetadata() {
301 return mMetaData;
302 }
303
Angus Kong56102c62013-12-19 12:16:54 -0800304 @Override
305 public boolean isMetadataUpdated() {
Sam Juddde3e9ab2014-03-17 13:07:22 -0700306 return MetadataLoader.isMetadataCached(this);
Angus Kong56102c62013-12-19 12:16:54 -0800307 }
308
ztenghuia16e7b52013-08-23 11:47:56 -0700309 public static final class PhotoData extends LocalMediaData {
Angus Kong2bca2102014-03-11 16:27:30 -0700310 private static final Log.Tag TAG = new Log.Tag("PhotoData");
Angus Kongbd260692013-08-07 14:52:56 -0700311
312 public static final int COL_ID = 0;
313 public static final int COL_TITLE = 1;
314 public static final int COL_MIME_TYPE = 2;
315 public static final int COL_DATE_TAKEN = 3;
316 public static final int COL_DATE_MODIFIED = 4;
317 public static final int COL_DATA = 5;
318 public static final int COL_ORIENTATION = 6;
319 public static final int COL_WIDTH = 7;
320 public static final int COL_HEIGHT = 8;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700321 public static final int COL_SIZE = 9;
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700322 public static final int COL_LATITUDE = 10;
323 public static final int COL_LONGITUDE = 11;
Angus Kongbd260692013-08-07 14:52:56 -0700324
Andy Huiberscaca8c72013-12-13 15:53:43 -0800325 // GL max texture size: keep bitmaps below this value.
326 private static final int MAXIMUM_TEXTURE_SIZE = 2048;
Andy Huiberscaca8c72013-12-13 15:53:43 -0800327
Angus Kongbd260692013-08-07 14:52:56 -0700328 static final Uri CONTENT_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
329
Sam Juddaeed91f2014-03-13 11:40:10 -0700330 private static final String QUERY_ORDER = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC, "
Angus Kongbd260692013-08-07 14:52:56 -0700331 + MediaStore.Images.ImageColumns._ID + " DESC";
332 /**
333 * These values should be kept in sync with column IDs (COL_*) above.
334 */
Sam Juddaeed91f2014-03-13 11:40:10 -0700335 private static final String[] QUERY_PROJECTION = {
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700336 MediaStore.Images.ImageColumns._ID, // 0, int
337 MediaStore.Images.ImageColumns.TITLE, // 1, string
338 MediaStore.Images.ImageColumns.MIME_TYPE, // 2, string
339 MediaStore.Images.ImageColumns.DATE_TAKEN, // 3, int
340 MediaStore.Images.ImageColumns.DATE_MODIFIED, // 4, int
341 MediaStore.Images.ImageColumns.DATA, // 5, string
342 MediaStore.Images.ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270
343 MediaStore.Images.ImageColumns.WIDTH, // 7, int
344 MediaStore.Images.ImageColumns.HEIGHT, // 8, int
345 MediaStore.Images.ImageColumns.SIZE, // 9, long
346 MediaStore.Images.ImageColumns.LATITUDE, // 10, double
347 MediaStore.Images.ImageColumns.LONGITUDE // 11, double
Angus Kongbd260692013-08-07 14:52:56 -0700348 };
349
Angus Konge0aff892013-12-11 20:51:01 -0800350 private static final int mSupportedUIActions = ACTION_DEMOTE | ACTION_PROMOTE | ACTION_ZOOM;
Angus Kongbd260692013-08-07 14:52:56 -0700351 private static final int mSupportedDataActions =
Angus Konge0aff892013-12-11 20:51:01 -0800352 DATA_ACTION_DELETE | DATA_ACTION_EDIT | DATA_ACTION_SHARE;
Angus Kongbd260692013-08-07 14:52:56 -0700353
Angus Kongbd260692013-08-07 14:52:56 -0700354 /** from MediaStore, can only be 0, 90, 180, 270 */
ztenghuia16e7b52013-08-23 11:47:56 -0700355 private final int mOrientation;
Sam Judd4021c892014-03-17 12:57:50 -0700356 /** @see #getSignature() */
357 private final String mSignature;
ztenghuia16e7b52013-08-23 11:47:56 -0700358
Angus Kong571a8c32014-03-13 12:53:03 -0700359 public static LocalData fromContentUri(ContentResolver cr, Uri contentUri) {
360 List<LocalData> newPhotos = query(cr, contentUri, QUERY_ALL_MEDIA_ID);
361 if (newPhotos.isEmpty()) {
362 return null;
363 }
364 return newPhotos.get(0);
365 }
366
ztenghuia16e7b52013-08-23 11:47:56 -0700367 public PhotoData(long id, String title, String mimeType,
368 long dateTakenInSeconds, long dateModifiedInSeconds,
369 String path, int orientation, int width, int height,
370 long sizeInBytes, double latitude, double longitude) {
371 super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
372 path, width, height, sizeInBytes, latitude, longitude);
373 mOrientation = orientation;
Sam Judd4021c892014-03-17 12:57:50 -0700374 mSignature = mimeType + orientation + dateModifiedInSeconds;
ztenghuia16e7b52013-08-23 11:47:56 -0700375 }
Angus Kongbd260692013-08-07 14:52:56 -0700376
Sam Juddaeed91f2014-03-13 11:40:10 -0700377 static List<LocalData> query(ContentResolver cr, Uri uri, long lastId) {
378 return queryLocalMediaData(cr, uri, QUERY_PROJECTION, lastId, QUERY_ORDER,
379 new PhotoDataBuilder());
380 }
381
382 private static PhotoData buildFromCursor(Cursor c) {
ztenghuia16e7b52013-08-23 11:47:56 -0700383 long id = c.getLong(COL_ID);
384 String title = c.getString(COL_TITLE);
385 String mimeType = c.getString(COL_MIME_TYPE);
386 long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
387 long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
388 String path = c.getString(COL_DATA);
389 int orientation = c.getInt(COL_ORIENTATION);
390 int width = c.getInt(COL_WIDTH);
391 int height = c.getInt(COL_HEIGHT);
392 if (width <= 0 || height <= 0) {
Angus Kong62a622b2013-08-30 15:10:38 -0700393 Log.w(TAG, "Zero dimension in ContentResolver for "
ztenghuia16e7b52013-08-23 11:47:56 -0700394 + path + ":" + width + "x" + height);
Angus Kongbd260692013-08-07 14:52:56 -0700395 BitmapFactory.Options opts = new BitmapFactory.Options();
396 opts.inJustDecodeBounds = true;
ztenghuia16e7b52013-08-23 11:47:56 -0700397 BitmapFactory.decodeFile(path, opts);
Angus Kong62a622b2013-08-30 15:10:38 -0700398 if (opts.outWidth > 0 && opts.outHeight > 0) {
ztenghuia16e7b52013-08-23 11:47:56 -0700399 width = opts.outWidth;
400 height = opts.outHeight;
Angus Kongbd260692013-08-07 14:52:56 -0700401 } else {
Angus Kong62a622b2013-08-30 15:10:38 -0700402 Log.w(TAG, "Dimension decode failed for " + path);
ztenghuia16e7b52013-08-23 11:47:56 -0700403 Bitmap b = BitmapFactory.decodeFile(path);
Angus Kongbd260692013-08-07 14:52:56 -0700404 if (b == null) {
Angus Kong32509872013-10-02 16:53:40 -0700405 Log.w(TAG, "PhotoData skipped."
Angus Kong62a622b2013-08-30 15:10:38 -0700406 + " Decoding " + path + "failed.");
Angus Kongbd260692013-08-07 14:52:56 -0700407 return null;
408 }
ztenghuia16e7b52013-08-23 11:47:56 -0700409 width = b.getWidth();
410 height = b.getHeight();
Angus Kong62a622b2013-08-30 15:10:38 -0700411 if (width == 0 || height == 0) {
412 Log.w(TAG, "PhotoData skipped. Bitmap size 0 for " + path);
413 return null;
414 }
Angus Kongbd260692013-08-07 14:52:56 -0700415 }
416 }
ztenghui06578b52013-10-07 14:54:55 -0700417
ztenghuia16e7b52013-08-23 11:47:56 -0700418 long sizeInBytes = c.getLong(COL_SIZE);
419 double latitude = c.getDouble(COL_LATITUDE);
420 double longitude = c.getDouble(COL_LONGITUDE);
421 PhotoData result = new PhotoData(id, title, mimeType, dateTakenInSeconds,
422 dateModifiedInSeconds, path, orientation, width, height,
423 sizeInBytes, latitude, longitude);
424 return result;
425 }
426
Doris Liu87fc5e12013-09-16 13:38:24 -0700427 @Override
Angus Kongc195e7a2014-02-20 16:56:37 -0800428 public int getRotation() {
ztenghuia16e7b52013-08-23 11:47:56 -0700429 return mOrientation;
Angus Kongbd260692013-08-07 14:52:56 -0700430 }
431
432 @Override
433 public String toString() {
ztenghuia16e7b52013-08-23 11:47:56 -0700434 return "Photo:" + ",data=" + mPath + ",mimeType=" + mMimeType
435 + "," + mWidth + "x" + mHeight + ",orientation=" + mOrientation
436 + ",date=" + new Date(mDateTakenInSeconds);
Angus Kongbd260692013-08-07 14:52:56 -0700437 }
438
439 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700440 public int getViewType() {
Angus Kongb21215a2013-09-20 18:41:46 -0700441 return VIEW_TYPE_REMOVABLE;
Angus Kongbd260692013-08-07 14:52:56 -0700442 }
443
444 @Override
445 public boolean isUIActionSupported(int action) {
446 return ((action & mSupportedUIActions) == action);
447 }
448
449 @Override
450 public boolean isDataActionSupported(int action) {
451 return ((action & mSupportedDataActions) == action);
452 }
453
454 @Override
Angus Kong8a2350a2013-12-16 15:02:34 -0800455 public boolean delete(Context context) {
456 ContentResolver cr = context.getContentResolver();
Angus Kong32509872013-10-02 16:53:40 -0700457 cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + mContentId, null);
Angus Kong8a2350a2013-12-16 15:02:34 -0800458 return super.delete(context);
Angus Kongbd260692013-08-07 14:52:56 -0700459 }
460
461 @Override
Angus Kong571a8c32014-03-13 12:53:03 -0700462 public Uri getUri() {
Angus Kongbd260692013-08-07 14:52:56 -0700463 Uri baseUri = CONTENT_URI;
Angus Kong32509872013-10-02 16:53:40 -0700464 return baseUri.buildUpon().appendPath(String.valueOf(mContentId)).build();
Angus Kongbd260692013-08-07 14:52:56 -0700465 }
466
467 @Override
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700468 public MediaDetails getMediaDetails(Context context) {
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700469 MediaDetails mediaDetails = super.getMediaDetails(context);
ztenghuia16e7b52013-08-23 11:47:56 -0700470 MediaDetails.extractExifInfo(mediaDetails, mPath);
Sascha Haeberlingbfd919a2013-09-26 16:01:23 -0700471 mediaDetails.addDetail(MediaDetails.INDEX_ORIENTATION, mOrientation);
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700472 return mediaDetails;
473 }
474
475 @Override
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700476 public int getLocalDataType() {
Angus Kongc27d21b2013-08-12 15:03:45 -0700477 return LOCAL_IMAGE;
478 }
479
480 @Override
Angus Kong8a2350a2013-12-16 15:02:34 -0800481 public LocalData refresh(Context context) {
Sam Juddaeed91f2014-03-13 11:40:10 -0700482 PhotoData newData = null;
Angus Kong571a8c32014-03-13 12:53:03 -0700483 Cursor c = context.getContentResolver().query(getUri(), QUERY_PROJECTION, null,
Angus Kong8a2350a2013-12-16 15:02:34 -0800484 null, null);
Sam Juddaeed91f2014-03-13 11:40:10 -0700485 if (c != null) {
486 if (c.moveToFirst()) {
487 newData = buildFromCursor(c);
488 }
489 c.close();
Angus Kongbd260692013-08-07 14:52:56 -0700490 }
Sam Juddaeed91f2014-03-13 11:40:10 -0700491
ztenghuia16e7b52013-08-23 11:47:56 -0700492 return newData;
Angus Kongbd260692013-08-07 14:52:56 -0700493 }
494
495 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700496 public String getSignature() {
497 return mSignature;
498 }
499
500 @Override
501 protected ImageView fillImageView(Context context, final ImageView v, final int decodeWidth,
502 final int decodeHeight, int placeHolderResourceId, LocalDataAdapter adapter,
503 boolean isInProgress) {
504 loadImage(context, v, decodeWidth, decodeHeight, placeHolderResourceId, false);
505 return v;
506 }
507
508 private void loadImage(Context context, ImageView imageView, int decodeWidth,
509 int decodeHeight, int placeHolderResourceId, boolean full) {
510 ThumbTarget thumbTarget = (ThumbTarget) imageView.getTag();
511 if (thumbTarget == null) {
512 thumbTarget = new ThumbTarget(imageView);
513 imageView.setTag(thumbTarget);
514 }
515 // Make sure we've reset all state related to showing thumb and full whenever
516 // we load a thumbnail because loading a thumbnail only happens when we're changing
517 // images.
518 if (!full) {
519 thumbTarget.clearTargets();
520 }
521
522 Glide.with(context)
523 .using(new ImageModelLoader(context))
524 .load(this)
525 .placeholder(placeHolderResourceId)
526 .fitCenter()
527 .into(thumbTarget.getTarget(decodeWidth, decodeHeight, full));
528 }
529
530 @Override
531 public void recycle(View view) {
532 super.recycle(view);
533 if (view != null) {
534 ThumbTarget thumbTarget = (ThumbTarget) view.getTag();
535 thumbTarget.clearTargets();
536 }
537 }
538
539 @Override
Sam Judd43bf03f2014-03-17 11:27:03 -0700540 public LocalDataViewType getItemViewType() {
541 return LocalDataViewType.PHOTO;
542 }
543
544 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700545 public void loadFullImage(Context context, int w, int h, View v, LocalDataAdapter adapter)
Andy Huiberscaca8c72013-12-13 15:53:43 -0800546 {
Sam Judd4021c892014-03-17 12:57:50 -0700547 loadImage(context, (ImageView) v, w, h, 0, true);
Angus Kongbd260692013-08-07 14:52:56 -0700548 }
ztenghuia16e7b52013-08-23 11:47:56 -0700549
Sam Juddaeed91f2014-03-13 11:40:10 -0700550 private static class PhotoDataBuilder implements CursorToLocalData {
551 @Override
552 public PhotoData build(Cursor cursor) {
553 return LocalMediaData.PhotoData.buildFromCursor(cursor);
554 }
ztenghuia16e7b52013-08-23 11:47:56 -0700555 }
Sam Judd4021c892014-03-17 12:57:50 -0700556
557 /**
558 * In the filmstrip we want to load a small version of an image and then load a
559 * larger version when we switch to full screen mode. This class manages
560 * that transition and loading process by keeping one target for the smaller thumbnail
561 * and a second target for the larger version.
562 */
563 private static class ThumbTarget {
564 private final SizedImageViewTarget mThumbTarget;
565 private final SizedImageViewTarget mFullTarget;
566 private boolean fullLoaded = false;
567
568 public ThumbTarget(ImageView imageView) {
569 mThumbTarget = new SizedImageViewTarget(imageView) {
570 @Override
571 public void onImageReady(Bitmap bitmap) {
572 // If we manage to load the thumb after the full, we don't
573 // want to replace the higher quality full with the thumb.
574 if (!fullLoaded) {
575 super.onImageReady(bitmap);
576 }
577 }
578 };
579
580 mFullTarget = new SizedImageViewTarget(imageView) {
581
582 @Override
583 public void onImageReady(Bitmap bitmap) {
584 // When the full is loaded, we no longer need the thumb.
585 fullLoaded = true;
586 Glide.cancel(mThumbTarget);
587 super.onImageReady(bitmap);
588 }
589
590 @Override
591 public void setPlaceholder(Drawable placeholder) {
592 // We always load the thumb first which will set the placeholder.
593 // If we were to set the placeholder here too, instead of showing
594 // the thumb while we load the full, we will instead revert back
595 // to the placeholder.
596 }
597 };
598 }
599
600 public Target getTarget(int width, int height, boolean full) {
601 final SizedImageViewTarget result = full ? mFullTarget : mThumbTarget;
602 // Limit the target size so we don't load a bitmap larger than the max size we
603 // can display.
604 width = Math.min(width, MAXIMUM_TEXTURE_SIZE);
605 height = Math.min(height, MAXIMUM_TEXTURE_SIZE);
606
607 result.setSize(width, height);
608 return result;
609 }
610
611 public void clearTargets() {
612 fullLoaded = false;
613 Glide.cancel(mThumbTarget);
614 Glide.cancel(mFullTarget);
615 }
616 }
Angus Kongbd260692013-08-07 14:52:56 -0700617 }
618
ztenghuia16e7b52013-08-23 11:47:56 -0700619 public static final class VideoData extends LocalMediaData {
Angus Kongbd260692013-08-07 14:52:56 -0700620 public static final int COL_ID = 0;
621 public static final int COL_TITLE = 1;
622 public static final int COL_MIME_TYPE = 2;
623 public static final int COL_DATE_TAKEN = 3;
624 public static final int COL_DATE_MODIFIED = 4;
625 public static final int COL_DATA = 5;
626 public static final int COL_WIDTH = 6;
627 public static final int COL_HEIGHT = 7;
Sam Judd43bf03f2014-03-17 11:27:03 -0700628 public static final int COL_SIZE = 8;
629 public static final int COL_LATITUDE = 9;
630 public static final int COL_LONGITUDE = 10;
631 public static final int COL_DURATION = 11;
Angus Kongbd260692013-08-07 14:52:56 -0700632
633 static final Uri CONTENT_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
634
Angus Konge0aff892013-12-11 20:51:01 -0800635 private static final int mSupportedUIActions = ACTION_DEMOTE | ACTION_PROMOTE;
Angus Kongbd260692013-08-07 14:52:56 -0700636 private static final int mSupportedDataActions =
Angus Konge0aff892013-12-11 20:51:01 -0800637 DATA_ACTION_DELETE | DATA_ACTION_PLAY | DATA_ACTION_SHARE;
Angus Kongbd260692013-08-07 14:52:56 -0700638
Sam Juddaeed91f2014-03-13 11:40:10 -0700639 private static final String QUERY_ORDER = MediaStore.Video.VideoColumns.DATE_TAKEN
640 + " DESC, " + MediaStore.Video.VideoColumns._ID + " DESC";
Angus Kongbd260692013-08-07 14:52:56 -0700641 /**
642 * These values should be kept in sync with column IDs (COL_*) above.
643 */
Sam Juddaeed91f2014-03-13 11:40:10 -0700644 private static final String[] QUERY_PROJECTION = {
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700645 MediaStore.Video.VideoColumns._ID, // 0, int
646 MediaStore.Video.VideoColumns.TITLE, // 1, string
647 MediaStore.Video.VideoColumns.MIME_TYPE, // 2, string
648 MediaStore.Video.VideoColumns.DATE_TAKEN, // 3, int
649 MediaStore.Video.VideoColumns.DATE_MODIFIED, // 4, int
650 MediaStore.Video.VideoColumns.DATA, // 5, string
651 MediaStore.Video.VideoColumns.WIDTH, // 6, int
652 MediaStore.Video.VideoColumns.HEIGHT, // 7, int
Sam Judd43bf03f2014-03-17 11:27:03 -0700653 MediaStore.Video.VideoColumns.SIZE, // 8 long
654 MediaStore.Video.VideoColumns.LATITUDE, // 9 double
655 MediaStore.Video.VideoColumns.LONGITUDE, // 10 double
656 MediaStore.Video.VideoColumns.DURATION // 11 long
Angus Kongbd260692013-08-07 14:52:56 -0700657 };
658
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700659 /** The duration in milliseconds. */
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800660 private final long mDurationInSeconds;
Sam Judd4021c892014-03-17 12:57:50 -0700661 private final String mSignature;
ztenghuia16e7b52013-08-23 11:47:56 -0700662
663 public VideoData(long id, String title, String mimeType,
664 long dateTakenInSeconds, long dateModifiedInSeconds,
665 String path, int width, int height, long sizeInBytes,
666 double latitude, double longitude, long durationInSeconds) {
667 super(id, title, mimeType, dateTakenInSeconds, dateModifiedInSeconds,
668 path, width, height, sizeInBytes, latitude, longitude);
669 mDurationInSeconds = durationInSeconds;
Sam Judd4021c892014-03-17 12:57:50 -0700670 mSignature = mimeType + dateModifiedInSeconds;
ztenghuia16e7b52013-08-23 11:47:56 -0700671 }
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700672
Angus Kong571a8c32014-03-13 12:53:03 -0700673 public static LocalData fromContentUri(ContentResolver cr, Uri contentUri) {
674 List<LocalData> newVideos = query(cr, contentUri, QUERY_ALL_MEDIA_ID);
675 if (newVideos.isEmpty()) {
676 return null;
677 }
678 return newVideos.get(0);
679 }
680
681 static List<LocalData> query(ContentResolver cr, Uri uri, long lastId) {
682 return queryLocalMediaData(cr, uri, QUERY_PROJECTION, lastId, QUERY_ORDER,
683 new VideoDataBuilder());
Sam Juddaeed91f2014-03-13 11:40:10 -0700684 }
685
Sam Juddde3e9ab2014-03-17 13:07:22 -0700686 /**
687 * We can't trust the media store and we can't afford the performance overhead of
688 * synchronously decoding the video header for every item when loading our data set
689 * from the media store, so we instead run the metadata loader in the background
690 * to decode the video header for each item and prefer whatever values it obtains.
691 */
692 private int getBestWidth() {
693 int metadataWidth = VideoRotationMetadataLoader.getWidth(this);
694 if (metadataWidth > 0) {
695 return metadataWidth;
696 } else {
697 return mWidth;
698 }
699 }
700
701 private int getBestHeight() {
702 int metadataHeight = VideoRotationMetadataLoader.getHeight(this);
703 if (metadataHeight > 0) {
704 return metadataHeight;
705 } else {
706 return mHeight;
707 }
708 }
709
710 /**
711 * If the metadata loader has determined from the video header that we need to rotate the video
712 * 90 or 270 degrees, then we swap the width and height.
713 */
714 @Override
715 public int getWidth() {
716 return VideoRotationMetadataLoader.isRotated(this) ? getBestHeight() : getBestWidth();
717 }
718
719 @Override
720 public int getHeight() {
721 return VideoRotationMetadataLoader.isRotated(this) ? getBestWidth() : getBestHeight();
722 }
723
Sam Juddaeed91f2014-03-13 11:40:10 -0700724 private static VideoData buildFromCursor(Cursor c) {
ztenghuia16e7b52013-08-23 11:47:56 -0700725 long id = c.getLong(COL_ID);
726 String title = c.getString(COL_TITLE);
727 String mimeType = c.getString(COL_MIME_TYPE);
728 long dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
729 long dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
730 String path = c.getString(COL_DATA);
731 int width = c.getInt(COL_WIDTH);
732 int height = c.getInt(COL_HEIGHT);
ztenghuia16e7b52013-08-23 11:47:56 -0700733
Sam Juddde3e9ab2014-03-17 13:07:22 -0700734 // If the media store doesn't contain a width and a height, use the width and height
735 // of the default camera mode instead. When the metadata loader runs, it will set the
736 // correct values.
ztenghuia16e7b52013-08-23 11:47:56 -0700737 if (width == 0 || height == 0) {
Sam Juddde3e9ab2014-03-17 13:07:22 -0700738 Log.w(TAG, "failed to retrieve width and height from the media store, defaulting " +
739 " to camera profile");
740 CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
741 width = profile.videoFrameWidth;
742 height = profile.videoFrameHeight;
Angus Kongbd260692013-08-07 14:52:56 -0700743 }
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700744
ztenghuia16e7b52013-08-23 11:47:56 -0700745 long sizeInBytes = c.getLong(COL_SIZE);
746 double latitude = c.getDouble(COL_LATITUDE);
747 double longitude = c.getDouble(COL_LONGITUDE);
748 long durationInSeconds = c.getLong(COL_DURATION) / 1000;
749 VideoData d = new VideoData(id, title, mimeType, dateTakenInSeconds,
750 dateModifiedInSeconds, path, width, height, sizeInBytes,
751 latitude, longitude, durationInSeconds);
Angus Kongbd260692013-08-07 14:52:56 -0700752 return d;
753 }
754
755 @Override
756 public String toString() {
ztenghuia16e7b52013-08-23 11:47:56 -0700757 return "Video:" + ",data=" + mPath + ",mimeType=" + mMimeType
758 + "," + mWidth + "x" + mHeight + ",date=" + new Date(mDateTakenInSeconds);
Angus Kongbd260692013-08-07 14:52:56 -0700759 }
760
761 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700762 public int getViewType() {
Angus Kongb21215a2013-09-20 18:41:46 -0700763 return VIEW_TYPE_REMOVABLE;
Angus Kongbd260692013-08-07 14:52:56 -0700764 }
765
766 @Override
767 public boolean isUIActionSupported(int action) {
768 return ((action & mSupportedUIActions) == action);
769 }
770
771 @Override
772 public boolean isDataActionSupported(int action) {
773 return ((action & mSupportedDataActions) == action);
774 }
775
776 @Override
Angus Kong8a2350a2013-12-16 15:02:34 -0800777 public boolean delete(Context context) {
778 ContentResolver cr = context.getContentResolver();
Angus Kong32509872013-10-02 16:53:40 -0700779 cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + mContentId, null);
Angus Kong8a2350a2013-12-16 15:02:34 -0800780 return super.delete(context);
Angus Kongbd260692013-08-07 14:52:56 -0700781 }
782
783 @Override
Angus Kong571a8c32014-03-13 12:53:03 -0700784 public Uri getUri() {
Angus Kongbd260692013-08-07 14:52:56 -0700785 Uri baseUri = CONTENT_URI;
Angus Kong32509872013-10-02 16:53:40 -0700786 return baseUri.buildUpon().appendPath(String.valueOf(mContentId)).build();
Angus Kongbd260692013-08-07 14:52:56 -0700787 }
788
789 @Override
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700790 public MediaDetails getMediaDetails(Context context) {
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700791 MediaDetails mediaDetails = super.getMediaDetails(context);
ztenghuia16e7b52013-08-23 11:47:56 -0700792 String duration = MediaDetails.formatDuration(context, mDurationInSeconds);
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700793 mediaDetails.addDetail(MediaDetails.INDEX_DURATION, duration);
794 return mediaDetails;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700795 }
796
797 @Override
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700798 public int getLocalDataType() {
Angus Kongc27d21b2013-08-12 15:03:45 -0700799 return LOCAL_VIDEO;
800 }
801
802 @Override
Angus Kong8a2350a2013-12-16 15:02:34 -0800803 public LocalData refresh(Context context) {
Angus Kong571a8c32014-03-13 12:53:03 -0700804 Cursor c = context.getContentResolver().query(getUri(), QUERY_PROJECTION, null,
Angus Kong8a2350a2013-12-16 15:02:34 -0800805 null, null);
Sascha Haeberlingb0a59292013-08-19 14:02:19 -0700806 if (c == null || !c.moveToFirst()) {
ztenghuia16e7b52013-08-23 11:47:56 -0700807 return null;
Angus Kongbd260692013-08-07 14:52:56 -0700808 }
809 VideoData newData = buildFromCursor(c);
ztenghuia16e7b52013-08-23 11:47:56 -0700810 return newData;
Angus Kongbd260692013-08-07 14:52:56 -0700811 }
812
813 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700814 public String getSignature() {
815 return mSignature;
816 }
817
818 @Override
819 protected ImageView fillImageView(Context context, final ImageView v, final int decodeWidth,
820 final int decodeHeight, int placeHolderResourceId, LocalDataAdapter adapter,
821 boolean isInProgress) {
822 SizedImageViewTarget target = (SizedImageViewTarget) v.getTag();
823 if (target == null) {
824 target = new SizedImageViewTarget(v);
825 v.setTag(target);
826 }
827 target.setSize(decodeWidth, decodeHeight);
828
829 Glide.with(context)
830 .using(new VideoModelLoader(context))
831 .loadFromVideo(this)
832 .placeholder(placeHolderResourceId)
833 .fitCenter()
834 .into(target);
835
836 return v;
837 }
838
839 @Override
Sam Judd43bf03f2014-03-17 11:27:03 -0700840 public View getView(final Context context, View recycled,
Sam Judd4021c892014-03-17 12:57:50 -0700841 int decodeWidth, int decodeHeight, int placeHolderResourceId,
Sascha Haeberling14ff6c82013-12-13 13:29:58 -0800842 LocalDataAdapter adapter, boolean isInProgress) {
Angus Kongbd260692013-08-07 14:52:56 -0700843
Sam Judd43bf03f2014-03-17 11:27:03 -0700844 final VideoViewHolder viewHolder;
845 final View result;
846 if (recycled != null) {
847 result = recycled;
848 viewHolder = (VideoViewHolder) recycled.getTag();
849 } else {
850 result = LayoutInflater.from(context).inflate(R.layout.filmstrip_video, null);
851 ImageView videoView = (ImageView) result.findViewById(R.id.video_view);
852 ImageView playButton = (ImageView) result.findViewById(R.id.play_button);
853 viewHolder = new VideoViewHolder(videoView, playButton);
854 result.setTag(viewHolder);
855 }
856
Sam Judd4021c892014-03-17 12:57:50 -0700857 fillImageView(context, viewHolder.mVideoView, decodeWidth, decodeHeight,
858 placeHolderResourceId, adapter, isInProgress);
Angus Kongbd260692013-08-07 14:52:56 -0700859
860 // ImageView for the play icon.
Sam Judd43bf03f2014-03-17 11:27:03 -0700861 viewHolder.mPlayButton.setOnClickListener(new View.OnClickListener() {
Angus Kongbd260692013-08-07 14:52:56 -0700862 @Override
863 public void onClick(View v) {
Sascha Haeberling280fd3e2013-11-21 13:52:15 -0800864 // TODO: refactor this into activities to avoid this class
865 // conversion.
Angus Kong571a8c32014-03-13 12:53:03 -0700866 CameraUtil.playVideo((Activity) context, getUri(), mTitle);
Angus Kongbd260692013-08-07 14:52:56 -0700867 }
868 });
869
Sam Judd43bf03f2014-03-17 11:27:03 -0700870 return result;
871 }
872
873 @Override
Sam Judd4021c892014-03-17 12:57:50 -0700874 public void recycle(View view) {
875 super.recycle(view);
876 VideoViewHolder videoViewHolder = (VideoViewHolder) view.getTag();
877 Target target = (Target) videoViewHolder.mVideoView.getTag();
878 Glide.cancel(target);
879 }
880
881 @Override
Sam Judd43bf03f2014-03-17 11:27:03 -0700882 public LocalDataViewType getItemViewType() {
883 return LocalDataViewType.VIDEO;
Angus Kongbd260692013-08-07 14:52:56 -0700884 }
Angus Kongbd260692013-08-07 14:52:56 -0700885 }
Andy Huiberscaca8c72013-12-13 15:53:43 -0800886
Sam Juddaeed91f2014-03-13 11:40:10 -0700887 private static class VideoDataBuilder implements CursorToLocalData {
888
889 @Override
890 public VideoData build(Cursor cursor) {
891 return LocalMediaData.VideoData.buildFromCursor(cursor);
892 }
893 }
894
Sam Judd43bf03f2014-03-17 11:27:03 -0700895 private static class VideoViewHolder {
896 private final ImageView mVideoView;
897 private final ImageView mPlayButton;
898
899 public VideoViewHolder(ImageView videoView, ImageView playButton) {
900 mVideoView = videoView;
901 mPlayButton = playButton;
902 }
903 }
Sam Judd4021c892014-03-17 12:57:50 -0700904
905 /**
906 * Normally Glide will figure out the necessary size based on the view
907 * the image is being loaded into. In filmstrip the view isn't immediately
908 * laid out after being requested from the data, which can cause Glide to give
909 * up on obtaining the view dimensions. To avoid that, we manually set the
910 * dimensions.
911 */
912 private static class SizedImageViewTarget extends ImageViewTarget {
913 private int mWidth;
914 private int mHeight;
915
916 public SizedImageViewTarget(ImageView imageView) {
917 super(imageView);
918 }
919
920 public void setSize(int width, int height) {
921 mWidth = width;
922 mHeight = height;
923 }
924
925 @Override
926 public void getSize(final SizeReadyCallback cb) {
927 cb.onSizeReady(mWidth, mHeight);
928 }
929 }
Angus Kongbd260692013-08-07 14:52:56 -0700930}