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 && !mItemLoadedFuture.isDone()) { 206 if (Log.isLoggable(LogTag.APP, Log.DEBUG)) { 207 Log.v(TAG, "cancelThumbnailLoading for: " + this); 208 } 209 mItemLoadedFuture.cancel(getUri()); 210 mItemLoadedFuture = null; 211 } 212 } 213} 214