blob: 54764c3d507e9a3445792976b0fd5653b11d2152 [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;
Ray Chen0f0af522009-04-22 13:26:07 +080020import com.android.camera.gallery.Image;
Owen Lin101d5282009-04-03 16:20:08 -070021
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080022import android.app.Activity;
23import android.app.AlertDialog;
24import android.content.ActivityNotFoundException;
25import android.content.DialogInterface;
26import android.content.Intent;
Ray Chen0f0af522009-04-22 13:26:07 +080027import android.location.Address;
28import android.location.Geocoder;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080029import android.media.MediaMetadataRetriever;
30import android.net.Uri;
31import android.os.Environment;
32import android.os.Handler;
33import android.os.StatFs;
34import 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;
Ray Chen0f0af522009-04-22 13:26:07 +080040import android.view.MenuItem.OnMenuItemClickListener;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080041import android.view.SubMenu;
42import android.view.View;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080043import android.widget.ImageView;
44import android.widget.TextView;
45import android.widget.Toast;
46
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070047import java.io.Closeable;
Ray Chen0f0af522009-04-22 13:26:07 +080048import java.io.IOException;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070049import java.text.SimpleDateFormat;
50import java.util.ArrayList;
51import java.util.Date;
Ray Chen0f0af522009-04-22 13:26:07 +080052import java.util.Iterator;
53import java.util.List;
54import java.util.Locale;
55import java.util.StringTokenizer;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080056
57public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070058 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080059
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070060 public static final int GENERIC_ITEM = 1;
61 public static final int IMAGE_SAVING_ITEM = 2;
62 public static final int VIDEO_SAVING_ITEM = 3;
63 public static final int IMAGE_MODE_ITEM = 4;
64 public static final int VIDEO_MODE_ITEM = 5;
65 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080066
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070067 public static final int INCLUDE_ALL = 0xFFFFFFFF;
68 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
69 public static final int INCLUDE_SHARE_MENU = (1 << 1);
70 public static final int INCLUDE_SET_MENU = (1 << 2);
71 public static final int INCLUDE_CROP_MENU = (1 << 3);
72 public static final int INCLUDE_DELETE_MENU = (1 << 4);
73 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
74 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080075
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070076 public static final int MENU_SWITCH_CAMERA_MODE = 0;
77 public static final int MENU_CAPTURE_PICTURE = 1;
78 public static final int MENU_CAPTURE_VIDEO = 2;
79 public static final int MENU_IMAGE_SHARE = 10;
80 public static final int MENU_IMAGE_SET = 14;
81 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070082 public static final int MENU_IMAGE_CROP = 18;
83 public static final int MENU_IMAGE_ROTATE = 19;
84 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
85 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
86 public static final int MENU_IMAGE_TOSS = 22;
87 public static final int MENU_VIDEO_PLAY = 23;
88 public static final int MENU_VIDEO_SHARE = 24;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080089
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070090 private static final long SHARE_FILE_LENGTH_LIMIT = 3L * 1024L * 1024L;
The Android Open Source Projecte3f45162009-03-11 12:11:58 -070091
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080092 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 = "";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080095
96 /** Activity result code used to report crop results.
97 */
98 public static final int RESULT_COMMON_MENU_CROP = 490;
99
100 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700101 public void gettingReadyToOpen(Menu menu, IImage image);
102 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800103 }
104
105 public interface MenuInvoker {
106 public void run(MenuCallback r);
107 }
108
109 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700110 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800111 }
112
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700113 public static void closeSilently(Closeable c) {
114 if (c != null) {
115 try {
116 c.close();
117 } catch (Throwable e) {
118 // ignore
119 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800120 }
121 }
122
Owen Lin101d5282009-04-03 16:20:08 -0700123 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800124 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800125 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800126 try {
127 return data.available();
128 } catch (java.io.IOException ex) {
129 return -1;
130 } finally {
131 closeSilently(data);
132 }
133 }
134
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700135 // This is a hack before we find a solution to pass a permission to other
136 // applications. See bug #1735149.
137 // Checks if the URI starts with "content://mms".
138 public static boolean isMMSUri(Uri uri) {
139 return (uri != null) &&
140 uri.getScheme().equals("content") &&
141 uri.getAuthority().equals("mms");
142 }
Owen Lin937fc482009-04-14 02:02:51 -0700143
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700144 public static void enableShareMenuItem(Menu menu, boolean enabled) {
145 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
146 if (item != null) {
147 item.setVisible(enabled);
148 item.setEnabled(enabled);
149 }
150 }
151
Ray Chen0f0af522009-04-22 13:26:07 +0800152 private static void setDetailsValue(View d, String text, int valueId) {
153 ((TextView) d.findViewById(valueId)).setText(text);
154 }
155
156 private static void hideDetailsRow(View d, int rowId) {
157 d.findViewById(rowId).setVisibility(View.GONE);
158 }
159
160 private static float setLatLngDetails(View d, Image img,
161 String tag, String refTag) {
162 String value = img.getExifTag(tag);
163 String ref = img.getExifTag(refTag);
164 float f = 0f;
165 if (value != null && ref != null) {
166 f = ExifInterface.convertRationalLatLonToFloat(
167 value, ref);
168 value = String.valueOf(f);
169 }
170 int valueId = R.id.details_latitude_value;
171 int rowId = R.id.details_latitude_row;
172
173 if (tag == ExifInterface.TAG_GPS_LONGITUDE) {
174 valueId = R.id.details_longitude_value;
175 rowId = R.id.details_longitude_row;
176 }
177 if (value != null) {
178 setDetailsValue(d, value, valueId);
179 } else {
180 hideDetailsRow(d, rowId);
181 }
182 return f;
183 }
184
185 private static void setReverseGeocodingDetails(View d, Activity context,
186 float lat, float lng) {
187 // Fill in reverse-geocoded address
188 String value = EMPTY_STRING;
189 try {
190 Geocoder geocoder = new Geocoder(context);
191 List<Address> address = geocoder.getFromLocation(
192 lat, lng, 1);
193 Iterator<Address> iterator = address.iterator();
194
195 while (iterator.hasNext()) {
196 Address addr = iterator.next();
197 value += addr.getAddressLine(
198 addr.getMaxAddressLineIndex());
199 Log.v(TAG, addr.toString());
200 }
201 } catch (IOException ex) {
202 // Ignore this exception.
203 value = EMPTY_STRING;
204 Log.e(TAG, "Geocoder exception: ", ex);
205 } catch (RuntimeException ex) {
206 // Ignore this exception.
207 value = EMPTY_STRING;
208 Log.e(TAG, "Geocoder exception: ", ex);
209 }
210 if (value != EMPTY_STRING) {
211 setDetailsValue(d, value, R.id.details_location_value);
212 } else {
213 hideDetailsRow(d, R.id.details_location_row);
214 }
215 }
216
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700217 // Called when "Details" is clicked.
218 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700219 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen993105a2009-04-10 02:11:35 -0700220 final Handler handler,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700221 final Activity activity,
222 final boolean isImage) {
223 onInvoke.run(new MenuCallback() {
224 public void run(Uri u, IImage image) {
225 if (image == null) {
226 return;
227 }
228
Ray Chen993105a2009-04-10 02:11:35 -0700229 final AlertDialog.Builder builder = new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700230
231 final View d = View.inflate(activity, R.layout.detailsview,
232 null);
233
234 ImageView imageView = (ImageView) d.findViewById(
235 R.id.details_thumbnail_image);
236 imageView.setImageBitmap(image.miniThumbBitmap());
237
238 TextView textView = (TextView) d.findViewById(
239 R.id.details_image_title);
240 textView.setText(image.getDisplayName());
241
242 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700243 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800244 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700245 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700246 ((TextView) d
247 .findViewById(R.id.details_file_size_value))
248 .setText(lengthString);
249
250 int dimensionWidth = 0;
251 int dimensionHeight = 0;
252 if (isImage) {
Ray Chen0f0af522009-04-22 13:26:07 +0800253 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700254 dimensionWidth = image.getWidth();
255 dimensionHeight = image.getHeight();
256 d.findViewById(R.id.details_duration_row)
257 .setVisibility(View.GONE);
258 d.findViewById(R.id.details_frame_rate_row)
259 .setVisibility(View.GONE);
260 d.findViewById(R.id.details_bit_rate_row)
261 .setVisibility(View.GONE);
262 d.findViewById(R.id.details_format_row)
263 .setVisibility(View.GONE);
264 d.findViewById(R.id.details_codec_row)
265 .setVisibility(View.GONE);
266 } else {
267 MediaMetadataRetriever retriever
268 = new MediaMetadataRetriever();
269 try {
270 retriever.setMode(MediaMetadataRetriever
271 .MODE_GET_METADATA_ONLY);
272 retriever.setDataSource(image.getDataPath());
273 try {
274 dimensionWidth = Integer.parseInt(
275 retriever.extractMetadata(
276 MediaMetadataRetriever
277 .METADATA_KEY_VIDEO_WIDTH));
278 dimensionHeight = Integer.parseInt(
279 retriever.extractMetadata(
280 MediaMetadataRetriever
281 .METADATA_KEY_VIDEO_HEIGHT));
282 } catch (NumberFormatException e) {
283 dimensionWidth = 0;
284 dimensionHeight = 0;
285 }
286
287 try {
288 int durationMs = Integer.parseInt(
289 retriever.extractMetadata(
290 MediaMetadataRetriever
291 .METADATA_KEY_DURATION));
292 String durationValue = formatDuration(
293 activity, durationMs);
294 ((TextView) d.findViewById(
295 R.id.details_duration_value))
296 .setText(durationValue);
297 } catch (NumberFormatException e) {
298 d.findViewById(
299 R.id.details_frame_rate_row)
300 .setVisibility(View.GONE);
301 }
302
303 try {
304 String frameRate = String.format(
305 activity.getString(R.string.details_fps),
306 Integer.parseInt(
307 retriever.extractMetadata(
308 MediaMetadataRetriever
309 .METADATA_KEY_FRAME_RATE)));
310 ((TextView) d.findViewById(
311 R.id.details_frame_rate_value))
312 .setText(frameRate);
313 } catch (NumberFormatException e) {
314 d.findViewById(
315 R.id.details_frame_rate_row)
316 .setVisibility(View.GONE);
317 }
318
319 try {
320 long bitRate = Long.parseLong(
321 retriever.extractMetadata(
322 MediaMetadataRetriever
323 .METADATA_KEY_BIT_RATE));
324 String bps;
325 if (bitRate < 1000000) {
326 bps = String.format(
327 activity.getString(
328 R.string.details_kbps),
329 bitRate / 1000);
330 } else {
331 bps = String.format(
332 activity.getString(
333 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700334 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700335 }
336 ((TextView) d.findViewById(
337 R.id.details_bit_rate_value))
338 .setText(bps);
339 } catch (NumberFormatException e) {
340 d.findViewById(R.id.details_bit_rate_row)
341 .setVisibility(View.GONE);
342 }
343
344 String format = retriever.extractMetadata(
345 MediaMetadataRetriever
346 .METADATA_KEY_VIDEO_FORMAT);
347 ((TextView) d.findViewById(
348 R.id.details_format_value))
349 .setText(format);
350
351 String codec = retriever.extractMetadata(
352 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800353 if (codec != null) {
354 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700355 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800356 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700357 }
Ray Chen0f0af522009-04-22 13:26:07 +0800358
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700359 } catch (RuntimeException ex) {
360 // Assume this is a corrupt video file.
361 } finally {
362 try {
363 retriever.release();
364 } catch (RuntimeException ex) {
365 // Ignore failures while cleaning up.
366 }
367 }
368 }
369
Ray Chen0f0af522009-04-22 13:26:07 +0800370 Image img = (Image) image;
371 String value = null;
372 if (dimensionWidth > 0 && dimensionHeight > 0) {
373 value = String.format(
374 activity.getString(R.string.details_dimension_x),
375 dimensionWidth, dimensionHeight);
376 } else {
377 String width = img.getExifTag(ExifInterface.TAG_IMAGE_WIDTH);
378 String height = img.getExifTag(ExifInterface.TAG_IMAGE_LENGTH);
379 if (width != null && height != null) {
380 value = EMPTY_STRING + width + " x " + height;
381 }
382 }
383 if (value != null) {
384 setDetailsValue(d, value, R.id.details_resolution_value);
385 } else {
386 hideDetailsRow(d, R.id.details_resolution_row);
387 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700388
Ray Chen0f0af522009-04-22 13:26:07 +0800389 value = img.getExifTag(ExifInterface.TAG_MAKE);
390 if (value != null) {
391 setDetailsValue(d, value, R.id.details_make_value);
392 } else {
393 hideDetailsRow(d, R.id.details_make_row);
394 }
395
396 value = img.getExifTag(ExifInterface.TAG_MODEL);
397 if (value != null) {
398 setDetailsValue(d, value, R.id.details_model_value);
399 } else {
400 hideDetailsRow(d, R.id.details_model_row);
401 }
402
403 value = img.getExifTag(ExifInterface.TAG_WHITE_BALANCE);
404 if (value != null) {
405 value = ExifInterface.whiteBalanceToString(
406 Integer.parseInt(value));
407 }
408 if (value != null) {
409 setDetailsValue(d, value, R.id.details_whitebalance_value);
410 } else {
411 hideDetailsRow(d, R.id.details_whitebalance_row);
412 }
413
414 float lat = setLatLngDetails(d, img,
415 ExifInterface.TAG_GPS_LATITUDE,
416 ExifInterface.TAG_GPS_LATITUDE_REF);
417 float lng = setLatLngDetails(d, img,
418 ExifInterface.TAG_GPS_LONGITUDE,
419 ExifInterface.TAG_GPS_LONGITUDE_REF);
420 setReverseGeocodingDetails(d, activity, lat, lng);
421
422 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700423 long dateTaken = image.getDateTaken();
424 if (dateTaken != 0) {
425 Date date = new Date(image.getDateTaken());
426 SimpleDateFormat dateFormat = new SimpleDateFormat();
Ray Chen0f0af522009-04-22 13:26:07 +0800427 value = dateFormat.format(date);
428 }
429 if (value != EMPTY_STRING) {
430 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700431 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800432 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700433 }
434
435 builder.setNeutralButton(R.string.details_ok,
436 new DialogInterface.OnClickListener() {
437 public void onClick(DialogInterface dialog,
438 int which) {
439 dialog.dismiss();
440 }
441 });
442
Ray Chen993105a2009-04-10 02:11:35 -0700443 handler.post(
444 new Runnable() {
445 public void run() {
446 builder.setIcon(android.R.drawable.ic_dialog_info)
447 .setTitle(R.string.details_panel_title)
448 .setView(d)
449 .show();
450 }
451 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700452 }
453 });
454 return true;
455 }
456
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700457 // Called when "Rotate left" or "Rotate right" is clicked.
458 private static boolean onRotateClicked(MenuInvoker onInvoke,
459 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700460 onInvoke.run(new MenuCallback() {
461 public void run(Uri u, IImage image) {
462 if (image == null || image.isReadonly()) {
463 return;
464 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700465 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700466 }
467 });
468 return true;
469 }
470
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700471 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700472 private static boolean onCropClicked(MenuInvoker onInvoke,
473 final Activity activity) {
474 onInvoke.run(new MenuCallback() {
475 public void run(Uri u, IImage image) {
476 if (u == null) {
477 return;
478 }
479
480 Intent cropIntent = new Intent();
481 cropIntent.setClass(activity, CropImage.class);
482 cropIntent.setData(u);
483 activity.startActivityForResult(cropIntent,
484 RESULT_COMMON_MENU_CROP);
485 }
486 });
487 return true;
488 }
489
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700490 // Called when "Set as" is clicked.
491 private static boolean onSetAsClicked(MenuInvoker onInvoke,
492 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700493 onInvoke.run(new MenuCallback() {
494 public void run(Uri u, IImage image) {
495 if (u == null || image == null) {
496 return;
497 }
498
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700499 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
500 intent.setDataAndType(u, image.getMimeType());
501 intent.putExtra("mimeType", image.getMimeType());
502 activity.startActivity(Intent.createChooser(intent,
503 activity.getText(R.string.setImage)));
504 }
505 });
506 return true;
507 }
508
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700509 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700510 private static boolean onImageShareClicked(MenuInvoker onInvoke,
511 final Activity activity, final boolean isImage) {
512 onInvoke.run(new MenuCallback() {
513 public void run(Uri u, IImage image) {
514 if (image == null) return;
515 if (!isImage && getImageFileSize(image)
516 > SHARE_FILE_LENGTH_LIMIT) {
517 Toast.makeText(activity,
518 R.string.too_large_to_attach,
519 Toast.LENGTH_LONG).show();
520 return;
521 }
522
523 Intent intent = new Intent();
524 intent.setAction(Intent.ACTION_SEND);
525 String mimeType = image.getMimeType();
526 intent.setType(mimeType);
527 intent.putExtra(Intent.EXTRA_STREAM, u);
528 boolean isImage = ImageManager.isImageMimeType(mimeType);
529 try {
530 activity.startActivity(Intent.createChooser(intent,
531 activity.getText(isImage
532 ? R.string.sendImage
533 : R.string.sendVideo)));
534 } catch (android.content.ActivityNotFoundException ex) {
535 Toast.makeText(activity, isImage
536 ? R.string.no_way_to_share_image
537 : R.string.no_way_to_share_video,
538 Toast.LENGTH_SHORT).show();
539 }
540 }
541 });
542 return true;
543 }
544
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700545 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700546 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
547 final Activity activity) {
548 onInvoke.run(new MenuCallback() {
549 public void run(Uri uri, IImage image) {
550 if (image != null) {
551 Intent intent = new Intent(Intent.ACTION_VIEW,
552 image.fullSizeImageUri());
553 activity.startActivity(intent);
554 }
555 }});
556 return true;
557 }
558
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800559 static MenuItemsResult addImageMenuItems(
560 Menu menu,
561 int inclusions,
562 final boolean isImage,
563 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700564 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800565 final Runnable onDelete,
566 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700567 final ArrayList<MenuItem> requiresWriteAccessItems =
568 new ArrayList<MenuItem>();
569 final ArrayList<MenuItem> requiresNoDrmAccessItems =
570 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800571
572 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700573 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
574 MENU_IMAGE_ROTATE, 40, R.string.rotate)
575 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800576 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700577 // since the items within the submenu won't be shown anyway. This
578 // is really a framework bug in that it shouldn't show the submenu
579 // if the submenu has no visible items.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800580 requiresWriteAccessItems.add(rotateSubmenu.getItem());
581 if (rotateSubmenu != null) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700582 requiresWriteAccessItems.add(
583 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50,
584 R.string.rotate_left)
585 .setOnMenuItemClickListener(
586 new MenuItem.OnMenuItemClickListener() {
587 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700588 return onRotateClicked(onInvoke, -90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800589 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700590 }).setAlphabeticShortcut('l'));
591 requiresWriteAccessItems.add(
592 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60,
593 R.string.rotate_right)
594 .setOnMenuItemClickListener(
595 new MenuItem.OnMenuItemClickListener() {
596 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700597 return onRotateClicked(onInvoke, 90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800598 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700599 }).setAlphabeticShortcut('r'));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800600 }
601 }
602
603 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
604 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700605 R.string.camera_crop);
606 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
607 autoCrop.setOnMenuItemClickListener(
608 new MenuItem.OnMenuItemClickListener() {
609 public boolean onMenuItemClick(MenuItem item) {
610 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800611 }
612 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800613 requiresWriteAccessItems.add(autoCrop);
614 }
615
616 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700617 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
618 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800619 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700620 setMenu.setOnMenuItemClickListener(
621 new MenuItem.OnMenuItemClickListener() {
622 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700623 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800624 }
625 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800626 }
627
628 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800629 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
630 R.string.camera_share).setOnMenuItemClickListener(
631 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700632 public boolean onMenuItemClick(MenuItem item) {
633 return onImageShareClicked(onInvoke, activity,
634 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800635 }
636 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800637 item1.setIcon(android.R.drawable.ic_menu_share);
638 MenuItem item = item1;
639 requiresNoDrmAccessItems.add(item);
640 }
641
642 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700643 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
644 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800645 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700646 deleteItem.setOnMenuItemClickListener(
647 new MenuItem.OnMenuItemClickListener() {
648 public boolean onMenuItemClick(MenuItem item) {
649 deleteImpl(activity, onDelete, isImage);
650 return true;
651 }
652 })
653 .setAlphabeticShortcut('d')
654 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800655 }
656
657 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700658 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
659 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800660 public boolean onMenuItemClick(MenuItem item) {
Ray Chen993105a2009-04-10 02:11:35 -0700661 return onDetailsClicked(onInvoke, handler, activity, isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800662 }
663 });
664 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
665 }
666
667 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
668 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700669 .setOnMenuItemClickListener(
670 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800671 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700672 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800673 }
674 });
675 }
676
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800677 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700678 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700679 // protect against null here. this isn't strictly speaking
680 // required but if a client app isn't handling sdcard removal
681 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800682 if (image == null) {
683 return;
684 }
685 boolean readOnly = image.isReadonly();
686 boolean isDrm = image.isDrm();
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800687
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700688 for (MenuItem item : requiresWriteAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700689 item.setVisible(!readOnly);
690 item.setEnabled(!readOnly);
691 }
692 for (MenuItem item : requiresNoDrmAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700693 item.setVisible(!isDrm);
694 item.setEnabled(!isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800695 }
696 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700697
698 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700699 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800700 }
701 };
702 }
703
704 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700705 deleteImpl(activity, onDelete, true);
706 }
Owen Lin095afa12009-03-24 23:41:27 -0700707
Ray Chen7638a542009-03-24 20:37:45 -0700708 static void deleteVideo(Activity activity, Runnable onDelete) {
709 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800710 }
711
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700712 static void deleteImage(Activity activity, Runnable onDelete,
713 IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800714 if (image != null) {
Ray Chen7638a542009-03-24 20:37:45 -0700715 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800716 }
717 }
718
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700719 private static void deleteImpl(Activity activity, final Runnable onDelete,
720 boolean isPhoto) {
721 boolean confirm = android.preference.PreferenceManager
722 .getDefaultSharedPreferences(activity)
723 .getBoolean("pref_gallery_confirm_delete_key", true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800724 if (!confirm) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700725 if (onDelete != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800726 onDelete.run();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700727 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800728 } else {
729 displayDeleteDialog(activity, onDelete, isPhoto);
730 }
731 }
732
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700733 private static void displayDeleteDialog(Activity activity,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800734 final Runnable onDelete, boolean isPhoto) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700735 android.app.AlertDialog.Builder b =
736 new android.app.AlertDialog.Builder(activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800737 b.setIcon(android.R.drawable.ic_dialog_alert);
738 b.setTitle(R.string.confirm_delete_title);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700739 b.setMessage(isPhoto
740 ? R.string.confirm_delete_message
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800741 : R.string.confirm_delete_video_message);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700742 b.setPositiveButton(android.R.string.ok,
743 new android.content.DialogInterface.OnClickListener() {
744 public void onClick(android.content.DialogInterface v,
745 int x) {
746 if (onDelete != null) {
747 onDelete.run();
748 }
749 }
750 });
751 b.setNegativeButton(android.R.string.cancel,
752 new android.content.DialogInterface.OnClickListener() {
753 public void onClick(android.content.DialogInterface v,
754 int x) {
755 }
756 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800757 b.create().show();
758 }
759
760 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
761 final boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700762 int group = switchToVideo
763 ? MenuHelper.IMAGE_MODE_ITEM
764 : MenuHelper.VIDEO_MODE_ITEM;
765 int labelId = switchToVideo
766 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800767 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700768 int iconId = switchToVideo
769 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800770 : android.R.drawable.ic_menu_camera;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700771 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
772 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
773 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700774 return onSwitchModeClicked(activity, switchToVideo);
775 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700776 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800777 item.setIcon(iconId);
778 }
779
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700780 private static boolean onSwitchModeClicked(Activity activity,
781 boolean switchToVideo) {
782 String action = switchToVideo
783 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
784 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
785 Intent intent = new Intent(action);
786 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
787 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
788 activity.startActivity(intent);
789 return true;
790 }
791
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800792 static void gotoStillImageCapture(Activity activity) {
793 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
794 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
795 try {
796 activity.startActivity(intent);
797 } catch (ActivityNotFoundException e) {
798 Log.e(TAG, "Could not start still image capture activity", e);
799 }
800 }
801
802 static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700803 gotoGallery(activity, R.string.gallery_camera_bucket_name,
804 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800805 }
806
807 static void gotoCameraVideoGallery(Activity activity) {
808 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
809 ImageManager.INCLUDE_VIDEOS);
810 }
811
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700812 private static void gotoGallery(Activity activity, int windowTitleId,
813 int mediaTypes) {
814 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
815 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800816 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
817 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700818 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800819 intent.putExtra("windowTitle", activity.getString(windowTitleId));
820 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700821
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800822 try {
823 activity.startActivity(intent);
824 } catch (ActivityNotFoundException e) {
825 Log.e(TAG, "Could not start gallery activity", e);
826 }
827 }
828
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700829 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800830 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700831 .setOnMenuItemClickListener(
832 new MenuItem.OnMenuItemClickListener() {
833 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700834 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700835 }
836 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700837 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800838
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700839 private static boolean onCapturePictureClicked(Activity activity) {
840 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
841 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
842 try {
843 activity.startActivity(intent);
844 } catch (android.content.ActivityNotFoundException e) {
845 // Ignore exception
846 }
847 return true;
848 }
849
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700850 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800851 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700852 .setOnMenuItemClickListener(
853 new MenuItem.OnMenuItemClickListener() {
854 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700855 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700856 }
857 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800858 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700859
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700860 private static boolean onCaptureVideoClicked(Activity activity) {
861 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
862 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
863 try {
864 activity.startActivity(intent);
865 } catch (android.content.ActivityNotFoundException e) {
866 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700867 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700868 return true;
869 }
870
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700871 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
872 addCapturePictureMenuItems(menu, activity);
873 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800874 }
875
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700876 public static String formatDuration(final Activity activity,
877 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800878 int duration = durationMs / 1000;
879 int h = duration / 3600;
880 int m = (duration - h * 3600) / 60;
881 int s = duration - (h * 3600 + m * 60);
882 String durationValue;
883 if (h == 0) {
884 durationValue = String.format(
885 activity.getString(R.string.details_ms), m, s);
886 } else {
887 durationValue = String.format(
888 activity.getString(R.string.details_hms), h, m, s);
889 }
890 return durationValue;
891 }
892
893 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700894 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800895 }
896
897 public static void showStorageToast(Activity activity, int remaining) {
898 String noStorageText = null;
899
900 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
901 String state = Environment.getExternalStorageState();
902 if (state == Environment.MEDIA_CHECKING) {
903 noStorageText = activity.getString(R.string.preparing_sd);
904 } else {
905 noStorageText = activity.getString(R.string.no_storage);
906 }
907 } else if (remaining < 1) {
908 noStorageText = activity.getString(R.string.not_enough_space);
909 }
910
911 if (noStorageText != null) {
912 Toast.makeText(activity, noStorageText, 5000).show();
913 }
914 }
915
916 public static int calculatePicturesRemaining() {
917 try {
918 if (!ImageManager.hasStorage()) {
919 return NO_STORAGE_ERROR;
920 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700921 String storageDirectory =
922 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800923 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700924 float remaining = ((float) stat.getAvailableBlocks()
925 * (float) stat.getBlockSize()) / 400000F;
926 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800927 }
928 } catch (Exception ex) {
929 // if we can't stat the filesystem then we don't know how many
930 // pictures are remaining. it might be zero but just leave it
931 // blank since we really don't know.
932 return CANNOT_STAT_ERROR;
933 }
934 }
935}