OSDN Git Service

Initial commit of 3D gallery source code to project.
[android-x86/packages-apps-Gallery2.git] / src / com / cooliris / media / Util.java
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
17 package com.cooliris.media;
18
19
20 import android.app.ProgressDialog;
21 import android.content.ActivityNotFoundException;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.Matrix;
28 import android.graphics.Rect;
29 import android.net.Uri;
30 import android.os.Handler;
31 import android.os.ParcelFileDescriptor;
32 import android.util.Log;
33 import android.webkit.MimeTypeMap;
34
35 import java.io.Closeable;
36
37 /**
38  * Collection of utility functions used in this package.
39  */
40 public class Util {
41     private static final String TAG = "db.Util";
42     private static final String MAPS_PACKAGE_NAME =
43             "com.google.android.apps.maps";
44     private static final String MAPS_CLASS_NAME =
45             "com.google.android.maps.MapsActivity";
46
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     public static Bitmap transform(Matrix scaler,
73                                    Bitmap source,
74                                    int targetWidth,
75                                    int targetHeight,
76                                    boolean scaleUp) {
77         int deltaX = source.getWidth() - targetWidth;
78         int deltaY = source.getHeight() - targetHeight;
79         if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
80             /*
81              * In this case the bitmap is smaller, at least in one dimension,
82              * than the target.  Transform it by placing as much of the image
83              * as possible into the target and leaving the top/bottom or
84              * left/right (or both) black.
85              */
86             Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
87                     Bitmap.Config.ARGB_8888);
88             Canvas c = new Canvas(b2);
89
90             int deltaXHalf = Math.max(0, deltaX / 2);
91             int deltaYHalf = Math.max(0, deltaY / 2);
92             Rect src = new Rect(
93                     deltaXHalf,
94                     deltaYHalf,
95                     deltaXHalf + Math.min(targetWidth, source.getWidth()),
96                     deltaYHalf + Math.min(targetHeight, source.getHeight()));
97             int dstX = (targetWidth  - src.width())  / 2;
98             int dstY = (targetHeight - src.height()) / 2;
99             Rect dst = new Rect(
100                     dstX,
101                     dstY,
102                     targetWidth - dstX,
103                     targetHeight - dstY);
104             c.drawBitmap(source, src, dst, null);
105             return b2;
106         }
107         float bitmapWidthF = source.getWidth();
108         float bitmapHeightF = source.getHeight();
109
110         float bitmapAspect = bitmapWidthF / bitmapHeightF;
111         float viewAspect   = (float) targetWidth / targetHeight;
112
113         if (bitmapAspect > viewAspect) {
114             float scale = targetHeight / bitmapHeightF;
115             if (scale < .9F || scale > 1F) {
116                 scaler.setScale(scale, scale);
117             } else {
118                 scaler = null;
119             }
120         } else {
121             float scale = targetWidth / bitmapWidthF;
122             if (scale < .9F || scale > 1F) {
123                 scaler.setScale(scale, scale);
124             } else {
125                 scaler = null;
126             }
127         }
128
129         Bitmap b1;
130         if (scaler != null) {
131             // this is used for minithumb and crop, so we want to filter here.
132             b1 = Bitmap.createBitmap(source, 0, 0,
133                     source.getWidth(), source.getHeight(), scaler, true);
134         } else {
135             b1 = source;
136         }
137
138         int dx1 = Math.max(0, b1.getWidth() - targetWidth);
139         int dy1 = Math.max(0, b1.getHeight() - targetHeight);
140
141         Bitmap b2 = Bitmap.createBitmap(
142                 b1,
143                 dx1 / 2,
144                 dy1 / 2,
145                 targetWidth,
146                 targetHeight);
147
148         if (b1 != source) {
149             b1.recycle();
150         }
151
152         return b2;
153     }
154
155     /**
156      * Creates a centered bitmap of the desired size. Recycles the input.
157      * @param source
158      */
159     public static Bitmap extractMiniThumb(
160             Bitmap source, int width, int height) {
161         return Util.extractMiniThumb(source, width, height, true);
162     }
163
164     public static Bitmap extractMiniThumb(
165             Bitmap source, int width, int height, boolean recycle) {
166         if (source == null) {
167             return null;
168         }
169
170         float scale;
171         if (source.getWidth() < source.getHeight()) {
172             scale = width / (float) source.getWidth();
173         } else {
174             scale = height / (float) source.getHeight();
175         }
176         Matrix matrix = new Matrix();
177         matrix.setScale(scale, scale);
178         Bitmap miniThumbnail = transform(matrix, source, width, height, false);
179
180         if (recycle && miniThumbnail != source) {
181             source.recycle();
182         }
183         return miniThumbnail;
184     }
185
186
187     public static <T>  int indexOf(T [] array, T s) {
188         for (int i = 0; i < array.length; i++) {
189             if (array[i].equals(s)) {
190                 return i;
191             }
192         }
193         return -1;
194     }
195
196     public static void closeSilently(Closeable c) {
197         if (c == null) return;
198         try {
199             c.close();
200         } catch (Throwable t) {
201             // do nothing
202         }
203     }
204
205     public static void closeSilently(ParcelFileDescriptor c) {
206         if (c == null) return;
207         try {
208             c.close();
209         } catch (Throwable t) {
210             // do nothing
211         }
212     }
213
214     public static void Assert(boolean cond) {
215         if (!cond) {
216             throw new AssertionError();
217         }
218     }
219
220     public static boolean equals(String a, String b) {
221         // return true if both string are null or the content equals
222         return a == b || a.equals(b);
223     }
224
225     private static class BackgroundJob
226             extends MonitoredActivity.LifeCycleAdapter implements Runnable {
227
228         private final MonitoredActivity mActivity;
229         private final ProgressDialog mDialog;
230         private final Runnable mJob;
231         private final Handler mHandler;
232         private final Runnable mCleanupRunner = new Runnable() {
233             public void run() {
234                 mActivity.removeLifeCycleListener(BackgroundJob.this);
235                 if (mDialog.getWindow() != null) mDialog.dismiss();
236             }
237         };
238
239         public BackgroundJob(MonitoredActivity activity, Runnable job,
240                 ProgressDialog dialog, Handler handler) {
241             mActivity = activity;
242             mDialog = dialog;
243             mJob = job;
244             mActivity.addLifeCycleListener(this);
245             mHandler = handler;
246         }
247
248         public void run() {
249             try {
250                 mJob.run();
251             } finally {
252                 mHandler.post(mCleanupRunner);
253             }
254         }
255
256
257         @Override
258         public void onActivityDestroyed(MonitoredActivity activity) {
259             // We get here only when the onDestroyed being called before
260             // the mCleanupRunner. So, run it now and remove it from the queue
261             mCleanupRunner.run();
262             mHandler.removeCallbacks(mCleanupRunner);
263         }
264
265         @Override
266         public void onActivityStopped(MonitoredActivity activity) {
267             mDialog.hide();
268         }
269
270         @Override
271         public void onActivityStarted(MonitoredActivity activity) {
272             mDialog.show();
273         }
274     }
275
276     public static void startBackgroundJob(MonitoredActivity activity,
277             String title, String message, Runnable job, Handler handler) {
278         // Make the progress dialog uncancelable, so that we can gurantee
279         // the thread will be done before the activity getting destroyed.
280         ProgressDialog dialog = ProgressDialog.show(
281                 activity, title, message, true, false);
282         new Thread(new BackgroundJob(activity, job, dialog, handler)).start();
283     }
284
285     // Returns an intent which is used for "set as" menu items.
286     public static Intent createSetAsIntent(Uri uri, String mimeType) {
287         // Infer MIME type if missing for file URLs.
288         if (uri.getScheme().equals("file")) {
289                 String path = uri.getPath();
290                 int lastDotIndex = path.lastIndexOf('.');
291                 if (lastDotIndex != -1) {
292                         mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
293                                         uri.getPath().substring(lastDotIndex + 1).toLowerCase());
294                 }
295         }
296         
297         Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
298         intent.setDataAndType(uri, mimeType);
299         intent.putExtra("mimeType", mimeType);
300         return intent;
301     }
302
303     // Opens Maps application for a map with given latlong. There is a bug
304     // which crashes the Browser when opening this kind of URL. So, we open
305     // it in GMM instead. For those platforms which have no GMM installed,
306     // the default Maps application will be chosen.
307
308     
309     public static void openMaps(Context context, double latitude, double longitude) {
310         try {
311             // Try to open the GMM first
312
313             // We don't use "geo:latitude,longitude" because it only centers
314             // the MapView to the specified location, but we need a marker
315             // for further operations (routing to/from).
316             // The q=(lat, lng) syntax is suggested by geo-team.
317             String url = String.format(
318                     "http://maps.google.com/maps?f=q&q=(%s,%s)", latitude, longitude);
319             ComponentName compName =
320                     new ComponentName(MAPS_PACKAGE_NAME, MAPS_CLASS_NAME);
321             Intent mapsIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
322                     .setComponent(compName);
323             context.startActivity(mapsIntent);
324         } catch (ActivityNotFoundException e) {
325             // Use the "geo intent" if no GMM is installed
326             Log.e(TAG, "GMM activity not found!", e);
327             String url = String.format("geo:%s,%s", latitude, longitude);
328             Intent mapsIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
329             context.startActivity(mapsIntent);
330         }
331     }
332 }