/*
* Copyright (C) 2008 Esmertec AG.
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.mms.model;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import org.w3c.dom.events.EventListener;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.util.Log;
import com.android.mms.LogTag;
import com.android.mms.MmsConfig;
import com.google.android.mms.MmsException;
// TODO: remove dependency for SDK build
public abstract class MediaModel extends Model implements EventListener {
protected static final String TAG = "Mms/media";
private final static String MUSIC_SERVICE_ACTION = "com.android.music.musicservicecommand";
protected Context mContext;
protected int mBegin;
protected int mDuration;
protected String mTag;
protected String mSrc;
protected String mContentType;
private Uri mUri;
private byte[] mData;
protected short mFill;
protected int mSize;
protected int mSeekTo;
protected boolean mMediaResizeable;
private final ArrayList<MediaAction> mMediaActions;
public static enum MediaAction {
NO_ACTIVE_ACTION,
START,
STOP,
PAUSE,
SEEK,
}
public MediaModel(Context context, String tag, String contentType,
String src, Uri uri) throws MmsException {
mContext = context;
mTag = tag;
mContentType = contentType;
mSrc = src;
mUri = uri;
initMediaSize();
mMediaActions = new ArrayList<MediaAction>();
}
public MediaModel(Context context, String tag, String contentType,
String src, byte[] data) {
if (data == null) {
throw new IllegalArgumentException("data may not be null.");
}
mContext = context;
mTag = tag;
mContentType = contentType;
mSrc = src;
mData = data;
mSize = data.length;
mMediaActions = new ArrayList<MediaAction>();
}
public int getBegin() {
return mBegin;
}
public void setBegin(int begin) {
mBegin = begin;
notifyModelChanged(true);
}
public int getDuration() {
return mDuration;
}
public void setDuration(int duration) {
if (isPlayable() && (duration < 0)) {
// 'indefinite' duration, we should try to find its exact value;
try {
initMediaDuration();
} catch (MmsException e) {
// On error, keep default duration.
Log.e(TAG, e.getMessage(), e);
return;
}
} else {
mDuration = duration;
}
notifyModelChanged(true);
}
public String getTag() {
return mTag;
}
public String getContentType() {
return mContentType;
}
/**
* Get the URI of the media.
*
* @return The URI of the media.
*/
public Uri getUri() {
return mUri;
}
public byte[] getData() {
if (mData != null) {
byte[] data = new byte[mData.length];
System.arraycopy(mData, 0, data, 0, mData.length);
return data;
}
return null;
}
/**
* @param uri the mUri to set
*/
void setUri(Uri uri) {
mUri = uri;
}
/**
* @return the mSrc
*/
public String getSrc() {
return mSrc;
}
/**
* @return the mFill
*/
public short getFill() {
return mFill;
}
/**
* @param fill the mFill to set
*/
public void setFill(short fill) {
mFill = fill;
notifyModelChanged(true);
}
/**
* @return whether the media is resizable or not. For instance, a picture can be resized
* to smaller dimensions or lower resolution. Other media, such as video and sounds, aren't
* currently able to be resized.
*/
public boolean getMediaResizable() {
return mMediaResizeable;
}
/**
* @return the size of the attached media
*/
public int getMediaSize() {
return mSize;
}
public boolean isText() {
return mTag.equals(SmilHelper.ELEMENT_TAG_TEXT);
}
public boolean isImage() {
return mTag.equals(SmilHelper.ELEMENT_TAG_IMAGE);
}
public boolean isVideo() {
return mTag.equals(SmilHelper.ELEMENT_TAG_VIDEO);
}
public boolean isAudio() {
return mTag.equals(SmilHelper.ELEMENT_TAG_AUDIO);
}
protected void initMediaDuration() throws MmsException {
if (mUri == null) {
throw new IllegalArgumentException("Uri may not be null.");
}
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
int duration = 0;
try {
retriever.setDataSource(mContext, mUri);
String dur = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
if (dur != null) {
duration = Integer.parseInt(dur);
}
mDuration = duration;
} catch (Exception ex) {
Log.e(TAG, "MediaMetadataRetriever failed to get duration for " + mUri.getPath(), ex);
throw new MmsException(ex);
} finally {
retriever.release();
}
}
private void initMediaSize() throws MmsException {
ContentResolver cr = mContext.getContentResolver();
InputStream input = null;
try {
input = cr.openInputStream(mUri);
if (input instanceof FileInputStream) {
// avoid reading the whole stream to get its length
FileInputStream f = (FileInputStream) input;
mSize = (int) f.getChannel().size();
// sometimes mSize will be zero here. It's tempting to count the bytes as the code
// does below, but that turns out to be very slow. We'll deal with a zero size
// when we resize the media.
if (isVideo() && mSize > MmsConfig.getMaxMessageSize()) {
Log.w(TAG, "initMediaSize: Video size: f.getChannel().size(): " + mSize +
" larger than max message size: " + MmsConfig.getMaxMessageSize());
}
} else {
while (-1 != input.read()) {
mSize++;
}
}
} catch (IOException e) {
// Ignore
Log.e(TAG, "IOException caught while opening or reading stream", e);
if (e instanceof FileNotFoundException) {
throw new MmsException(e.getMessage());
}
} finally {
if (null != input) {
try {
input.close();
} catch (IOException e) {
// Ignore
Log.e(TAG, "IOException caught while closing stream", e);
}
}
}
}
public static boolean isMmsUri(Uri uri) {
return uri.getAuthority().startsWith("mms");
}
public int getSeekTo() {
return mSeekTo;
}
public void appendAction(MediaAction action) {
mMediaActions.add(action);
}
public MediaAction getCurrentAction() {
if (0 == mMediaActions.size()) {
return MediaAction.NO_ACTIVE_ACTION;
}
return mMediaActions.remove(0);
}
protected boolean isPlayable() {
return false;
}
protected void pauseMusicPlayer() {
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.d(TAG, "pauseMusicPlayer");
}
Intent i = new Intent(MUSIC_SERVICE_ACTION);
i.putExtra("command", "pause");
mContext.sendBroadcast(i);
}
/**
* If the attached media is resizeable, resize it to fit within the byteLimit. Save the
* new part in the pdu.
* @param byteLimit the max size of the media attachment
* @throws MmsException
*/
protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
}
}