blob: 131c7e7dbda919166090ec2d3f246f20f43306bb [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
19import android.content.ContentResolver;
20import android.content.Context;
21import android.database.Cursor;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.Matrix;
25import android.graphics.drawable.BitmapDrawable;
26import android.graphics.drawable.Drawable;
27import android.media.MediaMetadataRetriever;
28import android.net.Uri;
29import android.os.AsyncTask;
30import android.provider.MediaStore;
31import android.util.Log;
32import android.view.Gravity;
33import android.view.View;
34import android.view.ViewGroup;
35import android.widget.FrameLayout;
36import android.widget.ImageView;
37
Angus Kongbd260692013-08-07 14:52:56 -070038import com.android.camera.ui.FilmStripView;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070039import com.android.camera.util.CameraUtil;
Angus Kongbd260692013-08-07 14:52:56 -070040import com.android.camera.util.PhotoSphereHelper;
41import com.android.camera2.R;
42
43import java.io.File;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070044import java.text.DateFormat;
Angus Kongbd260692013-08-07 14:52:56 -070045import java.util.Date;
46
47/**
48 * A base class for all the local media files. The bitmap is loaded in
49 * background thread. Subclasses should implement their own background
50 * loading thread by sub-classing BitmapLoadTask and overriding
51 * doInBackground() to return a bitmap.
52 */
53public abstract class LocalMediaData implements LocalData {
54 protected long id;
55 protected String title;
56 protected String mimeType;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070057 protected long dateTakenInSeconds;
58 protected long dateModifiedInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -070059 protected String path;
60 // width and height should be adjusted according to orientation.
61 protected int width;
62 protected int height;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070063 protected long sizeInBytes;
Sascha Haeberlingfae11a12013-08-15 14:29:49 -070064 protected double latitude;
65 protected double longitude;
Angus Kongbd260692013-08-07 14:52:56 -070066
67 /** The panorama metadata information of this media data. */
Angus Kongc27d21b2013-08-12 15:03:45 -070068 protected PhotoSphereHelper.PanoramaMetadata mPanoramaMetadata;
Angus Kongbd260692013-08-07 14:52:56 -070069
70 /** Used to load photo sphere metadata from image files. */
Angus Kongc27d21b2013-08-12 15:03:45 -070071 protected PanoramaMetadataLoader mPanoramaMetadataLoader = null;
Angus Kongbd260692013-08-07 14:52:56 -070072
Angus Kongc27d21b2013-08-12 15:03:45 -070073 /**
74 * Used for thumbnail loading optimization. True if this data
75 * has a corresponding visible view.
76 */
Angus Kongbd260692013-08-07 14:52:56 -070077 protected Boolean mUsing = false;
78
79 @Override
80 public long getDateTaken() {
Sascha Haeberling6f64b502013-08-14 16:23:18 -070081 return dateTakenInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -070082 }
83
84 @Override
85 public long getDateModified() {
Sascha Haeberling6f64b502013-08-14 16:23:18 -070086 return dateModifiedInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -070087 }
88
89 @Override
90 public String getTitle() {
91 return new String(title);
92 }
93
94 @Override
95 public int getWidth() {
96 return width;
97 }
98
99 @Override
100 public int getHeight() {
101 return height;
102 }
103
104 @Override
105 public String getPath() {
106 return path;
107 }
108
109 @Override
110 public boolean isUIActionSupported(int action) {
111 return false;
112 }
113
114 @Override
115 public boolean isDataActionSupported(int action) {
116 return false;
117 }
118
119 @Override
120 public boolean delete(Context ctx) {
121 File f = new File(path);
122 return f.delete();
123 }
124
125 @Override
126 public void viewPhotoSphere(PhotoSphereHelper.PanoramaViewHelper helper) {
127 helper.showPanorama(getContentUri());
128 }
129
130 @Override
131 public void isPhotoSphere(Context context, final PanoramaSupportCallback callback) {
132 // If we already have metadata, use it.
133 if (mPanoramaMetadata != null) {
134 callback.panoramaInfoAvailable(mPanoramaMetadata.mUsePanoramaViewer,
135 mPanoramaMetadata.mIsPanorama360);
136 }
137
138 // Otherwise prepare a loader, if we don't have one already.
139 if (mPanoramaMetadataLoader == null) {
140 mPanoramaMetadataLoader = new PanoramaMetadataLoader(getContentUri());
141 }
142
143 // Load the metadata asynchronously.
144 mPanoramaMetadataLoader.getPanoramaMetadata(context, new PanoramaMetadataLoader.PanoramaMetadataCallback() {
145 @Override
146 public void onPanoramaMetadataLoaded(PhotoSphereHelper.PanoramaMetadata metadata) {
147 // Store the metadata and remove the loader to free up space.
148 mPanoramaMetadata = metadata;
149 mPanoramaMetadataLoader = null;
150 callback.panoramaInfoAvailable(metadata.mUsePanoramaViewer,
151 metadata.mIsPanorama360);
152 }
153 });
154 }
155
156 @Override
157 public void onFullScreen(boolean fullScreen) {
158 // do nothing.
159 }
160
161 @Override
162 public boolean canSwipeInFullScreen() {
163 return true;
164 }
165
166 protected ImageView fillImageView(Context ctx, ImageView v,
167 int decodeWidth, int decodeHeight, Drawable placeHolder) {
168 v.setScaleType(ImageView.ScaleType.FIT_XY);
169 v.setImageDrawable(placeHolder);
170
171 BitmapLoadTask task = getBitmapLoadTask(v, decodeWidth, decodeHeight);
172 task.execute();
173 return v;
174 }
175
176 @Override
177 public View getView(Context ctx,
178 int decodeWidth, int decodeHeight, Drawable placeHolder) {
179 return fillImageView(ctx, new ImageView(ctx),
180 decodeWidth, decodeHeight, placeHolder);
181 }
182
183 @Override
184 public void prepare() {
185 synchronized (mUsing) {
186 mUsing = true;
187 }
188 }
189
190 @Override
191 public void recycle() {
192 synchronized (mUsing) {
193 mUsing = false;
194 }
195 }
196
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700197 @Override
198 public double[] getLatLong() {
199 if (latitude == 0 && longitude == 0) {
200 return null;
201 }
202 return new double[] {
203 latitude, longitude
204 };
205 }
206
Angus Kongbd260692013-08-07 14:52:56 -0700207 protected boolean isUsing() {
208 synchronized (mUsing) {
209 return mUsing;
210 }
211 }
212
213 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700214 public abstract int getViewType();
Angus Kongbd260692013-08-07 14:52:56 -0700215
216 protected abstract BitmapLoadTask getBitmapLoadTask(
217 ImageView v, int decodeWidth, int decodeHeight);
218
219 public static class PhotoData extends LocalMediaData {
220 private static final String TAG = "CAM_PhotoData";
221
222 public static final int COL_ID = 0;
223 public static final int COL_TITLE = 1;
224 public static final int COL_MIME_TYPE = 2;
225 public static final int COL_DATE_TAKEN = 3;
226 public static final int COL_DATE_MODIFIED = 4;
227 public static final int COL_DATA = 5;
228 public static final int COL_ORIENTATION = 6;
229 public static final int COL_WIDTH = 7;
230 public static final int COL_HEIGHT = 8;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700231 public static final int COL_SIZE = 9;
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700232 public static final int COL_LATITUDE = 10;
233 public static final int COL_LONGITUDE = 11;
Angus Kongbd260692013-08-07 14:52:56 -0700234
235 static final Uri CONTENT_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
236
237 static final String QUERY_ORDER = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC, "
238 + MediaStore.Images.ImageColumns._ID + " DESC";
239 /**
240 * These values should be kept in sync with column IDs (COL_*) above.
241 */
242 static final String[] QUERY_PROJECTION = {
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700243 MediaStore.Images.ImageColumns._ID, // 0, int
244 MediaStore.Images.ImageColumns.TITLE, // 1, string
245 MediaStore.Images.ImageColumns.MIME_TYPE, // 2, string
246 MediaStore.Images.ImageColumns.DATE_TAKEN, // 3, int
247 MediaStore.Images.ImageColumns.DATE_MODIFIED, // 4, int
248 MediaStore.Images.ImageColumns.DATA, // 5, string
249 MediaStore.Images.ImageColumns.ORIENTATION, // 6, int, 0, 90, 180, 270
250 MediaStore.Images.ImageColumns.WIDTH, // 7, int
251 MediaStore.Images.ImageColumns.HEIGHT, // 8, int
252 MediaStore.Images.ImageColumns.SIZE, // 9, long
253 MediaStore.Images.ImageColumns.LATITUDE, // 10, double
254 MediaStore.Images.ImageColumns.LONGITUDE // 11, double
Angus Kongbd260692013-08-07 14:52:56 -0700255 };
256
257 private static final int mSupportedUIActions =
258 FilmStripView.ImageData.ACTION_DEMOTE
259 | FilmStripView.ImageData.ACTION_PROMOTE;
260 private static final int mSupportedDataActions =
261 LocalData.ACTION_DELETE;
262
263 /** 32K buffer. */
264 private static final byte[] DECODE_TEMP_STORAGE = new byte[32 * 1024];
265
266 /** from MediaStore, can only be 0, 90, 180, 270 */
267 public int orientation;
268
269 static PhotoData buildFromCursor(Cursor c) {
270 PhotoData d = new PhotoData();
271 d.id = c.getLong(COL_ID);
272 d.title = c.getString(COL_TITLE);
273 d.mimeType = c.getString(COL_MIME_TYPE);
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700274 d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
275 d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
Angus Kongbd260692013-08-07 14:52:56 -0700276 d.path = c.getString(COL_DATA);
277 d.orientation = c.getInt(COL_ORIENTATION);
278 d.width = c.getInt(COL_WIDTH);
279 d.height = c.getInt(COL_HEIGHT);
280 if (d.width <= 0 || d.height <= 0) {
281 Log.w(TAG, "Warning! zero dimension for "
282 + d.path + ":" + d.width + "x" + d.height);
283 BitmapFactory.Options opts = new BitmapFactory.Options();
284 opts.inJustDecodeBounds = true;
285 BitmapFactory.decodeFile(d.path, opts);
286 if (opts.outWidth != -1 && opts.outHeight != -1) {
287 d.width = opts.outWidth;
288 d.height = opts.outHeight;
289 } else {
290 Log.w(TAG, "Warning! dimension decode failed for " + d.path);
291 Bitmap b = BitmapFactory.decodeFile(d.path);
292 if (b == null) {
293 return null;
294 }
295 d.width = b.getWidth();
296 d.height = b.getHeight();
297 }
298 }
299 if (d.orientation == 90 || d.orientation == 270) {
300 int b = d.width;
301 d.width = d.height;
302 d.height = b;
303 }
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700304 d.sizeInBytes = c.getLong(COL_SIZE);
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700305 d.latitude = c.getDouble(COL_LATITUDE);
306 d.longitude = c.getDouble(COL_LONGITUDE);
Angus Kongbd260692013-08-07 14:52:56 -0700307 return d;
308 }
309
310 @Override
311 public String toString() {
312 return "Photo:" + ",data=" + path + ",mimeType=" + mimeType
313 + "," + width + "x" + height + ",orientation=" + orientation
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700314 + ",date=" + new Date(dateTakenInSeconds);
Angus Kongbd260692013-08-07 14:52:56 -0700315 }
316
317 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700318 public int getViewType() {
319 return TYPE_REMOVABLE_VIEW;
Angus Kongbd260692013-08-07 14:52:56 -0700320 }
321
322 @Override
323 public boolean isUIActionSupported(int action) {
324 return ((action & mSupportedUIActions) == action);
325 }
326
327 @Override
328 public boolean isDataActionSupported(int action) {
329 return ((action & mSupportedDataActions) == action);
330 }
331
332 @Override
333 public boolean delete(Context c) {
334 ContentResolver cr = c.getContentResolver();
335 cr.delete(CONTENT_URI, MediaStore.Images.ImageColumns._ID + "=" + id, null);
336 return super.delete(c);
337 }
338
339 @Override
340 public Uri getContentUri() {
341 Uri baseUri = CONTENT_URI;
342 return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
343 }
344
345 @Override
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700346 public MediaDetails getMediaDetails(Context context) {
347 DateFormat formater = DateFormat.getDateTimeInstance();
348 MediaDetails mediaDetails = new MediaDetails();
349 mediaDetails.addDetail(MediaDetails.INDEX_TITLE, title);
350 mediaDetails.addDetail(MediaDetails.INDEX_WIDTH, width);
351 mediaDetails.addDetail(MediaDetails.INDEX_HEIGHT, height);
352 mediaDetails.addDetail(MediaDetails.INDEX_PATH, path);
353 mediaDetails.addDetail(MediaDetails.INDEX_DATETIME,
354 formater.format(new Date(dateModifiedInSeconds * 1000)));
355 if (sizeInBytes > 0)
356 mediaDetails.addDetail(MediaDetails.INDEX_SIZE, sizeInBytes);
357
358 MediaDetails.extractExifInfo(mediaDetails, path);
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700359
360 if (latitude != 0 && longitude != 0) {
361 mediaDetails.addDetail(MediaDetails.INDEX_LOCATION, latitude + ", " + longitude);
362 }
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700363 return mediaDetails;
364 }
365
366 @Override
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700367 public int getLocalDataType() {
Angus Kongc27d21b2013-08-12 15:03:45 -0700368 if (mPanoramaMetadata != null && mPanoramaMetadata.mUsePanoramaViewer) {
369 return LOCAL_PHOTO_SPHERE;
370 }
371 return LOCAL_IMAGE;
372 }
373
374 @Override
Angus Kongbd260692013-08-07 14:52:56 -0700375 public boolean refresh(ContentResolver resolver) {
376 Cursor c = resolver.query(
377 getContentUri(), QUERY_PROJECTION, null, null, null);
378 if (c == null || !c.moveToFirst()) {
379 return false;
380 }
381 PhotoData newData = buildFromCursor(c);
382 id = newData.id;
383 title = newData.title;
384 mimeType = newData.mimeType;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700385 dateTakenInSeconds = newData.dateTakenInSeconds;
386 dateModifiedInSeconds = newData.dateModifiedInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -0700387 path = newData.path;
388 orientation = newData.orientation;
389 width = newData.width;
390 height = newData.height;
391 return true;
392 }
393
394 @Override
395 protected BitmapLoadTask getBitmapLoadTask(
396 ImageView v, int decodeWidth, int decodeHeight) {
397 return new PhotoBitmapLoadTask(v, decodeWidth, decodeHeight);
398 }
399
400 private final class PhotoBitmapLoadTask extends BitmapLoadTask {
401 private int mDecodeWidth;
402 private int mDecodeHeight;
403
404 public PhotoBitmapLoadTask(ImageView v, int decodeWidth, int decodeHeight) {
405 super(v);
406 mDecodeWidth = decodeWidth;
407 mDecodeHeight = decodeHeight;
408 }
409
410 @Override
411 protected Bitmap doInBackground(Void... v) {
412 BitmapFactory.Options opts = null;
413 Bitmap b;
414 int sample = 1;
415 while (mDecodeWidth * sample < width
416 || mDecodeHeight * sample < height) {
417 sample *= 2;
418 }
419 opts = new BitmapFactory.Options();
420 opts.inSampleSize = sample;
421 opts.inTempStorage = DECODE_TEMP_STORAGE;
422 if (isCancelled() || !isUsing()) {
423 return null;
424 }
425 b = BitmapFactory.decodeFile(path, opts);
426 if (orientation != 0) {
427 if (isCancelled() || !isUsing()) {
428 return null;
429 }
430 Matrix m = new Matrix();
431 m.setRotate(orientation);
432 b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, false);
433 }
434 return b;
435 }
436 }
437 }
438
439 public static class VideoData extends LocalMediaData {
440 public static final int COL_ID = 0;
441 public static final int COL_TITLE = 1;
442 public static final int COL_MIME_TYPE = 2;
443 public static final int COL_DATE_TAKEN = 3;
444 public static final int COL_DATE_MODIFIED = 4;
445 public static final int COL_DATA = 5;
446 public static final int COL_WIDTH = 6;
447 public static final int COL_HEIGHT = 7;
448 public static final int COL_RESOLUTION = 8;
449
450 static final Uri CONTENT_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
451
452 private static final int mSupportedUIActions =
453 FilmStripView.ImageData.ACTION_DEMOTE
454 | FilmStripView.ImageData.ACTION_PROMOTE;
455 private static final int mSupportedDataActions =
456 LocalData.ACTION_DELETE
457 | LocalData.ACTION_PLAY;
458
459 static final String QUERY_ORDER = MediaStore.Video.VideoColumns.DATE_TAKEN + " DESC, "
460 + MediaStore.Video.VideoColumns._ID + " DESC";
461 /**
462 * These values should be kept in sync with column IDs (COL_*) above.
463 */
464 static final String[] QUERY_PROJECTION = {
465 MediaStore.Video.VideoColumns._ID, // 0, int
466 MediaStore.Video.VideoColumns.TITLE, // 1, string
467 MediaStore.Video.VideoColumns.MIME_TYPE, // 2, string
468 MediaStore.Video.VideoColumns.DATE_TAKEN, // 3, int
469 MediaStore.Video.VideoColumns.DATE_MODIFIED, // 4, int
470 MediaStore.Video.VideoColumns.DATA, // 5, string
471 MediaStore.Video.VideoColumns.WIDTH, // 6, int
472 MediaStore.Video.VideoColumns.HEIGHT, // 7, int
473 MediaStore.Video.VideoColumns.RESOLUTION // 8, string
474 };
475
476 private Uri mPlayUri;
477
478 static VideoData buildFromCursor(Cursor c) {
479 VideoData d = new VideoData();
480 d.id = c.getLong(COL_ID);
481 d.title = c.getString(COL_TITLE);
482 d.mimeType = c.getString(COL_MIME_TYPE);
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700483 d.dateTakenInSeconds = c.getLong(COL_DATE_TAKEN);
484 d.dateModifiedInSeconds = c.getLong(COL_DATE_MODIFIED);
Angus Kongbd260692013-08-07 14:52:56 -0700485 d.path = c.getString(COL_DATA);
486 d.width = c.getInt(COL_WIDTH);
487 d.height = c.getInt(COL_HEIGHT);
488 d.mPlayUri = d.getContentUri();
489 MediaMetadataRetriever retriever = new MediaMetadataRetriever();
Angus Kong0989e392013-08-14 10:52:40 -0700490 String rotation = null;
491 try {
492 retriever.setDataSource(d.path);
493 } catch (IllegalArgumentException ex) {
494 retriever.release();
495 Log.e(TAG, "MediaMetadataRetriever.setDataSource() fail:"
496 + ex.getMessage());
497 return null;
498 }
499 rotation = retriever.extractMetadata(
Angus Kongbd260692013-08-07 14:52:56 -0700500 MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
501 if (d.width == 0 || d.height == 0) {
Angus Kong0989e392013-08-14 10:52:40 -0700502 retrieveVideoDimension(retriever, d);
Angus Kongbd260692013-08-07 14:52:56 -0700503 }
504 retriever.release();
Angus Kong0989e392013-08-14 10:52:40 -0700505 if (d.width == 0 || d.height == 0) {
506 // Width or height is still not available.
507 Log.e(TAG, "Unable to retrieve dimension of video:" + d.path);
508 return null;
509 }
Angus Kongbd260692013-08-07 14:52:56 -0700510 if (rotation != null
511 && (rotation.equals("90") || rotation.equals("270"))) {
512 int b = d.width;
513 d.width = d.height;
514 d.height = b;
515 }
516 return d;
517 }
518
519 @Override
520 public String toString() {
521 return "Video:" + ",data=" + path + ",mimeType=" + mimeType
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700522 + "," + width + "x" + height + ",date=" + new Date(dateTakenInSeconds);
Angus Kongbd260692013-08-07 14:52:56 -0700523 }
524
525 @Override
Angus Kongc27d21b2013-08-12 15:03:45 -0700526 public int getViewType() {
527 return TYPE_REMOVABLE_VIEW;
Angus Kongbd260692013-08-07 14:52:56 -0700528 }
529
530 @Override
531 public boolean isUIActionSupported(int action) {
532 return ((action & mSupportedUIActions) == action);
533 }
534
535 @Override
536 public boolean isDataActionSupported(int action) {
537 return ((action & mSupportedDataActions) == action);
538 }
539
540 @Override
541 public boolean delete(Context ctx) {
542 ContentResolver cr = ctx.getContentResolver();
543 cr.delete(CONTENT_URI, MediaStore.Video.VideoColumns._ID + "=" + id, null);
544 return super.delete(ctx);
545 }
546
547 @Override
548 public Uri getContentUri() {
549 Uri baseUri = CONTENT_URI;
550 return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
551 }
552
553 @Override
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700554 public MediaDetails getMediaDetails(Context context) {
555 // TODO: Return valid MediaDetails for videos.
556 return new MediaDetails();
557 }
558
559 @Override
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700560 public int getLocalDataType() {
Angus Kongc27d21b2013-08-12 15:03:45 -0700561 return LOCAL_VIDEO;
562 }
563
564 @Override
Angus Kongbd260692013-08-07 14:52:56 -0700565 public boolean refresh(ContentResolver resolver) {
566 Cursor c = resolver.query(
567 getContentUri(), QUERY_PROJECTION, null, null, null);
568 if (c == null && !c.moveToFirst()) {
569 return false;
570 }
571 VideoData newData = buildFromCursor(c);
Angus Kong0989e392013-08-14 10:52:40 -0700572 if (newData == null) {
573 return false;
574 }
Angus Kongbd260692013-08-07 14:52:56 -0700575 id = newData.id;
576 title = newData.title;
577 mimeType = newData.mimeType;
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700578 dateTakenInSeconds = newData.dateTakenInSeconds;
579 dateModifiedInSeconds = newData.dateModifiedInSeconds;
Angus Kongbd260692013-08-07 14:52:56 -0700580 path = newData.path;
581 width = newData.width;
582 height = newData.height;
583 mPlayUri = newData.mPlayUri;
584 return true;
585 }
586
587 @Override
588 public View getView(final Context ctx,
589 int decodeWidth, int decodeHeight, Drawable placeHolder) {
590
591 // ImageView for the bitmap.
592 ImageView iv = new ImageView(ctx);
593 iv.setLayoutParams(new FrameLayout.LayoutParams(
594 ViewGroup.LayoutParams.MATCH_PARENT,
595 ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
596 fillImageView(ctx, iv, decodeWidth, decodeHeight, placeHolder);
597
598 // ImageView for the play icon.
599 ImageView icon = new ImageView(ctx);
600 icon.setImageResource(R.drawable.ic_control_play);
601 icon.setScaleType(ImageView.ScaleType.CENTER);
602 icon.setLayoutParams(new FrameLayout.LayoutParams(
603 ViewGroup.LayoutParams.WRAP_CONTENT,
604 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
605 icon.setOnClickListener(new View.OnClickListener() {
606 @Override
607 public void onClick(View v) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700608 CameraUtil.playVideo(ctx, mPlayUri, title);
Angus Kongbd260692013-08-07 14:52:56 -0700609 }
610 });
611
612 FrameLayout f = new FrameLayout(ctx);
613 f.addView(iv);
614 f.addView(icon);
615 return f;
616 }
617
618 @Override
619 protected BitmapLoadTask getBitmapLoadTask(
620 ImageView v, int decodeWidth, int decodeHeight) {
621 return new VideoBitmapLoadTask(v);
622 }
623
624 private final class VideoBitmapLoadTask extends BitmapLoadTask {
625
626 public VideoBitmapLoadTask(ImageView v) {
627 super(v);
628 }
629
630 @Override
631 protected Bitmap doInBackground(Void... v) {
632 if (isCancelled() || !isUsing()) {
633 return null;
634 }
635 MediaMetadataRetriever retriever = new MediaMetadataRetriever();
636 retriever.setDataSource(path);
637 byte[] data = retriever.getEmbeddedPicture();
638 Bitmap bitmap = null;
639 if (isCancelled() || !isUsing()) {
640 retriever.release();
641 return null;
642 }
643 if (data != null) {
644 bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
645 }
646 if (bitmap == null) {
647 bitmap = retriever.getFrameAtTime();
648 }
649 retriever.release();
650 return bitmap;
651 }
652 }
Angus Kong0989e392013-08-14 10:52:40 -0700653
654 /**
655 * Extracts video height/width if available. If
656 * unavailable, set to 0.
657 * @param retriever An initialized metadata retriever.
658 * @param d The {@link VideoData} whose width/height are to update.
659 */
660 private static void retrieveVideoDimension(
661 MediaMetadataRetriever retriever, VideoData d) {
662 String val = retriever.extractMetadata(
663 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
664 d.width = (val == null) ? 0 : Integer.parseInt(val);
665 val = retriever.extractMetadata(
666 MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
667 d.height = (val == null) ? 0 : Integer.parseInt(val);
668 }
Angus Kongbd260692013-08-07 14:52:56 -0700669 }
670
671 /**
672 * An {@link AsyncTask} class that loads the bitmap in the background thread.
673 * Sub-classes should implement their own
674 * {@code BitmapLoadTask#doInBackground(Void...)}."
675 */
676 protected abstract class BitmapLoadTask extends AsyncTask<Void, Void, Bitmap> {
677 protected ImageView mView;
678
679 protected BitmapLoadTask(ImageView v) {
680 mView = v;
681 }
682
683 @Override
684 protected void onPostExecute(Bitmap bitmap) {
685 if (!isUsing()) return;
686 if (bitmap == null) {
687 Log.e(TAG, "Failed decoding bitmap for file:" + path);
688 return;
689 }
690 BitmapDrawable d = new BitmapDrawable(bitmap);
691 mView.setScaleType(ImageView.ScaleType.FIT_XY);
692 mView.setImageDrawable(d);
693 }
694 }
695}