Util.java revision cd972b0cd67d7a45084d9757ebd49b8f22989edd
1101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin/*
2101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * Copyright (C) 2009 The Android Open Source Project
3101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin *
4101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * you may not use this file except in compliance with the License.
6101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * You may obtain a copy of the License at
7101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin *
8101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin *
10101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * Unless required by applicable law or agreed to in writing, software
11101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * See the License for the specific language governing permissions and
14101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * limitations under the License.
15101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin */
16101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
17937fc48b37fafe3ffc8f4b52bd9a171bbb4d3a37Owen Linpackage com.android.camera;
18101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
193f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport android.app.Activity;
203f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport android.app.AlertDialog;
21601ce56b034f111b2875e7636776f2f359f6a156Owen Linimport android.app.ProgressDialog;
22c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Changimport android.content.ContentResolver;
233f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport android.content.DialogInterface;
24bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Changimport android.content.Intent;
25101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.graphics.Bitmap;
26101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.graphics.BitmapFactory;
2796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Changimport android.graphics.Canvas;
28101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.graphics.Matrix;
2996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Changimport android.graphics.Rect;
30101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.media.MediaMetadataRetriever;
31c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Changimport android.net.Uri;
32482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Linimport android.os.Handler;
33101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.os.ParcelFileDescriptor;
34101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport android.util.Log;
35601ce56b034f111b2875e7636776f2f359f6a156Owen Linimport android.view.View;
36601ce56b034f111b2875e7636776f2f359f6a156Owen Linimport android.view.View.OnClickListener;
372c6c6174e2363fccb4e4f29b76290e99234fe140Owen Linimport android.view.animation.Animation;
382c6c6174e2363fccb4e4f29b76290e99234fe140Owen Linimport android.view.animation.TranslateAnimation;
39101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
403f3c857e3f34650c15d764810335024654b0fcc3Owen Linimport com.android.camera.gallery.IImage;
413f3c857e3f34650c15d764810335024654b0fcc3Owen Lin
42101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport java.io.ByteArrayOutputStream;
43101d528d59f447847d5c6b7578642132c2b76f4fOwen Linimport java.io.Closeable;
44c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Changimport java.io.FileDescriptor;
45d30b5877fbdedcebc0af5cccffddccfe9b11cb0bOwen Linimport java.io.IOException;
46101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
47101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin/**
48101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin * Collection of utility functions used in this package.
49101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin */
50101d528d59f447847d5c6b7578642132c2b76f4fOwen Linpublic class Util {
5134fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang    private static final String TAG = "Util";
522c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    public static final int DIRECTION_LEFT = 0;
532c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    public static final int DIRECTION_RIGHT = 1;
542c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    public static final int DIRECTION_UP = 2;
552c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    public static final int DIRECTION_DOWN = 3;
56101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
57601ce56b034f111b2875e7636776f2f359f6a156Owen Lin    private static OnClickListener sNullOnClickListener;
58601ce56b034f111b2875e7636776f2f359f6a156Owen Lin
59101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    private Util() {
60101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
61101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
62101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    // Rotates the bitmap by the specified degree.
63101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    // If a new bitmap is created, the original bitmap is recycled.
64101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    public static Bitmap rotate(Bitmap b, int degrees) {
65101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (degrees != 0 && b != null) {
66101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            Matrix m = new Matrix();
67101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            m.setRotate(degrees,
68101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
69101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            try {
70101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                Bitmap b2 = Bitmap.createBitmap(
71101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
72101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                if (b != b2) {
73101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                    b.recycle();
74101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                    b = b2;
75101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                }
76101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            } catch (OutOfMemoryError ex) {
77101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                // We have no memory to rotate. Return the original bitmap.
78101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            }
79101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
80101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        return b;
81101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
82101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
83101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    /*
84ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * Compute the sample size as a function of minSideLength
85ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * and maxNumOfPixels.
863c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * minSideLength is used to specify that minimal width or height of a
873c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * bitmap.
883c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * maxNumOfPixels is used to specify the maximal size in pixels that is
893c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * tolerable in terms of memory usage.
90ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     *
91ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * The function returns a sample size based on the constraints.
92ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
93ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * which indicates no care of the corresponding constraint.
94ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * The functions prefers returning a sample size that
95ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
963c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     *
973c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * Also, the function rounds up the sample size to a power of 2 or multiple
983c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * of 8 because BitmapFactory only honors sample size this way.
993c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * For example, BitmapFactory downsamples an image by 2 even though the
1003c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen     * request is 3. So we round up the sample size to avoid OOM.
101101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     */
102ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang    public static int computeSampleSize(BitmapFactory.Options options,
103ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            int minSideLength, int maxNumOfPixels) {
1043c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen        int initialSize = computeInitialSampleSize(options, minSideLength,
1053c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen                maxNumOfPixels);
1063c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen
1073c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen        int roundedSize;
108522e836abc90bcb2d4e7a4ac37e269154d06c715Chih-Chung Chang        if (initialSize <= 8) {
1093c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen            roundedSize = 1;
1103c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen            while (roundedSize < initialSize) {
1113c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen                roundedSize <<= 1;
1123c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen            }
1133c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen        } else {
1143c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen            roundedSize = (initialSize + 7) / 8 * 8;
1153c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen        }
1163c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen
1173c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen        return roundedSize;
1183c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen    }
1193c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen
1203c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen    private static int computeInitialSampleSize(BitmapFactory.Options options,
1213c21008c953cf6177f7771c176fa81d336e74ed8Wei-Ta Chen            int minSideLength, int maxNumOfPixels) {
122ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        double w = options.outWidth;
123ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        double h = options.outHeight;
124ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang
125ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 :
126ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
127ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 :
128ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                (int) Math.min(Math.floor(w / minSideLength),
129ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                Math.floor(h / minSideLength));
130ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang
131f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen        if (upperBound < lowerBound) {
132f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen            // return the larger one when there is no overlapping zone.
133f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen            return lowerBound;
134f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen        }
135f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen
136ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        if ((maxNumOfPixels == IImage.UNCONSTRAINED) &&
137ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                (minSideLength == IImage.UNCONSTRAINED)) {
138ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            return 1;
139ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        } else if (minSideLength == IImage.UNCONSTRAINED) {
140ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            return lowerBound;
141ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        } else {
142ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            return upperBound;
143101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
144101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
145101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
14634fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang    // Whether we should recycle the input (unless the output is the input).
147522e836abc90bcb2d4e7a4ac37e269154d06c715Chih-Chung Chang    public static final boolean RECYCLE_INPUT = true;
148522e836abc90bcb2d4e7a4ac37e269154d06c715Chih-Chung Chang    public static final boolean NO_RECYCLE_INPUT = false;
14934fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang
15096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang    public static Bitmap transform(Matrix scaler,
15196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                                   Bitmap source,
15296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                                   int targetWidth,
15396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                                   int targetHeight,
15434fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                                   boolean scaleUp,
15534fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                                   boolean recycle) {
15696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        int deltaX = source.getWidth() - targetWidth;
15796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        int deltaY = source.getHeight() - targetHeight;
15896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
15996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            /*
16096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang             * In this case the bitmap is smaller, at least in one dimension,
16196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang             * than the target.  Transform it by placing as much of the image
16296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang             * as possible into the target and leaving the top/bottom or
16396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang             * left/right (or both) black.
16496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang             */
16596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
16696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    Bitmap.Config.ARGB_8888);
16796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            Canvas c = new Canvas(b2);
16896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
16996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            int deltaXHalf = Math.max(0, deltaX / 2);
17096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            int deltaYHalf = Math.max(0, deltaY / 2);
17196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            Rect src = new Rect(
17296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    deltaXHalf,
17396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    deltaYHalf,
17496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    deltaXHalf + Math.min(targetWidth, source.getWidth()),
17596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    deltaYHalf + Math.min(targetHeight, source.getHeight()));
17696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            int dstX = (targetWidth  - src.width())  / 2;
17796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            int dstY = (targetHeight - src.height()) / 2;
17896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            Rect dst = new Rect(
17996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    dstX,
18096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    dstY,
18196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    targetWidth - dstX,
18296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    targetHeight - dstY);
18396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            c.drawBitmap(source, src, dst, null);
18434fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang            if (recycle) {
18534fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                source.recycle();
18634fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang            }
18796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            return b2;
18896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        }
18996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        float bitmapWidthF = source.getWidth();
19096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        float bitmapHeightF = source.getHeight();
19196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
19296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        float bitmapAspect = bitmapWidthF / bitmapHeightF;
19358e94eddd371a9bb7c04936dbeb99615fd1a0c2cChih-Chung Chang        float viewAspect   = (float) targetWidth / targetHeight;
19496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
19596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        if (bitmapAspect > viewAspect) {
19696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            float scale = targetHeight / bitmapHeightF;
19796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            if (scale < .9F || scale > 1F) {
19896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                scaler.setScale(scale, scale);
19996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            } else {
20096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                scaler = null;
20196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            }
20296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        } else {
20396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            float scale = targetWidth / bitmapWidthF;
20496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            if (scale < .9F || scale > 1F) {
20596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                scaler.setScale(scale, scale);
20696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            } else {
20796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                scaler = null;
20896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            }
20996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        }
21096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
21196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        Bitmap b1;
21296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        if (scaler != null) {
21396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            // this is used for minithumb and crop, so we want to filter here.
21496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            b1 = Bitmap.createBitmap(source, 0, 0,
21596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                    source.getWidth(), source.getHeight(), scaler, true);
21696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        } else {
21796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang            b1 = source;
21896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        }
21996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
22034fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang        if (recycle && b1 != source) {
22134fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang            source.recycle();
22234fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang        }
22334fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang
22496b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        int dx1 = Math.max(0, b1.getWidth() - targetWidth);
22596b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        int dy1 = Math.max(0, b1.getHeight() - targetHeight);
22696b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
22796b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        Bitmap b2 = Bitmap.createBitmap(
22896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                b1,
22996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                dx1 / 2,
23096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                dy1 / 2,
23196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                targetWidth,
23296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang                targetHeight);
23396b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
23434fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang        if (b2 != b1) {
23534fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang            if (recycle || b1 != source) {
23634fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                b1.recycle();
23734fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang            }
23896b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        }
23996b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
24096b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang        return b2;
24196b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang    }
24296b3c6ee6d93c136bb8270521158a989584966b1Chih-Chung Chang
243101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    /**
24434fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang     * Creates a centered bitmap of the desired size.
245101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * @param source
24634fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang     * @param recycle whether we want to recycle the input
247101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     */
248101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    public static Bitmap extractMiniThumb(
249101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            Bitmap source, int width, int height, boolean recycle) {
250101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (source == null) {
251101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            return null;
252101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
253101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
254101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        float scale;
255101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (source.getWidth() < source.getHeight()) {
256101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            scale = width / (float) source.getWidth();
257101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } else {
258101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            scale = height / (float) source.getHeight();
259101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
260101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        Matrix matrix = new Matrix();
261101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        matrix.setScale(scale, scale);
262522e836abc90bcb2d4e7a4ac37e269154d06c715Chih-Chung Chang        Bitmap miniThumbnail = transform(matrix, source, width, height,
263697ea6ef1d84bc707881e736ff1148e3a30ed445Chih-Chung Chang                true, recycle);
264101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        return miniThumbnail;
265101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
266101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
267101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    /**
268101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * Creates a byte[] for a given bitmap of the desired size. Recycles the
269101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * input bitmap.
270101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     */
271101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    public static byte[] miniThumbData(Bitmap source) {
272101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (source == null) return null;
273101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
274101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        Bitmap miniThumbnail = extractMiniThumb(
2750a475e11e2ceadbaa70308b52e06f4e6d9e56f12Chih-Chung Chang                source, IImage.MINI_THUMB_TARGET_SIZE,
27634fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                IImage.MINI_THUMB_TARGET_SIZE,
27734fe2a9d33a5b4ce49862ce4558a36c62ba6d540Chih-Chung Chang                Util.RECYCLE_INPUT);
278101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
279101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        ByteArrayOutputStream miniOutStream = new ByteArrayOutputStream();
280101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        miniThumbnail.compress(Bitmap.CompressFormat.JPEG, 75, miniOutStream);
281101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        miniThumbnail.recycle();
282101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
283101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        try {
284101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            miniOutStream.close();
285101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            byte [] data = miniOutStream.toByteArray();
286101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            return data;
287101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } catch (java.io.IOException ex) {
288101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            Log.e(TAG, "got exception ex " + ex);
289101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
290101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        return null;
291101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
292101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
293101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    /**
294101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * Create a video thumbnail for a video. May return null if the video is
295101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * corrupt.
296101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     *
297101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     * @param filePath
298101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin     */
299101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    public static Bitmap createVideoThumbnail(String filePath) {
300101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        Bitmap bitmap = null;
301101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
302101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        try {
303101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
304101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            retriever.setDataSource(filePath);
305101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            bitmap = retriever.captureFrame();
306101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } catch (IllegalArgumentException ex) {
307101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            // Assume this is a corrupt video file
308101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } catch (RuntimeException ex) {
309101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            // Assume this is a corrupt video file.
310101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } finally {
311101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            try {
312101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                retriever.release();
313101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            } catch (RuntimeException ex) {
314101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                // Ignore failures while cleaning up.
315101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            }
316101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
317101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        return bitmap;
318101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
319101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
320bbc2f2f15a865cf0330cd78a6ea67177c5863b3bOwen Lin    public static <T>  int indexOf(T [] array, T s) {
321101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        for (int i = 0; i < array.length; i++) {
322101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            if (array[i].equals(s)) {
323101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin                return i;
324101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            }
325101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
326101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        return -1;
327101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
328101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
329724e9b3db93677b753c1267a716e30e6cf5ff3b6Chih-Chung Chang    public static void closeSilently(Closeable c) {
330101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (c == null) return;
331101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        try {
332101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            c.close();
333101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } catch (Throwable t) {
334101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            // do nothing
335101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
336101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
337101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
338724e9b3db93677b753c1267a716e30e6cf5ff3b6Chih-Chung Chang    public static void closeSilently(ParcelFileDescriptor c) {
339101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        if (c == null) return;
340101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        try {
341101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            c.close();
342101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        } catch (Throwable t) {
343101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin            // do nothing
344101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin        }
345101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin    }
346101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin
347c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang    /**
348c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang     * Make a bitmap from a given Uri.
349c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang     *
350c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang     * @param uri
351c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang     */
352ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
353ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            Uri uri, ContentResolver cr) {
354ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        return makeBitmap(minSideLength, maxNumOfPixels, uri, cr,
355ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                IImage.NO_NATIVE);
3564250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang    }
357f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen
358ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
359ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            Uri uri, ContentResolver cr, boolean useNative) {
360c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        ParcelFileDescriptor input = null;
361c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        try {
362c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            input = cr.openFileDescriptor(uri, "r");
3634250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang            BitmapFactory.Options options = null;
3644250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang            if (useNative) {
3654250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang                options = createNativeAllocOptions();
3664250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang            }
367ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            return makeBitmap(minSideLength, maxNumOfPixels, uri, cr, input,
368ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                    options);
369c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        } catch (IOException ex) {
370c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            return null;
371c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        } finally {
372724e9b3db93677b753c1267a716e30e6cf5ff3b6Chih-Chung Chang            closeSilently(input);
373c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        }
374c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang    }
375c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang
376ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
3774250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang            ParcelFileDescriptor pfd, boolean useNative) {
3784250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang        BitmapFactory.Options options = null;
3794250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang        if (useNative) {
3804250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang            options = createNativeAllocOptions();
3814250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang        }
382ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang        return makeBitmap(minSideLength, maxNumOfPixels, null, null, pfd,
383ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                options);
3844250e214f539a3caee1434889c5660d594b45dfdChih-Chung Chang    }
385f2c7e983fafaf7639a6ad090a93d1892015777edWei-Ta Chen
386ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang    public static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
387ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
388c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            BitmapFactory.Options options) {
389c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        try {
390c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            if (pfd == null) pfd = makeInputStream(uri, cr);
391c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            if (pfd == null) return null;
392c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            if (options == null) options = new BitmapFactory.Options();
393c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang
394c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            FileDescriptor fd = pfd.getFileDescriptor();
395ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            options.inJustDecodeBounds = true;
396ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            BitmapManager.instance().decodeFileDescriptor(fd, options);
397ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            if (options.mCancel || options.outWidth == -1
398ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                    || options.outHeight == -1) {
399ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                return null;
400c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            }
401ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            options.inSampleSize = computeSampleSize(
402ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang                    options, minSideLength, maxNumOfPixels);
403ce033a5871a0b678521411f9a3acaee2ab279178Chih-Chung Chang            options.inJustDecodeBounds = false;
404c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang
405c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            options.inDither = false;
406c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
407cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            return BitmapManager.instance().decodeFileDescriptor(fd, options);
408c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        } catch (OutOfMemoryError ex) {
409c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            Log.e(TAG, "Got oom exception ", ex);
410c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            return null;
411c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        } finally {
412724e9b3db93677b753c1267a716e30e6cf5ff3b6Chih-Chung Chang            closeSilently(pfd);
413c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        }
414cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang    }
415cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang
416cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang    public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) {
417cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang        try {
418cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            BitmapFactory.Options options = new BitmapFactory.Options();
419cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            options.inJustDecodeBounds = true;
420cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
421cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang                    options);
422cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            if (options.mCancel || options.outWidth == -1
423cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang                    || options.outHeight == -1) {
424cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang                return null;
425cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            }
426cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            options.inSampleSize = computeSampleSize(
427cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang                    options, IImage.UNCONSTRAINED, maxNumOfPixels);
428cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            options.inJustDecodeBounds = false;
429cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang
430cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            options.inDither = false;
431cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
432cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
433cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang                    options);
434cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang        } catch (OutOfMemoryError ex) {
435cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            Log.e(TAG, "Got oom exception ", ex);
436cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang            return null;
437cd972b0cd67d7a45084d9757ebd49b8f22989eddChih-Chung Chang        }
438c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang    }
439c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang
440c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang    private static ParcelFileDescriptor makeInputStream(
441c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            Uri uri, ContentResolver cr) {
442c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        try {
443c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            return cr.openFileDescriptor(uri, "r");
444c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        } catch (IOException ex) {
445c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang            return null;
446c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang        }
447c1c20e9ce386c1f660059acaede591d2d718995aChih-Chung Chang    }
448fa021af9397011cf180003139e80a88902c4b70aChih-Chung Chang
4494177add422a041ea9f1007c05ff42361bf529d52Chih-Chung Chang    public static synchronized OnClickListener getNullOnClickListener() {
450601ce56b034f111b2875e7636776f2f359f6a156Owen Lin        if (sNullOnClickListener == null) {
451601ce56b034f111b2875e7636776f2f359f6a156Owen Lin            sNullOnClickListener = new OnClickListener() {
452601ce56b034f111b2875e7636776f2f359f6a156Owen Lin                public void onClick(View v) {
453601ce56b034f111b2875e7636776f2f359f6a156Owen Lin                }
454601ce56b034f111b2875e7636776f2f359f6a156Owen Lin            };
455601ce56b034f111b2875e7636776f2f359f6a156Owen Lin        }
456601ce56b034f111b2875e7636776f2f359f6a156Owen Lin        return sNullOnClickListener;
457601ce56b034f111b2875e7636776f2f359f6a156Owen Lin    }
458341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang
459341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang    public static void Assert(boolean cond) {
460341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang        if (!cond) {
461341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang            throw new AssertionError();
462341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang        }
463341ad98e59d92769e1cc8ba68fa86c72dce0543bChih-Chung Chang    }
464059daa36dc6e83ca24a16d9b99925e71010bccebOwen Lin
465b0e1282810ec68fd7585beb9ab9afab9bcafe1bdOwen Lin    public static boolean equals(String a, String b) {
466b0e1282810ec68fd7585beb9ab9afab9bcafe1bdOwen Lin        // return true if both string are null or the content equals
467b0e1282810ec68fd7585beb9ab9afab9bcafe1bdOwen Lin        return a == b || a.equals(b);
468b0e1282810ec68fd7585beb9ab9afab9bcafe1bdOwen Lin    }
469482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
470482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin    private static class BackgroundJob
471482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            extends MonitoredActivity.LifeCycleAdapter implements Runnable {
472482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
473482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        private final MonitoredActivity mActivity;
474482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        private final ProgressDialog mDialog;
475482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        private final Runnable mJob;
476482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        private final Handler mHandler;
4776fa5404fd6c34d401c56accd059a706d8a832812Owen Lin        private final Runnable mCleanupRunner = new Runnable() {
4786fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            public void run() {
4796fa5404fd6c34d401c56accd059a706d8a832812Owen Lin                mActivity.removeLifeCycleListener(BackgroundJob.this);
4806fa5404fd6c34d401c56accd059a706d8a832812Owen Lin                if (mDialog.getWindow() != null) mDialog.dismiss();
4816fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            }
4826fa5404fd6c34d401c56accd059a706d8a832812Owen Lin        };
483482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
484482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        public BackgroundJob(MonitoredActivity activity, Runnable job,
485482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin                ProgressDialog dialog, Handler handler) {
486482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mActivity = activity;
487482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mDialog = dialog;
488482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mJob = job;
489482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mActivity.addLifeCycleListener(this);
490482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mHandler = handler;
491482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        }
492482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
493482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        public void run() {
494482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            try {
495482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin                mJob.run();
496482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            } finally {
4976fa5404fd6c34d401c56accd059a706d8a832812Owen Lin                mHandler.post(mCleanupRunner);
498482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            }
499482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        }
500482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
5016fa5404fd6c34d401c56accd059a706d8a832812Owen Lin
5026fa5404fd6c34d401c56accd059a706d8a832812Owen Lin        @Override
5036fa5404fd6c34d401c56accd059a706d8a832812Owen Lin        public void onActivityDestroyed(MonitoredActivity activity) {
5046fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            // We get here only when the onDestroyed being called before
5056fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            // the mCleanupRunner. So, run it now and remove it from the queue
5066fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            mCleanupRunner.run();
5076fa5404fd6c34d401c56accd059a706d8a832812Owen Lin            mHandler.removeCallbacks(mCleanupRunner);
5086fa5404fd6c34d401c56accd059a706d8a832812Owen Lin        }
5096fa5404fd6c34d401c56accd059a706d8a832812Owen Lin
510482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        @Override
511482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        public void onActivityStopped(MonitoredActivity activity) {
512482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mDialog.hide();
513482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        }
514482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
515482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        @Override
516482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        public void onActivityStarted(MonitoredActivity activity) {
517482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            mDialog.show();
518482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        }
519482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin    }
520482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin
521482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin    public static void startBackgroundJob(MonitoredActivity activity,
522482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin            String title, String message, Runnable job, Handler handler) {
523482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        // Make the progress dialog uncancelable, so that we can gurantee
524482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        // the thread will be done before the activity getting destroyed.
525482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        ProgressDialog dialog = ProgressDialog.show(
526482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin                activity, title, message, true, false);
527482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin        new Thread(new BackgroundJob(activity, job, dialog, handler)).start();
528482a3a54cfa8ec2cc913efc0e73a01d78453ee6dOwen Lin    }
529bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang
530bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang    // Returns an intent which is used for "set as" menu items.
531bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang    public static Intent createSetAsIntent(IImage image) {
532bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang        Uri u = image.fullSizeImageUri();
533bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang        Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
534bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang        intent.setDataAndType(u, image.getMimeType());
535bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang        intent.putExtra("mimeType", image.getMimeType());
536bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang        return intent;
537bb1877847ae050918fe591d9b7fa63deaf645746Chih-Chung Chang    }
538d5d74642d7c0b43578b43cdd46b70671b64c88e2Wei-Ta Chen
539d5d74642d7c0b43578b43cdd46b70671b64c88e2Wei-Ta Chen    // Returns Options that set the puregeable flag for Bitmap decode.
540026dff889c80763fc322ed03e2d3a76750320138Wei-Ta Chen    public static BitmapFactory.Options createNativeAllocOptions() {
541d5d74642d7c0b43578b43cdd46b70671b64c88e2Wei-Ta Chen        BitmapFactory.Options options = new BitmapFactory.Options();
542026dff889c80763fc322ed03e2d3a76750320138Wei-Ta Chen        options.inNativeAlloc = true;
543d5d74642d7c0b43578b43cdd46b70671b64c88e2Wei-Ta Chen        return options;
544d5d74642d7c0b43578b43cdd46b70671b64c88e2Wei-Ta Chen    }
5453f3c857e3f34650c15d764810335024654b0fcc3Owen Lin
5463f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    public static void showFatalErrorAndFinish(
5473f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            final Activity activity, String title, String message) {
5483f3c857e3f34650c15d764810335024654b0fcc3Owen Lin        DialogInterface.OnClickListener buttonListener =
5493f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                new DialogInterface.OnClickListener() {
5503f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            public void onClick(DialogInterface dialog, int which) {
5513f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                activity.finish();
5523f3c857e3f34650c15d764810335024654b0fcc3Owen Lin            }
5533f3c857e3f34650c15d764810335024654b0fcc3Owen Lin        };
5543f3c857e3f34650c15d764810335024654b0fcc3Owen Lin        new AlertDialog.Builder(activity)
5553f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .setCancelable(false)
5563f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .setIcon(android.R.drawable.ic_dialog_alert)
5573f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .setTitle(title)
5583f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .setMessage(message)
5593f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .setNeutralButton(R.string.details_ok, buttonListener)
5603f3c857e3f34650c15d764810335024654b0fcc3Owen Lin                .show();
5613f3c857e3f34650c15d764810335024654b0fcc3Owen Lin    }
5622c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin
563c1f2e3021bf7b12f2a33561e09bd0ab32530382eOwen Lin    public static Animation slideOut(View view, int to) {
5642c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        view.setVisibility(View.INVISIBLE);
5652c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        Animation anim;
5662c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        switch (to) {
5672c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_LEFT:
5682c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, -view.getWidth(), 0, 0);
5692c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5702c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_RIGHT:
5712c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, view.getWidth(), 0, 0);
5722c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5732c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_UP:
5742c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, 0, 0, -view.getHeight());
5752c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5762c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_DOWN:
5772c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, 0, 0, view.getHeight());
5782c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5792c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            default:
5802c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                throw new IllegalArgumentException(Integer.toString(to));
5812c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        }
5822c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        anim.setDuration(500);
5832c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        view.startAnimation(anim);
584c1f2e3021bf7b12f2a33561e09bd0ab32530382eOwen Lin        return anim;
5852c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    }
5862c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin
587c1f2e3021bf7b12f2a33561e09bd0ab32530382eOwen Lin    public static Animation slideIn(View view, int from) {
5882c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        view.setVisibility(View.VISIBLE);
5892c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        Animation anim;
5902c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        switch (from) {
5912c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_LEFT:
5922c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0);
5932c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5942c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_RIGHT:
5952c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(view.getWidth(), 0, 0, 0);
5962c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
5972c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_UP:
5982c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, 0, -view.getHeight(), 0);
5992c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
6002c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            case DIRECTION_DOWN:
6012c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                anim = new TranslateAnimation(0, 0, view.getHeight(), 0);
6022c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                break;
6032c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin            default:
6042c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin                throw new IllegalArgumentException(Integer.toString(from));
6052c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        }
6062c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        anim.setDuration(500);
6072c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        view.startAnimation(anim);
608c1f2e3021bf7b12f2a33561e09bd0ab32530382eOwen Lin        return anim;
6092c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    }
6102c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin
6112c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    public static <T> T checkNotNull(T object) {
6122c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        if (object == null) throw new NullPointerException();
6132c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin        return object;
6142c6c6174e2363fccb4e4f29b76290e99234fe140Owen Lin    }
615101d528d59f447847d5c6b7578642132c2b76f4fOwen Lin}
616