blob: b5722329acd027b77ce815201d59608097995dba [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;
26import android.content.SharedPreferences;
27import android.content.res.Configuration;
28import 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;
35import android.util.Config;
36import android.util.Log;
37import android.view.Menu;
38import android.view.MenuItem;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070039import 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;
47import java.text.SimpleDateFormat;
48import java.util.ArrayList;
49import java.util.Date;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080050
51public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070052 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080053
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070054 public static final int GENERIC_ITEM = 1;
55 public static final int IMAGE_SAVING_ITEM = 2;
56 public static final int VIDEO_SAVING_ITEM = 3;
57 public static final int IMAGE_MODE_ITEM = 4;
58 public static final int VIDEO_MODE_ITEM = 5;
59 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080060
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070061 public static final int INCLUDE_ALL = 0xFFFFFFFF;
62 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
63 public static final int INCLUDE_SHARE_MENU = (1 << 1);
64 public static final int INCLUDE_SET_MENU = (1 << 2);
65 public static final int INCLUDE_CROP_MENU = (1 << 3);
66 public static final int INCLUDE_DELETE_MENU = (1 << 4);
67 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
68 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080069
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070070 public static final int MENU_SWITCH_CAMERA_MODE = 0;
71 public static final int MENU_CAPTURE_PICTURE = 1;
72 public static final int MENU_CAPTURE_VIDEO = 2;
73 public static final int MENU_IMAGE_SHARE = 10;
74 public static final int MENU_IMAGE_SET = 14;
75 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
76 public static final int MENU_IMAGE_SET_CONTACT = 16;
77 public static final int MENU_IMAGE_SET_MYFAVE = 17;
78 public static final int MENU_IMAGE_CROP = 18;
79 public static final int MENU_IMAGE_ROTATE = 19;
80 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
81 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
82 public static final int MENU_IMAGE_TOSS = 22;
83 public static final int MENU_VIDEO_PLAY = 23;
84 public static final int MENU_VIDEO_SHARE = 24;
85 public static final int MENU_VIDEO_TOSS = 27;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080086
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070087 private static final long SHARE_FILE_LENGTH_LIMIT = 3L * 1024L * 1024L;
The Android Open Source Projecte3f45162009-03-11 12:11:58 -070088
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080089 public static final int NO_STORAGE_ERROR = -1;
90 public static final int CANNOT_STAT_ERROR = -2;
91
92 /** Activity result code used to report crop results.
93 */
94 public static final int RESULT_COMMON_MENU_CROP = 490;
95
96 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -070097 public void gettingReadyToOpen(Menu menu, IImage image);
98 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080099 }
100
101 public interface MenuInvoker {
102 public void run(MenuCallback r);
103 }
104
105 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700106 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800107 }
108
109 private static void closeSilently(Closeable target) {
110 try {
111 if (target != null) target.close();
112 } catch (Throwable t) {
113 // ignore all exceptions, that's what silently means
114 }
115 }
116
Owen Lin101d5282009-04-03 16:20:08 -0700117 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800118 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800119 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800120 try {
121 return data.available();
122 } catch (java.io.IOException ex) {
123 return -1;
124 } finally {
125 closeSilently(data);
126 }
127 }
128
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700129 // This is a hack before we find a solution to pass a permission to other
130 // applications. See bug #1735149.
131 // Checks if the URI starts with "content://mms".
132 public static boolean isMMSUri(Uri uri) {
133 return (uri != null) &&
134 uri.getScheme().equals("content") &&
135 uri.getAuthority().equals("mms");
136 }
137
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700138 public static void enableShareMenuItem(Menu menu, boolean enabled) {
139 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
140 if (item != null) {
141 item.setVisible(enabled);
142 item.setEnabled(enabled);
143 }
144 }
145
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
337 private static boolean onRotateLeftClicked(MenuInvoker onInvoke) {
338 onInvoke.run(new MenuCallback() {
339 public void run(Uri u, IImage image) {
340 if (image == null || image.isReadonly()) {
341 return;
342 }
343 image.rotateImageBy(-90);
344 }
345 });
346 return true;
347 }
348
349 private static boolean onRotateRightClicked(MenuInvoker onInvoke) {
350 onInvoke.run(new MenuCallback() {
351 public void run(Uri u, IImage image) {
352 if (image == null || image.isReadonly()) {
353 return;
354 }
355 image.rotateImageBy(90);
356 }
357 });
358 return true;
359 }
360
361 private static boolean onCropClicked(MenuInvoker onInvoke,
362 final Activity activity) {
363 onInvoke.run(new MenuCallback() {
364 public void run(Uri u, IImage image) {
365 if (u == null) {
366 return;
367 }
368
369 Intent cropIntent = new Intent();
370 cropIntent.setClass(activity, CropImage.class);
371 cropIntent.setData(u);
372 activity.startActivityForResult(cropIntent,
373 RESULT_COMMON_MENU_CROP);
374 }
375 });
376 return true;
377 }
378
379 private static boolean onImageSaveClicked(MenuInvoker onInvoke,
380 final Activity activity) {
381 onInvoke.run(new MenuCallback() {
382 public void run(Uri u, IImage image) {
383 if (u == null || image == null) {
384 return;
385 }
386
387 if (Config.LOGV) {
388 Log.v(TAG, "in callback u is " + u + "; mime type is "
389 + image.getMimeType());
390 }
391 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
392 intent.setDataAndType(u, image.getMimeType());
393 intent.putExtra("mimeType", image.getMimeType());
394 activity.startActivity(Intent.createChooser(intent,
395 activity.getText(R.string.setImage)));
396 }
397 });
398 return true;
399 }
400
401 private static boolean onImageShareClicked(MenuInvoker onInvoke,
402 final Activity activity, final boolean isImage) {
403 onInvoke.run(new MenuCallback() {
404 public void run(Uri u, IImage image) {
405 if (image == null) return;
406 if (!isImage && getImageFileSize(image)
407 > SHARE_FILE_LENGTH_LIMIT) {
408 Toast.makeText(activity,
409 R.string.too_large_to_attach,
410 Toast.LENGTH_LONG).show();
411 return;
412 }
413
414 Intent intent = new Intent();
415 intent.setAction(Intent.ACTION_SEND);
416 String mimeType = image.getMimeType();
417 intent.setType(mimeType);
418 intent.putExtra(Intent.EXTRA_STREAM, u);
419 boolean isImage = ImageManager.isImageMimeType(mimeType);
420 try {
421 activity.startActivity(Intent.createChooser(intent,
422 activity.getText(isImage
423 ? R.string.sendImage
424 : R.string.sendVideo)));
425 } catch (android.content.ActivityNotFoundException ex) {
426 Toast.makeText(activity, isImage
427 ? R.string.no_way_to_share_image
428 : R.string.no_way_to_share_video,
429 Toast.LENGTH_SHORT).show();
430 }
431 }
432 });
433 return true;
434 }
435
436 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
437 final Activity activity) {
438 onInvoke.run(new MenuCallback() {
439 public void run(Uri uri, IImage image) {
440 if (image != null) {
441 Intent intent = new Intent(Intent.ACTION_VIEW,
442 image.fullSizeImageUri());
443 activity.startActivity(intent);
444 }
445 }});
446 return true;
447 }
448
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800449 static MenuItemsResult addImageMenuItems(
450 Menu menu,
451 int inclusions,
452 final boolean isImage,
453 final Activity activity,
454 final Handler handler,
455 final Runnable onDelete,
456 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700457 final ArrayList<MenuItem> requiresWriteAccessItems =
458 new ArrayList<MenuItem>();
459 final ArrayList<MenuItem> requiresNoDrmAccessItems =
460 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800461
462 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700463 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
464 MENU_IMAGE_ROTATE, 40, R.string.rotate)
465 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800466 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700467 // since the items within the submenu won't be shown anyway. This
468 // is really a framework bug in that it shouldn't show the submenu
469 // if the submenu has no visible items.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800470 requiresWriteAccessItems.add(rotateSubmenu.getItem());
471 if (rotateSubmenu != null) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700472 requiresWriteAccessItems.add(
473 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50,
474 R.string.rotate_left)
475 .setOnMenuItemClickListener(
476 new MenuItem.OnMenuItemClickListener() {
477 public boolean onMenuItemClick(MenuItem item) {
478 return onRotateLeftClicked(onInvoke);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800479 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700480 }).setAlphabeticShortcut('l'));
481 requiresWriteAccessItems.add(
482 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60,
483 R.string.rotate_right)
484 .setOnMenuItemClickListener(
485 new MenuItem.OnMenuItemClickListener() {
486 public boolean onMenuItemClick(MenuItem item) {
487 return onRotateRightClicked(onInvoke);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800488 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700489 }).setAlphabeticShortcut('r'));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800490 }
491 }
492
493 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
494 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700495 R.string.camera_crop);
496 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
497 autoCrop.setOnMenuItemClickListener(
498 new MenuItem.OnMenuItemClickListener() {
499 public boolean onMenuItemClick(MenuItem item) {
500 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800501 }
502 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800503 requiresWriteAccessItems.add(autoCrop);
504 }
505
506 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700507 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
508 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800509 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700510 setMenu.setOnMenuItemClickListener(
511 new MenuItem.OnMenuItemClickListener() {
512 public boolean onMenuItemClick(MenuItem item) {
513 return onImageSaveClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800514 }
515 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800516 }
517
518 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800519 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
520 R.string.camera_share).setOnMenuItemClickListener(
521 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700522 public boolean onMenuItemClick(MenuItem item) {
523 return onImageShareClicked(onInvoke, activity,
524 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800525 }
526 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800527 item1.setIcon(android.R.drawable.ic_menu_share);
528 MenuItem item = item1;
529 requiresNoDrmAccessItems.add(item);
530 }
531
532 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700533 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
534 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800535 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700536 deleteItem.setOnMenuItemClickListener(
537 new MenuItem.OnMenuItemClickListener() {
538 public boolean onMenuItemClick(MenuItem item) {
539 deleteImpl(activity, onDelete, isImage);
540 return true;
541 }
542 })
543 .setAlphabeticShortcut('d')
544 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800545 }
546
547 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700548 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
549 .setOnMenuItemClickListener(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 onDetailsClicked(onInvoke, activity, isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800552 }
553 });
554 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
555 }
556
557 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
558 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700559 .setOnMenuItemClickListener(
560 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800561 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700562 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800563 }
564 });
565 }
566
567
568 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700569 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700570 // protect against null here. this isn't strictly speaking
571 // required but if a client app isn't handling sdcard removal
572 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800573 if (image == null) {
574 return;
575 }
576 boolean readOnly = image.isReadonly();
577 boolean isDrm = image.isDrm();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700578 if (Config.LOGV) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800579 Log.v(TAG, "readOnly: " + readOnly + "; drm: " + isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800580 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700581 for (MenuItem item : requiresWriteAccessItems) {
582 if (Config.LOGV) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800583 Log.v(TAG, "item is " + item.toString());
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700584 }
585 item.setVisible(!readOnly);
586 item.setEnabled(!readOnly);
587 }
588 for (MenuItem item : requiresNoDrmAccessItems) {
589 if (Config.LOGV) {
590 Log.v(TAG, "item is " + item.toString());
591 }
592 item.setVisible(!isDrm);
593 item.setEnabled(!isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800594 }
595 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700596
Owen Lin101d5282009-04-03 16:20:08 -0700597 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800598 }
599 };
600 }
601
602 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700603 deleteImpl(activity, onDelete, true);
604 }
Owen Lin095afa12009-03-24 23:41:27 -0700605
Ray Chen7638a542009-03-24 20:37:45 -0700606 static void deleteVideo(Activity activity, Runnable onDelete) {
607 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800608 }
609
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700610 static void deleteImage(Activity activity, Runnable onDelete,
611 IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800612 if (image != null) {
Ray Chen7638a542009-03-24 20:37:45 -0700613 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800614 }
615 }
616
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700617 private static void deleteImpl(Activity activity, final Runnable onDelete,
618 boolean isPhoto) {
619 boolean confirm = android.preference.PreferenceManager
620 .getDefaultSharedPreferences(activity)
621 .getBoolean("pref_gallery_confirm_delete_key", true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800622 if (!confirm) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700623 if (onDelete != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800624 onDelete.run();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700625 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800626 } else {
627 displayDeleteDialog(activity, onDelete, isPhoto);
628 }
629 }
630
631 public static void displayDeleteDialog(Activity activity,
632 final Runnable onDelete, boolean isPhoto) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700633 android.app.AlertDialog.Builder b =
634 new android.app.AlertDialog.Builder(activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800635 b.setIcon(android.R.drawable.ic_dialog_alert);
636 b.setTitle(R.string.confirm_delete_title);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700637 b.setMessage(isPhoto
638 ? R.string.confirm_delete_message
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800639 : R.string.confirm_delete_video_message);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700640 b.setPositiveButton(android.R.string.ok,
641 new android.content.DialogInterface.OnClickListener() {
642 public void onClick(android.content.DialogInterface v,
643 int x) {
644 if (onDelete != null) {
645 onDelete.run();
646 }
647 }
648 });
649 b.setNegativeButton(android.R.string.cancel,
650 new android.content.DialogInterface.OnClickListener() {
651 public void onClick(android.content.DialogInterface v,
652 int x) {
653 }
654 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800655 b.create().show();
656 }
657
658 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
659 final boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700660 int group = switchToVideo
661 ? MenuHelper.IMAGE_MODE_ITEM
662 : MenuHelper.VIDEO_MODE_ITEM;
663 int labelId = switchToVideo
664 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800665 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700666 int iconId = switchToVideo
667 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800668 : android.R.drawable.ic_menu_camera;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700669 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
670 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
671 public boolean onMenuItemClick(MenuItem item) {
672 String action = switchToVideo
673 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
674 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
675 Intent intent = new Intent(action);
676 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
677 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
678 activity.startActivity(intent);
679 return true;
680 }
681 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800682 item.setIcon(iconId);
683 }
684
685 static void gotoStillImageCapture(Activity activity) {
686 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
687 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
688 try {
689 activity.startActivity(intent);
690 } catch (ActivityNotFoundException e) {
691 Log.e(TAG, "Could not start still image capture activity", e);
692 }
693 }
694
695 static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700696 gotoGallery(activity, R.string.gallery_camera_bucket_name,
697 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800698 }
699
700 static void gotoCameraVideoGallery(Activity activity) {
701 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
702 ImageManager.INCLUDE_VIDEOS);
703 }
704
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700705 private static void gotoGallery(Activity activity, int windowTitleId,
706 int mediaTypes) {
707 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
708 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800709 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
710 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700711 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800712 intent.putExtra("windowTitle", activity.getString(windowTitleId));
713 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700714
715 // Request unspecified so that we match the current camera orientation
716 // rather than matching the "flip orientation" preference.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800717 // Disabled because people don't care for it. Also it's
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700718 // not as compelling now that we have implemented have quick orientation
719 // flipping.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800720 // intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700721 // android.content.pm.ActivityInfo
722 // .SCREEN_ORIENTATION_UNSPECIFIED);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800723 try {
724 activity.startActivity(intent);
725 } catch (ActivityNotFoundException e) {
726 Log.e(TAG, "Could not start gallery activity", e);
727 }
728 }
729
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700730 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800731 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700732 .setOnMenuItemClickListener(
733 new MenuItem.OnMenuItemClickListener() {
734 public boolean onMenuItemClick(MenuItem item) {
735 Intent intent = new Intent(
736 MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700737 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800738 try {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700739 activity.startActivity(intent);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800740 } catch (android.content.ActivityNotFoundException e) {
741 // Ignore exception
742 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700743 return true;
744 }
745 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700746 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800747
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700748 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800749 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700750 .setOnMenuItemClickListener(
751 new MenuItem.OnMenuItemClickListener() {
752 public boolean onMenuItemClick(MenuItem item) {
753 Intent intent = new Intent(
754 MediaStore.INTENT_ACTION_VIDEO_CAMERA);
755 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
756 try {
757 activity.startActivity(intent);
758 } catch (android.content.ActivityNotFoundException e) {
759 // Ignore exception
760 }
761 return true;
762 }
763 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800764 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700765
766 static void addCaptureMenuItems(Menu menu, final Activity activity) {
767 addCapturePictureMenuItems(menu, activity);
768 addCaptureVideoMenuItems(menu, activity);
769 }
770
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700771 private static boolean onFlipOrientationClicked(Activity activity,
772 SharedPreferences prefs) {
773 // Check what our actual orientation is
774 int current = activity.getResources().getConfiguration().orientation;
775 int newOrientation = android.content.pm.ActivityInfo
776 .SCREEN_ORIENTATION_LANDSCAPE;
777 if (current == Configuration.ORIENTATION_LANDSCAPE) {
778 newOrientation = android.content.pm.ActivityInfo
779 .SCREEN_ORIENTATION_UNSPECIFIED;
780 }
781 SharedPreferences.Editor editor = prefs.edit();
782 editor.putInt("nuorientation", newOrientation);
783 editor.commit();
784 requestOrientation(activity, prefs, true);
785 return true;
786 }
787
788 static MenuItem addFlipOrientation(Menu menu, final Activity activity,
789 final SharedPreferences prefs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800790 // position 41 after rotate
791 // D
792 return menu
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700793 .add(Menu.CATEGORY_SECONDARY, 304, 41,
794 R.string.flip_orientation)
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800795 .setOnMenuItemClickListener(
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700796 new MenuItem.OnMenuItemClickListener() {
797 public boolean onMenuItemClick(MenuItem item) {
798 return onFlipOrientationClicked(activity, prefs);
799 }
800 }).setIcon(
801 android.R.drawable.ic_menu_always_landscape_portrait);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800802 }
803
804 static void requestOrientation(Activity activity, SharedPreferences prefs) {
805 requestOrientation(activity, prefs, false);
806 }
807
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700808 private static void requestOrientation(Activity activity,
809 SharedPreferences prefs, boolean ignoreIntentExtra) {
810 // Disable orientation for now. If it is set to
811 // SCREEN_ORIENTATION_SENSOR, a duplicated orientation will be observed.
The Android Open Source Project8d0dd0e2009-03-13 13:04:24 -0700812
813 return;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800814 }
815
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700816 static void setFlipOrientationEnabled(Activity activity,
817 MenuItem flipItem) {
818 int keyboard = activity.getResources().getConfiguration()
819 .hardKeyboardHidden;
820 flipItem.setEnabled(keyboard != android.content.res.Configuration
821 .HARDKEYBOARDHIDDEN_NO);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800822 }
823
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700824 public static String formatDuration(final Activity activity,
825 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800826 int duration = durationMs / 1000;
827 int h = duration / 3600;
828 int m = (duration - h * 3600) / 60;
829 int s = duration - (h * 3600 + m * 60);
830 String durationValue;
831 if (h == 0) {
832 durationValue = String.format(
833 activity.getString(R.string.details_ms), m, s);
834 } else {
835 durationValue = String.format(
836 activity.getString(R.string.details_hms), h, m, s);
837 }
838 return durationValue;
839 }
840
841 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700842 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800843 }
844
845 public static void showStorageToast(Activity activity, int remaining) {
846 String noStorageText = null;
847
848 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
849 String state = Environment.getExternalStorageState();
850 if (state == Environment.MEDIA_CHECKING) {
851 noStorageText = activity.getString(R.string.preparing_sd);
852 } else {
853 noStorageText = activity.getString(R.string.no_storage);
854 }
855 } else if (remaining < 1) {
856 noStorageText = activity.getString(R.string.not_enough_space);
857 }
858
859 if (noStorageText != null) {
860 Toast.makeText(activity, noStorageText, 5000).show();
861 }
862 }
863
864 public static int calculatePicturesRemaining() {
865 try {
866 if (!ImageManager.hasStorage()) {
867 return NO_STORAGE_ERROR;
868 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700869 String storageDirectory =
870 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800871 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700872 float remaining = ((float) stat.getAvailableBlocks()
873 * (float) stat.getBlockSize()) / 400000F;
874 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800875 }
876 } catch (Exception ex) {
877 // if we can't stat the filesystem then we don't know how many
878 // pictures are remaining. it might be zero but just leave it
879 // blank since we really don't know.
880 return CANNOT_STAT_ERROR;
881 }
882 }
883}