ImageModel.java revision ae1f5cf2fe15970cbcdcf0188cd8733facac0e05
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
18package com.android.mms.model;
19
20import com.android.mms.ContentRestrictionException;
21import com.android.mms.ExceedMessageSizeException;
22import com.android.mms.LogTag;
23import com.android.mms.MmsConfig;
24import com.android.mms.dom.smil.SmilMediaElementImpl;
25import android.drm.mobile1.DrmException;
26import com.android.mms.drm.DrmWrapper;
27import com.android.mms.ui.UriImage;
28import com.android.mms.ui.MessageUtils;
29import com.android.mmscommon.MmsException;
30import com.android.mmscommon.mms.pdu.PduPart;
31import com.android.mmscommon.mms.pdu.PduPersister;
32
33import org.w3c.dom.events.Event;
34import org.w3c.dom.smil.ElementTime;
35
36import android.content.Context;
37import android.graphics.Bitmap;
38import android.graphics.BitmapFactory;
39import android.net.Uri;
40import android.text.TextUtils;
41import android.util.Config;
42import android.util.Log;
43
44import java.io.FileNotFoundException;
45import java.io.IOException;
46import java.io.InputStream;
47import java.lang.ref.SoftReference;
48
49
50public class ImageModel extends RegionMediaModel {
51    private static final String TAG = "Mms/image";
52    private static final boolean DEBUG = false;
53    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
54
55    private static final int THUMBNAIL_BOUNDS_LIMIT = 480;
56
57    private int mWidth;
58    private int mHeight;
59    private SoftReference<Bitmap> mBitmapCache = new SoftReference<Bitmap>(null);
60
61    public ImageModel(Context context, Uri uri, RegionModel region)
62            throws MmsException {
63        super(context, SmilHelper.ELEMENT_TAG_IMAGE, uri, region);
64        initModelFromUri(uri);
65        checkContentRestriction();
66    }
67
68    public ImageModel(Context context, String contentType, String src,
69            Uri uri, RegionModel region) throws DrmException, MmsException {
70        super(context, SmilHelper.ELEMENT_TAG_IMAGE,
71                contentType, src, uri, region);
72        decodeImageBounds();
73    }
74
75    public ImageModel(Context context, String contentType, String src,
76            DrmWrapper wrapper, RegionModel regionModel) throws IOException {
77        super(context, SmilHelper.ELEMENT_TAG_IMAGE, contentType, src,
78                wrapper, regionModel);
79    }
80
81    private void initModelFromUri(Uri uri) throws MmsException {
82        UriImage uriImage = new UriImage(mContext, uri);
83
84        mContentType = uriImage.getContentType();
85        if (TextUtils.isEmpty(mContentType)) {
86            throw new MmsException("Type of media is unknown.");
87        }
88        mSrc = uriImage.getSrc();
89        mWidth = uriImage.getWidth();
90        mHeight = uriImage.getHeight();
91
92        if (LOCAL_LOGV) {
93            Log.v(TAG, "New ImageModel created:"
94                    + " mSrc=" + mSrc
95                    + " mContentType=" + mContentType
96                    + " mUri=" + uri);
97        }
98    }
99
100    private void decodeImageBounds() throws DrmException {
101        UriImage uriImage = new UriImage(mContext, getUriWithDrmCheck());
102        mWidth = uriImage.getWidth();
103        mHeight = uriImage.getHeight();
104
105        if (LOCAL_LOGV) {
106            Log.v(TAG, "Image bounds: " + mWidth + "x" + mHeight);
107        }
108    }
109
110    // EventListener Interface
111    public void handleEvent(Event evt) {
112        if (evt.getType().equals(SmilMediaElementImpl.SMIL_MEDIA_START_EVENT)) {
113            mVisible = true;
114        } else if (mFill != ElementTime.FILL_FREEZE) {
115            mVisible = false;
116        }
117
118        notifyModelChanged(false);
119    }
120
121    public int getWidth() {
122        return mWidth;
123    }
124
125    public int getHeight() {
126        return mHeight;
127    }
128
129    protected void checkContentRestriction() throws ContentRestrictionException {
130        ContentRestriction cr = ContentRestrictionFactory.getContentRestriction();
131        cr.checkImageContentType(mContentType);
132    }
133
134    public Bitmap getBitmap() {
135        return internalGetBitmap(getUri());
136    }
137
138    public Bitmap getBitmapWithDrmCheck() throws DrmException {
139        return internalGetBitmap(getUriWithDrmCheck());
140    }
141
142    private Bitmap internalGetBitmap(Uri uri) {
143        Bitmap bm = mBitmapCache.get();
144        if (bm == null) {
145            try {
146                bm = createThumbnailBitmap(THUMBNAIL_BOUNDS_LIMIT, uri);
147                if (bm != null) {
148                    mBitmapCache = new SoftReference<Bitmap>(bm);
149                }
150            } catch (OutOfMemoryError ex) {
151                // fall through and return a null bitmap. The callers can handle a null
152                // result and show R.drawable.ic_missing_thumbnail_picture
153            }
154        }
155        return bm;
156    }
157
158    private Bitmap createThumbnailBitmap(int thumbnailBoundsLimit, Uri uri) {
159        int outWidth = mWidth;
160        int outHeight = mHeight;
161
162        int s = 1;
163        while ((outWidth / s > thumbnailBoundsLimit)
164                || (outHeight / s > thumbnailBoundsLimit)) {
165            s *= 2;
166        }
167        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
168            Log.v(TAG, "createThumbnailBitmap: scale=" + s + ", w=" + outWidth / s
169                    + ", h=" + outHeight / s);
170        }
171        BitmapFactory.Options options = new BitmapFactory.Options();
172        options.inSampleSize = s;
173
174        InputStream input = null;
175        try {
176            input = mContext.getContentResolver().openInputStream(uri);
177            return BitmapFactory.decodeStream(input, null, options);
178        } catch (FileNotFoundException e) {
179            Log.e(TAG, e.getMessage(), e);
180            return null;
181        } catch (OutOfMemoryError ex) {
182            MessageUtils.writeHprofDataToFile();
183            throw ex;
184        } finally {
185            if (input != null) {
186                try {
187                    input.close();
188                } catch (IOException e) {
189                    Log.e(TAG, e.getMessage(), e);
190                }
191            }
192        }
193    }
194
195    @Override
196    public boolean getMediaResizable() {
197        return true;
198    }
199
200    @Override
201    protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
202        UriImage image = new UriImage(mContext, getUri());
203        if (image == null) {
204            throw new ExceedMessageSizeException("No room to resize picture: " + getUri());
205        }
206        PduPart part = image.getResizedImageAsPart(
207                MmsConfig.getMaxImageWidth(),
208                MmsConfig.getMaxImageHeight(),
209                byteLimit);
210
211        PduPersister persister = PduPersister.getPduPersister(mContext);
212        this.mSize = part.getData().length;
213        Uri newUri = persister.persistPart(part, messageId);
214        setUri(newUri);
215    }
216}
217