172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project/*
272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2008 Esmertec AG.
372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * you may not use this file except in compliance with the License.
772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * You may obtain a copy of the License at
872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
1072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project *
1172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * See the License for the specific language governing permissions and
1572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project * limitations under the License.
1672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project */
1772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
1872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectpackage com.android.mms.ui;
1972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport com.android.mms.model.ImageModel;
219242286c35743422051d439d7460cd0a1426899cWei Huangimport com.android.mms.LogTag;
22bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev
23bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjievimport com.google.android.mms.ContentType;
24f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport com.google.android.mms.pdu.PduPart;
25fd644551e8506266aad2b76463b51b44154ed62fTom Taylorimport android.database.sqlite.SqliteWrapper;
2672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.content.Context;
2872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.database.Cursor;
2972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.graphics.Bitmap;
3072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.graphics.BitmapFactory;
3172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.graphics.Bitmap.CompressFormat;
3272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.net.Uri;
3372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.provider.MediaStore.Images;
34f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms.Part;
3572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.text.TextUtils;
3672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.util.Log;
3772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport android.webkit.MimeTypeMap;
3872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
3972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport java.io.ByteArrayOutputStream;
4072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport java.io.FileNotFoundException;
4172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport java.io.IOException;
4272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectimport java.io.InputStream;
4337880013315d8b7abefd9081b2c36b060083d6faTom Taylorimport java.io.OutputStream;
4472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
4572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectpublic class UriImage {
469242286c35743422051d439d7460cd0a1426899cWei Huang    private static final String TAG = "Mms/image";
47b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor    private static final boolean DEBUG = false;
48150c4179995cc0a75f934ef194372f9295957ca2Joe Onorato    private static final boolean LOCAL_LOGV = false;
4972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
5072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private final Context mContext;
5172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private final Uri mUri;
5272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private String mContentType;
5372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private String mPath;
5472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private String mSrc;
5572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private int mWidth;
5672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private int mHeight;
5772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
5872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public UriImage(Context context, Uri uri) {
5972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if ((null == context) || (null == uri)) {
6072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            throw new IllegalArgumentException();
6172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
6272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
6372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        String scheme = uri.getScheme();
6472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if (scheme.equals("content")) {
6572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            initFromContentUri(context, uri);
6672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } else if (uri.getScheme().equals("file")) {
6772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            initFromFile(context, uri);
6872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
69325a37787f144421f995eeb385f67a5d255d7820Tom Taylor
7072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        mContext = context;
7172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        mUri = uri;
7272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
7372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        decodeBoundsInfo();
74b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor
75b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor        if (LOCAL_LOGV) {
76b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor            Log.v(TAG, "UriImage uri: " + uri + " mPath: " + mPath + " mWidth: " + mWidth +
77b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor                    " mHeight: " + mHeight);
78b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor        }
7972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
8072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
8172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private void initFromFile(Context context, Uri uri) {
82325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        mPath = uri.getPath();
8372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
84325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        String extension = MimeTypeMap.getFileExtensionFromUrl(mPath);
85325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        if (TextUtils.isEmpty(extension)) {
86325a37787f144421f995eeb385f67a5d255d7820Tom Taylor            // getMimeTypeFromExtension() doesn't handle spaces in filenames nor can it handle
87325a37787f144421f995eeb385f67a5d255d7820Tom Taylor            // urlEncoded strings. Let's try one last time at finding the extension.
88325a37787f144421f995eeb385f67a5d255d7820Tom Taylor            int dotPos = mPath.lastIndexOf('.');
89325a37787f144421f995eeb385f67a5d255d7820Tom Taylor            if (0 <= dotPos) {
90325a37787f144421f995eeb385f67a5d255d7820Tom Taylor                extension = mPath.substring(dotPos + 1);
91325a37787f144421f995eeb385f67a5d255d7820Tom Taylor            }
9272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
93325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        mContentType = mimeTypeMap.getMimeTypeFromExtension(extension);
94325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        // It's ok if mContentType is null. Eventually we'll show a toast telling the
95325a37787f144421f995eeb385f67a5d255d7820Tom Taylor        // user the picture couldn't be attached.
96f5b73486499febb57260bd63f694c47b07e240faTom Taylor
97f5b73486499febb57260bd63f694c47b07e240faTom Taylor        buildSrcFromPath();
98f5b73486499febb57260bd63f694c47b07e240faTom Taylor    }
99f5b73486499febb57260bd63f694c47b07e240faTom Taylor
100f5b73486499febb57260bd63f694c47b07e240faTom Taylor    private void buildSrcFromPath() {
101f5b73486499febb57260bd63f694c47b07e240faTom Taylor        mSrc = mPath.substring(mPath.lastIndexOf('/') + 1);
102f5b73486499febb57260bd63f694c47b07e240faTom Taylor
103f5b73486499febb57260bd63f694c47b07e240faTom Taylor        if(mSrc.startsWith(".") && mSrc.length() > 1) {
104f5b73486499febb57260bd63f694c47b07e240faTom Taylor            mSrc = mSrc.substring(1);
105f5b73486499febb57260bd63f694c47b07e240faTom Taylor        }
106f5b73486499febb57260bd63f694c47b07e240faTom Taylor
107f5b73486499febb57260bd63f694c47b07e240faTom Taylor        // Some MMSCs appear to have problems with filenames
108f5b73486499febb57260bd63f694c47b07e240faTom Taylor        // containing a space.  So just replace them with
109f5b73486499febb57260bd63f694c47b07e240faTom Taylor        // underscores in the name, which is typically not
110f5b73486499febb57260bd63f694c47b07e240faTom Taylor        // visible to the user anyway.
111f5b73486499febb57260bd63f694c47b07e240faTom Taylor        mSrc = mSrc.replace(' ', '_');
11272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
113325a37787f144421f995eeb385f67a5d255d7820Tom Taylor
11472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private void initFromContentUri(Context context, Uri uri) {
11572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        Cursor c = SqliteWrapper.query(context, context.getContentResolver(),
11672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                            uri, null, null, null, null);
11772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
118f5b73486499febb57260bd63f694c47b07e240faTom Taylor        mSrc = null;
11972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if (c == null) {
12072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            throw new IllegalArgumentException(
12172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    "Query on " + uri + " returns null result.");
12272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
12372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
12472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        try {
12572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if ((c.getCount() != 1) || !c.moveToFirst()) {
12672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                throw new IllegalArgumentException(
12772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                        "Query on " + uri + " returns 0 or multiple rows.");
12872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
12972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
13072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            String filePath;
13172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (ImageModel.isMmsUri(uri)) {
13272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                filePath = c.getString(c.getColumnIndexOrThrow(Part.FILENAME));
13372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                if (TextUtils.isEmpty(filePath)) {
13472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    filePath = c.getString(
13572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                            c.getColumnIndexOrThrow(Part._DATA));
13672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                }
13772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                mContentType = c.getString(
13872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                        c.getColumnIndexOrThrow(Part.CONTENT_TYPE));
13972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            } else {
140e7cee115c2e0deada90627fd265139c22fdc32d4Tom Taylor                filePath = uri.getPath();
1412c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                try {
1422c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                    mContentType = c.getString(
1432c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                            c.getColumnIndexOrThrow(Images.Media.MIME_TYPE)); // mime_type
1442c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                } catch (IllegalArgumentException e) {
1452c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                    mContentType = c.getString(c.getColumnIndexOrThrow("mimetype"));
1462c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor                }
147f5b73486499febb57260bd63f694c47b07e240faTom Taylor
148f5b73486499febb57260bd63f694c47b07e240faTom Taylor                // use the original filename if possible
149f5b73486499febb57260bd63f694c47b07e240faTom Taylor                int nameIndex = c.getColumnIndex(Images.Media.DISPLAY_NAME);
150f5b73486499febb57260bd63f694c47b07e240faTom Taylor                if (nameIndex != -1) {
151f5b73486499febb57260bd63f694c47b07e240faTom Taylor                    mSrc = c.getString(nameIndex);
152f5b73486499febb57260bd63f694c47b07e240faTom Taylor                    if (!TextUtils.isEmpty(mSrc)) {
153f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        // Some MMSCs appear to have problems with filenames
154f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        // containing a space.  So just replace them with
155f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        // underscores in the name, which is typically not
156f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        // visible to the user anyway.
157f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        mSrc = mSrc.replace(' ', '_');
158f5b73486499febb57260bd63f694c47b07e240faTom Taylor                    } else {
159f5b73486499febb57260bd63f694c47b07e240faTom Taylor                        mSrc = null;
160f5b73486499febb57260bd63f694c47b07e240faTom Taylor                    }
161f5b73486499febb57260bd63f694c47b07e240faTom Taylor                }
16272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
16372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mPath = filePath;
164f5b73486499febb57260bd63f694c47b07e240faTom Taylor            if (mSrc == null) {
165f5b73486499febb57260bd63f694c47b07e240faTom Taylor                buildSrcFromPath();
166f5b73486499febb57260bd63f694c47b07e240faTom Taylor            }
1672c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor        } catch (IllegalArgumentException e) {
1682c48c7894f1c4d4265bfe499e596fdd6fd3e9484Tom Taylor            Log.e(TAG, "initFromContentUri couldn't load image uri: " + uri, e);
16972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } finally {
17072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            c.close();
17172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
17272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
17372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
17472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    private void decodeBoundsInfo() {
17572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        InputStream input = null;
17672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        try {
17772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            input = mContext.getContentResolver().openInputStream(mUri);
17872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            BitmapFactory.Options opt = new BitmapFactory.Options();
17972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            opt.inJustDecodeBounds = true;
18072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            BitmapFactory.decodeStream(input, null, opt);
18172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mWidth = opt.outWidth;
18272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            mHeight = opt.outHeight;
18372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } catch (FileNotFoundException e) {
18472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            // Ignore
18572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            Log.e(TAG, "IOException caught while opening stream", e);
18672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } finally {
18772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (null != input) {
18872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                try {
18972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    input.close();
19072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                } catch (IOException e) {
19172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    // Ignore
19272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                    Log.e(TAG, "IOException caught while closing stream", e);
19372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                }
19472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
19572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
19672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
19772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
19872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public String getContentType() {
19972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return mContentType;
20072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
20172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
20272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public String getSrc() {
20372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return mSrc;
20472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
20572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
206721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public String getPath() {
207721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return mPath;
208721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
209721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
21072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public int getWidth() {
21172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return mWidth;
21272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
21372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
21472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    public int getHeight() {
21572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return mHeight;
21672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
21772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
218bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev    /**
219bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * Get a version of this image resized to fit the given dimension and byte-size limits. Note
220bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * that the content type of the resulting PduPart may not be the same as the content type of
221bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * this UriImage; always call {@link PduPart#getContentType()} to get the new content type.
222bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     *
223bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param widthLimit The width limit, in pixels
224bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param heightLimit The height limit, in pixels
225bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param byteLimit The binary size limit, in bytes
226bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @return A new PduPart containing the resized image data
227bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     */
228f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson    public PduPart getResizedImageAsPart(int widthLimit, int heightLimit, int byteLimit) {
22972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        PduPart part = new PduPart();
23072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
23137880013315d8b7abefd9081b2c36b060083d6faTom Taylor        byte[] data =  getResizedImageData(mWidth, mHeight,
23237880013315d8b7abefd9081b2c36b060083d6faTom Taylor                widthLimit, heightLimit, byteLimit, mUri, mContext);
23372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        if (data == null) {
23472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            if (LOCAL_LOGV) {
23572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project                Log.v(TAG, "Resize image failed.");
23672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            }
23772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            return null;
23872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
23972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
24072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        part.setData(data);
241bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev        // getResizedImageData ALWAYS compresses to JPEG, regardless of the original content type
242bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev        part.setContentType(ContentType.IMAGE_JPEG.getBytes());
24372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
24472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        return part;
24572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
24672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
247f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson    private static final int NUMBER_OF_RESIZE_ATTEMPTS = 4;
248f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson
249bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev    /**
250bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * Resize and recompress the image such that it fits the given limits. The resulting byte
251bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * array contains an image in JPEG format, regardless of the original image's content type.
252bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param widthLimit The width limit, in pixels
253bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param heightLimit The height limit, in pixels
254bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @param byteLimit The binary size limit, in bytes
255bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     * @return A resized/recompressed version of this image, in JPEG format
256bbc5b8d0076b0cdae851bfec636a854ef77bd6ceTodor Kalaydjiev     */
25737880013315d8b7abefd9081b2c36b060083d6faTom Taylor    public static byte[] getResizedImageData(int width, int height,
25837880013315d8b7abefd9081b2c36b060083d6faTom Taylor            int widthLimit, int heightLimit, int byteLimit, Uri uri, Context context) {
25937880013315d8b7abefd9081b2c36b060083d6faTom Taylor        int outWidth = width;
26037880013315d8b7abefd9081b2c36b060083d6faTom Taylor        int outHeight = height;
26172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2628c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor        float scaleFactor = 1.F;
2639aeb4e9e6aebe9d99cf817cedbdeb0bb0ae5a024Mark Wagner        while ((outWidth * scaleFactor > widthLimit) || (outHeight * scaleFactor > heightLimit)) {
2649aeb4e9e6aebe9d99cf817cedbdeb0bb0ae5a024Mark Wagner            scaleFactor *= .75F;
26572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
26672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project
2679242286c35743422051d439d7460cd0a1426899cWei Huang        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
26837880013315d8b7abefd9081b2c36b060083d6faTom Taylor            Log.v(TAG, "getResizedBitmap: wlimit=" + widthLimit +
2699242286c35743422051d439d7460cd0a1426899cWei Huang                    ", hlimit=" + heightLimit + ", sizeLimit=" + byteLimit +
27037880013315d8b7abefd9081b2c36b060083d6faTom Taylor                    ", width=" + width + ", height=" + height +
271b0664c5822ee07642617a73c932b02a2636a2223Tom Taylor                    ", initialScaleFactor=" + scaleFactor +
27237880013315d8b7abefd9081b2c36b060083d6faTom Taylor                    ", uri=" + uri);
2739242286c35743422051d439d7460cd0a1426899cWei Huang        }
2749242286c35743422051d439d7460cd0a1426899cWei Huang
27572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        InputStream input = null;
27672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        try {
277f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson            ByteArrayOutputStream os = null;
278f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson            int attempts = 1;
2798c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor            int sampleSize = 1;
280946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            BitmapFactory.Options options = new BitmapFactory.Options();
281946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            int quality = MessageUtils.IMAGE_COMPRESSION_QUALITY;
282946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            Bitmap b = null;
283f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson
284946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            // In this loop, attempt to decode the stream with the best possible subsampling (we
285946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            // start with 1, which means no subsampling - get the original content) without running
286946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            // out of memory.
287f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson            do {
28837880013315d8b7abefd9081b2c36b060083d6faTom Taylor                input = context.getContentResolver().openInputStream(uri);
289946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                options.inSampleSize = sampleSize;
2901cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                try {
291946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                    b = BitmapFactory.decodeStream(input, null, options);
2923d8877fa3e07c019d1779793ca0081beca177b6aTom Taylor                    if (b == null) {
2933d8877fa3e07c019d1779793ca0081beca177b6aTom Taylor                        return null;    // Couldn't decode and it wasn't because of an exception,
2943d8877fa3e07c019d1779793ca0081beca177b6aTom Taylor                                        // bail.
2953d8877fa3e07c019d1779793ca0081beca177b6aTom Taylor                    }
296946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                } catch (OutOfMemoryError e) {
29737880013315d8b7abefd9081b2c36b060083d6faTom Taylor                    Log.w(TAG, "getResizedBitmap: img too large to decode (OutOfMemoryError), " +
298946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                            "may try with larger sampleSize. Curr sampleSize=" + sampleSize);
299946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                    sampleSize *= 2;    // works best as a power of two
300946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                    attempts++;
301946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                    continue;
302946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                } finally {
303946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                    if (input != null) {
304946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                        try {
305946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                            input.close();
306946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                        } catch (IOException e) {
307946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                            Log.e(TAG, e.getMessage(), e);
308946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                        }
309f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                    }
310946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                }
311946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            } while (b == null && attempts < NUMBER_OF_RESIZE_ATTEMPTS);
312946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev
313946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            if (b == null) {
31451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)
31551e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                        && attempts >= NUMBER_OF_RESIZE_ATTEMPTS) {
31651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                    Log.v(TAG, "getResizedImageData: gave up after too many attempts to resize");
31751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                }
318946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                return null;
319946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            }
320946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev
321852b629e3be73f8eb01651f1868f3ed1857df815Tom Taylor            boolean resultTooBig = true;
322946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            attempts = 1;   // reset count for second loop
323946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            // In this loop, we attempt to compress/resize the content to fit the given dimension
324946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            // and file-size limits.
325946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev            do {
326946c8e09668d50bd1657a253a0220b807a4d1f28Todor Kalaydjiev                try {
3271c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                    if (options.outWidth > widthLimit || options.outHeight > heightLimit ||
3281c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                            (os != null && os.size() > byteLimit)) {
3291cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                        // The decoder does not support the inSampleSize option.
3301cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                        // Scale the bitmap using Bitmap library.
3318c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                        int scaledWidth = (int)(outWidth * scaleFactor);
3328c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                        int scaledHeight = (int)(outHeight * scaleFactor);
3339242286c35743422051d439d7460cd0a1426899cWei Huang
3349242286c35743422051d439d7460cd0a1426899cWei Huang                        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
3351cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                            Log.v(TAG, "getResizedImageData: retry scaling using " +
3361cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                                    "Bitmap.createScaledBitmap: w=" + scaledWidth +
3371cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                                    ", h=" + scaledHeight);
3389242286c35743422051d439d7460cd0a1426899cWei Huang                        }
3399242286c35743422051d439d7460cd0a1426899cWei Huang
3408c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                        b = Bitmap.createScaledBitmap(b, scaledWidth, scaledHeight, false);
3411cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                        if (b == null) {
34251e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
34351e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                                Log.v(TAG, "Bitmap.createScaledBitmap returned NULL!");
34451e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                            }
3451cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                            return null;
3461cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                        }
347f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                    }
348f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson
3491cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    // Compress the image into a JPG. Start with MessageUtils.IMAGE_COMPRESSION_QUALITY.
3501cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    // In case that the image byte size is still too large reduce the quality in
3516798ba71f8b01d382bdc83b7f40757aa89c280cdTodor Kalaydjiev                    // proportion to the desired byte size.
3521cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    os = new ByteArrayOutputStream();
3531cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    b.compress(CompressFormat.JPEG, quality, os);
3541cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    int jpgFileSize = os.size();
3551cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    if (jpgFileSize > byteLimit) {
3566798ba71f8b01d382bdc83b7f40757aa89c280cdTodor Kalaydjiev                        quality = (quality * byteLimit) / jpgFileSize;  // watch for int division!
3571c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                        if (quality < MessageUtils.MINIMUM_IMAGE_COMPRESSION_QUALITY) {
3581c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                            quality = MessageUtils.MINIMUM_IMAGE_COMPRESSION_QUALITY;
3591c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                        }
3601cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor
3611c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
3621c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                            Log.v(TAG, "getResizedImageData: compress(2) w/ quality=" + quality);
3631cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                        }
3641c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev
3651c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                        os = new ByteArrayOutputStream();
3661c4ee29d168bf380054499201e6e7c262d53e4a7Todor Kalaydjiev                        b.compress(CompressFormat.JPEG, quality, os);
3671cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    }
3681cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                } catch (java.lang.OutOfMemoryError e) {
3695a3151adedd991252dd529c75e85a3374970dfb0Tom Taylor                    Log.w(TAG, "getResizedImageData - image too big (OutOfMemoryError), will try "
3705a3151adedd991252dd529c75e85a3374970dfb0Tom Taylor                            + " with smaller scale factor, cur scale factor: " + scaleFactor);
3711cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                    // fall through and keep trying with a smaller scale factor.
3721cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                }
3739242286c35743422051d439d7460cd0a1426899cWei Huang                if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
374f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                    Log.v(TAG, "attempt=" + attempts
3751cf7f03f05cf0a1172f027f75f67a6fff336c79bTom Taylor                            + " size=" + (os == null ? 0 : os.size())
3768c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                            + " width=" + outWidth * scaleFactor
3778c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                            + " height=" + outHeight * scaleFactor
378f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                            + " scaleFactor=" + scaleFactor
379f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                            + " quality=" + quality);
380f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                }
3818c86a52a2c13083a25761256ab0dfc416260c162Tom Taylor                scaleFactor *= .75F;
382f79b803adc0cee3778cf6aac52d203896cde5d95Christian Gustafsson                attempts++;
383852b629e3be73f8eb01651f1868f3ed1857df815Tom Taylor                resultTooBig = os == null || os.size() > byteLimit;
384852b629e3be73f8eb01651f1868f3ed1857df815Tom Taylor            } while (resultTooBig && attempts < NUMBER_OF_RESIZE_ATTEMPTS);
385fa9be169b5a572e0e143e582079259ee074d9d5aTom Taylor            b.recycle();        // done with the bitmap, release the memory
38651e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            if (Log.isLoggable(LogTag.APP, Log.VERBOSE) && resultTooBig) {
38751e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor                Log.v(TAG, "getResizedImageData returning NULL because the result is too big: " +
388852b629e3be73f8eb01651f1868f3ed1857df815Tom Taylor                        " requested max: " + byteLimit + " actual: " + os.size());
38951e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor            }
39051e4621fa12400b1e79cc18b7bb0f9a83af6b622Tom Taylor
391852b629e3be73f8eb01651f1868f3ed1857df815Tom Taylor            return resultTooBig ? null : os.toByteArray();
39272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        } catch (FileNotFoundException e) {
39372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            Log.e(TAG, e.getMessage(), e);
39472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project            return null;
3950ca1428f36dce56a402e19d6b6c04c4c1cc1e69aTom Taylor        } catch (java.lang.OutOfMemoryError e) {
3960ca1428f36dce56a402e19d6b6c04c4c1cc1e69aTom Taylor            Log.e(TAG, e.getMessage(), e);
3970ca1428f36dce56a402e19d6b6c04c4c1cc1e69aTom Taylor            return null;
39872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project        }
39972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project    }
40072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project}
401