blob: 3ea30902b712fda06da8f1193be008929d10df98 [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;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080026import android.media.MediaMetadataRetriever;
27import android.net.Uri;
28import android.os.Environment;
29import android.os.Handler;
30import android.os.StatFs;
31import android.provider.MediaStore;
32import android.provider.MediaStore.Images;
33import android.util.Config;
34import android.util.Log;
35import android.view.Menu;
36import android.view.MenuItem;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070037import android.view.MenuItem.OnMenuItemClickListener;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080038import android.view.SubMenu;
39import android.view.View;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080040import android.widget.ImageView;
41import android.widget.TextView;
42import android.widget.Toast;
43
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070044import java.io.Closeable;
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -070045import java.io.IOException;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070046import java.text.SimpleDateFormat;
47import java.util.ArrayList;
48import java.util.Date;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080049
50public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070051 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080052
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070053 public static final int GENERIC_ITEM = 1;
54 public static final int IMAGE_SAVING_ITEM = 2;
55 public static final int VIDEO_SAVING_ITEM = 3;
56 public static final int IMAGE_MODE_ITEM = 4;
57 public static final int VIDEO_MODE_ITEM = 5;
58 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080059
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070060 public static final int INCLUDE_ALL = 0xFFFFFFFF;
61 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
62 public static final int INCLUDE_SHARE_MENU = (1 << 1);
63 public static final int INCLUDE_SET_MENU = (1 << 2);
64 public static final int INCLUDE_CROP_MENU = (1 << 3);
65 public static final int INCLUDE_DELETE_MENU = (1 << 4);
66 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
67 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080068
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070069 public static final int MENU_SWITCH_CAMERA_MODE = 0;
70 public static final int MENU_CAPTURE_PICTURE = 1;
71 public static final int MENU_CAPTURE_VIDEO = 2;
72 public static final int MENU_IMAGE_SHARE = 10;
73 public static final int MENU_IMAGE_SET = 14;
74 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070075 public static final int MENU_IMAGE_CROP = 18;
76 public static final int MENU_IMAGE_ROTATE = 19;
77 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
78 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
79 public static final int MENU_IMAGE_TOSS = 22;
80 public static final int MENU_VIDEO_PLAY = 23;
81 public static final int MENU_VIDEO_SHARE = 24;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080082
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070083 private static final long SHARE_FILE_LENGTH_LIMIT = 3L * 1024L * 1024L;
The Android Open Source Projecte3f45162009-03-11 12:11:58 -070084
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080085 public static final int NO_STORAGE_ERROR = -1;
86 public static final int CANNOT_STAT_ERROR = -2;
87
88 /** Activity result code used to report crop results.
89 */
90 public static final int RESULT_COMMON_MENU_CROP = 490;
91
92 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -070093 public void gettingReadyToOpen(Menu menu, IImage image);
94 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080095 }
96
97 public interface MenuInvoker {
98 public void run(MenuCallback r);
99 }
100
101 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700102 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800103 }
104
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700105 public static void closeSilently(Closeable c) {
106 if (c != null) {
107 try {
108 c.close();
109 } catch (Throwable e) {
110 // ignore
111 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800112 }
113 }
114
Owen Lin101d5282009-04-03 16:20:08 -0700115 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800116 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800117 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800118 try {
119 return data.available();
120 } catch (java.io.IOException ex) {
121 return -1;
122 } finally {
123 closeSilently(data);
124 }
125 }
126
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700127 // This is a hack before we find a solution to pass a permission to other
128 // applications. See bug #1735149.
129 // Checks if the URI starts with "content://mms".
130 public static boolean isMMSUri(Uri uri) {
131 return (uri != null) &&
132 uri.getScheme().equals("content") &&
133 uri.getAuthority().equals("mms");
134 }
135
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700136 public static void enableShareMenuItem(Menu menu, boolean enabled) {
137 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
138 if (item != null) {
139 item.setVisible(enabled);
140 item.setEnabled(enabled);
141 }
142 }
143
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700144 // Called when "Details" is clicked.
145 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700146 private static boolean onDetailsClicked(MenuInvoker onInvoke,
147 final Activity activity,
148 final boolean isImage) {
149 onInvoke.run(new MenuCallback() {
150 public void run(Uri u, IImage image) {
151 if (image == null) {
152 return;
153 }
154
155 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
156
157 final View d = View.inflate(activity, R.layout.detailsview,
158 null);
159
160 ImageView imageView = (ImageView) d.findViewById(
161 R.id.details_thumbnail_image);
162 imageView.setImageBitmap(image.miniThumbBitmap());
163
164 TextView textView = (TextView) d.findViewById(
165 R.id.details_image_title);
166 textView.setText(image.getDisplayName());
167
168 long length = getImageFileSize(image);
169 String lengthString = lengthString = length < 0 ? ""
170 : android.text.format.Formatter.formatFileSize(
171 activity, length);
172 ((TextView) d
173 .findViewById(R.id.details_file_size_value))
174 .setText(lengthString);
175
176 int dimensionWidth = 0;
177 int dimensionHeight = 0;
178 if (isImage) {
179 dimensionWidth = image.getWidth();
180 dimensionHeight = image.getHeight();
181 d.findViewById(R.id.details_duration_row)
182 .setVisibility(View.GONE);
183 d.findViewById(R.id.details_frame_rate_row)
184 .setVisibility(View.GONE);
185 d.findViewById(R.id.details_bit_rate_row)
186 .setVisibility(View.GONE);
187 d.findViewById(R.id.details_format_row)
188 .setVisibility(View.GONE);
189 d.findViewById(R.id.details_codec_row)
190 .setVisibility(View.GONE);
191 } else {
192 MediaMetadataRetriever retriever
193 = new MediaMetadataRetriever();
194 try {
195 retriever.setMode(MediaMetadataRetriever
196 .MODE_GET_METADATA_ONLY);
197 retriever.setDataSource(image.getDataPath());
198 try {
199 dimensionWidth = Integer.parseInt(
200 retriever.extractMetadata(
201 MediaMetadataRetriever
202 .METADATA_KEY_VIDEO_WIDTH));
203 dimensionHeight = Integer.parseInt(
204 retriever.extractMetadata(
205 MediaMetadataRetriever
206 .METADATA_KEY_VIDEO_HEIGHT));
207 } catch (NumberFormatException e) {
208 dimensionWidth = 0;
209 dimensionHeight = 0;
210 }
211
212 try {
213 int durationMs = Integer.parseInt(
214 retriever.extractMetadata(
215 MediaMetadataRetriever
216 .METADATA_KEY_DURATION));
217 String durationValue = formatDuration(
218 activity, durationMs);
219 ((TextView) d.findViewById(
220 R.id.details_duration_value))
221 .setText(durationValue);
222 } catch (NumberFormatException e) {
223 d.findViewById(
224 R.id.details_frame_rate_row)
225 .setVisibility(View.GONE);
226 }
227
228 try {
229 String frameRate = String.format(
230 activity.getString(R.string.details_fps),
231 Integer.parseInt(
232 retriever.extractMetadata(
233 MediaMetadataRetriever
234 .METADATA_KEY_FRAME_RATE)));
235 ((TextView) d.findViewById(
236 R.id.details_frame_rate_value))
237 .setText(frameRate);
238 } catch (NumberFormatException e) {
239 d.findViewById(
240 R.id.details_frame_rate_row)
241 .setVisibility(View.GONE);
242 }
243
244 try {
245 long bitRate = Long.parseLong(
246 retriever.extractMetadata(
247 MediaMetadataRetriever
248 .METADATA_KEY_BIT_RATE));
249 String bps;
250 if (bitRate < 1000000) {
251 bps = String.format(
252 activity.getString(
253 R.string.details_kbps),
254 bitRate / 1000);
255 } else {
256 bps = String.format(
257 activity.getString(
258 R.string.details_mbps),
259 ((double) bitRate) / 1000000.0);
260 }
261 ((TextView) d.findViewById(
262 R.id.details_bit_rate_value))
263 .setText(bps);
264 } catch (NumberFormatException e) {
265 d.findViewById(R.id.details_bit_rate_row)
266 .setVisibility(View.GONE);
267 }
268
269 String format = retriever.extractMetadata(
270 MediaMetadataRetriever
271 .METADATA_KEY_VIDEO_FORMAT);
272 ((TextView) d.findViewById(
273 R.id.details_format_value))
274 .setText(format);
275
276 String codec = retriever.extractMetadata(
277 MediaMetadataRetriever.METADATA_KEY_CODEC);
278
279 if (codec == null) {
280 d.findViewById(R.id.details_codec_row).
281 setVisibility(View.GONE);
282 } else {
283 ((TextView) d.findViewById(
284 R.id.details_codec_value))
285 .setText(codec);
286 }
287 } catch (RuntimeException ex) {
288 // Assume this is a corrupt video file.
289 } finally {
290 try {
291 retriever.release();
292 } catch (RuntimeException ex) {
293 // Ignore failures while cleaning up.
294 }
295 }
296 }
297
298 String dimensionsString = String.format(
299 activity.getString(R.string.details_dimension_x),
300 dimensionWidth, dimensionHeight);
301 ((TextView) d
302 .findViewById(R.id.details_resolution_value))
303 .setText(dimensionsString);
304
305 String dateString = "";
306 long dateTaken = image.getDateTaken();
307 if (dateTaken != 0) {
308 Date date = new Date(image.getDateTaken());
309 SimpleDateFormat dateFormat = new SimpleDateFormat();
310 dateString = dateFormat.format(date);
311
312 ((TextView) d
313 .findViewById(R.id.details_date_taken_value))
314 .setText(dateString);
315 } else {
316 d.findViewById(R.id.details_date_taken_row)
317 .setVisibility(View.GONE);
318 }
319
320 builder.setNeutralButton(R.string.details_ok,
321 new DialogInterface.OnClickListener() {
322 public void onClick(DialogInterface dialog,
323 int which) {
324 dialog.dismiss();
325 }
326 });
327
328 builder.setIcon(android.R.drawable.ic_dialog_info)
329 .setTitle(R.string.details_panel_title)
330 .setView(d)
331 .show();
332 }
333 });
334 return true;
335 }
336
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700337 // Called when "Rotate left" or "Rotate right" is clicked.
338 private static boolean onRotateClicked(MenuInvoker onInvoke,
339 final int degree) {
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 || image.isReadonly()) {
343 return;
344 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700345 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700346 }
347 });
348 return true;
349 }
350
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700351 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700352 private static boolean onCropClicked(MenuInvoker onInvoke,
353 final Activity activity) {
354 onInvoke.run(new MenuCallback() {
355 public void run(Uri u, IImage image) {
356 if (u == null) {
357 return;
358 }
359
360 Intent cropIntent = new Intent();
361 cropIntent.setClass(activity, CropImage.class);
362 cropIntent.setData(u);
363 activity.startActivityForResult(cropIntent,
364 RESULT_COMMON_MENU_CROP);
365 }
366 });
367 return true;
368 }
369
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700370 // Called when "Set as" is clicked.
371 private static boolean onSetAsClicked(MenuInvoker onInvoke,
372 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700373 onInvoke.run(new MenuCallback() {
374 public void run(Uri u, IImage image) {
375 if (u == null || image == null) {
376 return;
377 }
378
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700379 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
380 intent.setDataAndType(u, image.getMimeType());
381 intent.putExtra("mimeType", image.getMimeType());
382 activity.startActivity(Intent.createChooser(intent,
383 activity.getText(R.string.setImage)));
384 }
385 });
386 return true;
387 }
388
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700389 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700390 private static boolean onImageShareClicked(MenuInvoker onInvoke,
391 final Activity activity, final boolean isImage) {
392 onInvoke.run(new MenuCallback() {
393 public void run(Uri u, IImage image) {
394 if (image == null) return;
395 if (!isImage && getImageFileSize(image)
396 > SHARE_FILE_LENGTH_LIMIT) {
397 Toast.makeText(activity,
398 R.string.too_large_to_attach,
399 Toast.LENGTH_LONG).show();
400 return;
401 }
402
403 Intent intent = new Intent();
404 intent.setAction(Intent.ACTION_SEND);
405 String mimeType = image.getMimeType();
406 intent.setType(mimeType);
407 intent.putExtra(Intent.EXTRA_STREAM, u);
408 boolean isImage = ImageManager.isImageMimeType(mimeType);
409 try {
410 activity.startActivity(Intent.createChooser(intent,
411 activity.getText(isImage
412 ? R.string.sendImage
413 : R.string.sendVideo)));
414 } catch (android.content.ActivityNotFoundException ex) {
415 Toast.makeText(activity, isImage
416 ? R.string.no_way_to_share_image
417 : R.string.no_way_to_share_video,
418 Toast.LENGTH_SHORT).show();
419 }
420 }
421 });
422 return true;
423 }
424
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700425 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700426 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
427 final Activity activity) {
428 onInvoke.run(new MenuCallback() {
429 public void run(Uri uri, IImage image) {
430 if (image != null) {
431 Intent intent = new Intent(Intent.ACTION_VIEW,
432 image.fullSizeImageUri());
433 activity.startActivity(intent);
434 }
435 }});
436 return true;
437 }
438
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800439 static MenuItemsResult addImageMenuItems(
440 Menu menu,
441 int inclusions,
442 final boolean isImage,
443 final Activity activity,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800444 final Runnable onDelete,
445 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700446 final ArrayList<MenuItem> requiresWriteAccessItems =
447 new ArrayList<MenuItem>();
448 final ArrayList<MenuItem> requiresNoDrmAccessItems =
449 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800450
451 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700452 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
453 MENU_IMAGE_ROTATE, 40, R.string.rotate)
454 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800455 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700456 // since the items within the submenu won't be shown anyway. This
457 // is really a framework bug in that it shouldn't show the submenu
458 // if the submenu has no visible items.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800459 requiresWriteAccessItems.add(rotateSubmenu.getItem());
460 if (rotateSubmenu != null) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700461 requiresWriteAccessItems.add(
462 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50,
463 R.string.rotate_left)
464 .setOnMenuItemClickListener(
465 new MenuItem.OnMenuItemClickListener() {
466 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700467 return onRotateClicked(onInvoke, -90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800468 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700469 }).setAlphabeticShortcut('l'));
470 requiresWriteAccessItems.add(
471 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60,
472 R.string.rotate_right)
473 .setOnMenuItemClickListener(
474 new MenuItem.OnMenuItemClickListener() {
475 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700476 return onRotateClicked(onInvoke, 90);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800477 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700478 }).setAlphabeticShortcut('r'));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800479 }
480 }
481
482 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
483 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700484 R.string.camera_crop);
485 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
486 autoCrop.setOnMenuItemClickListener(
487 new MenuItem.OnMenuItemClickListener() {
488 public boolean onMenuItemClick(MenuItem item) {
489 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800490 }
491 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800492 requiresWriteAccessItems.add(autoCrop);
493 }
494
495 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700496 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
497 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800498 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700499 setMenu.setOnMenuItemClickListener(
500 new MenuItem.OnMenuItemClickListener() {
501 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700502 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800503 }
504 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800505 }
506
507 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800508 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
509 R.string.camera_share).setOnMenuItemClickListener(
510 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700511 public boolean onMenuItemClick(MenuItem item) {
512 return onImageShareClicked(onInvoke, activity,
513 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800514 }
515 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800516 item1.setIcon(android.R.drawable.ic_menu_share);
517 MenuItem item = item1;
518 requiresNoDrmAccessItems.add(item);
519 }
520
521 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700522 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
523 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800524 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700525 deleteItem.setOnMenuItemClickListener(
526 new MenuItem.OnMenuItemClickListener() {
527 public boolean onMenuItemClick(MenuItem item) {
528 deleteImpl(activity, onDelete, isImage);
529 return true;
530 }
531 })
532 .setAlphabeticShortcut('d')
533 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800534 }
535
536 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700537 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
538 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800539 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700540 return onDetailsClicked(onInvoke, activity, isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800541 }
542 });
543 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
544 }
545
546 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
547 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700548 .setOnMenuItemClickListener(
549 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800550 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700551 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800552 }
553 });
554 }
555
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800556 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700557 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700558 // protect against null here. this isn't strictly speaking
559 // required but if a client app isn't handling sdcard removal
560 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800561 if (image == null) {
562 return;
563 }
564 boolean readOnly = image.isReadonly();
565 boolean isDrm = image.isDrm();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700566 if (Config.LOGV) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800567 Log.v(TAG, "readOnly: " + readOnly + "; drm: " + isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800568 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700569 for (MenuItem item : requiresWriteAccessItems) {
570 if (Config.LOGV) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800571 Log.v(TAG, "item is " + item.toString());
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700572 }
573 item.setVisible(!readOnly);
574 item.setEnabled(!readOnly);
575 }
576 for (MenuItem item : requiresNoDrmAccessItems) {
577 if (Config.LOGV) {
578 Log.v(TAG, "item is " + item.toString());
579 }
580 item.setVisible(!isDrm);
581 item.setEnabled(!isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800582 }
583 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700584
585 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700586 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800587 }
588 };
589 }
590
591 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700592 deleteImpl(activity, onDelete, true);
593 }
Owen Lin095afa12009-03-24 23:41:27 -0700594
Ray Chen7638a542009-03-24 20:37:45 -0700595 static void deleteVideo(Activity activity, Runnable onDelete) {
596 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800597 }
598
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700599 static void deleteImage(Activity activity, Runnable onDelete,
600 IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800601 if (image != null) {
Ray Chen7638a542009-03-24 20:37:45 -0700602 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800603 }
604 }
605
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700606 private static void deleteImpl(Activity activity, final Runnable onDelete,
607 boolean isPhoto) {
608 boolean confirm = android.preference.PreferenceManager
609 .getDefaultSharedPreferences(activity)
610 .getBoolean("pref_gallery_confirm_delete_key", true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800611 if (!confirm) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700612 if (onDelete != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800613 onDelete.run();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700614 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800615 } else {
616 displayDeleteDialog(activity, onDelete, isPhoto);
617 }
618 }
619
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700620 private static void displayDeleteDialog(Activity activity,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800621 final Runnable onDelete, boolean isPhoto) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700622 android.app.AlertDialog.Builder b =
623 new android.app.AlertDialog.Builder(activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800624 b.setIcon(android.R.drawable.ic_dialog_alert);
625 b.setTitle(R.string.confirm_delete_title);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700626 b.setMessage(isPhoto
627 ? R.string.confirm_delete_message
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800628 : R.string.confirm_delete_video_message);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700629 b.setPositiveButton(android.R.string.ok,
630 new android.content.DialogInterface.OnClickListener() {
631 public void onClick(android.content.DialogInterface v,
632 int x) {
633 if (onDelete != null) {
634 onDelete.run();
635 }
636 }
637 });
638 b.setNegativeButton(android.R.string.cancel,
639 new android.content.DialogInterface.OnClickListener() {
640 public void onClick(android.content.DialogInterface v,
641 int x) {
642 }
643 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800644 b.create().show();
645 }
646
647 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
648 final boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700649 int group = switchToVideo
650 ? MenuHelper.IMAGE_MODE_ITEM
651 : MenuHelper.VIDEO_MODE_ITEM;
652 int labelId = switchToVideo
653 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800654 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700655 int iconId = switchToVideo
656 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800657 : android.R.drawable.ic_menu_camera;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700658 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
659 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
660 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700661 return onSwitchModeClicked(activity, switchToVideo);
662 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700663 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800664 item.setIcon(iconId);
665 }
666
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700667 private static boolean onSwitchModeClicked(Activity activity,
668 boolean switchToVideo) {
669 String action = switchToVideo
670 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
671 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
672 Intent intent = new Intent(action);
673 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
674 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
675 activity.startActivity(intent);
676 return true;
677 }
678
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800679 static void gotoStillImageCapture(Activity activity) {
680 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
681 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
682 try {
683 activity.startActivity(intent);
684 } catch (ActivityNotFoundException e) {
685 Log.e(TAG, "Could not start still image capture activity", e);
686 }
687 }
688
689 static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700690 gotoGallery(activity, R.string.gallery_camera_bucket_name,
691 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800692 }
693
694 static void gotoCameraVideoGallery(Activity activity) {
695 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
696 ImageManager.INCLUDE_VIDEOS);
697 }
698
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700699 private static void gotoGallery(Activity activity, int windowTitleId,
700 int mediaTypes) {
701 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
702 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800703 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
704 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700705 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800706 intent.putExtra("windowTitle", activity.getString(windowTitleId));
707 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700708
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800709 try {
710 activity.startActivity(intent);
711 } catch (ActivityNotFoundException e) {
712 Log.e(TAG, "Could not start gallery activity", e);
713 }
714 }
715
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700716 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800717 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700718 .setOnMenuItemClickListener(
719 new MenuItem.OnMenuItemClickListener() {
720 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700721 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700722 }
723 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700724 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800725
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700726 private static boolean onCapturePictureClicked(Activity activity) {
727 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
728 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
729 try {
730 activity.startActivity(intent);
731 } catch (android.content.ActivityNotFoundException e) {
732 // Ignore exception
733 }
734 return true;
735 }
736
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700737 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800738 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700739 .setOnMenuItemClickListener(
740 new MenuItem.OnMenuItemClickListener() {
741 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700742 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700743 }
744 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800745 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700746
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700747 private static boolean onCaptureVideoClicked(Activity activity) {
748 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
749 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
750 try {
751 activity.startActivity(intent);
752 } catch (android.content.ActivityNotFoundException e) {
753 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700754 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700755 return true;
756 }
757
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700758 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
759 addCapturePictureMenuItems(menu, activity);
760 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800761 }
762
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700763 public static String formatDuration(final Activity activity,
764 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800765 int duration = durationMs / 1000;
766 int h = duration / 3600;
767 int m = (duration - h * 3600) / 60;
768 int s = duration - (h * 3600 + m * 60);
769 String durationValue;
770 if (h == 0) {
771 durationValue = String.format(
772 activity.getString(R.string.details_ms), m, s);
773 } else {
774 durationValue = String.format(
775 activity.getString(R.string.details_hms), h, m, s);
776 }
777 return durationValue;
778 }
779
780 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700781 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800782 }
783
784 public static void showStorageToast(Activity activity, int remaining) {
785 String noStorageText = null;
786
787 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
788 String state = Environment.getExternalStorageState();
789 if (state == Environment.MEDIA_CHECKING) {
790 noStorageText = activity.getString(R.string.preparing_sd);
791 } else {
792 noStorageText = activity.getString(R.string.no_storage);
793 }
794 } else if (remaining < 1) {
795 noStorageText = activity.getString(R.string.not_enough_space);
796 }
797
798 if (noStorageText != null) {
799 Toast.makeText(activity, noStorageText, 5000).show();
800 }
801 }
802
803 public static int calculatePicturesRemaining() {
804 try {
805 if (!ImageManager.hasStorage()) {
806 return NO_STORAGE_ERROR;
807 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700808 String storageDirectory =
809 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800810 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700811 float remaining = ((float) stat.getAvailableBlocks()
812 * (float) stat.getBlockSize()) / 400000F;
813 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800814 }
815 } catch (Exception ex) {
816 // if we can't stat the filesystem then we don't know how many
817 // pictures are remaining. it might be zero but just leave it
818 // blank since we really don't know.
819 return CANNOT_STAT_ERROR;
820 }
821 }
822}