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