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 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.MediaMetadataRetriever;
     32 import android.net.Uri;
     33 import android.util.Log;
     34 
     35 import com.android.mms.LogTag;
     36 import com.android.mms.MmsConfig;
     37 import com.google.android.mms.MmsException;
     38 // TODO: remove dependency for SDK build
     39 
     40 public abstract class MediaModel extends Model implements EventListener {
     41     protected static final String TAG = "Mms/media";
     42 
     43     private final static String MUSIC_SERVICE_ACTION = "com.android.music.musicservicecommand";
     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 
     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 
     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 
     93     public int getBegin() {
     94         return mBegin;
     95     }
     96 
     97     public void setBegin(int begin) {
     98         mBegin = begin;
     99         notifyModelChanged(true);
    100     }
    101 
    102     public int getDuration() {
    103         return mDuration;
    104     }
    105 
    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 
    122     public String getTag() {
    123         return mTag;
    124     }
    125 
    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      */
    135     public Uri getUri() {
    136         return mUri;
    137     }
    138 
    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      */
    151     void setUri(Uri uri) {
    152         mUri = uri;
    153     }
    154 
    155     /**
    156      * @return the mSrc
    157      */
    158     public String getSrc() {
    159         return mSrc;
    160     }
    161 
    162     /**
    163      * @return the mFill
    164      */
    165     public short getFill() {
    166         return mFill;
    167     }
    168 
    169     /**
    170      * @param fill the mFill to set
    171      */
    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      */
    182     public boolean getMediaResizable() {
    183         return mMediaResizeable;
    184     }
    185 
    186     /**
    187      * @return the size of the attached media
    188      */
    189     public int getMediaSize() {
    190         return mSize;
    191     }
    192 
    193     public boolean isText() {
    194         return mTag.equals(SmilHelper.ELEMENT_TAG_TEXT);
    195     }
    196 
    197     public boolean isImage() {
    198         return mTag.equals(SmilHelper.ELEMENT_TAG_IMAGE);
    199     }
    200 
    201     public boolean isVideo() {
    202         return mTag.equals(SmilHelper.ELEMENT_TAG_VIDEO);
    203     }
    204 
    205     public boolean isAudio() {
    206         return mTag.equals(SmilHelper.ELEMENT_TAG_AUDIO);
    207     }
    208 
    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 
    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 
    272     public static boolean isMmsUri(Uri uri) {
    273         return uri.getAuthority().startsWith("mms");
    274     }
    275 
    276     public int getSeekTo() {
    277         return mSeekTo;
    278     }
    279 
    280     public void appendAction(MediaAction action) {
    281         mMediaActions.add(action);
    282     }
    283 
    284     public MediaAction getCurrentAction() {
    285         if (0 == mMediaActions.size()) {
    286             return MediaAction.NO_ACTIVE_ACTION;
    287         }
    288         return mMediaActions.remove(0);
    289     }
    290 
    291     protected boolean isPlayable() {
    292         return false;
    293     }
    294 
    295     protected void pauseMusicPlayer() {
    296         if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    297             Log.d(TAG, "pauseMusicPlayer");
    298         }
    299 
    300         Intent i = new Intent(MUSIC_SERVICE_ACTION);
    301         i.putExtra("command", "pause");
    302         mContext.sendBroadcast(i);
    303     }
    304 
    305     /**
    306      * If the attached media is resizeable, resize it to fit within the byteLimit. Save the
    307      * new part in the pdu.
    308      * @param byteLimit the max size of the media attachment
    309      * @throws MmsException
    310      */
    311     protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
    312     }
    313 }
    314