blob: c8566489e3f332f238d496864df7d430e76432de [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
Owen Lin101d5282009-04-03 16:20:08 -070019import com.android.camera.gallery.IImage;
20
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080021import android.app.Activity;
22import android.app.AlertDialog;
23import android.content.ActivityNotFoundException;
24import android.content.DialogInterface;
25import android.content.Intent;
Ray Chen0f0af522009-04-22 13:26:07 +080026import android.location.Address;
27import android.location.Geocoder;
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;
33import android.provider.MediaStore;
34import android.provider.MediaStore.Images;
Owen Lin937fc482009-04-14 02:02:51 -070035import android.text.format.Formatter;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080036import android.util.Log;
37import android.view.Menu;
38import android.view.MenuItem;
Ray Chen0f0af522009-04-22 13:26:07 +080039import android.view.MenuItem.OnMenuItemClickListener;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080040import 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
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070046import java.io.Closeable;
Ray Chen0f0af522009-04-22 13:26:07 +080047import java.io.IOException;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070048import java.text.SimpleDateFormat;
49import java.util.ArrayList;
50import java.util.Date;
Ray Chend5142de2009-05-07 15:24:48 +080051import java.util.HashMap;
Ray Chen0f0af522009-04-22 13:26:07 +080052import java.util.Iterator;
53import java.util.List;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080054
Ray Chend5142de2009-05-07 15:24:48 +080055/**
56 * A utility class to handle various kinds of menu operations.
57 */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080058public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070059 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080060
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070061 public static final int GENERIC_ITEM = 1;
62 public static final int IMAGE_SAVING_ITEM = 2;
63 public static final int VIDEO_SAVING_ITEM = 3;
64 public static final int IMAGE_MODE_ITEM = 4;
65 public static final int VIDEO_MODE_ITEM = 5;
66 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080067
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070068 public static final int INCLUDE_ALL = 0xFFFFFFFF;
69 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
70 public static final int INCLUDE_SHARE_MENU = (1 << 1);
71 public static final int INCLUDE_SET_MENU = (1 << 2);
72 public static final int INCLUDE_CROP_MENU = (1 << 3);
73 public static final int INCLUDE_DELETE_MENU = (1 << 4);
74 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
75 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080076 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080077
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070078 public static final int MENU_SWITCH_CAMERA_MODE = 0;
79 public static final int MENU_CAPTURE_PICTURE = 1;
80 public static final int MENU_CAPTURE_VIDEO = 2;
81 public static final int MENU_IMAGE_SHARE = 10;
82 public static final int MENU_IMAGE_SET = 14;
83 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070084 public static final int MENU_IMAGE_CROP = 18;
85 public static final int MENU_IMAGE_ROTATE = 19;
86 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
87 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
88 public static final int MENU_IMAGE_TOSS = 22;
89 public static final int MENU_VIDEO_PLAY = 23;
90 public static final int MENU_VIDEO_SHARE = 24;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080091
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070092 private static final long SHARE_FILE_LENGTH_LIMIT = 3L * 1024L * 1024L;
The Android Open Source Projecte3f45162009-03-11 12:11:58 -070093
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080094 public static final int NO_STORAGE_ERROR = -1;
95 public static final int CANNOT_STAT_ERROR = -2;
Ray Chen0f0af522009-04-22 13:26:07 +080096 public static final String EMPTY_STRING = "";
Ray Chend5142de2009-05-07 15:24:48 +080097 public static final String JPEG_MIME_TYPE = "image/jpeg";
Ray Chen0a5a78c2009-04-24 14:13:01 +080098 // valid range is -180f to +180f
99 public static final float INVALID_LATLNG = 255f;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800100
101 /** Activity result code used to report crop results.
102 */
103 public static final int RESULT_COMMON_MENU_CROP = 490;
104
105 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700106 public void gettingReadyToOpen(Menu menu, IImage image);
107 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800108 }
109
110 public interface MenuInvoker {
111 public void run(MenuCallback r);
112 }
113
114 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700115 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800116 }
117
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700118 public static void closeSilently(Closeable c) {
119 if (c != null) {
120 try {
121 c.close();
122 } catch (Throwable e) {
123 // ignore
124 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800125 }
126 }
127
Owen Lin101d5282009-04-03 16:20:08 -0700128 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800129 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800130 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800131 try {
132 return data.available();
133 } catch (java.io.IOException ex) {
134 return -1;
135 } finally {
136 closeSilently(data);
137 }
138 }
139
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700140 // This is a hack before we find a solution to pass a permission to other
141 // applications. See bug #1735149.
142 // Checks if the URI starts with "content://mms".
143 public static boolean isMMSUri(Uri uri) {
144 return (uri != null) &&
145 uri.getScheme().equals("content") &&
146 uri.getAuthority().equals("mms");
147 }
Owen Lin937fc482009-04-14 02:02:51 -0700148
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700149 public static void enableShareMenuItem(Menu menu, boolean enabled) {
150 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
151 if (item != null) {
152 item.setVisible(enabled);
153 item.setEnabled(enabled);
154 }
155 }
156
Ray Chen0f0af522009-04-22 13:26:07 +0800157 private static void setDetailsValue(View d, String text, int valueId) {
158 ((TextView) d.findViewById(valueId)).setText(text);
159 }
160
161 private static void hideDetailsRow(View d, int rowId) {
162 d.findViewById(rowId).setVisibility(View.GONE);
163 }
164
Ray Chend5142de2009-05-07 15:24:48 +0800165 private static float[] getLatLng(HashMap<String, String> exifData) {
166 if (exifData == null) {
167 return null;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800168 }
169
Ray Chend5142de2009-05-07 15:24:48 +0800170 String latValue = exifData.get(ExifInterface.TAG_GPS_LATITUDE);
171 String latRef = exifData.get(ExifInterface.TAG_GPS_LATITUDE_REF);
172 String lngValue = exifData.get(ExifInterface.TAG_GPS_LONGITUDE);
173 String lngRef = exifData.get(ExifInterface.TAG_GPS_LONGITUDE_REF);
174 float[] latlng = null;
175
176 if (latValue != null && latRef != null
177 && lngValue != null && lngRef != null) {
178 latlng = new float[2];
179 latlng[0] = ExifInterface.convertRationalLatLonToFloat(
180 latValue, latRef);
181 latlng[1] = ExifInterface.convertRationalLatLonToFloat(
182 lngValue, lngRef);
Ray Chen0f0af522009-04-22 13:26:07 +0800183 }
Ray Chend5142de2009-05-07 15:24:48 +0800184
185 return latlng;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800186 }
187
Ray Chend5142de2009-05-07 15:24:48 +0800188 private static void setLatLngDetails(View d, Activity context,
189 HashMap<String, String> exifData) {
190 float[] latlng = getLatLng(exifData);
191 if (latlng != null) {
192 setDetailsValue(d, String.valueOf(latlng[0]),
193 R.id.details_latitude_value);
194 setDetailsValue(d, String.valueOf(latlng[1]),
195 R.id.details_longitude_value);
196 setReverseGeocodingDetails(d, context, latlng[0], latlng[1]);
197 } else {
198 hideDetailsRow(d, R.id.details_latitude_row);
199 hideDetailsRow(d, R.id.details_longitude_row);
200 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800201 }
Ray Chen0f0af522009-04-22 13:26:07 +0800202 }
203
204 private static void setReverseGeocodingDetails(View d, Activity context,
205 float lat, float lng) {
206 // Fill in reverse-geocoded address
207 String value = EMPTY_STRING;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800208 if (lat == INVALID_LATLNG || lng == INVALID_LATLNG) {
209 hideDetailsRow(d, R.id.details_location_row);
210 return;
211 }
212
Ray Chen0f0af522009-04-22 13:26:07 +0800213 try {
214 Geocoder geocoder = new Geocoder(context);
215 List<Address> address = geocoder.getFromLocation(
216 lat, lng, 1);
217 Iterator<Address> iterator = address.iterator();
218
219 while (iterator.hasNext()) {
220 Address addr = iterator.next();
221 value += addr.getAddressLine(
222 addr.getMaxAddressLineIndex());
223 Log.v(TAG, addr.toString());
224 }
225 } catch (IOException ex) {
226 // Ignore this exception.
227 value = EMPTY_STRING;
228 Log.e(TAG, "Geocoder exception: ", ex);
229 } catch (RuntimeException ex) {
230 // Ignore this exception.
231 value = EMPTY_STRING;
232 Log.e(TAG, "Geocoder exception: ", ex);
233 }
234 if (value != EMPTY_STRING) {
235 setDetailsValue(d, value, R.id.details_location_value);
236 } else {
237 hideDetailsRow(d, R.id.details_location_row);
238 }
239 }
240
Ray Chend5142de2009-05-07 15:24:48 +0800241 private static HashMap<String, String> getExifData(IImage image) {
242 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
243 return null;
244 }
245
246 ExifInterface exif = new ExifInterface(image.getDataPath());
247 HashMap<String, String> exifData = null;
248 if (exif != null) {
249 exifData = exif.getAttributes();
250 }
251 return exifData;
252 }
Ray Chen0a5a78c2009-04-24 14:13:01 +0800253 // Called when "Show on Maps" is clicked.
254 // Displays image location on Google Maps for further operations.
255 private static boolean onShowMapClicked(MenuInvoker onInvoke,
256 final Handler handler,
257 final Activity activity) {
258 onInvoke.run(new MenuCallback() {
259 public void run(Uri u, IImage image) {
260 if (image == null) {
261 return;
262 }
Ray Chend5142de2009-05-07 15:24:48 +0800263 float[] latlng = getLatLng(getExifData(image));
264 if (latlng == null) {
Ray Chen0a5a78c2009-04-24 14:13:01 +0800265 handler.post(new Runnable() {
266 public void run() {
267 Toast.makeText(activity,
268 R.string.no_location_image,
269 Toast.LENGTH_SHORT).show();
270 }
271 });
272 return;
273 }
274
275 // Can't use geo:latitude,longitude because it only centers
276 // the MapView to specified location, but we need a bubble
277 // for further operations (routing to/from).
278 // The q=(lat, lng) syntax is suggested by geo-team.
279 String uri = "http://maps.google.com/maps?f=q&" +
Ray Chend5142de2009-05-07 15:24:48 +0800280 "q=(" + latlng[0] + "," + latlng[1] + ")";
Ray Chen0a5a78c2009-04-24 14:13:01 +0800281 activity.startActivity(new Intent(
282 android.content.Intent.ACTION_VIEW,
283 Uri.parse(uri)));
284 }
285 });
286 return true;
287 }
288
Ray Chend5142de2009-05-07 15:24:48 +0800289 private static void hideExifInformation(View d) {
290 hideDetailsRow(d, R.id.details_resolution_row);
291 hideDetailsRow(d, R.id.details_make_row);
292 hideDetailsRow(d, R.id.details_model_row);
293 hideDetailsRow(d, R.id.details_whitebalance_row);
294 hideDetailsRow(d, R.id.details_latitude_row);
295 hideDetailsRow(d, R.id.details_longitude_row);
296 hideDetailsRow(d, R.id.details_location_row);
297 }
298
299 private static void showExifInformation(IImage image, View d, Activity activity) {
300 HashMap<String, String> exifData = getExifData(image);
301 if (exifData == null) {
302 hideExifInformation(d);
303 return;
304 }
305
306 String value = exifData.get(ExifInterface.TAG_MAKE);
307 if (value != null) {
308 setDetailsValue(d, value, R.id.details_make_value);
309 } else {
310 hideDetailsRow(d, R.id.details_make_row);
311 }
312
313 value = exifData.get(ExifInterface.TAG_MODEL);
314 if (value != null) {
315 setDetailsValue(d, value, R.id.details_model_value);
316 } else {
317 hideDetailsRow(d, R.id.details_model_row);
318 }
319
320 value = exifData.get(ExifInterface.TAG_WHITE_BALANCE);
321 if (value != null) {
322 value = ExifInterface.whiteBalanceToString(
323 Integer.parseInt(value));
324 }
325 if (value != null && value != EMPTY_STRING) {
326 setDetailsValue(d, value, R.id.details_whitebalance_value);
327 } else {
328 hideDetailsRow(d, R.id.details_whitebalance_row);
329 }
330
331 setLatLngDetails(d, activity, exifData);
332 }
333
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700334 // Called when "Details" is clicked.
335 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700336 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800337 final Handler handler,
338 final Activity activity,
339 final boolean isImage) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700340 onInvoke.run(new MenuCallback() {
341 public void run(Uri u, IImage image) {
342 if (image == null) {
343 return;
344 }
345
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800346 final AlertDialog.Builder builder =
347 new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700348
349 final View d = View.inflate(activity, R.layout.detailsview,
350 null);
351
352 ImageView imageView = (ImageView) d.findViewById(
353 R.id.details_thumbnail_image);
354 imageView.setImageBitmap(image.miniThumbBitmap());
355
356 TextView textView = (TextView) d.findViewById(
357 R.id.details_image_title);
358 textView.setText(image.getDisplayName());
359
360 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700361 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800362 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700363 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700364 ((TextView) d
365 .findViewById(R.id.details_file_size_value))
366 .setText(lengthString);
367
368 int dimensionWidth = 0;
369 int dimensionHeight = 0;
370 if (isImage) {
Ray Chen0f0af522009-04-22 13:26:07 +0800371 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700372 dimensionWidth = image.getWidth();
373 dimensionHeight = image.getHeight();
374 d.findViewById(R.id.details_duration_row)
375 .setVisibility(View.GONE);
376 d.findViewById(R.id.details_frame_rate_row)
377 .setVisibility(View.GONE);
378 d.findViewById(R.id.details_bit_rate_row)
379 .setVisibility(View.GONE);
380 d.findViewById(R.id.details_format_row)
381 .setVisibility(View.GONE);
382 d.findViewById(R.id.details_codec_row)
383 .setVisibility(View.GONE);
384 } else {
385 MediaMetadataRetriever retriever
386 = new MediaMetadataRetriever();
387 try {
388 retriever.setMode(MediaMetadataRetriever
389 .MODE_GET_METADATA_ONLY);
390 retriever.setDataSource(image.getDataPath());
391 try {
392 dimensionWidth = Integer.parseInt(
393 retriever.extractMetadata(
394 MediaMetadataRetriever
395 .METADATA_KEY_VIDEO_WIDTH));
396 dimensionHeight = Integer.parseInt(
397 retriever.extractMetadata(
398 MediaMetadataRetriever
399 .METADATA_KEY_VIDEO_HEIGHT));
400 } catch (NumberFormatException e) {
401 dimensionWidth = 0;
402 dimensionHeight = 0;
403 }
404
405 try {
406 int durationMs = Integer.parseInt(
407 retriever.extractMetadata(
408 MediaMetadataRetriever
409 .METADATA_KEY_DURATION));
410 String durationValue = formatDuration(
411 activity, durationMs);
412 ((TextView) d.findViewById(
413 R.id.details_duration_value))
414 .setText(durationValue);
415 } catch (NumberFormatException e) {
416 d.findViewById(
417 R.id.details_frame_rate_row)
418 .setVisibility(View.GONE);
419 }
420
421 try {
422 String frameRate = String.format(
423 activity.getString(R.string.details_fps),
424 Integer.parseInt(
425 retriever.extractMetadata(
426 MediaMetadataRetriever
427 .METADATA_KEY_FRAME_RATE)));
428 ((TextView) d.findViewById(
429 R.id.details_frame_rate_value))
430 .setText(frameRate);
431 } catch (NumberFormatException e) {
432 d.findViewById(
433 R.id.details_frame_rate_row)
434 .setVisibility(View.GONE);
435 }
436
437 try {
438 long bitRate = Long.parseLong(
439 retriever.extractMetadata(
440 MediaMetadataRetriever
441 .METADATA_KEY_BIT_RATE));
442 String bps;
443 if (bitRate < 1000000) {
444 bps = String.format(
445 activity.getString(
446 R.string.details_kbps),
447 bitRate / 1000);
448 } else {
449 bps = String.format(
450 activity.getString(
451 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700452 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700453 }
454 ((TextView) d.findViewById(
455 R.id.details_bit_rate_value))
456 .setText(bps);
457 } catch (NumberFormatException e) {
458 d.findViewById(R.id.details_bit_rate_row)
459 .setVisibility(View.GONE);
460 }
461
462 String format = retriever.extractMetadata(
463 MediaMetadataRetriever
464 .METADATA_KEY_VIDEO_FORMAT);
465 ((TextView) d.findViewById(
466 R.id.details_format_value))
467 .setText(format);
468
469 String codec = retriever.extractMetadata(
470 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800471 if (codec != null) {
472 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700473 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800474 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700475 }
Ray Chen0f0af522009-04-22 13:26:07 +0800476
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700477 } catch (RuntimeException ex) {
478 // Assume this is a corrupt video file.
479 } finally {
480 try {
481 retriever.release();
482 } catch (RuntimeException ex) {
483 // Ignore failures while cleaning up.
484 }
485 }
486 }
487
Ray Chen0f0af522009-04-22 13:26:07 +0800488 String value = null;
489 if (dimensionWidth > 0 && dimensionHeight > 0) {
490 value = String.format(
491 activity.getString(R.string.details_dimension_x),
492 dimensionWidth, dimensionHeight);
Ray Chen0f0af522009-04-22 13:26:07 +0800493 }
Ray Chend5142de2009-05-07 15:24:48 +0800494
Ray Chen0f0af522009-04-22 13:26:07 +0800495 if (value != null) {
496 setDetailsValue(d, value, R.id.details_resolution_value);
497 } else {
498 hideDetailsRow(d, R.id.details_resolution_row);
499 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700500
Ray Chen0f0af522009-04-22 13:26:07 +0800501 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700502 long dateTaken = image.getDateTaken();
503 if (dateTaken != 0) {
504 Date date = new Date(image.getDateTaken());
505 SimpleDateFormat dateFormat = new SimpleDateFormat();
Ray Chen0f0af522009-04-22 13:26:07 +0800506 value = dateFormat.format(date);
507 }
508 if (value != EMPTY_STRING) {
509 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700510 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800511 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700512 }
513
Ray Chend5142de2009-05-07 15:24:48 +0800514 // Show more EXIF header details for JPEG images.
515 if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
516 showExifInformation(image, d, activity);
517 } else {
518 hideExifInformation(d);
519 }
520
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700521 builder.setNeutralButton(R.string.details_ok,
522 new DialogInterface.OnClickListener() {
523 public void onClick(DialogInterface dialog,
524 int which) {
525 dialog.dismiss();
526 }
527 });
528
Ray Chen993105a2009-04-10 02:11:35 -0700529 handler.post(
530 new Runnable() {
531 public void run() {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800532 builder.setIcon(
533 android.R.drawable.ic_dialog_info)
Ray Chen993105a2009-04-10 02:11:35 -0700534 .setTitle(R.string.details_panel_title)
535 .setView(d)
536 .show();
537 }
538 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700539 }
540 });
541 return true;
542 }
543
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700544 // Called when "Rotate left" or "Rotate right" is clicked.
545 private static boolean onRotateClicked(MenuInvoker onInvoke,
546 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700547 onInvoke.run(new MenuCallback() {
548 public void run(Uri u, IImage image) {
549 if (image == null || image.isReadonly()) {
550 return;
551 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700552 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700553 }
554 });
555 return true;
556 }
557
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700558 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700559 private static boolean onCropClicked(MenuInvoker onInvoke,
560 final Activity activity) {
561 onInvoke.run(new MenuCallback() {
562 public void run(Uri u, IImage image) {
563 if (u == null) {
564 return;
565 }
566
567 Intent cropIntent = new Intent();
568 cropIntent.setClass(activity, CropImage.class);
569 cropIntent.setData(u);
570 activity.startActivityForResult(cropIntent,
571 RESULT_COMMON_MENU_CROP);
572 }
573 });
574 return true;
575 }
576
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700577 // Called when "Set as" is clicked.
578 private static boolean onSetAsClicked(MenuInvoker onInvoke,
579 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700580 onInvoke.run(new MenuCallback() {
581 public void run(Uri u, IImage image) {
582 if (u == null || image == null) {
583 return;
584 }
585
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700586 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
587 intent.setDataAndType(u, image.getMimeType());
588 intent.putExtra("mimeType", image.getMimeType());
589 activity.startActivity(Intent.createChooser(intent,
590 activity.getText(R.string.setImage)));
591 }
592 });
593 return true;
594 }
595
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700596 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700597 private static boolean onImageShareClicked(MenuInvoker onInvoke,
598 final Activity activity, final boolean isImage) {
599 onInvoke.run(new MenuCallback() {
600 public void run(Uri u, IImage image) {
601 if (image == null) return;
602 if (!isImage && getImageFileSize(image)
603 > SHARE_FILE_LENGTH_LIMIT) {
604 Toast.makeText(activity,
605 R.string.too_large_to_attach,
606 Toast.LENGTH_LONG).show();
607 return;
608 }
609
610 Intent intent = new Intent();
611 intent.setAction(Intent.ACTION_SEND);
612 String mimeType = image.getMimeType();
613 intent.setType(mimeType);
614 intent.putExtra(Intent.EXTRA_STREAM, u);
615 boolean isImage = ImageManager.isImageMimeType(mimeType);
616 try {
617 activity.startActivity(Intent.createChooser(intent,
618 activity.getText(isImage
619 ? R.string.sendImage
620 : R.string.sendVideo)));
621 } catch (android.content.ActivityNotFoundException ex) {
622 Toast.makeText(activity, isImage
623 ? R.string.no_way_to_share_image
624 : R.string.no_way_to_share_video,
625 Toast.LENGTH_SHORT).show();
626 }
627 }
628 });
629 return true;
630 }
631
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700632 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700633 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
634 final Activity activity) {
635 onInvoke.run(new MenuCallback() {
636 public void run(Uri uri, IImage image) {
637 if (image != null) {
638 Intent intent = new Intent(Intent.ACTION_VIEW,
639 image.fullSizeImageUri());
640 activity.startActivity(intent);
641 }
642 }});
643 return true;
644 }
645
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800646 static MenuItemsResult addImageMenuItems(
647 Menu menu,
648 int inclusions,
649 final boolean isImage,
650 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700651 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800652 final Runnable onDelete,
653 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700654 final ArrayList<MenuItem> requiresWriteAccessItems =
655 new ArrayList<MenuItem>();
656 final ArrayList<MenuItem> requiresNoDrmAccessItems =
657 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800658
659 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700660 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
661 MENU_IMAGE_ROTATE, 40, R.string.rotate)
662 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800663 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700664 // since the items within the submenu won't be shown anyway. This
665 // is really a framework bug in that it shouldn't show the submenu
666 // if the submenu has no visible items.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800667 requiresWriteAccessItems.add(rotateSubmenu.getItem());
668 if (rotateSubmenu != null) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700669 requiresWriteAccessItems.add(
670 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50,
671 R.string.rotate_left)
672 .setOnMenuItemClickListener(
673 new MenuItem.OnMenuItemClickListener() {
674 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700675 return onRotateClicked(onInvoke, -90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800676 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700677 }).setAlphabeticShortcut('l'));
678 requiresWriteAccessItems.add(
679 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60,
680 R.string.rotate_right)
681 .setOnMenuItemClickListener(
682 new MenuItem.OnMenuItemClickListener() {
683 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700684 return onRotateClicked(onInvoke, 90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800685 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700686 }).setAlphabeticShortcut('r'));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800687 }
688 }
689
690 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
691 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700692 R.string.camera_crop);
693 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
694 autoCrop.setOnMenuItemClickListener(
695 new MenuItem.OnMenuItemClickListener() {
696 public boolean onMenuItemClick(MenuItem item) {
697 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800698 }
699 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800700 requiresWriteAccessItems.add(autoCrop);
701 }
702
703 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700704 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
705 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800706 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700707 setMenu.setOnMenuItemClickListener(
708 new MenuItem.OnMenuItemClickListener() {
709 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700710 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800711 }
712 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800713 }
714
715 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800716 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
717 R.string.camera_share).setOnMenuItemClickListener(
718 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700719 public boolean onMenuItemClick(MenuItem item) {
720 return onImageShareClicked(onInvoke, activity,
721 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800722 }
723 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800724 item1.setIcon(android.R.drawable.ic_menu_share);
725 MenuItem item = item1;
726 requiresNoDrmAccessItems.add(item);
727 }
728
729 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700730 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
731 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800732 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700733 deleteItem.setOnMenuItemClickListener(
734 new MenuItem.OnMenuItemClickListener() {
735 public boolean onMenuItemClick(MenuItem item) {
736 deleteImpl(activity, onDelete, isImage);
737 return true;
738 }
739 })
740 .setAlphabeticShortcut('d')
741 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800742 }
743
744 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700745 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
746 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800747 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800748 return onDetailsClicked(onInvoke, handler, activity,
749 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800750 }
751 });
752 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
753 }
754
Ray Chen0a5a78c2009-04-24 14:13:01 +0800755 if ((isImage) && ((inclusions & INCLUDE_SHOWMAP_MENU) != 0)) {
Ray Chend5142de2009-05-07 15:24:48 +0800756 // TODO: assign an icon to this menu item.
Ray Chen0a5a78c2009-04-24 14:13:01 +0800757 menu.add(0, 0, 80, R.string.show_on_map)
758 .setOnMenuItemClickListener(
759 new MenuItem.OnMenuItemClickListener() {
760 public boolean onMenuItemClick(MenuItem item) {
761 return onShowMapClicked(onInvoke,
762 handler, activity);
763 }
764 });
765 }
766
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800767 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
768 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700769 .setOnMenuItemClickListener(
770 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800771 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700772 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800773 }
774 });
775 }
776
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800777 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700778 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700779 // protect against null here. this isn't strictly speaking
780 // required but if a client app isn't handling sdcard removal
781 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800782 if (image == null) {
783 return;
784 }
785 boolean readOnly = image.isReadonly();
786 boolean isDrm = image.isDrm();
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800787
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700788 for (MenuItem item : requiresWriteAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700789 item.setVisible(!readOnly);
790 item.setEnabled(!readOnly);
791 }
792 for (MenuItem item : requiresNoDrmAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700793 item.setVisible(!isDrm);
794 item.setEnabled(!isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800795 }
796 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700797
798 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700799 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800800 }
801 };
802 }
803
804 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700805 deleteImpl(activity, onDelete, true);
806 }
Owen Lin095afa12009-03-24 23:41:27 -0700807
Ray Chen7638a542009-03-24 20:37:45 -0700808 static void deleteVideo(Activity activity, Runnable onDelete) {
809 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800810 }
811
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700812 static void deleteImage(Activity activity, Runnable onDelete,
813 IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800814 if (image != null) {
Ray Chen7638a542009-03-24 20:37:45 -0700815 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800816 }
817 }
818
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700819 private static void deleteImpl(Activity activity, final Runnable onDelete,
820 boolean isPhoto) {
821 boolean confirm = android.preference.PreferenceManager
822 .getDefaultSharedPreferences(activity)
823 .getBoolean("pref_gallery_confirm_delete_key", true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800824 if (!confirm) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700825 if (onDelete != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800826 onDelete.run();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700827 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800828 } else {
829 displayDeleteDialog(activity, onDelete, isPhoto);
830 }
831 }
832
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700833 private static void displayDeleteDialog(Activity activity,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800834 final Runnable onDelete, boolean isPhoto) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700835 android.app.AlertDialog.Builder b =
836 new android.app.AlertDialog.Builder(activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800837 b.setIcon(android.R.drawable.ic_dialog_alert);
838 b.setTitle(R.string.confirm_delete_title);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700839 b.setMessage(isPhoto
840 ? R.string.confirm_delete_message
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800841 : R.string.confirm_delete_video_message);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700842 b.setPositiveButton(android.R.string.ok,
843 new android.content.DialogInterface.OnClickListener() {
844 public void onClick(android.content.DialogInterface v,
845 int x) {
846 if (onDelete != null) {
847 onDelete.run();
848 }
849 }
850 });
851 b.setNegativeButton(android.R.string.cancel,
852 new android.content.DialogInterface.OnClickListener() {
853 public void onClick(android.content.DialogInterface v,
854 int x) {
855 }
856 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800857 b.create().show();
858 }
859
860 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
861 final boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700862 int group = switchToVideo
863 ? MenuHelper.IMAGE_MODE_ITEM
864 : MenuHelper.VIDEO_MODE_ITEM;
865 int labelId = switchToVideo
866 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800867 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700868 int iconId = switchToVideo
869 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800870 : android.R.drawable.ic_menu_camera;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700871 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
872 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
873 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700874 return onSwitchModeClicked(activity, switchToVideo);
875 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700876 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800877 item.setIcon(iconId);
878 }
879
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700880 private static boolean onSwitchModeClicked(Activity activity,
881 boolean switchToVideo) {
882 String action = switchToVideo
883 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
884 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
885 Intent intent = new Intent(action);
886 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
887 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
888 activity.startActivity(intent);
889 return true;
890 }
891
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800892 static void gotoStillImageCapture(Activity activity) {
893 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
894 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
895 try {
896 activity.startActivity(intent);
897 } catch (ActivityNotFoundException e) {
898 Log.e(TAG, "Could not start still image capture activity", e);
899 }
900 }
901
902 static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700903 gotoGallery(activity, R.string.gallery_camera_bucket_name,
904 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800905 }
906
907 static void gotoCameraVideoGallery(Activity activity) {
908 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
909 ImageManager.INCLUDE_VIDEOS);
910 }
911
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700912 private static void gotoGallery(Activity activity, int windowTitleId,
913 int mediaTypes) {
914 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
915 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800916 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
917 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700918 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800919 intent.putExtra("windowTitle", activity.getString(windowTitleId));
920 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700921
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800922 try {
923 activity.startActivity(intent);
924 } catch (ActivityNotFoundException e) {
925 Log.e(TAG, "Could not start gallery activity", e);
926 }
927 }
928
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700929 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800930 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700931 .setOnMenuItemClickListener(
932 new MenuItem.OnMenuItemClickListener() {
933 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700934 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700935 }
936 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700937 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800938
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700939 private static boolean onCapturePictureClicked(Activity activity) {
940 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
941 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
942 try {
943 activity.startActivity(intent);
944 } catch (android.content.ActivityNotFoundException e) {
945 // Ignore exception
946 }
947 return true;
948 }
949
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700950 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800951 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700952 .setOnMenuItemClickListener(
953 new MenuItem.OnMenuItemClickListener() {
954 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700955 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700956 }
957 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800958 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700959
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700960 private static boolean onCaptureVideoClicked(Activity activity) {
961 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
962 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
963 try {
964 activity.startActivity(intent);
965 } catch (android.content.ActivityNotFoundException e) {
966 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700967 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700968 return true;
969 }
970
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700971 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
972 addCapturePictureMenuItems(menu, activity);
973 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800974 }
975
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700976 public static String formatDuration(final Activity activity,
977 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800978 int duration = durationMs / 1000;
979 int h = duration / 3600;
980 int m = (duration - h * 3600) / 60;
981 int s = duration - (h * 3600 + m * 60);
982 String durationValue;
983 if (h == 0) {
984 durationValue = String.format(
985 activity.getString(R.string.details_ms), m, s);
986 } else {
987 durationValue = String.format(
988 activity.getString(R.string.details_hms), h, m, s);
989 }
990 return durationValue;
991 }
992
993 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700994 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800995 }
996
997 public static void showStorageToast(Activity activity, int remaining) {
998 String noStorageText = null;
999
1000 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
1001 String state = Environment.getExternalStorageState();
1002 if (state == Environment.MEDIA_CHECKING) {
1003 noStorageText = activity.getString(R.string.preparing_sd);
1004 } else {
1005 noStorageText = activity.getString(R.string.no_storage);
1006 }
1007 } else if (remaining < 1) {
1008 noStorageText = activity.getString(R.string.not_enough_space);
1009 }
1010
1011 if (noStorageText != null) {
1012 Toast.makeText(activity, noStorageText, 5000).show();
1013 }
1014 }
1015
1016 public static int calculatePicturesRemaining() {
1017 try {
1018 if (!ImageManager.hasStorage()) {
1019 return NO_STORAGE_ERROR;
1020 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001021 String storageDirectory =
1022 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001023 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001024 float remaining = ((float) stat.getAvailableBlocks()
1025 * (float) stat.getBlockSize()) / 400000F;
1026 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001027 }
1028 } catch (Exception ex) {
1029 // if we can't stat the filesystem then we don't know how many
1030 // pictures are remaining. it might be zero but just leave it
1031 // blank since we really don't know.
1032 return CANNOT_STAT_ERROR;
1033 }
1034 }
1035}