Util.java revision c97d23826a28dd725951eccb62651244f9fb8228
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.Surface;
27import android.view.View;
28import android.view.animation.Animation;
29import android.view.animation.TranslateAnimation;
30
31import com.android.camera.gallery.IImage;
32import com.android.camera.R;
33
34import java.io.Closeable;
35
36/**
37 * Collection of utility functions used in this package.
38 */
39public class Util {
40    private static final String TAG = "Util";
41    public static final int DIRECTION_LEFT = 0;
42    public static final int DIRECTION_RIGHT = 1;
43    public static final int DIRECTION_UP = 2;
44    public static final int DIRECTION_DOWN = 3;
45
46    public static final String REVIEW_ACTION = "com.cooliris.media.action.REVIEW";
47
48    private Util() {
49    }
50
51    // Rotates the bitmap by the specified degree.
52    // If a new bitmap is created, the original bitmap is recycled.
53    public static Bitmap rotate(Bitmap b, int degrees) {
54        if (degrees != 0 && b != null) {
55            Matrix m = new Matrix();
56            m.setRotate(degrees,
57                    (float) b.getWidth() / 2, (float) b.getHeight() / 2);
58            try {
59                Bitmap b2 = Bitmap.createBitmap(
60                        b, 0, 0, b.getWidth(), b.getHeight(), m, true);
61                if (b != b2) {
62                    b.recycle();
63                    b = b2;
64                }
65            } catch (OutOfMemoryError ex) {
66                // We have no memory to rotate. Return the original bitmap.
67            }
68        }
69        return b;
70    }
71
72    /*
73     * Compute the sample size as a function of minSideLength
74     * and maxNumOfPixels.
75     * minSideLength is used to specify that minimal width or height of a
76     * bitmap.
77     * maxNumOfPixels is used to specify the maximal size in pixels that is
78     * tolerable in terms of memory usage.
79     *
80     * The function returns a sample size based on the constraints.
81     * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
82     * which indicates no care of the corresponding constraint.
83     * The functions prefers returning a sample size that
84     * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
85     *
86     * Also, the function rounds up the sample size to a power of 2 or multiple
87     * of 8 because BitmapFactory only honors sample size this way.
88     * For example, BitmapFactory downsamples an image by 2 even though the
89     * request is 3. So we round up the sample size to avoid OOM.
90     */
91    public static int computeSampleSize(BitmapFactory.Options options,
92            int minSideLength, int maxNumOfPixels) {
93        int initialSize = computeInitialSampleSize(options, minSideLength,
94                maxNumOfPixels);
95
96        int roundedSize;
97        if (initialSize <= 8) {
98            roundedSize = 1;
99            while (roundedSize < initialSize) {
100                roundedSize <<= 1;
101            }
102        } else {
103            roundedSize = (initialSize + 7) / 8 * 8;
104        }
105
106        return roundedSize;
107    }
108
109    private static int computeInitialSampleSize(BitmapFactory.Options options,
110            int minSideLength, int maxNumOfPixels) {
111        double w = options.outWidth;
112        double h = options.outHeight;
113
114        int lowerBound = (maxNumOfPixels == IImage.UNCONSTRAINED) ? 1 :
115                (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
116        int upperBound = (minSideLength == IImage.UNCONSTRAINED) ? 128 :
117                (int) Math.min(Math.floor(w / minSideLength),
118                Math.floor(h / minSideLength));
119
120        if (upperBound < lowerBound) {
121            // return the larger one when there is no overlapping zone.
122            return lowerBound;
123        }
124
125        if ((maxNumOfPixels == IImage.UNCONSTRAINED) &&
126                (minSideLength == IImage.UNCONSTRAINED)) {
127            return 1;
128        } else if (minSideLength == IImage.UNCONSTRAINED) {
129            return lowerBound;
130        } else {
131            return upperBound;
132        }
133    }
134
135    public static <T>  int indexOf(T [] array, T s) {
136        for (int i = 0; i < array.length; i++) {
137            if (array[i].equals(s)) {
138                return i;
139            }
140        }
141        return -1;
142    }
143
144    public static void closeSilently(Closeable c) {
145        if (c == null) return;
146        try {
147            c.close();
148        } catch (Throwable t) {
149            // do nothing
150        }
151    }
152
153    public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) {
154        try {
155            BitmapFactory.Options options = new BitmapFactory.Options();
156            options.inJustDecodeBounds = true;
157            BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
158                    options);
159            if (options.mCancel || options.outWidth == -1
160                    || options.outHeight == -1) {
161                return null;
162            }
163            options.inSampleSize = computeSampleSize(
164                    options, IImage.UNCONSTRAINED, maxNumOfPixels);
165            options.inJustDecodeBounds = false;
166
167            options.inDither = false;
168            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
169            return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length,
170                    options);
171        } catch (OutOfMemoryError ex) {
172            Log.e(TAG, "Got oom exception ", ex);
173            return null;
174        }
175    }
176
177    public static void Assert(boolean cond) {
178        if (!cond) {
179            throw new AssertionError();
180        }
181    }
182
183    public static void showFatalErrorAndFinish(
184            final Activity activity, String title, String message) {
185        DialogInterface.OnClickListener buttonListener =
186                new DialogInterface.OnClickListener() {
187            public void onClick(DialogInterface dialog, int which) {
188                activity.finish();
189            }
190        };
191        new AlertDialog.Builder(activity)
192                .setCancelable(false)
193                .setIcon(android.R.drawable.ic_dialog_alert)
194                .setTitle(title)
195                .setMessage(message)
196                .setNeutralButton(R.string.details_ok, buttonListener)
197                .show();
198    }
199
200    public static Animation slideOut(View view, int to) {
201        view.setVisibility(View.INVISIBLE);
202        Animation anim;
203        switch (to) {
204            case DIRECTION_LEFT:
205                anim = new TranslateAnimation(0, -view.getWidth(), 0, 0);
206                break;
207            case DIRECTION_RIGHT:
208                anim = new TranslateAnimation(0, view.getWidth(), 0, 0);
209                break;
210            case DIRECTION_UP:
211                anim = new TranslateAnimation(0, 0, 0, -view.getHeight());
212                break;
213            case DIRECTION_DOWN:
214                anim = new TranslateAnimation(0, 0, 0, view.getHeight());
215                break;
216            default:
217                throw new IllegalArgumentException(Integer.toString(to));
218        }
219        anim.setDuration(500);
220        view.startAnimation(anim);
221        return anim;
222    }
223
224    public static Animation slideIn(View view, int from) {
225        view.setVisibility(View.VISIBLE);
226        Animation anim;
227        switch (from) {
228            case DIRECTION_LEFT:
229                anim = new TranslateAnimation(-view.getWidth(), 0, 0, 0);
230                break;
231            case DIRECTION_RIGHT:
232                anim = new TranslateAnimation(view.getWidth(), 0, 0, 0);
233                break;
234            case DIRECTION_UP:
235                anim = new TranslateAnimation(0, 0, -view.getHeight(), 0);
236                break;
237            case DIRECTION_DOWN:
238                anim = new TranslateAnimation(0, 0, view.getHeight(), 0);
239                break;
240            default:
241                throw new IllegalArgumentException(Integer.toString(from));
242        }
243        anim.setDuration(500);
244        view.startAnimation(anim);
245        return anim;
246    }
247
248    public static <T> T checkNotNull(T object) {
249        if (object == null) throw new NullPointerException();
250        return object;
251    }
252
253    public static boolean equals(Object a, Object b) {
254        return (a == b) || (a == null ? false : a.equals(b));
255    }
256
257    public static boolean isPowerOf2(int n) {
258        return (n & -n) == n;
259    }
260
261    public static int nextPowerOf2(int n) {
262        n -= 1;
263        n |= n >>> 16;
264        n |= n >>> 8;
265        n |= n >>> 4;
266        n |= n >>> 2;
267        n |= n >>> 1;
268        return n + 1;
269    }
270
271    public static float distance(float x, float y, float sx, float sy) {
272        float dx = x - sx;
273        float dy = y - sy;
274        return (float) Math.sqrt(dx * dx + dy * dy);
275    }
276
277    public static int clamp(int x, int min, int max) {
278        if (x > max) return max;
279        if (x < min) return min;
280        return x;
281    }
282
283    public static int getDisplayRotation(Activity activity) {
284        int rotation = activity.getWindowManager().getDefaultDisplay()
285                .getRotation();
286        switch (rotation) {
287            case Surface.ROTATION_0: return 0;
288            case Surface.ROTATION_90: return 90;
289            case Surface.ROTATION_180: return 180;
290            case Surface.ROTATION_270: return 270;
291        }
292        return 0;
293    }
294
295    public static void setCameraDisplayOrientation(Activity activity,
296            int cameraId, android.hardware.Camera camera) {
297        android.hardware.Camera.CameraInfo info =
298                new android.hardware.Camera.CameraInfo();
299        android.hardware.Camera.getCameraInfo(cameraId, info);
300        int degrees = getDisplayRotation(activity);
301        int result = (info.orientation - degrees + 360) % 360;
302        camera.setDisplayOrientation(result);
303    }
304}
305