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
17package com.android.gallery3d.data;
18
19import android.content.ContentResolver;
20import android.database.Cursor;
21import android.graphics.Bitmap;
22import android.graphics.BitmapRegionDecoder;
23import android.net.Uri;
24import android.provider.MediaStore.Video;
25import android.provider.MediaStore.Video.VideoColumns;
26
27import com.android.gallery3d.app.GalleryApp;
28import com.android.gallery3d.common.BitmapUtils;
29import com.android.gallery3d.util.GalleryUtils;
30import com.android.gallery3d.util.ThreadPool.Job;
31import com.android.gallery3d.util.ThreadPool.JobContext;
32import com.android.gallery3d.util.UpdateHelper;
33
34// LocalVideo represents a video in the local storage.
35public 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
75    public LocalVideo(Path path, GalleryApp application, Cursor cursor) {
76        super(path, nextVersionNumber());
77        mApplication = application;
78        loadFromCursor(cursor);
79    }
80
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
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
117    private void parseResolution(String resolution) {
118        if (resolution == null) return;
119        int m = resolution.indexOf('x');
120        if (m == -1) return;
121        try {
122            int w = Integer.parseInt(resolution.substring(0, m));
123            int h = Integer.parseInt(resolution.substring(m + 1));
124            width = w;
125            height = h;
126        } catch (Throwable t) {
127            Log.w(TAG, t);
128        }
129    }
130
131    @Override
132    protected boolean updateFromCursor(Cursor cursor) {
133        UpdateHelper uh = new UpdateHelper();
134        id = uh.update(id, cursor.getInt(INDEX_ID));
135        caption = uh.update(caption, cursor.getString(INDEX_CAPTION));
136        mimeType = uh.update(mimeType, cursor.getString(INDEX_MIME_TYPE));
137        latitude = uh.update(latitude, cursor.getDouble(INDEX_LATITUDE));
138        longitude = uh.update(longitude, cursor.getDouble(INDEX_LONGITUDE));
139        dateTakenInMs = uh.update(
140                dateTakenInMs, cursor.getLong(INDEX_DATE_TAKEN));
141        dateAddedInSec = uh.update(
142                dateAddedInSec, cursor.getLong(INDEX_DATE_ADDED));
143        dateModifiedInSec = uh.update(
144                dateModifiedInSec, cursor.getLong(INDEX_DATE_MODIFIED));
145        filePath = uh.update(filePath, cursor.getString(INDEX_DATA));
146        durationInSec = uh.update(
147                durationInSec, cursor.getInt(INDEX_DURATION) / 1000);
148        bucketId = uh.update(bucketId, cursor.getInt(INDEX_BUCKET_ID));
149        fileSize = uh.update(fileSize, cursor.getLong(INDEX_SIZE));
150        return uh.isUpdated();
151    }
152
153    @Override
154    public Job<Bitmap> requestImage(int type) {
155        return new LocalVideoRequest(mApplication, getPath(), dateModifiedInSec,
156                type, filePath);
157    }
158
159    public static class LocalVideoRequest extends ImageCacheRequest {
160        private String mLocalFilePath;
161
162        LocalVideoRequest(GalleryApp application, Path path, long timeModified,
163                int type, String localFilePath) {
164            super(application, path, timeModified, type,
165                    MediaItem.getTargetSize(type));
166            mLocalFilePath = localFilePath;
167        }
168
169        @Override
170        public Bitmap onDecodeOriginal(JobContext jc, int type) {
171            Bitmap bitmap = BitmapUtils.createVideoThumbnail(mLocalFilePath);
172            if (bitmap == null || jc.isCancelled()) return null;
173            return bitmap;
174        }
175    }
176
177    @Override
178    public Job<BitmapRegionDecoder> requestLargeImage() {
179        throw new UnsupportedOperationException("Cannot regquest a large image"
180                + " to a local video!");
181    }
182
183    @Override
184    public int getSupportedOperations() {
185        return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM | SUPPORT_MUTE;
186    }
187
188    @Override
189    public void delete() {
190        GalleryUtils.assertNotInRenderThread();
191        Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI;
192        mApplication.getContentResolver().delete(baseUri, "_id=?",
193                new String[]{String.valueOf(id)});
194    }
195
196    @Override
197    public void rotate(int degrees) {
198        // TODO
199    }
200
201    @Override
202    public Uri getContentUri() {
203        Uri baseUri = Video.Media.EXTERNAL_CONTENT_URI;
204        return baseUri.buildUpon().appendPath(String.valueOf(id)).build();
205    }
206
207    @Override
208    public Uri getPlayUri() {
209        return getContentUri();
210    }
211
212    @Override
213    public int getMediaType() {
214        return MEDIA_TYPE_VIDEO;
215    }
216
217    @Override
218    public MediaDetails getDetails() {
219        MediaDetails details = super.getDetails();
220        int s = durationInSec;
221        if (s > 0) {
222            details.addDetail(MediaDetails.INDEX_DURATION, GalleryUtils.formatDuration(
223                    mApplication.getAndroidContext(), durationInSec));
224        }
225        return details;
226    }
227
228    @Override
229    public int getWidth() {
230        return width;
231    }
232
233    @Override
234    public int getHeight() {
235        return height;
236    }
237
238    @Override
239    public String getFilePath() {
240        return filePath;
241    }
242}
243