ImageModel.java revision 1009d4bf5ee9fd1f2523e530828de5babb78eb19
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.google.android.mms.MmsException;
30import com.google.android.mms.pdu.PduPart;
31import com.google.android.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.Log;
42
43import java.io.FileNotFoundException;
44import java.io.IOException;
45import java.io.InputStream;
46import java.lang.ref.SoftReference;
47import java.util.Arrays;
48import java.util.HashSet;
49import java.util.Set;
50
51
52public class ImageModel extends RegionMediaModel {
53    @SuppressWarnings("hiding")
54    private static final String TAG = "Mms/image";
55    private static final boolean DEBUG = false;
56    private static final boolean LOCAL_LOGV = false;
57
58    private static final int THUMBNAIL_BOUNDS_LIMIT = 480;
59
60    /**
61     * These are the image content types that MMS supports. Anything else needs to be transcoded
62     * into one of these content types before being sent over MMS.
63     */
64    private static final Set<String> SUPPORTED_MMS_IMAGE_CONTENT_TYPES =
65        new HashSet<String>(Arrays.asList(new String[] {
66                "image/jpeg",
67            }));
68
69    private int mWidth;
70    private int mHeight;
71    private SoftReference<Bitmap> mBitmapCache = new SoftReference<Bitmap>(null);
72
73    public ImageModel(Context context, Uri uri, RegionModel region)
74            throws MmsException {
75        super(context, SmilHelper.ELEMENT_TAG_IMAGE, uri, region);
76        initModelFromUri(uri);
77        checkContentRestriction();
78    }
79
80    public ImageModel(Context context, String contentType, String src,
81            Uri uri, RegionModel region) throws DrmException, MmsException {
82        super(context, SmilHelper.ELEMENT_TAG_IMAGE,
83                contentType, src, uri, region);
84        decodeImageBounds();
85    }
86
87    public ImageModel(Context context, String contentType, String src,
88            DrmWrapper wrapper, RegionModel regionModel) throws IOException {
89        super(context, SmilHelper.ELEMENT_TAG_IMAGE, contentType, src,
90                wrapper, regionModel);
91    }
92
93    private void initModelFromUri(Uri uri) throws MmsException {
94        UriImage uriImage = new UriImage(mContext, uri);
95
96        mContentType = uriImage.getContentType();
97        if (TextUtils.isEmpty(mContentType)) {
98            throw new MmsException("Type of media is unknown.");
99        }
100        mSrc = uriImage.getSrc();
101        mWidth = uriImage.getWidth();
102        mHeight = uriImage.getHeight();
103
104        if (LOCAL_LOGV) {
105            Log.v(TAG, "New ImageModel created:"
106                    + " mSrc=" + mSrc
107                    + " mContentType=" + mContentType
108                    + " mUri=" + uri);
109        }
110    }
111
112    private void decodeImageBounds() throws DrmException {
113        UriImage uriImage = new UriImage(mContext, getUriWithDrmCheck());
114        mWidth = uriImage.getWidth();
115        mHeight = uriImage.getHeight();
116
117        if (LOCAL_LOGV) {
118            Log.v(TAG, "Image bounds: " + mWidth + "x" + mHeight);
119        }
120    }
121
122    // EventListener Interface
123    @Override
124    public void handleEvent(Event evt) {
125        if (evt.getType().equals(SmilMediaElementImpl.SMIL_MEDIA_START_EVENT)) {
126            mVisible = true;
127        } else if (mFill != ElementTime.FILL_FREEZE) {
128            mVisible = false;
129        }
130
131        notifyModelChanged(false);
132    }
133
134    public int getWidth() {
135        return mWidth;
136    }
137
138    public int getHeight() {
139        return mHeight;
140    }
141
142    protected void checkContentRestriction() throws ContentRestrictionException {
143        ContentRestriction cr = ContentRestrictionFactory.getContentRestriction();
144        cr.checkImageContentType(mContentType);
145    }
146
147    public Bitmap getBitmap() {
148        return internalGetBitmap(getUri());
149    }
150
151    public Bitmap getBitmapWithDrmCheck() throws DrmException {
152        return internalGetBitmap(getUriWithDrmCheck());
153    }
154
155    private Bitmap internalGetBitmap(Uri uri) {
156        Bitmap bm = mBitmapCache.get();
157        if (bm == null) {
158            try {
159                bm = createThumbnailBitmap(THUMBNAIL_BOUNDS_LIMIT, uri);
160                if (bm != null) {
161                    mBitmapCache = new SoftReference<Bitmap>(bm);
162                }
163            } catch (OutOfMemoryError ex) {
164                // fall through and return a null bitmap. The callers can handle a null
165                // result and show R.drawable.ic_missing_thumbnail_picture
166            }
167        }
168        return bm;
169    }
170
171    private Bitmap createThumbnailBitmap(int thumbnailBoundsLimit, Uri uri) {
172        int outWidth = mWidth;
173        int outHeight = mHeight;
174
175        int s = 1;
176        while ((outWidth / s > thumbnailBoundsLimit)
177                || (outHeight / s > thumbnailBoundsLimit)) {
178            s *= 2;
179        }
180        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
181            Log.v(TAG, "createThumbnailBitmap: scale=" + s + ", w=" + outWidth / s
182                    + ", h=" + outHeight / s);
183        }
184        BitmapFactory.Options options = new BitmapFactory.Options();
185        options.inSampleSize = s;
186
187        InputStream input = null;
188        try {
189            input = mContext.getContentResolver().openInputStream(uri);
190            return BitmapFactory.decodeStream(input, null, options);
191        } catch (FileNotFoundException e) {
192            Log.e(TAG, e.getMessage(), e);
193            return null;
194        } catch (OutOfMemoryError ex) {
195            if (DEBUG) {
196                MessageUtils.writeHprofDataToFile();
197            }
198            throw ex;
199        } finally {
200            if (input != null) {
201                try {
202                    input.close();
203                } catch (IOException e) {
204                    Log.e(TAG, e.getMessage(), e);
205                }
206            }
207        }
208    }
209
210    @Override
211    public boolean getMediaResizable() {
212        return true;
213    }
214
215    @Override
216    protected void resizeMedia(int byteLimit, long messageId) throws MmsException {
217        UriImage image = new UriImage(mContext, getUri());
218
219        int widthLimit = MmsConfig.getMaxImageWidth();
220        int heightLimit = MmsConfig.getMaxImageHeight();
221        // In mms_config.xml, the max width has always been declared larger than the max height.
222        // Swap the width and height limits if necessary so we scale the picture as little as
223        // possible.
224        if (image.getHeight() > image.getWidth()) {
225            int temp = widthLimit;
226            widthLimit = heightLimit;
227            heightLimit = temp;
228        }
229
230        // Check if we're already within the limits - in which case we don't need to resize
231        if (getMediaSize() <= byteLimit &&
232                image.getWidth() <= widthLimit &&
233                image.getHeight() <= heightLimit &&
234                SUPPORTED_MMS_IMAGE_CONTENT_TYPES.contains(image.getContentType())) {
235            return;
236        }
237
238        PduPart part = image.getResizedImageAsPart(
239                widthLimit,
240                heightLimit,
241                byteLimit);
242
243        if (part == null) {
244            throw new ExceedMessageSizeException("Not enough memory to turn image into part: " +
245                    getUri());
246        }
247
248        String src = getSrc();
249        byte[] srcBytes = src.getBytes();
250        part.setContentLocation(srcBytes);
251        int period = src.lastIndexOf(".");
252        byte[] contentId = period != -1 ? src.substring(0, period).getBytes() : srcBytes;
253        part.setContentId(contentId);
254
255        PduPersister persister = PduPersister.getPduPersister(mContext);
256        this.mSize = part.getData().length;
257        Uri newUri = persister.persistPart(part, messageId);
258        setUri(newUri);
259    }
260}
261