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