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