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