1 /* 2 * Copyright (C) 2010 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 17 package com.android.gallery3d.data; 18 19 import android.content.ContentResolver; 20 import android.database.Cursor; 21 import android.graphics.Bitmap; 22 import android.graphics.BitmapRegionDecoder; 23 import android.net.Uri; 24 import android.provider.MediaStore.Video; 25 import android.provider.MediaStore.Video.VideoColumns; 26 27 import com.android.gallery3d.app.GalleryApp; 28 import com.android.gallery3d.common.BitmapUtils; 29 import com.android.gallery3d.util.GalleryUtils; 30 import com.android.gallery3d.util.ThreadPool.Job; 31 import com.android.gallery3d.util.ThreadPool.JobContext; 32 import com.android.gallery3d.util.UpdateHelper; 33 34 // LocalVideo represents a video in the local storage. 35 public class LocalVideo extends LocalMediaItem { 36 private static final String TAG = "LocalVideo"; 37 static final Path ITEM_PATH = Path.fromString("/local/video/item"); 38 39 // Must preserve order between these indices and the order of the terms in 40 // the following PROJECTION array. 41 private static final int INDEX_ID = 0; 42 private static final int INDEX_CAPTION = 1; 43 private static final int INDEX_MIME_TYPE = 2; 44 private static final int INDEX_LATITUDE = 3; 45 private static final int INDEX_LONGITUDE = 4; 46 private static final int INDEX_DATE_TAKEN = 5; 47 private static final int INDEX_DATE_ADDED = 6; 48 private static final int INDEX_DATE_MODIFIED = 7; 49 private static final int INDEX_DATA = 8; 50 private static final int INDEX_DURATION = 9; 51 private static final int INDEX_BUCKET_ID = 10; 52 private static final int INDEX_SIZE = 11; 53 private static final int INDEX_RESOLUTION = 12; 54 55 static final String[] PROJECTION = new String[] { 56 VideoColumns._ID, 57 VideoColumns.TITLE, 58 VideoColumns.MIME_TYPE, 59 VideoColumns.LATITUDE, 60 VideoColumns.LONGITUDE, 61 VideoColumns.DATE_TAKEN, 62 VideoColumns.DATE_ADDED, 63 VideoColumns.DATE_MODIFIED, 64 VideoColumns.DATA, 65 VideoColumns.DURATION, 66 VideoColumns.BUCKET_ID, 67 VideoColumns.SIZE, 68 VideoColumns.RESOLUTION, 69 }; 70 71 private final GalleryApp mApplication; 72 73 public int durationInSec; 74 LocalVideo(Path path, GalleryApp application, Cursor cursor)75 public LocalVideo(Path path, GalleryApp application, Cursor cursor) { 76 super(path, nextVersionNumber()); 77 mApplication = application; 78 loadFromCursor(cursor); 79 } 80 LocalVideo(Path path, GalleryApp context, int id)81 public LocalVideo(Path path, GalleryApp context, int id) { 82 super(path, nextVersionNumber()); 83 mApplication = context; 84 ContentResolver resolver = mApplication.getContentResolver(); 85 Uri uri = Video.Media.EXTERNAL_CONTENT_URI; 86 Cursor cursor = LocalAlbum.getItemCursor(resolver, uri, PROJECTION, id); 87 if (cursor == null) { 88 throw new RuntimeException("cannot get cursor for: " + path); 89 } 90 try { 91 if (cursor.moveToNext()) { 92 loadFromCursor(cursor); 93 } else { 94 throw new RuntimeException("cannot find data for: " + path); 95 } 96 } finally { 97 cursor.close(); 98 } 99 } 100 loadFromCursor(Cursor cursor)101 private void loadFromCursor(Cursor cursor) { 102 id = cursor.getInt(INDEX_ID); 103 caption = cursor.getString(INDEX_CAPTION); 104 mimeType = cursor.getString(INDEX_MIME_TYPE); 105 latitude = cursor.getDouble(INDEX_LATITUDE); 106 longitude = cursor.getDouble(INDEX_LONGITUDE); 107 dateTakenInMs = cursor.getLong(INDEX_DATE_TAKEN); 108 dateAddedInSec = cursor.getLong(INDEX_DATE_ADDED); 109 dateModifiedInSec = cursor.getLong(INDEX_DATE_MODIFIED); 110 filePath = cursor.getString(INDEX_DATA); 111 durationInSec = cursor.getInt(INDEX_DURATION) / 1000; 112 bucketId = cursor.getInt(INDEX_BUCKET_ID); 113 fileSize = cursor.getLong(INDEX_SIZE); 114 parseResolution(cursor.getString(INDEX_RESOLUTION)); 115 } 116 parseResolution(String resolution)117 private void parseResolution(String resolution) { 118 if (resolution == null) return; 119 int m = resolution.indexOf('x'); 120 if (m == -1) { 121 // Fix b/216176283 - Handle special character '×' in the resolution. 122 m = resolution.indexOf('×'); 123 if (m == -1) return; 124 } 125 try { 126 int w = Integer.parseInt(resolution.substring(0, m)); 127 int h = Integer.parseInt(resolution.substring(m + 1)); 128 width = w; 129 height = h; 130 } catch (Throwable t) { 131 Log.w(TAG, t); 132 } 133 } 134 135 @Override updateFromCursor(Cursor cursor)136 protected boolean updateFromCursor(Cursor cursor) { 137 UpdateHelper uh = new UpdateHelper(); 138 id = uh.update(id, cursor.getInt(INDEX_ID)); 139 caption = uh.update(caption, cursor.getString(INDEX_CAPTION)); 140 mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE)); 141 latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE)); 142 longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE)); 143 dateTakenInMs = uh.update( 144 dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN)); 145 dateAddedInSec = uh.update( 146 dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED)); 147 dateModifiedInSec = uh.update( 148 dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED)); 149 filePath = uh.update(filePath, cursor.getString(INDEX_DATA)); 150 durationInSec = uh.update( 151 durationInSec, cursor.getInt(INDEX_DURATION) / 1000); 152 bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID)); 153 fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE)); 154 return uh.isUpdated(); 155 } 156 157 @Override requestImage(int type)158 public Job<Bitmap> requestImage(int type) { 159 return new LocalVideoRequest(mApplication, getPath(), dateModifiedInSec, 160 type, filePath); 161 } 162 163 public static class LocalVideoRequest extends ImageCacheRequest { 164 private String mLocalFilePath; 165 LocalVideoRequest(GalleryApp application, Path path, long timeModified, int type, String localFilePath)166 LocalVideoRequest(GalleryApp application, Path path, long timeModified, 167 int type, String localFilePath) { 168 super(application, path, timeModified, type, 169 MediaItem.getTargetSize(type)); 170 mLocalFilePath = localFilePath; 171 } 172 173 @Override onDecodeOriginal(JobContext jc, int type)174 public Bitmap onDecodeOriginal(JobContext jc, int type) { 175 Bitmap bitmap = BitmapUtils.createVideoThumbnail(mLocalFilePath); 176 if (bitmap == null || jc.isCancelled()) return null; 177 return bitmap; 178 } 179 } 180 181 @Override requestLargeImage()182 public Job<BitmapRegionDecoder> requestLargeImage() { 183 throw new UnsupportedOperationException("Cannot regquest a large image" 184 + " to a local video!"); 185 } 186 187 @Override getSupportedOperations()188 public int getSupportedOperations() { 189 return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM | SUPPORT_MUTE; 190 } 191 192 @Override delete()193 public void delete() { 194 GalleryUtils.assertNotInRenderThread(); 195 Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI; 196 mApplication.getContentResolver().delete(baseUri, "_id=?", 197 new String[]{String.valueOf(id)}); 198 } 199 200 @Override rotate(int degrees)201 public void rotate(int degrees) { 202 // TODO 203 } 204 205 @Override getContentUri()206 public Uri getContentUri() { 207 Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI; 208 return baseUri.buildUpon().appendPath(String.valueOf(id)).build(); 209 } 210 211 @Override getPlayUri()212 public Uri getPlayUri() { 213 return getContentUri(); 214 } 215 216 @Override getMediaType()217 public int getMediaType() { 218 return MEDIA_TYPE_VIDEO; 219 } 220 221 @Override getDetails()222 public MediaDetails getDetails() { 223 MediaDetails details = super.getDetails(); 224 int s = durationInSec; 225 if (s > 0) { 226 details.addDetail(MediaDetails.INDEX_DURATION, GalleryUtils.formatDuration( 227 mApplication.getAndroidContext(), durationInSec)); 228 } 229 return details; 230 } 231 232 @Override getWidth()233 public int getWidth() { 234 return width; 235 } 236 237 @Override getHeight()238 public int getHeight() { 239 return height; 240 } 241 242 @Override getFilePath()243 public String getFilePath() { 244 return filePath; 245 } 246 } 247