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