Util.java revision 9a4c2cac83f50e29063d27605c9f8d9e676a6f22
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.DialogInterface;
22import android.graphics.Bitmap;
23import android.graphics.BitmapFactory;
24import android.graphics.Matrix;
25import android.util.Log;
26import android.view.View;
27import android.view.animation.Animation;
28import android.view.animation.TranslateAnimation;
29
30import com.android.camera.gallery.IImage;
31
32import java.io.Closeable;
33
34/**
35 * Collection of utility functions used in this package.
36 */
37public class Util {
38    private static final String TAG = "Util";
39    public static final int DIRECTION_LEFT = 0;
40    public static final int DIRECTION_RIGHT = 1;
41    public static final int DIRECTION_UP = 2;
42    public static final int DIRECTION_DOWN = 3;
43
44    private Util() {
45    }
46
47    // Rotates the bitmap by the specified degree.
48    // If a new bitmap is created, the original bitmap is recycled.
49    public static Bitmap rotate(Bitmap b, int degrees) {
50        if (degrees != 0 && b != null) {
51            Matrix m = new Matrix();
52            m.setRotate(degrees,
53                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
54            try {
55                Bitmap b2 = Bitmap.createBitmap(
56                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
57                if (b != b2) {
58                    b.recycle();
59                    b = b2;
60                }
61            } catch (OutOfMemoryError ex) {
62                // We have no memory to rotate. Return the original bitmap.
63            }
64        }
65        return b;
66    }
67
68    /*
69     * Compute the sample size as a function of minSideLength
70     * and maxNumOfPixels.
71     * minSideLength is used to specify that minimal width or height of a
72     * bitmap.
73     * maxNumOfPixels is used to specify the maximal size in pixels that is
74     * tolerable in terms of memory usage.
75     *
76     * The function returns a sample size based on the constraints.
77     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
78     * which indicates no care of the corresponding constraint.
79     * The functions prefers returning a sample size that
80     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
81     *
82     * Also, the function rounds up the sample size to a power of 2 or multiple
83     * of 8 because BitmapFactory only honors sample size this way.
84     * For example, BitmapFactory downsamples an image by 2 even though the
85     * request is 3. So we round up the sample size to avoid OOM.
86     */
87    public static int computeSampleSize(BitmapFactory.Options options,
88            int minSideLength, int maxNumOfPixels) {
89        int initialSize = computeInitialSampleSize(options, minSideLength,
90                maxNumOfPixels);
91
92        int roundedSize;
93        if (initialSize <= 8) {
94            roundedSize = 1;
95            while (roundedSize < initialSize) {
96                roundedSize <<= 1;
97            }
98        } else {
99            roundedSize = (initialSize + 7) / 8 * 8;
100        }
101
102        return roundedSize;
103    }
104
105    private static int computeInitialSampleSize(BitmapFactory.Options options,
106            int minSideLength, int maxNumOfPixels) {
107        double w = options.outWidth;
108        double h = options.outHeight;
109
110        int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 :
111                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
112        int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 :
113                (int) Math.min(Math.floor(w / minSideLength),
114                Math.floor(h / minSideLength));
115
116        if (upperBound < lowerBound) {
117            // return the larger one when there is no overlapping zone.
118            return lowerBound;
119        }
120
121        if ((maxNumOfPixels == IImage.UNCONSTRAINED) &&
122                (minSideLength == IImage.UNCONSTRAINED)) {
123            return 1;
124        } else if (minSideLength == IImage.UNCONSTRAINED) {
125            return lowerBound;
126        } else {
127            return upperBound;
128        }
129    }
130
131    public static <T>  int indexOf(T [] array, T s) {
132        for (int i = 0; i < array.length; i++) {
133            if (array[i].equals(s)) {
134                return i;
135            }
136        }
137        return -1;
138    }
139
140    public static void closeSilently(Closeable c) {
141        if (c == null) return;
142        try {
143            c.close();
144        } catch (Throwable t) {
145            // do nothing
146        }
147    }
148
149    public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) {
150        try {
151            BitmapFactory.Options options = new BitmapFactory.Options();
152            options.inJustDecodeBounds = true;
153            BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
154                    options);
155            if (options.mCancel || options.outWidth == -1
156                    || options.outHeight == -1) {
157                return null;
158            }
159            options.inSampleSize = computeSampleSize(
160                    options, IImage.UNCONSTRAINED, maxNumOfPixels);
161            options.inJustDecodeBounds = false;
162
163            options.inDither = false;
164            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
165            return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
166                    options);
167        } catch (OutOfMemoryError ex) {
168            Log.e(TAG, "Got oom exception ", ex);
169            return null;
170        }
171    }
172
173    public static void Assert(boolean cond) {
174        if (!cond) {
175            throw new AssertionError();
176        }
177    }
178
179    public static void showFatalErrorAndFinish(
180            final Activity activity, String title, String message) {
181        DialogInterface.OnClickListener buttonListener =
182                new DialogInterface.OnClickListener() {
183            public void onClick(DialogInterface dialog, int which) {
184                activity.finish();
185            }
186        };
187        new AlertDialog.Builder(activity)
188                .setCancelable(false)
189                .setIcon(android.R.drawable.ic_dialog_alert)
190                .setTitle(title)
191                .setMessage(message)
192                .setNeutralButton(R.string.details_ok, buttonListener)
193                .show();
194    }
195
196    public static Animation slideOut(View view, int to) {
197        view.setVisibility(View.INVISIBLE);
198        Animation anim;
199        switch (to) {
200            case DIRECTION_LEFT:
201                anim = new TranslateAnimation(0, -view.getWidth(), 0, 0);
202                break;
203            case DIRECTION_RIGHT:
204                anim = new TranslateAnimation(0, view.getWidth(), 0, 0);
205                break;
206            case DIRECTION_UP:
207                anim = new TranslateAnimation(0, 0, 0, -view.getHeight());
208                break;
209            case DIRECTION_DOWN:
210                anim = new TranslateAnimation(0, 0, 0, view.getHeight());
211                break;
212            default:
213                throw new IllegalArgumentException(Integer.toString(to));
214        }
215        anim.setDuration(500);
216        view.startAnimation(anim);
217        return anim;
218    }
219
220    public static Animation slideIn(View view, int from) {
221        view.setVisibility(View.VISIBLE);
222        Animation anim;
223        switch (from) {
224            case DIRECTION_LEFT:
225                anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0);
226                break;
227            case DIRECTION_RIGHT:
228                anim = new TranslateAnimation(view.getWidth(), 0, 0, 0);
229                break;
230            case DIRECTION_UP:
231                anim = new TranslateAnimation(0, 0, -view.getHeight(), 0);
232                break;
233            case DIRECTION_DOWN:
234                anim = new TranslateAnimation(0, 0, view.getHeight(), 0);
235                break;
236            default:
237                throw new IllegalArgumentException(Integer.toString(from));
238        }
239        anim.setDuration(500);
240        view.startAnimation(anim);
241        return anim;
242    }
243
244    public static <T> T checkNotNull(T object) {
245        if (object == null) throw new NullPointerException();
246        return object;
247    }
248
249    public static boolean equals(Object a, Object b) {
250        return (a == b) || (a == null ? false : a.equals(b));
251    }
252
253    public static boolean isPowerOf2(int n) {
254        return (n & -n) == n;
255    }
256
257    public static int nextPowerOf2(int n) {
258        n -= 1;
259        n |= n >>> 16;
260        n |= n >>> 8;
261        n |= n >>> 4;
262        n |= n >>> 2;
263        n |= n >>> 1;
264        return n + 1;
265    }
266}
267