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 java.io.FileInputStream; 21 import java.io.FileNotFoundException; 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.util.ArrayList; 25 26 import org.w3c.dom.events.EventListener; 27 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.media.AudioManager; 32 import android.media.MediaMetadataRetriever; 33 import android.net.Uri; 34 import android.util.Log; 35 36 import com.android.mms.LogTag; 37 import com.android.mms.MmsConfig; 38 39 import com.google.android.mms.MmsException; 40 // TODO: remove dependency for SDK build 41 42 public abstract class MediaModel extends Model implements EventListener { 43 protected static final String TAG = LogTag.TAG; 44 45 protected Context mContext; 46 protected int mBegin; 47 protected int mDuration; 48 protected String mTag; 49 protected String mSrc; 50 protected String mContentType; 51 private Uri mUri; 52 private byte[] mData; 53 protected short mFill; 54 protected int mSize; 55 protected int mSeekTo; 56 protected boolean mMediaResizeable; 57 58 private final ArrayList<MediaAction> mMediaActions; 59 public static enum MediaAction { 60 NO_ACTIVE_ACTION, 61 START, 62 STOP, 63 PAUSE, 64 SEEK, 65 } 66 MediaModel(Context context, String tag, String contentType, String src, Uri uri)67 public MediaModel(Context context, String tag, String contentType, 68 String src, Uri uri) throws MmsException { 69 mContext = context; 70 mTag = tag; 71 mContentType = contentType; 72 mSrc = src; 73 mUri = uri; 74 initMediaSize(); 75 mMediaActions = new ArrayList<MediaAction>(); 76 } 77 MediaModel(Context context, String tag, String contentType, String src, byte[] data)78 public MediaModel(Context context, String tag, String contentType, 79 String src, byte[] data) { 80 if (data == null) { 81 throw new IllegalArgumentException("data may not be null."); 82 } 83 84 mContext = context; 85 mTag = tag; 86 mContentType = contentType; 87 mSrc = src; 88 mData = data; 89 mSize = data.length; 90 mMediaActions = new ArrayList<MediaAction>(); 91 } 92 getBegin()93 public int getBegin() { 94 return mBegin; 95 } 96 setBegin(int begin)97 public void setBegin(int begin) { 98 mBegin = begin; 99 notifyModelChanged(true); 100 } 101 getDuration()102 public int getDuration() { 103 return mDuration; 104 } 105 setDuration(int duration)106 public void setDuration(int duration) { 107 if (isPlayable() && (duration < 0)) { 108 // 'indefinite' duration, we should try to find its exact value; 109 try { 110 initMediaDuration(); 111 } catch (MmsException e) { 112 // On error, keep default duration. 113 Log.e(TAG, e.getMessage(), e); 114 return; 115 } 116 } else { 117 mDuration = duration; 118 } 119 notifyModelChanged(true); 120 } 121 getTag()122 public String getTag() { 123 return mTag; 124 } 125 getContentType()126 public String getContentType() { 127 return mContentType; 128 } 129 130 /** 131 * Get the URI of the media. 132 * 133 * @return The URI of the media. 134 */ getUri()135 public Uri getUri() { 136 return mUri; 137 } 138 getData()139 public byte[] getData() { 140 if (mData != null) { 141 byte[] data = new byte[mData.length]; 142 System.arraycopy(mData, 0, data, 0, mData.length); 143 return data; 144 } 145 return null; 146 } 147 148 /** 149 * @param uri the mUri to set 150 */ setUri(Uri uri)151 void setUri(Uri uri) { 152 mUri = uri; 153 } 154 155 /** 156 * @return the mSrc 157 */ getSrc()158 public String getSrc() { 159 return mSrc; 160 } 161 162 /** 163 * @return the mFill 164 */ getFill()165 public short getFill() { 166 return mFill; 167 } 168 169 /** 170 * @param fill the mFill to set 171 */ setFill(short fill)172 public void setFill(short fill) { 173 mFill = fill; 174 notifyModelChanged(true); 175 } 176 177 /** 178 * @return whether the media is resizable or not. For instance, a picture can be resized 179 * to smaller dimensions or lower resolution. Other media, such as video and sounds, aren't 180 * currently able to be resized. 181 */ getMediaResizable()182 public boolean getMediaResizable() { 183 return mMediaResizeable; 184 } 185 186 /** 187 * @return the size of the attached media 188 */ getMediaSize()189 public int getMediaSize() { 190 return mSize; 191 } 192 isText()193 public boolean isText() { 194 return mTag.equals(SmilHelper.ELEMENT_TAG_TEXT); 195 } 196 isImage()197 public boolean isImage() { 198 return mTag.equals(SmilHelper.ELEMENT_TAG_IMAGE); 199 } 200 isVideo()201 public boolean isVideo() { 202 return mTag.equals(SmilHelper.ELEMENT_TAG_VIDEO); 203 } 204 isAudio()205 public boolean isAudio() { 206 return mTag.equals(SmilHelper.ELEMENT_TAG_AUDIO); 207 } 208 initMediaDuration()209 protected void initMediaDuration() throws MmsException { 210 if (mUri == null) { 211 throw new IllegalArgumentException("Uri may not be null."); 212 } 213 214 MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 215 int duration = 0; 216 try { 217 retriever.setDataSource(mContext, mUri); 218 String dur = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION); 219 if (dur != null) { 220 duration = Integer.parseInt(dur); 221 } 222 mDuration = duration; 223 } catch (Exception ex) { 224 Log.e(TAG, "MediaMetadataRetriever failed to get duration for " + mUri.getPath(), ex); 225 throw new MmsException(ex); 226 } finally { 227 retriever.release(); 228 } 229 } 230 initMediaSize()231 private void initMediaSize() throws MmsException { 232 ContentResolver cr = mContext.getContentResolver(); 233 InputStream input = null; 234 try { 235 input = cr.openInputStream(mUri); 236 if (input instanceof FileInputStream) { 237 // avoid reading the whole stream to get its length 238 FileInputStream f = (FileInputStream) input; 239 mSize = (int) f.getChannel().size(); 240 // sometimes mSize will be zero here. It's tempting to count the bytes as the code 241 // does below, but that turns out to be very slow. We'll deal with a zero size 242 // when we resize the media. 243 244 if (isVideo() && mSize > MmsConfig.getMaxMessageSize()) { 245 Log.w(TAG, "initMediaSize: Video size: f.getChannel().size(): " + mSize + 246 " larger than max message size: " + MmsConfig.getMaxMessageSize()); 247 } 248 } else { 249 while (-1 != input.read()) { 250 mSize++; 251 } 252 } 253 254 } catch (IOException e) { 255 // Ignore 256 Log.e(TAG, "IOException caught while opening or reading stream", e); 257 if (e instanceof FileNotFoundException) { 258 throw new MmsException(e.getMessage()); 259 } 260 } finally { 261 if (null != input) { 262 try { 263 input.close(); 264 } catch (IOException e) { 265 // Ignore 266 Log.e(TAG, "IOException caught while closing stream", e); 267 } 268 } 269 } 270 } 271 isMmsUri(Uri uri)272 public static boolean isMmsUri(Uri uri) { 273 return uri.getAuthority().startsWith("mms"); 274 } 275 getSeekTo()276 public int getSeekTo() { 277 return mSeekTo; 278 } 279 appendAction(MediaAction action)280 public void appendAction(MediaAction action) { 281 mMediaActions.add(action); 282 } 283 getCurrentAction()284 public MediaAction getCurrentAction() { 285 if (0 == mMediaActions.size()) { 286 return MediaAction.NO_ACTIVE_ACTION; 287 } 288 return mMediaActions.remove(0); 289 } 290 isPlayable()291 protected boolean isPlayable() { 292 return false; 293 } 294 pauseMusicPlayer()295 protected void pauseMusicPlayer() { 296 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 297 Log.d(TAG, "pauseMusicPlayer"); 298 } 299 300 AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 301 am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); 302 } 303 304 /** 305 * If the attached media is resizeable, resize it to fit within the byteLimit. Save the 306 * new part in the pdu. 307 * @param byteLimit the max size of the media attachment 308 * @throws MmsException 309 */ resizeMedia(int byteLimit, long messageId)310 protected void resizeMedia(int byteLimit, long messageId) throws MmsException { 311 } 312 } 313