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 com.android.mms.ContentRestrictionException; 21 import com.android.mms.ExceedMessageSizeException; 22 import com.android.mms.LogTag; 23 import com.android.mms.MmsConfig; 24 import com.android.mms.dom.smil.SmilMediaElementImpl; 25 import android.drm.mobile1.DrmException; 26 import com.android.mms.drm.DrmWrapper; 27 import com.android.mms.ui.UriImage; 28 import com.android.mms.ui.MessageUtils; 29 import com.google.android.mms.MmsException; 30 import com.google.android.mms.pdu.PduPart; 31 import com.google.android.mms.pdu.PduPersister; 32 33 import org.w3c.dom.events.Event; 34 import org.w3c.dom.smil.ElementTime; 35 36 import android.content.Context; 37 import android.graphics.Bitmap; 38 import android.graphics.BitmapFactory; 39 import android.net.Uri; 40 import android.text.TextUtils; 41 import android.util.Config; 42 import android.util.Log; 43 44 import java.io.FileNotFoundException; 45 import java.io.IOException; 46 import java.io.InputStream; 47 import java.lang.ref.SoftReference; 48 49 50 public 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 if (part == null) { 212 throw new ExceedMessageSizeException("Not enough memory to turn image into part: " + 213 getUri()); 214 } 215 216 String src = getSrc(); 217 byte[] srcBytes = src.getBytes(); 218 part.setContentLocation(srcBytes); 219 int period = src.lastIndexOf("."); 220 byte[] contentId = period != -1 ? src.substring(0, period).getBytes() : srcBytes; 221 part.setContentId(contentId); 222 223 PduPersister persister = PduPersister.getPduPersister(mContext); 224 this.mSize = part.getData().length; 225 Uri newUri = persister.persistPart(part, messageId); 226 setUri(newUri); 227 } 228 } 229