VideoModel.java revision d64419030e1fec1e751695dab3bd7236e2fb0214
1/*
2 * Copyright (C) 2008 Esmertec AG.
3 * Copyright (C) 2008 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mms.model;
19
20import org.w3c.dom.events.Event;
21import org.w3c.dom.smil.ElementTime;
22
23import android.content.ContentResolver;
24import android.content.Context;
25import android.database.Cursor;
26import android.database.sqlite.SqliteWrapper;
27import android.net.Uri;
28import android.provider.MediaStore.Images;
29import android.text.TextUtils;
30import android.util.Log;
31import android.webkit.MimeTypeMap;
32
33import com.android.mms.ContentRestrictionException;
34import com.android.mms.LogTag;
35import com.android.mms.MmsApp;
36import com.android.mms.dom.events.EventImpl;
37import com.android.mms.dom.smil.SmilMediaElementImpl;
38import com.android.mms.util.ItemLoadedCallback;
39import com.android.mms.util.ItemLoadedFuture;
40import com.android.mms.util.ThumbnailManager;
41import com.google.android.mms.ContentType;
42import com.google.android.mms.MmsException;
43
44public class VideoModel extends RegionMediaModel {
45    private static final String TAG = MediaModel.TAG;
46    private static final boolean DEBUG = true;
47    private static final boolean LOCAL_LOGV = false;
48    private ItemLoadedFuture mItemLoadedFuture;
49
50    public VideoModel(Context context, Uri uri, RegionModel region)
51            throws MmsException {
52        this(context, null, null, uri, region);
53        initModelFromUri(uri);
54        checkContentRestriction();
55    }
56
57    public VideoModel(Context context, String contentType, String src,
58            Uri uri, RegionModel region) throws MmsException {
59        super(context, SmilHelper.ELEMENT_TAG_VIDEO, contentType, src, uri, region);
60    }
61
62    private void initModelFromUri(Uri uri) throws MmsException {
63        String scheme = uri.getScheme();
64        if (scheme.equals("content")) {
65            initFromContentUri(uri);
66        } else if (uri.getScheme().equals("file")) {
67            initFromFile(uri);
68        }
69        initMediaDuration();
70    }
71
72    private void initFromFile(Uri uri) {
73        mSrc = uri.getPath();
74        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
75        String extension = MimeTypeMap.getFileExtensionFromUrl(mSrc);
76        if (TextUtils.isEmpty(extension)) {
77            // getMimeTypeFromExtension() doesn't handle spaces in filenames nor can it handle
78            // urlEncoded strings. Let's try one last time at finding the extension.
79            int dotPos = mSrc.lastIndexOf('.');
80            if (0 <= dotPos) {
81                extension = mSrc.substring(dotPos + 1);
82            }
83        }
84        mContentType = mimeTypeMap.getMimeTypeFromExtension(extension);
85        // It's ok if mContentType is null. Eventually we'll show a toast telling the
86        // user the video couldn't be attached.
87
88        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
89            Log.v(TAG, "New VideoModel initFromFile created:"
90                    + " mSrc=" + mSrc
91                    + " mContentType=" + mContentType
92                    + " mUri=" + uri);
93        }
94    }
95
96    private void initFromContentUri(Uri uri) throws MmsException {
97        ContentResolver cr = mContext.getContentResolver();
98        Cursor c = SqliteWrapper.query(mContext, cr, uri, null, null, null, null);
99
100        if (c != null) {
101            try {
102                if (c.moveToFirst()) {
103                    String path;
104                    try {
105                        // Local videos will have a data column
106                        path = c.getString(c.getColumnIndexOrThrow(Images.Media.DATA));
107                    } catch (IllegalArgumentException e) {
108                        // For non-local videos, the path is the uri
109                        path = uri.toString();
110                    }
111                    mSrc = path.substring(path.lastIndexOf('/') + 1);
112                    mContentType = c.getString(c.getColumnIndexOrThrow(
113                            Images.Media.MIME_TYPE));
114                    if (TextUtils.isEmpty(mContentType)) {
115                        throw new MmsException("Type of media is unknown.");
116                    }
117
118                    if (mContentType.equals(ContentType.VIDEO_MP4) && !(TextUtils.isEmpty(mSrc))) {
119                        int index = mSrc.lastIndexOf(".");
120                        if (index != -1) {
121                            try {
122                                String extension = mSrc.substring(index + 1);
123                                if (!(TextUtils.isEmpty(extension)) &&
124                                        (extension.equalsIgnoreCase("3gp") ||
125                                        extension.equalsIgnoreCase("3gpp") ||
126                                        extension.equalsIgnoreCase("3g2"))) {
127                                    mContentType = ContentType.VIDEO_3GPP;
128                                }
129                            } catch(IndexOutOfBoundsException ex) {
130                                if (LOCAL_LOGV) {
131                                    Log.v(TAG, "Media extension is unknown.");
132                                }
133                            }
134                        }
135                    }
136
137                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
138                        Log.v(TAG, "New VideoModel initFromContentUri created:"
139                                + " mSrc=" + mSrc
140                                + " mContentType=" + mContentType
141                                + " mUri=" + uri);
142                    }
143                } else {
144                    throw new MmsException("Nothing found: " + uri);
145                }
146            } finally {
147                c.close();
148            }
149        } else {
150            throw new MmsException("Bad URI: " + uri);
151        }
152    }
153
154    // EventListener Interface
155    public void handleEvent(Event evt) {
156        String evtType = evt.getType();
157        if (LOCAL_LOGV || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
158            Log.v(TAG, "[VideoModel] handleEvent " + evt.getType() + " on " + this);
159        }
160
161        MediaAction action = MediaAction.NO_ACTIVE_ACTION;
162        if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_START_EVENT)) {
163            action = MediaAction.START;
164
165            // if the Music player app is playing audio, we should pause that so it won't
166            // interfere with us playing video here.
167            pauseMusicPlayer();
168
169            mVisible = true;
170        } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_END_EVENT)) {
171            action = MediaAction.STOP;
172            if (mFill != ElementTime.FILL_FREEZE) {
173                mVisible = false;
174            }
175        } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_PAUSE_EVENT)) {
176            action = MediaAction.PAUSE;
177            mVisible = true;
178        } else if (evtType.equals(SmilMediaElementImpl.SMIL_MEDIA_SEEK_EVENT)) {
179            action = MediaAction.SEEK;
180            mSeekTo = ((EventImpl) evt).getSeekTo();
181            mVisible = true;
182        }
183
184        appendAction(action);
185        notifyModelChanged(false);
186    }
187
188    protected void checkContentRestriction() throws ContentRestrictionException {
189        ContentRestriction cr = ContentRestrictionFactory.getContentRestriction();
190        cr.checkVideoContentType(mContentType);
191    }
192
193    @Override
194    protected boolean isPlayable() {
195        return true;
196    }
197
198    public ItemLoadedFuture loadThumbnailBitmap(ItemLoadedCallback callback) {
199        ThumbnailManager thumbnailManager = MmsApp.getApplication().getThumbnailManager();
200        mItemLoadedFuture = thumbnailManager.getVideoThumbnail(getUri(), callback);
201        return mItemLoadedFuture;
202    }
203
204    public void cancelThumbnailLoading() {
205        if (mItemLoadedFuture != null) {
206            if (Log.isLoggable(LogTag.APP, Log.DEBUG)) {
207                Log.v(TAG, "cancelThumbnailLoading for: " + this);
208            }
209            mItemLoadedFuture.cancel();
210            mItemLoadedFuture = null;
211        }
212    }
213}
214