blob: 4cdc912d27f185ea279af311fb4ab8578355807b [file] [log] [blame]
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001/*
2 * Copyright (C) 2008 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
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080019import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.ActivityNotFoundException;
Owen Lin6795ff12009-06-09 13:39:00 -070022import android.content.Context;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080023import android.content.DialogInterface;
24import android.content.Intent;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080025import android.content.DialogInterface.OnClickListener;
Ray Chen0f0af522009-04-22 13:26:07 +080026import android.location.Geocoder;
repo synca274cf12009-07-06 15:05:23 +080027import android.media.ExifInterface;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080028import android.media.MediaMetadataRetriever;
29import android.net.Uri;
30import android.os.Environment;
31import android.os.Handler;
32import android.os.StatFs;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080033import android.preference.PreferenceManager;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080034import android.provider.MediaStore;
35import android.provider.MediaStore.Images;
Owen Lin937fc482009-04-14 02:02:51 -070036import android.text.format.Formatter;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080037import android.util.Log;
38import android.view.Menu;
39import android.view.MenuItem;
40import android.view.SubMenu;
41import android.view.View;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080042import android.widget.ImageView;
43import android.widget.TextView;
44import android.widget.Toast;
45
Owen Line594b192009-08-13 18:04:45 +080046import com.android.camera.gallery.IImage;
47
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070048import java.io.Closeable;
Chih-Chung Changb147ba22009-09-16 18:42:46 +080049import java.io.IOException;
repo syncaa7075c2009-06-24 14:59:48 +080050import java.lang.ref.WeakReference;
Ray Chen2e862df2010-01-04 14:44:51 -080051import java.text.DateFormat;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070052import java.text.SimpleDateFormat;
53import java.util.ArrayList;
54import java.util.Date;
Ray Chen0f0af522009-04-22 13:26:07 +080055import java.util.List;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080056
Ray Chend5142de2009-05-07 15:24:48 +080057/**
58 * A utility class to handle various kinds of menu operations.
59 */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080060public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070061 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080062
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070063 public static final int INCLUDE_ALL = 0xFFFFFFFF;
64 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
65 public static final int INCLUDE_SHARE_MENU = (1 << 1);
66 public static final int INCLUDE_SET_MENU = (1 << 2);
67 public static final int INCLUDE_CROP_MENU = (1 << 3);
68 public static final int INCLUDE_DELETE_MENU = (1 << 4);
69 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
70 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080071 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080072
Chih-Chung Changd1890832009-09-08 13:32:52 +080073 public static final int MENU_IMAGE_SHARE = 1;
74 public static final int MENU_IMAGE_SHOWMAP = 2;
75
76 public static final int POSITION_SWITCH_CAMERA_MODE = 1;
77 public static final int POSITION_GOTO_GALLERY = 2;
78 public static final int POSITION_VIEWPLAY = 3;
79 public static final int POSITION_CAPTURE_PICTURE = 4;
80 public static final int POSITION_CAPTURE_VIDEO = 5;
81 public static final int POSITION_IMAGE_SHARE = 6;
82 public static final int POSITION_IMAGE_ROTATE = 7;
83 public static final int POSITION_IMAGE_TOSS = 8;
84 public static final int POSITION_IMAGE_CROP = 9;
85 public static final int POSITION_IMAGE_SET = 10;
86 public static final int POSITION_DETAILS = 11;
87 public static final int POSITION_SHOWMAP = 12;
88 public static final int POSITION_SLIDESHOW = 13;
89 public static final int POSITION_MULTISELECT = 14;
90 public static final int POSITION_CAMERA_SETTING = 15;
91 public static final int POSITION_GALLERY_SETTING = 16;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080092
93 public static final int NO_STORAGE_ERROR = -1;
94 public static final int CANNOT_STAT_ERROR = -2;
Ray Chen0f0af522009-04-22 13:26:07 +080095 public static final String EMPTY_STRING = "";
Ray Chend5142de2009-05-07 15:24:48 +080096 public static final String JPEG_MIME_TYPE = "image/jpeg";
Ray Chen0a5a78c2009-04-24 14:13:01 +080097 // valid range is -180f to +180f
98 public static final float INVALID_LATLNG = 255f;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080099
100 /** Activity result code used to report crop results.
101 */
102 public static final int RESULT_COMMON_MENU_CROP = 490;
103
Wei-Ta Chenb6be8b62010-02-24 11:34:22 +0800104 private static final int NO_ANIMATION = 0;
105
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800106 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700107 public void gettingReadyToOpen(Menu menu, IImage image);
108 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800109 }
110
111 public interface MenuInvoker {
112 public void run(MenuCallback r);
113 }
114
Chih-Chung Chang271b3092009-11-02 18:25:43 +0800115 /** A callback to be invoked when a menu item is clicked. */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800116 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700117 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800118 }
119
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700120 public static void closeSilently(Closeable c) {
121 if (c != null) {
122 try {
123 c.close();
124 } catch (Throwable e) {
125 // ignore
126 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800127 }
128 }
129
Owen Lin101d5282009-04-03 16:20:08 -0700130 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800131 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800132 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800133 try {
134 return data.available();
135 } catch (java.io.IOException ex) {
136 return -1;
137 } finally {
138 closeSilently(data);
139 }
140 }
141
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700142 // This is a hack before we find a solution to pass a permission to other
Chih-Chung Changb2a97652009-07-10 18:39:49 +0800143 // applications. See bug #1735149, #1836138.
144 // Checks if the URI is on our whitelist:
145 // content://media/... (MediaProvider)
146 // file:///sdcard/... (Browser download)
147 public static boolean isWhiteListUri(Uri uri) {
148 if (uri == null) return false;
149
150 String scheme = uri.getScheme();
151 String authority = uri.getAuthority();
152
153 if (scheme.equals("content") && authority.equals("media")) {
154 return true;
155 }
156
157 if (scheme.equals("file")) {
158 List<String> p = uri.getPathSegments();
159
160 if (p.size() >= 1 && p.get(0).equals("sdcard")) {
161 return true;
162 }
163 }
164
165 return false;
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700166 }
Owen Lin937fc482009-04-14 02:02:51 -0700167
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700168 public static void enableShareMenuItem(Menu menu, boolean enabled) {
169 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
170 if (item != null) {
171 item.setVisible(enabled);
172 item.setEnabled(enabled);
173 }
174 }
175
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800176 public static boolean hasLatLngData(IImage image) {
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800177 ExifInterface exif = getExif(image);
178 if (exif == null) return false;
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800179 float latlng[] = new float[2];
180 return exif.getLatLong(latlng);
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800181 }
Chih-Chung Changd1890832009-09-08 13:32:52 +0800182
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800183 public static void enableShowOnMapMenuItem(Menu menu, boolean enabled) {
184 MenuItem item = menu.findItem(MENU_IMAGE_SHOWMAP);
185 if (item != null) {
186 item.setEnabled(enabled);
187 }
188 }
189
Ray Chen0f0af522009-04-22 13:26:07 +0800190 private static void setDetailsValue(View d, String text, int valueId) {
191 ((TextView) d.findViewById(valueId)).setText(text);
192 }
193
194 private static void hideDetailsRow(View d, int rowId) {
195 d.findViewById(rowId).setVisibility(View.GONE);
196 }
197
repo syncaa7075c2009-06-24 14:59:48 +0800198 private static class UpdateLocationCallback implements
199 ReverseGeocoderTask.Callback {
200 WeakReference<View> mView;
201
202 public UpdateLocationCallback(WeakReference<View> view) {
203 mView = view;
204 }
205
206 public void onComplete(String location) {
207 // View d is per-thread data, so when setDetailsValue is
208 // executed by UI thread, it doesn't matter whether the
209 // details dialog is dismissed or not.
210 View view = mView.get();
211 if (view == null) return;
Chih-Chung Chang9b93bcb2009-09-04 19:46:14 +0800212 if (!location.equals(MenuHelper.EMPTY_STRING)) {
repo syncaa7075c2009-06-24 14:59:48 +0800213 MenuHelper.setDetailsValue(view, location,
214 R.id.details_location_value);
215 } else {
216 MenuHelper.hideDetailsRow(view, R.id.details_location_row);
217 }
218 }
219 }
220
221 private static void setLatLngDetails(final View d, Activity context,
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800222 ExifInterface exif) {
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800223 float[] latlng = new float[2];
224 if (exif.getLatLong(latlng)) {
Ray Chend5142de2009-05-07 15:24:48 +0800225 setDetailsValue(d, String.valueOf(latlng[0]),
226 R.id.details_latitude_value);
227 setDetailsValue(d, String.valueOf(latlng[1]),
repo syncaa7075c2009-06-24 14:59:48 +0800228 R.id.details_longitude_value);
229
230 if (latlng[0] == INVALID_LATLNG || latlng[1] == INVALID_LATLNG) {
231 hideDetailsRow(d, R.id.details_latitude_row);
232 hideDetailsRow(d, R.id.details_longitude_row);
233 hideDetailsRow(d, R.id.details_location_row);
234 return;
235 }
236
237 UpdateLocationCallback cb = new UpdateLocationCallback(
238 new WeakReference<View>(d));
239 Geocoder geocoder = new Geocoder(context);
240 new ReverseGeocoderTask(geocoder, latlng, cb).execute();
Ray Chend5142de2009-05-07 15:24:48 +0800241 } else {
242 hideDetailsRow(d, R.id.details_latitude_row);
243 hideDetailsRow(d, R.id.details_longitude_row);
244 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800245 }
Ray Chen0f0af522009-04-22 13:26:07 +0800246 }
247
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800248 private static ExifInterface getExif(IImage image) {
Ray Chend5142de2009-05-07 15:24:48 +0800249 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
250 return null;
251 }
252
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800253 try {
254 return new ExifInterface(image.getDataPath());
255 } catch (IOException ex) {
256 Log.e(TAG, "cannot read exif", ex);
257 return null;
258 }
Ray Chend5142de2009-05-07 15:24:48 +0800259 }
Ray Chen0a5a78c2009-04-24 14:13:01 +0800260 // Called when "Show on Maps" is clicked.
261 // Displays image location on Google Maps for further operations.
262 private static boolean onShowMapClicked(MenuInvoker onInvoke,
263 final Handler handler,
264 final Activity activity) {
265 onInvoke.run(new MenuCallback() {
266 public void run(Uri u, IImage image) {
267 if (image == null) {
268 return;
269 }
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800270
271 boolean ok = false;
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800272 ExifInterface exif = getExif(image);
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800273 float latlng[] = null;
274 if (exif != null) {
275 latlng = new float[2];
276 if (exif.getLatLong(latlng)) {
277 ok = true;
278 }
279 }
Ray Chen4e3fd262009-09-21 11:05:58 -0700280
281 if (!ok) {
Ray Chen0a5a78c2009-04-24 14:13:01 +0800282 handler.post(new Runnable() {
283 public void run() {
284 Toast.makeText(activity,
285 R.string.no_location_image,
286 Toast.LENGTH_SHORT).show();
287 }
288 });
289 return;
290 }
291
292 // Can't use geo:latitude,longitude because it only centers
293 // the MapView to specified location, but we need a bubble
294 // for further operations (routing to/from).
295 // The q=(lat, lng) syntax is suggested by geo-team.
296 String uri = "http://maps.google.com/maps?f=q&" +
Ray Chend5142de2009-05-07 15:24:48 +0800297 "q=(" + latlng[0] + "," + latlng[1] + ")";
Ray Chen0a5a78c2009-04-24 14:13:01 +0800298 activity.startActivity(new Intent(
299 android.content.Intent.ACTION_VIEW,
300 Uri.parse(uri)));
301 }
302 });
303 return true;
304 }
305
Ray Chend5142de2009-05-07 15:24:48 +0800306 private static void hideExifInformation(View d) {
307 hideDetailsRow(d, R.id.details_resolution_row);
308 hideDetailsRow(d, R.id.details_make_row);
309 hideDetailsRow(d, R.id.details_model_row);
310 hideDetailsRow(d, R.id.details_whitebalance_row);
311 hideDetailsRow(d, R.id.details_latitude_row);
312 hideDetailsRow(d, R.id.details_longitude_row);
313 hideDetailsRow(d, R.id.details_location_row);
314 }
315
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800316 private static void showExifInformation(IImage image, View d,
317 Activity activity) {
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800318 ExifInterface exif = getExif(image);
319 if (exif == null) {
Ray Chend5142de2009-05-07 15:24:48 +0800320 hideExifInformation(d);
321 return;
322 }
323
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800324 String value = exif.getAttribute(ExifInterface.TAG_MAKE);
Ray Chend5142de2009-05-07 15:24:48 +0800325 if (value != null) {
326 setDetailsValue(d, value, R.id.details_make_value);
327 } else {
328 hideDetailsRow(d, R.id.details_make_row);
329 }
330
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800331 value = exif.getAttribute(ExifInterface.TAG_MODEL);
Ray Chend5142de2009-05-07 15:24:48 +0800332 if (value != null) {
333 setDetailsValue(d, value, R.id.details_model_value);
334 } else {
335 hideDetailsRow(d, R.id.details_model_row);
336 }
337
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800338 value = getWhiteBalanceString(exif);
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800339 if (value != null && !value.equals(EMPTY_STRING)) {
Ray Chend5142de2009-05-07 15:24:48 +0800340 setDetailsValue(d, value, R.id.details_whitebalance_value);
341 } else {
342 hideDetailsRow(d, R.id.details_whitebalance_row);
343 }
344
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800345 setLatLngDetails(d, activity, exif);
Ray Chend5142de2009-05-07 15:24:48 +0800346 }
347
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800348 /**
Chih-Chung Chang77c1cdc2009-11-02 15:06:36 +0800349 * Returns a human-readable string describing the white balance value.
350 * Returns empty string if there is no white balance value or it is not
351 * recognized.
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800352 */
353 private static String getWhiteBalanceString(ExifInterface exif) {
Chih-Chung Chang77c1cdc2009-11-02 15:06:36 +0800354 int whitebalance = exif.getAttributeInt(
355 ExifInterface.TAG_WHITE_BALANCE, -1);
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800356 if (whitebalance == -1) return "";
357
358 switch (whitebalance) {
359 case ExifInterface.WHITEBALANCE_AUTO:
360 return "Auto";
361 case ExifInterface.WHITEBALANCE_MANUAL:
362 return "Manual";
363 default:
364 return "";
365 }
366 }
367
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700368 // Called when "Details" is clicked.
369 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700370 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800371 final Handler handler,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800372 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700373 onInvoke.run(new MenuCallback() {
374 public void run(Uri u, IImage image) {
375 if (image == null) {
376 return;
377 }
378
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800379 final AlertDialog.Builder builder =
380 new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700381
382 final View d = View.inflate(activity, R.layout.detailsview,
383 null);
384
385 ImageView imageView = (ImageView) d.findViewById(
386 R.id.details_thumbnail_image);
387 imageView.setImageBitmap(image.miniThumbBitmap());
388
389 TextView textView = (TextView) d.findViewById(
390 R.id.details_image_title);
Chih-Chung Chang6d08c5d2010-01-05 14:34:36 +0800391 textView.setText(image.getTitle());
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700392
393 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700394 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800395 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700396 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700397 ((TextView) d
398 .findViewById(R.id.details_file_size_value))
399 .setText(lengthString);
400
401 int dimensionWidth = 0;
402 int dimensionHeight = 0;
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800403 if (ImageManager.isImage(image)) {
Ray Chen0f0af522009-04-22 13:26:07 +0800404 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700405 dimensionWidth = image.getWidth();
406 dimensionHeight = image.getHeight();
407 d.findViewById(R.id.details_duration_row)
408 .setVisibility(View.GONE);
409 d.findViewById(R.id.details_frame_rate_row)
410 .setVisibility(View.GONE);
411 d.findViewById(R.id.details_bit_rate_row)
412 .setVisibility(View.GONE);
413 d.findViewById(R.id.details_format_row)
414 .setVisibility(View.GONE);
415 d.findViewById(R.id.details_codec_row)
416 .setVisibility(View.GONE);
417 } else {
418 MediaMetadataRetriever retriever
419 = new MediaMetadataRetriever();
420 try {
421 retriever.setMode(MediaMetadataRetriever
422 .MODE_GET_METADATA_ONLY);
423 retriever.setDataSource(image.getDataPath());
424 try {
425 dimensionWidth = Integer.parseInt(
426 retriever.extractMetadata(
427 MediaMetadataRetriever
428 .METADATA_KEY_VIDEO_WIDTH));
429 dimensionHeight = Integer.parseInt(
430 retriever.extractMetadata(
431 MediaMetadataRetriever
432 .METADATA_KEY_VIDEO_HEIGHT));
433 } catch (NumberFormatException e) {
434 dimensionWidth = 0;
435 dimensionHeight = 0;
436 }
437
438 try {
439 int durationMs = Integer.parseInt(
440 retriever.extractMetadata(
441 MediaMetadataRetriever
442 .METADATA_KEY_DURATION));
443 String durationValue = formatDuration(
444 activity, durationMs);
445 ((TextView) d.findViewById(
446 R.id.details_duration_value))
447 .setText(durationValue);
448 } catch (NumberFormatException e) {
449 d.findViewById(
450 R.id.details_frame_rate_row)
451 .setVisibility(View.GONE);
452 }
453
454 try {
455 String frameRate = String.format(
456 activity.getString(R.string.details_fps),
457 Integer.parseInt(
458 retriever.extractMetadata(
459 MediaMetadataRetriever
460 .METADATA_KEY_FRAME_RATE)));
461 ((TextView) d.findViewById(
462 R.id.details_frame_rate_value))
463 .setText(frameRate);
464 } catch (NumberFormatException e) {
465 d.findViewById(
466 R.id.details_frame_rate_row)
467 .setVisibility(View.GONE);
468 }
469
470 try {
471 long bitRate = Long.parseLong(
472 retriever.extractMetadata(
473 MediaMetadataRetriever
474 .METADATA_KEY_BIT_RATE));
475 String bps;
476 if (bitRate < 1000000) {
477 bps = String.format(
478 activity.getString(
479 R.string.details_kbps),
480 bitRate / 1000);
481 } else {
482 bps = String.format(
483 activity.getString(
484 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700485 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700486 }
487 ((TextView) d.findViewById(
488 R.id.details_bit_rate_value))
489 .setText(bps);
490 } catch (NumberFormatException e) {
491 d.findViewById(R.id.details_bit_rate_row)
492 .setVisibility(View.GONE);
493 }
494
495 String format = retriever.extractMetadata(
496 MediaMetadataRetriever
497 .METADATA_KEY_VIDEO_FORMAT);
498 ((TextView) d.findViewById(
499 R.id.details_format_value))
500 .setText(format);
501
502 String codec = retriever.extractMetadata(
503 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800504 if (codec != null) {
505 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700506 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800507 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700508 }
Ray Chen0f0af522009-04-22 13:26:07 +0800509
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700510 } catch (RuntimeException ex) {
511 // Assume this is a corrupt video file.
512 } finally {
513 try {
514 retriever.release();
515 } catch (RuntimeException ex) {
516 // Ignore failures while cleaning up.
517 }
518 }
519 }
520
Ray Chen0f0af522009-04-22 13:26:07 +0800521 String value = null;
522 if (dimensionWidth > 0 && dimensionHeight > 0) {
523 value = String.format(
524 activity.getString(R.string.details_dimension_x),
525 dimensionWidth, dimensionHeight);
Ray Chen0f0af522009-04-22 13:26:07 +0800526 }
Ray Chend5142de2009-05-07 15:24:48 +0800527
Ray Chen0f0af522009-04-22 13:26:07 +0800528 if (value != null) {
529 setDetailsValue(d, value, R.id.details_resolution_value);
530 } else {
531 hideDetailsRow(d, R.id.details_resolution_row);
532 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700533
Ray Chen0f0af522009-04-22 13:26:07 +0800534 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700535 long dateTaken = image.getDateTaken();
536 if (dateTaken != 0) {
537 Date date = new Date(image.getDateTaken());
Ray Chen2e862df2010-01-04 14:44:51 -0800538 // Date/Time preferences are stored differently so we query them separately.
539 DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(activity);
540 DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(activity);
541 value = dateFormat.format(date) + ' ' + timeFormat.format(date);
Ray Chen0f0af522009-04-22 13:26:07 +0800542 }
543 if (value != EMPTY_STRING) {
544 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700545 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800546 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700547 }
548
Ray Chend5142de2009-05-07 15:24:48 +0800549 // Show more EXIF header details for JPEG images.
550 if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
551 showExifInformation(image, d, activity);
552 } else {
553 hideExifInformation(d);
554 }
555
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700556 builder.setNeutralButton(R.string.details_ok,
557 new DialogInterface.OnClickListener() {
558 public void onClick(DialogInterface dialog,
559 int which) {
560 dialog.dismiss();
561 }
562 });
563
Ray Chen993105a2009-04-10 02:11:35 -0700564 handler.post(
565 new Runnable() {
566 public void run() {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800567 builder.setIcon(
568 android.R.drawable.ic_dialog_info)
Ray Chen993105a2009-04-10 02:11:35 -0700569 .setTitle(R.string.details_panel_title)
570 .setView(d)
571 .show();
572 }
573 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700574 }
575 });
576 return true;
577 }
578
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700579 // Called when "Rotate left" or "Rotate right" is clicked.
580 private static boolean onRotateClicked(MenuInvoker onInvoke,
581 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700582 onInvoke.run(new MenuCallback() {
583 public void run(Uri u, IImage image) {
584 if (image == null || image.isReadonly()) {
585 return;
586 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700587 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700588 }
589 });
590 return true;
591 }
592
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700593 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700594 private static boolean onCropClicked(MenuInvoker onInvoke,
595 final Activity activity) {
596 onInvoke.run(new MenuCallback() {
597 public void run(Uri u, IImage image) {
598 if (u == null) {
599 return;
600 }
601
Cheng-Ru Linda460872009-10-16 00:07:29 -0700602 Intent cropIntent = new Intent(
603 "com.android.camera.action.CROP");
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700604 cropIntent.setData(u);
Cheng-Ru Linda460872009-10-16 00:07:29 -0700605 activity.startActivityForResult(
606 cropIntent, RESULT_COMMON_MENU_CROP);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700607 }
608 });
609 return true;
610 }
611
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700612 // Called when "Set as" is clicked.
613 private static boolean onSetAsClicked(MenuInvoker onInvoke,
614 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700615 onInvoke.run(new MenuCallback() {
616 public void run(Uri u, IImage image) {
617 if (u == null || image == null) {
618 return;
619 }
620
Chih-Chung Changbb187782009-07-02 16:46:30 +0800621 Intent intent = Util.createSetAsIntent(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700622 activity.startActivity(Intent.createChooser(intent,
623 activity.getText(R.string.setImage)));
624 }
625 });
626 return true;
627 }
628
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700629 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700630 private static boolean onImageShareClicked(MenuInvoker onInvoke,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800631 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700632 onInvoke.run(new MenuCallback() {
633 public void run(Uri u, IImage image) {
634 if (image == null) return;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700635
636 Intent intent = new Intent();
637 intent.setAction(Intent.ACTION_SEND);
638 String mimeType = image.getMimeType();
639 intent.setType(mimeType);
640 intent.putExtra(Intent.EXTRA_STREAM, u);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800641 boolean isImage = ImageManager.isImage(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700642 try {
643 activity.startActivity(Intent.createChooser(intent,
644 activity.getText(isImage
645 ? R.string.sendImage
646 : R.string.sendVideo)));
647 } catch (android.content.ActivityNotFoundException ex) {
648 Toast.makeText(activity, isImage
649 ? R.string.no_way_to_share_image
650 : R.string.no_way_to_share_video,
651 Toast.LENGTH_SHORT).show();
652 }
653 }
654 });
655 return true;
656 }
657
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700658 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700659 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
660 final Activity activity) {
661 onInvoke.run(new MenuCallback() {
662 public void run(Uri uri, IImage image) {
663 if (image != null) {
664 Intent intent = new Intent(Intent.ACTION_VIEW,
665 image.fullSizeImageUri());
666 activity.startActivity(intent);
667 }
668 }});
669 return true;
670 }
671
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800672 // Called when "Delete" is clicked.
673 private static boolean onDeleteClicked(MenuInvoker onInvoke,
674 final Activity activity, final Runnable onDelete) {
675 onInvoke.run(new MenuCallback() {
676 public void run(Uri uri, IImage image) {
677 if (image != null) {
678 deleteImage(activity, onDelete, image);
679 }
680 }});
681 return true;
682 }
683
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800684 static MenuItemsResult addImageMenuItems(
685 Menu menu,
686 int inclusions,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800687 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700688 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800689 final Runnable onDelete,
690 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700691 final ArrayList<MenuItem> requiresWriteAccessItems =
692 new ArrayList<MenuItem>();
693 final ArrayList<MenuItem> requiresNoDrmAccessItems =
694 new ArrayList<MenuItem>();
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800695 final ArrayList<MenuItem> requiresImageItems =
696 new ArrayList<MenuItem>();
697 final ArrayList<MenuItem> requiresVideoItems =
698 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800699
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800700 if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800701 SubMenu rotateSubmenu = menu.addSubMenu(Menu.NONE, Menu.NONE,
702 POSITION_IMAGE_ROTATE, R.string.rotate)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700703 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800704 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700705 // since the items within the submenu won't be shown anyway. This
706 // is really a framework bug in that it shouldn't show the submenu
707 // if the submenu has no visible items.
Chih-Chung Changd1890832009-09-08 13:32:52 +0800708 MenuItem rotateLeft = rotateSubmenu.add(R.string.rotate_left)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800709 .setOnMenuItemClickListener(
710 new MenuItem.OnMenuItemClickListener() {
711 public boolean onMenuItemClick(MenuItem item) {
712 return onRotateClicked(onInvoke, -90);
713 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800714 }).setAlphabeticShortcut('l');
715
Chih-Chung Changd1890832009-09-08 13:32:52 +0800716 MenuItem rotateRight = rotateSubmenu.add(R.string.rotate_right)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800717 .setOnMenuItemClickListener(
718 new MenuItem.OnMenuItemClickListener() {
719 public boolean onMenuItemClick(MenuItem item) {
720 return onRotateClicked(onInvoke, 90);
721 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800722 }).setAlphabeticShortcut('r');
723
724 requiresWriteAccessItems.add(rotateSubmenu.getItem());
725 requiresWriteAccessItems.add(rotateLeft);
726 requiresWriteAccessItems.add(rotateRight);
727
728 requiresImageItems.add(rotateSubmenu.getItem());
729 requiresImageItems.add(rotateLeft);
730 requiresImageItems.add(rotateRight);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800731 }
732
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800733 if ((inclusions & INCLUDE_CROP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800734 MenuItem autoCrop = menu.add(Menu.NONE, Menu.NONE,
735 POSITION_IMAGE_CROP, R.string.camera_crop);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700736 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
737 autoCrop.setOnMenuItemClickListener(
738 new MenuItem.OnMenuItemClickListener() {
739 public boolean onMenuItemClick(MenuItem item) {
740 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800741 }
742 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800743 requiresWriteAccessItems.add(autoCrop);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800744 requiresImageItems.add(autoCrop);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800745 }
746
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800747 if ((inclusions & INCLUDE_SET_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800748 MenuItem setMenu = menu.add(Menu.NONE, Menu.NONE,
749 POSITION_IMAGE_SET, R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800750 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700751 setMenu.setOnMenuItemClickListener(
752 new MenuItem.OnMenuItemClickListener() {
753 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700754 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800755 }
756 });
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800757 requiresImageItems.add(setMenu);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800758 }
759
760 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800761 MenuItem item1 = menu.add(Menu.NONE, MENU_IMAGE_SHARE,
762 POSITION_IMAGE_SHARE, R.string.camera_share)
763 .setOnMenuItemClickListener(
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800764 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700765 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800766 return onImageShareClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800767 }
768 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800769 item1.setIcon(android.R.drawable.ic_menu_share);
770 MenuItem item = item1;
771 requiresNoDrmAccessItems.add(item);
772 }
773
774 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800775 MenuItem deleteItem = menu.add(Menu.NONE, Menu.NONE,
776 POSITION_IMAGE_TOSS, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800777 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700778 deleteItem.setOnMenuItemClickListener(
779 new MenuItem.OnMenuItemClickListener() {
780 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800781 return onDeleteClicked(onInvoke, activity,
782 onDelete);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700783 }
784 })
785 .setAlphabeticShortcut('d')
786 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800787 }
788
789 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800790 MenuItem detailsMenu = menu.add(Menu.NONE, Menu.NONE,
791 POSITION_DETAILS, R.string.details)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700792 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800793 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800794 return onDetailsClicked(onInvoke, handler, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800795 }
796 });
797 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
798 }
799
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800800 if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800801 MenuItem showOnMapItem = menu.add(Menu.NONE, MENU_IMAGE_SHOWMAP,
802 POSITION_SHOWMAP, R.string.show_on_map);
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800803 showOnMapItem.setOnMenuItemClickListener(
Ray Chen0a5a78c2009-04-24 14:13:01 +0800804 new MenuItem.OnMenuItemClickListener() {
805 public boolean onMenuItemClick(MenuItem item) {
806 return onShowMapClicked(onInvoke,
807 handler, activity);
808 }
Ray Chen1e99d7c2009-05-13 13:55:29 +0800809 }).setIcon(R.drawable.ic_menu_3d_globe);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800810 requiresImageItems.add(showOnMapItem);
Ray Chen0a5a78c2009-04-24 14:13:01 +0800811 }
812
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800813 if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800814 MenuItem videoPlayItem = menu.add(Menu.NONE, Menu.NONE,
815 POSITION_VIEWPLAY, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700816 .setOnMenuItemClickListener(
817 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800818 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700819 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800820 }
821 });
Chih-Chung Chang2a8efc12010-01-05 19:07:13 +0800822 videoPlayItem.setIcon(R.drawable.ic_menu_play_clip);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800823 requiresVideoItems.add(videoPlayItem);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800824 }
825
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800826 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700827 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700828 // protect against null here. this isn't strictly speaking
829 // required but if a client app isn't handling sdcard removal
830 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800831 if (image == null) {
832 return;
833 }
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800834
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800835 ArrayList<MenuItem> enableList = new ArrayList<MenuItem>();
836 ArrayList<MenuItem> disableList = new ArrayList<MenuItem>();
837 ArrayList<MenuItem> list;
838
839 list = image.isReadonly() ? disableList : enableList;
840 list.addAll(requiresWriteAccessItems);
841
842 list = image.isDrm() ? disableList : enableList;
843 list.addAll(requiresNoDrmAccessItems);
844
845 list = ImageManager.isImage(image) ? enableList : disableList;
846 list.addAll(requiresImageItems);
847
848 list = ImageManager.isVideo(image) ? enableList : disableList;
849 list.addAll(requiresVideoItems);
repo synca274cf12009-07-06 15:05:23 +0800850
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800851 for (MenuItem item : enableList) {
852 item.setVisible(true);
853 item.setEnabled(true);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700854 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800855
856 for (MenuItem item : disableList) {
857 item.setVisible(false);
858 item.setEnabled(false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800859 }
860 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700861
862 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700863 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800864 }
865 };
866 }
867
868 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700869 deleteImpl(activity, onDelete, true);
870 }
Owen Lin095afa12009-03-24 23:41:27 -0700871
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800872 static void deleteImage(
873 Activity activity, Runnable onDelete, IImage image) {
874 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800875 }
876
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800877 static void deleteImpl(
878 Activity activity, Runnable onDelete, boolean isImage) {
879 boolean needConfirm = PreferenceManager
880 .getDefaultSharedPreferences(activity)
881 .getBoolean("pref_gallery_confirm_delete_key", true);
882 if (!needConfirm) {
883 if (onDelete != null) onDelete.run();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800884 } else {
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800885 String title = activity.getString(R.string.confirm_delete_title);
886 String message = activity.getString(isImage
887 ? R.string.confirm_delete_message
888 : R.string.confirm_delete_video_message);
889 confirmAction(activity, title, message, onDelete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800890 }
891 }
892
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800893 public static void confirmAction(Context context, String title,
894 String message, final Runnable action) {
895 OnClickListener listener = new OnClickListener() {
896 public void onClick(DialogInterface dialog, int which) {
897 switch (which) {
898 case DialogInterface.BUTTON_POSITIVE:
899 if (action != null) action.run();
900 }
901 }
902 };
903 new AlertDialog.Builder(context)
904 .setIcon(android.R.drawable.ic_dialog_alert)
905 .setTitle(title)
906 .setMessage(message)
907 .setPositiveButton(android.R.string.ok, listener)
908 .setNegativeButton(android.R.string.cancel, listener)
909 .create()
910 .show();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800911 }
912
Owen Lin3283e042009-06-26 11:59:58 -0700913 private static void startCameraActivity(Activity activity, String action) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700914 Intent intent = new Intent(action);
915 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
916 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
Chih-Chung Changcd65be32009-06-09 13:51:29 +0800917
918 // Keep the camera instance for a while.
919 // This avoids re-opening the camera and saves time.
920 CameraHolder.instance().keep();
921
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700922 activity.startActivity(intent);
Wei-Ta Chenb6be8b62010-02-24 11:34:22 +0800923 activity.overridePendingTransition(NO_ANIMATION, NO_ANIMATION);
Owen Lin059daa32009-05-18 15:31:17 -0700924 }
925
926 public static void gotoVideoMode(Activity activity) {
Owen Lin3283e042009-06-26 11:59:58 -0700927 startCameraActivity(activity, MediaStore.INTENT_ACTION_VIDEO_CAMERA);
Owen Lin059daa32009-05-18 15:31:17 -0700928 }
929
Owen Lin3283e042009-06-26 11:59:58 -0700930 public static void gotoCameraMode(Activity activity) {
931 startCameraActivity(
932 activity, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800933 }
934
Owen Lin3283e042009-06-26 11:59:58 -0700935 public static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700936 gotoGallery(activity, R.string.gallery_camera_bucket_name,
937 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800938 }
939
Owen Lin3283e042009-06-26 11:59:58 -0700940 public static void gotoCameraVideoGallery(Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800941 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
942 ImageManager.INCLUDE_VIDEOS);
943 }
944
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700945 private static void gotoGallery(Activity activity, int windowTitleId,
946 int mediaTypes) {
Ray Chendf926b62009-11-16 16:36:21 -0800947 Uri target = Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700948 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800949 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
950 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700951 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800952 intent.putExtra("windowTitle", activity.getString(windowTitleId));
953 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700954
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800955 try {
956 activity.startActivity(intent);
957 } catch (ActivityNotFoundException e) {
958 Log.e(TAG, "Could not start gallery activity", e);
959 }
960 }
961
Owen Lin6795ff12009-06-09 13:39:00 -0700962 public static String formatDuration(final Context context,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700963 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800964 int duration = durationMs / 1000;
965 int h = duration / 3600;
966 int m = (duration - h * 3600) / 60;
967 int s = duration - (h * 3600 + m * 60);
968 String durationValue;
969 if (h == 0) {
970 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -0700971 context.getString(R.string.details_ms), m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800972 } else {
973 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -0700974 context.getString(R.string.details_hms), h, m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800975 }
976 return durationValue;
977 }
978
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800979 public static int calculatePicturesRemaining() {
980 try {
981 if (!ImageManager.hasStorage()) {
982 return NO_STORAGE_ERROR;
983 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700984 String storageDirectory =
985 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800986 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang05d20c02009-11-04 16:21:57 +0800987 final int PICTURE_BYTES = 1500000;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700988 float remaining = ((float) stat.getAvailableBlocks()
Chih-Chung Chang05d20c02009-11-04 16:21:57 +0800989 * (float) stat.getBlockSize()) / PICTURE_BYTES;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700990 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800991 }
992 } catch (Exception ex) {
993 // if we can't stat the filesystem then we don't know how many
994 // pictures are remaining. it might be zero but just leave it
995 // blank since we really don't know.
996 return CANNOT_STAT_ERROR;
997 }
998 }
999}