blob: 0ab7612f76d085fe2f9b7e41586753d3eb30a987 [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;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070051import java.text.SimpleDateFormat;
52import java.util.ArrayList;
53import java.util.Date;
Ray Chen0f0af522009-04-22 13:26:07 +080054import java.util.List;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080055
Ray Chend5142de2009-05-07 15:24:48 +080056/**
57 * A utility class to handle various kinds of menu operations.
58 */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080059public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070060 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080061
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070062 public static final int INCLUDE_ALL = 0xFFFFFFFF;
63 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
64 public static final int INCLUDE_SHARE_MENU = (1 << 1);
65 public static final int INCLUDE_SET_MENU = (1 << 2);
66 public static final int INCLUDE_CROP_MENU = (1 << 3);
67 public static final int INCLUDE_DELETE_MENU = (1 << 4);
68 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
69 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080070 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080071
Chih-Chung Changd1890832009-09-08 13:32:52 +080072 public static final int MENU_IMAGE_SHARE = 1;
73 public static final int MENU_IMAGE_SHOWMAP = 2;
74
75 public static final int POSITION_SWITCH_CAMERA_MODE = 1;
76 public static final int POSITION_GOTO_GALLERY = 2;
77 public static final int POSITION_VIEWPLAY = 3;
78 public static final int POSITION_CAPTURE_PICTURE = 4;
79 public static final int POSITION_CAPTURE_VIDEO = 5;
80 public static final int POSITION_IMAGE_SHARE = 6;
81 public static final int POSITION_IMAGE_ROTATE = 7;
82 public static final int POSITION_IMAGE_TOSS = 8;
83 public static final int POSITION_IMAGE_CROP = 9;
84 public static final int POSITION_IMAGE_SET = 10;
85 public static final int POSITION_DETAILS = 11;
86 public static final int POSITION_SHOWMAP = 12;
87 public static final int POSITION_SLIDESHOW = 13;
88 public static final int POSITION_MULTISELECT = 14;
89 public static final int POSITION_CAMERA_SETTING = 15;
90 public static final int POSITION_GALLERY_SETTING = 16;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080091
92 public static final int NO_STORAGE_ERROR = -1;
93 public static final int CANNOT_STAT_ERROR = -2;
Ray Chen0f0af522009-04-22 13:26:07 +080094 public static final String EMPTY_STRING = "";
Ray Chend5142de2009-05-07 15:24:48 +080095 public static final String JPEG_MIME_TYPE = "image/jpeg";
Ray Chen0a5a78c2009-04-24 14:13:01 +080096 // valid range is -180f to +180f
97 public static final float INVALID_LATLNG = 255f;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080098
99 /** Activity result code used to report crop results.
100 */
101 public static final int RESULT_COMMON_MENU_CROP = 490;
102
103 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700104 public void gettingReadyToOpen(Menu menu, IImage image);
105 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800106 }
107
108 public interface MenuInvoker {
109 public void run(MenuCallback r);
110 }
111
112 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700113 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800114 }
115
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700116 public static void closeSilently(Closeable c) {
117 if (c != null) {
118 try {
119 c.close();
120 } catch (Throwable e) {
121 // ignore
122 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800123 }
124 }
125
Owen Lin101d5282009-04-03 16:20:08 -0700126 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800127 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800128 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800129 try {
130 return data.available();
131 } catch (java.io.IOException ex) {
132 return -1;
133 } finally {
134 closeSilently(data);
135 }
136 }
137
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700138 // This is a hack before we find a solution to pass a permission to other
Chih-Chung Changb2a97652009-07-10 18:39:49 +0800139 // applications. See bug #1735149, #1836138.
140 // Checks if the URI is on our whitelist:
141 // content://media/... (MediaProvider)
142 // file:///sdcard/... (Browser download)
143 public static boolean isWhiteListUri(Uri uri) {
144 if (uri == null) return false;
145
146 String scheme = uri.getScheme();
147 String authority = uri.getAuthority();
148
149 if (scheme.equals("content") && authority.equals("media")) {
150 return true;
151 }
152
153 if (scheme.equals("file")) {
154 List<String> p = uri.getPathSegments();
155
156 if (p.size() >= 1 && p.get(0).equals("sdcard")) {
157 return true;
158 }
159 }
160
161 return false;
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700162 }
Owen Lin937fc482009-04-14 02:02:51 -0700163
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700164 public static void enableShareMenuItem(Menu menu, boolean enabled) {
165 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
166 if (item != null) {
167 item.setVisible(enabled);
168 item.setEnabled(enabled);
169 }
170 }
171
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800172 public static boolean hasLatLngData(IImage image) {
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800173 ExifInterface exif = getExif(image);
174 if (exif == null) return false;
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800175 float latlng[] = new float[2];
176 return exif.getLatLong(latlng);
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800177 }
Chih-Chung Changd1890832009-09-08 13:32:52 +0800178
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800179 public static void enableShowOnMapMenuItem(Menu menu, boolean enabled) {
180 MenuItem item = menu.findItem(MENU_IMAGE_SHOWMAP);
181 if (item != null) {
182 item.setEnabled(enabled);
183 }
184 }
185
Ray Chen0f0af522009-04-22 13:26:07 +0800186 private static void setDetailsValue(View d, String text, int valueId) {
187 ((TextView) d.findViewById(valueId)).setText(text);
188 }
189
190 private static void hideDetailsRow(View d, int rowId) {
191 d.findViewById(rowId).setVisibility(View.GONE);
192 }
193
repo syncaa7075c2009-06-24 14:59:48 +0800194 private static class UpdateLocationCallback implements
195 ReverseGeocoderTask.Callback {
196 WeakReference<View> mView;
197
198 public UpdateLocationCallback(WeakReference<View> view) {
199 mView = view;
200 }
201
202 public void onComplete(String location) {
203 // View d is per-thread data, so when setDetailsValue is
204 // executed by UI thread, it doesn't matter whether the
205 // details dialog is dismissed or not.
206 View view = mView.get();
207 if (view == null) return;
Chih-Chung Chang9b93bcb2009-09-04 19:46:14 +0800208 if (!location.equals(MenuHelper.EMPTY_STRING)) {
repo syncaa7075c2009-06-24 14:59:48 +0800209 MenuHelper.setDetailsValue(view, location,
210 R.id.details_location_value);
211 } else {
212 MenuHelper.hideDetailsRow(view, R.id.details_location_row);
213 }
214 }
215 }
216
217 private static void setLatLngDetails(final View d, Activity context,
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800218 ExifInterface exif) {
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800219 float[] latlng = new float[2];
220 if (exif.getLatLong(latlng)) {
Ray Chend5142de2009-05-07 15:24:48 +0800221 setDetailsValue(d, String.valueOf(latlng[0]),
222 R.id.details_latitude_value);
223 setDetailsValue(d, String.valueOf(latlng[1]),
repo syncaa7075c2009-06-24 14:59:48 +0800224 R.id.details_longitude_value);
225
226 if (latlng[0] == INVALID_LATLNG || latlng[1] == INVALID_LATLNG) {
227 hideDetailsRow(d, R.id.details_latitude_row);
228 hideDetailsRow(d, R.id.details_longitude_row);
229 hideDetailsRow(d, R.id.details_location_row);
230 return;
231 }
232
233 UpdateLocationCallback cb = new UpdateLocationCallback(
234 new WeakReference<View>(d));
235 Geocoder geocoder = new Geocoder(context);
236 new ReverseGeocoderTask(geocoder, latlng, cb).execute();
Ray Chend5142de2009-05-07 15:24:48 +0800237 } else {
238 hideDetailsRow(d, R.id.details_latitude_row);
239 hideDetailsRow(d, R.id.details_longitude_row);
240 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800241 }
Ray Chen0f0af522009-04-22 13:26:07 +0800242 }
243
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800244 private static ExifInterface getExif(IImage image) {
Ray Chend5142de2009-05-07 15:24:48 +0800245 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
246 return null;
247 }
248
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800249 try {
250 return new ExifInterface(image.getDataPath());
251 } catch (IOException ex) {
252 Log.e(TAG, "cannot read exif", ex);
253 return null;
254 }
Ray Chend5142de2009-05-07 15:24:48 +0800255 }
Ray Chen0a5a78c2009-04-24 14:13:01 +0800256 // Called when "Show on Maps" is clicked.
257 // Displays image location on Google Maps for further operations.
258 private static boolean onShowMapClicked(MenuInvoker onInvoke,
259 final Handler handler,
260 final Activity activity) {
261 onInvoke.run(new MenuCallback() {
262 public void run(Uri u, IImage image) {
263 if (image == null) {
264 return;
265 }
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800266
267 boolean ok = false;
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800268 ExifInterface exif = getExif(image);
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800269 float latlng[] = null;
270 if (exif != null) {
271 latlng = new float[2];
272 if (exif.getLatLong(latlng)) {
273 ok = true;
274 }
275 }
Ray Chen4e3fd262009-09-21 11:05:58 -0700276
277 if (!ok) {
Ray Chen0a5a78c2009-04-24 14:13:01 +0800278 handler.post(new Runnable() {
279 public void run() {
280 Toast.makeText(activity,
281 R.string.no_location_image,
282 Toast.LENGTH_SHORT).show();
283 }
284 });
285 return;
286 }
287
288 // Can't use geo:latitude,longitude because it only centers
289 // the MapView to specified location, but we need a bubble
290 // for further operations (routing to/from).
291 // The q=(lat, lng) syntax is suggested by geo-team.
292 String uri = "http://maps.google.com/maps?f=q&" +
Ray Chend5142de2009-05-07 15:24:48 +0800293 "q=(" + latlng[0] + "," + latlng[1] + ")";
Ray Chen0a5a78c2009-04-24 14:13:01 +0800294 activity.startActivity(new Intent(
295 android.content.Intent.ACTION_VIEW,
296 Uri.parse(uri)));
297 }
298 });
299 return true;
300 }
301
Ray Chend5142de2009-05-07 15:24:48 +0800302 private static void hideExifInformation(View d) {
303 hideDetailsRow(d, R.id.details_resolution_row);
304 hideDetailsRow(d, R.id.details_make_row);
305 hideDetailsRow(d, R.id.details_model_row);
306 hideDetailsRow(d, R.id.details_whitebalance_row);
307 hideDetailsRow(d, R.id.details_latitude_row);
308 hideDetailsRow(d, R.id.details_longitude_row);
309 hideDetailsRow(d, R.id.details_location_row);
310 }
311
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800312 private static void showExifInformation(IImage image, View d,
313 Activity activity) {
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800314 ExifInterface exif = getExif(image);
315 if (exif == null) {
Ray Chend5142de2009-05-07 15:24:48 +0800316 hideExifInformation(d);
317 return;
318 }
319
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800320 String value = exif.getAttribute(ExifInterface.TAG_MAKE);
Ray Chend5142de2009-05-07 15:24:48 +0800321 if (value != null) {
322 setDetailsValue(d, value, R.id.details_make_value);
323 } else {
324 hideDetailsRow(d, R.id.details_make_row);
325 }
326
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800327 value = exif.getAttribute(ExifInterface.TAG_MODEL);
Ray Chend5142de2009-05-07 15:24:48 +0800328 if (value != null) {
329 setDetailsValue(d, value, R.id.details_model_value);
330 } else {
331 hideDetailsRow(d, R.id.details_model_row);
332 }
333
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800334 value = getWhiteBalanceString(exif);
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800335 if (value != null && !value.equals(EMPTY_STRING)) {
Ray Chend5142de2009-05-07 15:24:48 +0800336 setDetailsValue(d, value, R.id.details_whitebalance_value);
337 } else {
338 hideDetailsRow(d, R.id.details_whitebalance_row);
339 }
340
Chih-Chung Changb147ba22009-09-16 18:42:46 +0800341 setLatLngDetails(d, activity, exif);
Ray Chend5142de2009-05-07 15:24:48 +0800342 }
343
Chih-Chung Chang55345e92009-09-17 18:09:49 +0800344 /**
345 * Returns a human-readable string describing the white balance value. Returns empty
346 * string if there is no white balance value or it is not recognized.
347 */
348 private static String getWhiteBalanceString(ExifInterface exif) {
349 int whitebalance = exif.getAttributeInt(ExifInterface.TAG_WHITE_BALANCE, -1);
350 if (whitebalance == -1) return "";
351
352 switch (whitebalance) {
353 case ExifInterface.WHITEBALANCE_AUTO:
354 return "Auto";
355 case ExifInterface.WHITEBALANCE_MANUAL:
356 return "Manual";
357 default:
358 return "";
359 }
360 }
361
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700362 // Called when "Details" is clicked.
363 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700364 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800365 final Handler handler,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800366 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700367 onInvoke.run(new MenuCallback() {
368 public void run(Uri u, IImage image) {
369 if (image == null) {
370 return;
371 }
372
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800373 final AlertDialog.Builder builder =
374 new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700375
376 final View d = View.inflate(activity, R.layout.detailsview,
377 null);
378
379 ImageView imageView = (ImageView) d.findViewById(
380 R.id.details_thumbnail_image);
381 imageView.setImageBitmap(image.miniThumbBitmap());
382
383 TextView textView = (TextView) d.findViewById(
384 R.id.details_image_title);
385 textView.setText(image.getDisplayName());
386
387 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700388 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800389 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700390 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700391 ((TextView) d
392 .findViewById(R.id.details_file_size_value))
393 .setText(lengthString);
394
395 int dimensionWidth = 0;
396 int dimensionHeight = 0;
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800397 if (ImageManager.isImage(image)) {
Ray Chen0f0af522009-04-22 13:26:07 +0800398 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700399 dimensionWidth = image.getWidth();
400 dimensionHeight = image.getHeight();
401 d.findViewById(R.id.details_duration_row)
402 .setVisibility(View.GONE);
403 d.findViewById(R.id.details_frame_rate_row)
404 .setVisibility(View.GONE);
405 d.findViewById(R.id.details_bit_rate_row)
406 .setVisibility(View.GONE);
407 d.findViewById(R.id.details_format_row)
408 .setVisibility(View.GONE);
409 d.findViewById(R.id.details_codec_row)
410 .setVisibility(View.GONE);
411 } else {
412 MediaMetadataRetriever retriever
413 = new MediaMetadataRetriever();
414 try {
415 retriever.setMode(MediaMetadataRetriever
416 .MODE_GET_METADATA_ONLY);
417 retriever.setDataSource(image.getDataPath());
418 try {
419 dimensionWidth = Integer.parseInt(
420 retriever.extractMetadata(
421 MediaMetadataRetriever
422 .METADATA_KEY_VIDEO_WIDTH));
423 dimensionHeight = Integer.parseInt(
424 retriever.extractMetadata(
425 MediaMetadataRetriever
426 .METADATA_KEY_VIDEO_HEIGHT));
427 } catch (NumberFormatException e) {
428 dimensionWidth = 0;
429 dimensionHeight = 0;
430 }
431
432 try {
433 int durationMs = Integer.parseInt(
434 retriever.extractMetadata(
435 MediaMetadataRetriever
436 .METADATA_KEY_DURATION));
437 String durationValue = formatDuration(
438 activity, durationMs);
439 ((TextView) d.findViewById(
440 R.id.details_duration_value))
441 .setText(durationValue);
442 } catch (NumberFormatException e) {
443 d.findViewById(
444 R.id.details_frame_rate_row)
445 .setVisibility(View.GONE);
446 }
447
448 try {
449 String frameRate = String.format(
450 activity.getString(R.string.details_fps),
451 Integer.parseInt(
452 retriever.extractMetadata(
453 MediaMetadataRetriever
454 .METADATA_KEY_FRAME_RATE)));
455 ((TextView) d.findViewById(
456 R.id.details_frame_rate_value))
457 .setText(frameRate);
458 } catch (NumberFormatException e) {
459 d.findViewById(
460 R.id.details_frame_rate_row)
461 .setVisibility(View.GONE);
462 }
463
464 try {
465 long bitRate = Long.parseLong(
466 retriever.extractMetadata(
467 MediaMetadataRetriever
468 .METADATA_KEY_BIT_RATE));
469 String bps;
470 if (bitRate < 1000000) {
471 bps = String.format(
472 activity.getString(
473 R.string.details_kbps),
474 bitRate / 1000);
475 } else {
476 bps = String.format(
477 activity.getString(
478 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700479 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700480 }
481 ((TextView) d.findViewById(
482 R.id.details_bit_rate_value))
483 .setText(bps);
484 } catch (NumberFormatException e) {
485 d.findViewById(R.id.details_bit_rate_row)
486 .setVisibility(View.GONE);
487 }
488
489 String format = retriever.extractMetadata(
490 MediaMetadataRetriever
491 .METADATA_KEY_VIDEO_FORMAT);
492 ((TextView) d.findViewById(
493 R.id.details_format_value))
494 .setText(format);
495
496 String codec = retriever.extractMetadata(
497 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800498 if (codec != null) {
499 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700500 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800501 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700502 }
Ray Chen0f0af522009-04-22 13:26:07 +0800503
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700504 } catch (RuntimeException ex) {
505 // Assume this is a corrupt video file.
506 } finally {
507 try {
508 retriever.release();
509 } catch (RuntimeException ex) {
510 // Ignore failures while cleaning up.
511 }
512 }
513 }
514
Ray Chen0f0af522009-04-22 13:26:07 +0800515 String value = null;
516 if (dimensionWidth > 0 && dimensionHeight > 0) {
517 value = String.format(
518 activity.getString(R.string.details_dimension_x),
519 dimensionWidth, dimensionHeight);
Ray Chen0f0af522009-04-22 13:26:07 +0800520 }
Ray Chend5142de2009-05-07 15:24:48 +0800521
Ray Chen0f0af522009-04-22 13:26:07 +0800522 if (value != null) {
523 setDetailsValue(d, value, R.id.details_resolution_value);
524 } else {
525 hideDetailsRow(d, R.id.details_resolution_row);
526 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700527
Ray Chen0f0af522009-04-22 13:26:07 +0800528 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700529 long dateTaken = image.getDateTaken();
530 if (dateTaken != 0) {
531 Date date = new Date(image.getDateTaken());
532 SimpleDateFormat dateFormat = new SimpleDateFormat();
Ray Chen0f0af522009-04-22 13:26:07 +0800533 value = dateFormat.format(date);
534 }
535 if (value != EMPTY_STRING) {
536 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700537 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800538 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700539 }
540
Ray Chend5142de2009-05-07 15:24:48 +0800541 // Show more EXIF header details for JPEG images.
542 if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
543 showExifInformation(image, d, activity);
544 } else {
545 hideExifInformation(d);
546 }
547
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700548 builder.setNeutralButton(R.string.details_ok,
549 new DialogInterface.OnClickListener() {
550 public void onClick(DialogInterface dialog,
551 int which) {
552 dialog.dismiss();
553 }
554 });
555
Ray Chen993105a2009-04-10 02:11:35 -0700556 handler.post(
557 new Runnable() {
558 public void run() {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800559 builder.setIcon(
560 android.R.drawable.ic_dialog_info)
Ray Chen993105a2009-04-10 02:11:35 -0700561 .setTitle(R.string.details_panel_title)
562 .setView(d)
563 .show();
564 }
565 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700566 }
567 });
568 return true;
569 }
570
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700571 // Called when "Rotate left" or "Rotate right" is clicked.
572 private static boolean onRotateClicked(MenuInvoker onInvoke,
573 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700574 onInvoke.run(new MenuCallback() {
575 public void run(Uri u, IImage image) {
576 if (image == null || image.isReadonly()) {
577 return;
578 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700579 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700580 }
581 });
582 return true;
583 }
584
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700585 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700586 private static boolean onCropClicked(MenuInvoker onInvoke,
587 final Activity activity) {
588 onInvoke.run(new MenuCallback() {
589 public void run(Uri u, IImage image) {
590 if (u == null) {
591 return;
592 }
593
Cheng-Ru Linda460872009-10-16 00:07:29 -0700594 Intent cropIntent = new Intent(
595 "com.android.camera.action.CROP");
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700596 cropIntent.setData(u);
Cheng-Ru Linda460872009-10-16 00:07:29 -0700597 activity.startActivityForResult(
598 cropIntent, RESULT_COMMON_MENU_CROP);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700599 }
600 });
601 return true;
602 }
603
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700604 // Called when "Set as" is clicked.
605 private static boolean onSetAsClicked(MenuInvoker onInvoke,
606 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700607 onInvoke.run(new MenuCallback() {
608 public void run(Uri u, IImage image) {
609 if (u == null || image == null) {
610 return;
611 }
612
Chih-Chung Changbb187782009-07-02 16:46:30 +0800613 Intent intent = Util.createSetAsIntent(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700614 activity.startActivity(Intent.createChooser(intent,
615 activity.getText(R.string.setImage)));
616 }
617 });
618 return true;
619 }
620
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700621 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700622 private static boolean onImageShareClicked(MenuInvoker onInvoke,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800623 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700624 onInvoke.run(new MenuCallback() {
625 public void run(Uri u, IImage image) {
626 if (image == null) return;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700627
628 Intent intent = new Intent();
629 intent.setAction(Intent.ACTION_SEND);
630 String mimeType = image.getMimeType();
631 intent.setType(mimeType);
632 intent.putExtra(Intent.EXTRA_STREAM, u);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800633 boolean isImage = ImageManager.isImage(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700634 try {
635 activity.startActivity(Intent.createChooser(intent,
636 activity.getText(isImage
637 ? R.string.sendImage
638 : R.string.sendVideo)));
639 } catch (android.content.ActivityNotFoundException ex) {
640 Toast.makeText(activity, isImage
641 ? R.string.no_way_to_share_image
642 : R.string.no_way_to_share_video,
643 Toast.LENGTH_SHORT).show();
644 }
645 }
646 });
647 return true;
648 }
649
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700650 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700651 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
652 final Activity activity) {
653 onInvoke.run(new MenuCallback() {
654 public void run(Uri uri, IImage image) {
655 if (image != null) {
656 Intent intent = new Intent(Intent.ACTION_VIEW,
657 image.fullSizeImageUri());
658 activity.startActivity(intent);
659 }
660 }});
661 return true;
662 }
663
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800664 // Called when "Delete" is clicked.
665 private static boolean onDeleteClicked(MenuInvoker onInvoke,
666 final Activity activity, final Runnable onDelete) {
667 onInvoke.run(new MenuCallback() {
668 public void run(Uri uri, IImage image) {
669 if (image != null) {
670 deleteImage(activity, onDelete, image);
671 }
672 }});
673 return true;
674 }
675
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800676 static MenuItemsResult addImageMenuItems(
677 Menu menu,
678 int inclusions,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800679 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700680 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800681 final Runnable onDelete,
682 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700683 final ArrayList<MenuItem> requiresWriteAccessItems =
684 new ArrayList<MenuItem>();
685 final ArrayList<MenuItem> requiresNoDrmAccessItems =
686 new ArrayList<MenuItem>();
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800687 final ArrayList<MenuItem> requiresImageItems =
688 new ArrayList<MenuItem>();
689 final ArrayList<MenuItem> requiresVideoItems =
690 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800691
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800692 if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800693 SubMenu rotateSubmenu = menu.addSubMenu(Menu.NONE, Menu.NONE,
694 POSITION_IMAGE_ROTATE, R.string.rotate)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700695 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800696 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700697 // since the items within the submenu won't be shown anyway. This
698 // is really a framework bug in that it shouldn't show the submenu
699 // if the submenu has no visible items.
Chih-Chung Changd1890832009-09-08 13:32:52 +0800700 MenuItem rotateLeft = rotateSubmenu.add(R.string.rotate_left)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800701 .setOnMenuItemClickListener(
702 new MenuItem.OnMenuItemClickListener() {
703 public boolean onMenuItemClick(MenuItem item) {
704 return onRotateClicked(onInvoke, -90);
705 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800706 }).setAlphabeticShortcut('l');
707
Chih-Chung Changd1890832009-09-08 13:32:52 +0800708 MenuItem rotateRight = rotateSubmenu.add(R.string.rotate_right)
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('r');
715
716 requiresWriteAccessItems.add(rotateSubmenu.getItem());
717 requiresWriteAccessItems.add(rotateLeft);
718 requiresWriteAccessItems.add(rotateRight);
719
720 requiresImageItems.add(rotateSubmenu.getItem());
721 requiresImageItems.add(rotateLeft);
722 requiresImageItems.add(rotateRight);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800723 }
724
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800725 if ((inclusions & INCLUDE_CROP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800726 MenuItem autoCrop = menu.add(Menu.NONE, Menu.NONE,
727 POSITION_IMAGE_CROP, R.string.camera_crop);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700728 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
729 autoCrop.setOnMenuItemClickListener(
730 new MenuItem.OnMenuItemClickListener() {
731 public boolean onMenuItemClick(MenuItem item) {
732 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800733 }
734 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800735 requiresWriteAccessItems.add(autoCrop);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800736 requiresImageItems.add(autoCrop);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800737 }
738
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800739 if ((inclusions & INCLUDE_SET_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800740 MenuItem setMenu = menu.add(Menu.NONE, Menu.NONE,
741 POSITION_IMAGE_SET, R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800742 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700743 setMenu.setOnMenuItemClickListener(
744 new MenuItem.OnMenuItemClickListener() {
745 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700746 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800747 }
748 });
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800749 requiresImageItems.add(setMenu);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800750 }
751
752 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800753 MenuItem item1 = menu.add(Menu.NONE, MENU_IMAGE_SHARE,
754 POSITION_IMAGE_SHARE, R.string.camera_share)
755 .setOnMenuItemClickListener(
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800756 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700757 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800758 return onImageShareClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800759 }
760 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800761 item1.setIcon(android.R.drawable.ic_menu_share);
762 MenuItem item = item1;
763 requiresNoDrmAccessItems.add(item);
764 }
765
766 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800767 MenuItem deleteItem = menu.add(Menu.NONE, Menu.NONE,
768 POSITION_IMAGE_TOSS, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800769 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700770 deleteItem.setOnMenuItemClickListener(
771 new MenuItem.OnMenuItemClickListener() {
772 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800773 return onDeleteClicked(onInvoke, activity,
774 onDelete);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700775 }
776 })
777 .setAlphabeticShortcut('d')
778 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800779 }
780
781 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800782 MenuItem detailsMenu = menu.add(Menu.NONE, Menu.NONE,
783 POSITION_DETAILS, R.string.details)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700784 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800785 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800786 return onDetailsClicked(onInvoke, handler, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800787 }
788 });
789 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
790 }
791
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800792 if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800793 MenuItem showOnMapItem = menu.add(Menu.NONE, MENU_IMAGE_SHOWMAP,
794 POSITION_SHOWMAP, R.string.show_on_map);
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800795 showOnMapItem.setOnMenuItemClickListener(
Ray Chen0a5a78c2009-04-24 14:13:01 +0800796 new MenuItem.OnMenuItemClickListener() {
797 public boolean onMenuItemClick(MenuItem item) {
798 return onShowMapClicked(onInvoke,
799 handler, activity);
800 }
Ray Chen1e99d7c2009-05-13 13:55:29 +0800801 }).setIcon(R.drawable.ic_menu_3d_globe);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800802 requiresImageItems.add(showOnMapItem);
Ray Chen0a5a78c2009-04-24 14:13:01 +0800803 }
804
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800805 if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800806 MenuItem videoPlayItem = menu.add(Menu.NONE, Menu.NONE,
807 POSITION_VIEWPLAY, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700808 .setOnMenuItemClickListener(
809 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800810 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700811 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800812 }
813 });
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800814 videoPlayItem.setIcon(
815 com.android.internal.R.drawable.ic_menu_play_clip);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800816 requiresVideoItems.add(videoPlayItem);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800817 }
818
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800819 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700820 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700821 // protect against null here. this isn't strictly speaking
822 // required but if a client app isn't handling sdcard removal
823 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800824 if (image == null) {
825 return;
826 }
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800827
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800828 ArrayList<MenuItem> enableList = new ArrayList<MenuItem>();
829 ArrayList<MenuItem> disableList = new ArrayList<MenuItem>();
830 ArrayList<MenuItem> list;
831
832 list = image.isReadonly() ? disableList : enableList;
833 list.addAll(requiresWriteAccessItems);
834
835 list = image.isDrm() ? disableList : enableList;
836 list.addAll(requiresNoDrmAccessItems);
837
838 list = ImageManager.isImage(image) ? enableList : disableList;
839 list.addAll(requiresImageItems);
840
841 list = ImageManager.isVideo(image) ? enableList : disableList;
842 list.addAll(requiresVideoItems);
repo synca274cf12009-07-06 15:05:23 +0800843
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800844 for (MenuItem item : enableList) {
845 item.setVisible(true);
846 item.setEnabled(true);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700847 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800848
849 for (MenuItem item : disableList) {
850 item.setVisible(false);
851 item.setEnabled(false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800852 }
853 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700854
855 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700856 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800857 }
858 };
859 }
860
861 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700862 deleteImpl(activity, onDelete, true);
863 }
Owen Lin095afa12009-03-24 23:41:27 -0700864
Ray Chen7638a542009-03-24 20:37:45 -0700865 static void deleteVideo(Activity activity, Runnable onDelete) {
866 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800867 }
868
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800869 static void deleteImage(
870 Activity activity, Runnable onDelete, IImage image) {
871 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800872 }
873
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800874 static void deleteImpl(
875 Activity activity, Runnable onDelete, boolean isImage) {
876 boolean needConfirm = PreferenceManager
877 .getDefaultSharedPreferences(activity)
878 .getBoolean("pref_gallery_confirm_delete_key", true);
879 if (!needConfirm) {
880 if (onDelete != null) onDelete.run();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800881 } else {
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800882 String title = activity.getString(R.string.confirm_delete_title);
883 String message = activity.getString(isImage
884 ? R.string.confirm_delete_message
885 : R.string.confirm_delete_video_message);
886 confirmAction(activity, title, message, onDelete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800887 }
888 }
889
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800890 public static void deleteMultiple(Context context, Runnable action) {
891 boolean needConfirm = PreferenceManager
892 .getDefaultSharedPreferences(context)
893 .getBoolean("pref_gallery_confirm_delete_key", true);
894 if (!needConfirm) {
895 if (action != null) action.run();
896 } else {
897 String title = context.getString(R.string.confirm_delete_title);
898 String message = context.getString(
899 R.string.confirm_delete_multiple_message);
900 confirmAction(context, title, message, action);
901 }
902 }
903
904 public static void confirmAction(Context context, String title,
905 String message, final Runnable action) {
906 OnClickListener listener = new OnClickListener() {
907 public void onClick(DialogInterface dialog, int which) {
908 switch (which) {
909 case DialogInterface.BUTTON_POSITIVE:
910 if (action != null) action.run();
911 }
912 }
913 };
914 new AlertDialog.Builder(context)
915 .setIcon(android.R.drawable.ic_dialog_alert)
916 .setTitle(title)
917 .setMessage(message)
918 .setPositiveButton(android.R.string.ok, listener)
919 .setNegativeButton(android.R.string.cancel, listener)
920 .create()
921 .show();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800922 }
923
Owen Lin3283e042009-06-26 11:59:58 -0700924 private static void startCameraActivity(Activity activity, String action) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700925 Intent intent = new Intent(action);
926 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
927 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
Chih-Chung Changcd65be32009-06-09 13:51:29 +0800928
929 // Keep the camera instance for a while.
930 // This avoids re-opening the camera and saves time.
931 CameraHolder.instance().keep();
932
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700933 activity.startActivity(intent);
Owen Lin059daa32009-05-18 15:31:17 -0700934 }
935
936 public static void gotoVideoMode(Activity activity) {
Owen Lin3283e042009-06-26 11:59:58 -0700937 startCameraActivity(activity, MediaStore.INTENT_ACTION_VIDEO_CAMERA);
Owen Lin059daa32009-05-18 15:31:17 -0700938 }
939
Owen Lin3283e042009-06-26 11:59:58 -0700940 public static void gotoCameraMode(Activity activity) {
941 startCameraActivity(
942 activity, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800943 }
944
Owen Lin3283e042009-06-26 11:59:58 -0700945 public static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700946 gotoGallery(activity, R.string.gallery_camera_bucket_name,
947 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800948 }
949
Owen Lin3283e042009-06-26 11:59:58 -0700950 public static void gotoCameraVideoGallery(Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800951 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
952 ImageManager.INCLUDE_VIDEOS);
953 }
954
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700955 private static void gotoGallery(Activity activity, int windowTitleId,
956 int mediaTypes) {
957 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
958 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800959 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
960 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700961 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800962 intent.putExtra("windowTitle", activity.getString(windowTitleId));
963 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700964
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800965 try {
966 activity.startActivity(intent);
967 } catch (ActivityNotFoundException e) {
968 Log.e(TAG, "Could not start gallery activity", e);
969 }
970 }
971
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700972 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800973 menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_PICTURE,
974 R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700975 .setOnMenuItemClickListener(
976 new MenuItem.OnMenuItemClickListener() {
977 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700978 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700979 }
980 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700981 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800982
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700983 private static boolean onCapturePictureClicked(Activity activity) {
984 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
985 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
986 try {
987 activity.startActivity(intent);
988 } catch (android.content.ActivityNotFoundException e) {
989 // Ignore exception
990 }
991 return true;
992 }
993
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700994 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800995 menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_VIDEO,
996 R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700997 .setOnMenuItemClickListener(
998 new MenuItem.OnMenuItemClickListener() {
999 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -07001000 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001001 }
1002 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001003 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -07001004
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -07001005 private static boolean onCaptureVideoClicked(Activity activity) {
1006 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
1007 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
1008 try {
1009 activity.startActivity(intent);
1010 } catch (android.content.ActivityNotFoundException e) {
1011 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001012 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001013 return true;
1014 }
1015
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -07001016 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
1017 addCapturePictureMenuItems(menu, activity);
1018 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001019 }
1020
Owen Lin6795ff12009-06-09 13:39:00 -07001021 public static String formatDuration(final Context context,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001022 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001023 int duration = durationMs / 1000;
1024 int h = duration / 3600;
1025 int m = (duration - h * 3600) / 60;
1026 int s = duration - (h * 3600 + m * 60);
1027 String durationValue;
1028 if (h == 0) {
1029 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -07001030 context.getString(R.string.details_ms), m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001031 } else {
1032 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -07001033 context.getString(R.string.details_hms), h, m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001034 }
1035 return durationValue;
1036 }
1037
1038 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001039 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001040 }
1041
1042 public static void showStorageToast(Activity activity, int remaining) {
1043 String noStorageText = null;
1044
1045 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
1046 String state = Environment.getExternalStorageState();
1047 if (state == Environment.MEDIA_CHECKING) {
1048 noStorageText = activity.getString(R.string.preparing_sd);
1049 } else {
1050 noStorageText = activity.getString(R.string.no_storage);
1051 }
1052 } else if (remaining < 1) {
1053 noStorageText = activity.getString(R.string.not_enough_space);
1054 }
1055
1056 if (noStorageText != null) {
1057 Toast.makeText(activity, noStorageText, 5000).show();
1058 }
1059 }
1060
1061 public static int calculatePicturesRemaining() {
1062 try {
1063 if (!ImageManager.hasStorage()) {
1064 return NO_STORAGE_ERROR;
1065 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001066 String storageDirectory =
1067 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001068 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang05d20c02009-11-04 16:21:57 +08001069 final int PICTURE_BYTES = 1500000;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001070 float remaining = ((float) stat.getAvailableBlocks()
Chih-Chung Chang05d20c02009-11-04 16:21:57 +08001071 * (float) stat.getBlockSize()) / PICTURE_BYTES;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001072 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001073 }
1074 } catch (Exception ex) {
1075 // if we can't stat the filesystem then we don't know how many
1076 // pictures are remaining. it might be zero but just leave it
1077 // blank since we really don't know.
1078 return CANNOT_STAT_ERROR;
1079 }
1080 }
1081}