blob: 7148cd68d8949a9874cf1e00982702a8faf60f5d [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
19import java.io.Closeable;
20import java.util.ArrayList;
21
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.content.ActivityNotFoundException;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.content.SharedPreferences;
28import android.content.res.Configuration;
29import 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;
36import android.util.Config;
37import android.util.Log;
38import android.view.Menu;
39import android.view.MenuItem;
40import android.view.SubMenu;
41import android.view.View;
42import android.view.MenuItem.OnMenuItemClickListener;
43import android.widget.ImageView;
44import android.widget.TextView;
45import android.widget.Toast;
46
47import com.android.camera.ImageManager.IImage;
48
49public class MenuHelper {
50 static private final String TAG = "MenuHelper";
51
52 static public final int GENERIC_ITEM = 1;
53 static public final int IMAGE_SAVING_ITEM = 2;
54 static public final int VIDEO_SAVING_ITEM = 3;
55 static public final int IMAGE_MODE_ITEM = 4;
56 static public final int VIDEO_MODE_ITEM = 5;
57 static public final int MENU_ITEM_MAX = 5;
58
59 static public final int INCLUDE_ALL = 0xFFFFFFFF;
60 static public final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
61 static public final int INCLUDE_SHARE_MENU = (1 << 1);
62 static public final int INCLUDE_SET_MENU = (1 << 2);
63 static public final int INCLUDE_CROP_MENU = (1 << 3);
64 static public final int INCLUDE_DELETE_MENU = (1 << 4);
65 static public final int INCLUDE_ROTATE_MENU = (1 << 5);
66 static public final int INCLUDE_DETAILS_MENU = (1 << 5);
67
68 static public final int MENU_SWITCH_CAMERA_MODE = 0;
69 static public final int MENU_CAPTURE_PICTURE = 1;
70 static public final int MENU_CAPTURE_VIDEO = 2;
71 static public final int MENU_IMAGE_SHARE = 10;
72 static public final int MENU_IMAGE_SET = 14;
73 static public final int MENU_IMAGE_SET_WALLPAPER = 15;
74 static public final int MENU_IMAGE_SET_CONTACT = 16;
75 static public final int MENU_IMAGE_SET_MYFAVE = 17;
76 static public final int MENU_IMAGE_CROP = 18;
77 static public final int MENU_IMAGE_ROTATE = 19;
78 static public final int MENU_IMAGE_ROTATE_LEFT = 20;
79 static public final int MENU_IMAGE_ROTATE_RIGHT = 21;
80 static public final int MENU_IMAGE_TOSS = 22;
81 static public final int MENU_VIDEO_PLAY = 23;
82 static public final int MENU_VIDEO_SHARE = 24;
83 static public final int MENU_VIDEO_TOSS = 27;
84
85 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 {
93 public void gettingReadyToOpen(Menu menu, ImageManager.IImage image);
94 public void aboutToCall(MenuItem item, ImageManager.IImage image);
95 }
96
97 public interface MenuInvoker {
98 public void run(MenuCallback r);
99 }
100
101 public interface MenuCallback {
102 public void run(Uri uri, ImageManager.IImage image);
103 }
104
105 private static void closeSilently(Closeable target) {
106 try {
107 if (target != null) target.close();
108 } catch (Throwable t) {
109 // ignore all exceptions, that's what silently means
110 }
111 }
112
113 public static long getImageFileSize(ImageManager.IImage image) {
114 java.io.InputStream data = image.fullSizeImageData();
115 try {
116 return data.available();
117 } catch (java.io.IOException ex) {
118 return -1;
119 } finally {
120 closeSilently(data);
121 }
122 }
123
124 static MenuItemsResult addImageMenuItems(
125 Menu menu,
126 int inclusions,
127 final boolean isImage,
128 final Activity activity,
129 final Handler handler,
130 final Runnable onDelete,
131 final MenuInvoker onInvoke) {
132 final ArrayList<MenuItem> requiresWriteAccessItems = new ArrayList<MenuItem>();
133 final ArrayList<MenuItem> requiresNoDrmAccessItems = new ArrayList<MenuItem>();
134
135 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
136 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM, MENU_IMAGE_ROTATE,
137 40, R.string.rotate).setIcon(android.R.drawable.ic_menu_rotate);
138 // Don't show the rotate submenu if the item at hand is read only
139 // since the items within the submenu won't be shown anyway. This is
140 // really a framework bug in that it shouldn't show the submenu if
141 // the submenu has no visible items.
142 requiresWriteAccessItems.add(rotateSubmenu.getItem());
143 if (rotateSubmenu != null) {
144 requiresWriteAccessItems.add(rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50, R.string.rotate_left).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
145 public boolean onMenuItemClick(MenuItem item) {
146 onInvoke.run(new MenuCallback() {
147 public void run(Uri u, ImageManager.IImage image) {
148 if (image == null || image.isReadonly())
149 return;
150 image.rotateImageBy(-90);
151 }
152 });
153 return true;
154 }
155 }).setAlphabeticShortcut('l'));
156 requiresWriteAccessItems.add(rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60, R.string.rotate_right).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
157 public boolean onMenuItemClick(MenuItem item) {
158 onInvoke.run(new MenuCallback() {
159 public void run(Uri u, ImageManager.IImage image) {
160 if (image == null || image.isReadonly())
161 return;
162
163 image.rotateImageBy(90);
164 }
165 });
166 return true;
167 }
168 }).setAlphabeticShortcut('r'));
169 }
170 }
171
172 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
173 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
174 R.string.camera_crop).setOnMenuItemClickListener(
175 new MenuItem.OnMenuItemClickListener() {
176 public boolean onMenuItemClick(MenuItem item) {
177 onInvoke.run(new MenuCallback() {
178 public void run(Uri u, ImageManager.IImage image) {
179 if (u == null)
180 return;
181
182 Intent cropIntent = new Intent();
183 cropIntent.setClass(activity, CropImage.class);
184 cropIntent.setData(u);
185 activity.startActivityForResult(cropIntent, RESULT_COMMON_MENU_CROP);
186 }
187 });
188 return true;
189 }
190 });
191 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
192 requiresWriteAccessItems.add(autoCrop);
193 }
194
195 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
196 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75, R.string.camera_set);
197 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
198
199 setMenu.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
200 public boolean onMenuItemClick(MenuItem item) {
201 onInvoke.run(new MenuCallback() {
202 public void run(Uri u, ImageManager.IImage image) {
203 if (u == null || image == null)
204 return;
205
206 if (Config.LOGV)
207 Log.v(TAG, "in callback u is " + u + "; mime type is " + image.getMimeType());
208 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
209 intent.setDataAndType(u, image.getMimeType());
210 intent.putExtra("mimeType", image.getMimeType());
211 activity.startActivity(Intent.createChooser(intent, activity.getText(R.string.setImage)));
212 }
213 });
214 return true;
215 }
216 });
217 }
218
219 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
220 if (Config.LOGV)
221 Log.v(TAG, ">>>>> add share");
222 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
223 R.string.camera_share).setOnMenuItemClickListener(
224 new MenuItem.OnMenuItemClickListener() {
225 public boolean onMenuItemClick(MenuItem item) {
226 onInvoke.run(new MenuCallback() {
227 public void run(Uri u, ImageManager.IImage image) {
228 if (image == null)
229 return;
230 Intent intent = new Intent();
231 intent.setAction(Intent.ACTION_SEND);
232 String mimeType = image.getMimeType();
233 intent.setType(mimeType);
234 intent.putExtra(Intent.EXTRA_STREAM, u);
235 boolean isImage = ImageManager.isImageMimeType(mimeType);
236 try {
237 activity.startActivity(Intent.createChooser(intent,
238 activity.getText(
239 isImage ? R.string.sendImage : R.string.sendVideo)));
240 } catch (android.content.ActivityNotFoundException ex) {
241 Toast.makeText(activity,
242 isImage ? R.string.no_way_to_share_image
243 : R.string.no_way_to_share_video,
244 Toast.LENGTH_SHORT).show();
245 }
246 }
247 });
248 return true;
249 }
250 });
251 item1.setIcon(android.R.drawable.ic_menu_share);
252 MenuItem item = item1;
253 requiresNoDrmAccessItems.add(item);
254 }
255
256 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
257 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS, 70, R.string.camera_toss);
258 requiresWriteAccessItems.add(deleteItem);
259 deleteItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
260 public boolean onMenuItemClick(MenuItem item) {
261 deleteImageImpl(activity, onDelete, isImage);
262 return true;
263 }
264 })
265 .setAlphabeticShortcut('d')
266 .setIcon(android.R.drawable.ic_menu_delete);
267 }
268
269 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
270 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
271 public boolean onMenuItemClick(MenuItem item) {
272 onInvoke.run(new MenuCallback() {
273 public void run(Uri u, ImageManager.IImage image) {
274 if (image == null)
275 return;
276
277 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
278
279 final View d = View.inflate(activity, R.layout.detailsview, null);
280
281 ImageView imageView = (ImageView) d.findViewById(R.id.details_thumbnail_image);
282 imageView.setImageBitmap(image.miniThumbBitmap());
283
284 TextView textView = (TextView) d.findViewById(R.id.details_image_title);
285 textView.setText(image.getDisplayName());
286
287 long length = getImageFileSize(image);
288 String lengthString = lengthString = length < 0 ? ""
289 : android.text.format.Formatter.formatFileSize(activity, length);
290 ((TextView)d.findViewById(R.id.details_file_size_value))
291 .setText(lengthString);
292
293 int dimensionWidth = 0;
294 int dimensionHeight = 0;
295 if (isImage) {
296 dimensionWidth = image.getWidth();
297 dimensionHeight = image.getHeight();
298 d.findViewById(R.id.details_duration_row).setVisibility(View.GONE);
299 d.findViewById(R.id.details_frame_rate_row).setVisibility(View.GONE);
300 d.findViewById(R.id.details_bit_rate_row).setVisibility(View.GONE);
301 d.findViewById(R.id.details_format_row).setVisibility(View.GONE);
302 d.findViewById(R.id.details_codec_row).setVisibility(View.GONE);
303 } else {
304 MediaMetadataRetriever retriever = new MediaMetadataRetriever();
305 try {
306 retriever.setMode(MediaMetadataRetriever.MODE_GET_METADATA_ONLY);
307 retriever.setDataSource(image.getDataPath());
308 try {
309 dimensionWidth = Integer.parseInt(
310 retriever.extractMetadata(
311 MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
312 dimensionHeight = Integer.parseInt(
313 retriever.extractMetadata(
314 MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
315 } catch (NumberFormatException e) {
316 dimensionWidth = 0;
317 dimensionHeight = 0;
318 }
319
320 try {
321 int durationMs = Integer.parseInt(retriever.extractMetadata(
322 MediaMetadataRetriever.METADATA_KEY_DURATION));
323 String durationValue = formatDuration(
324 activity, durationMs);
325 ((TextView)d.findViewById(R.id.details_duration_value))
326 .setText(durationValue);
327 } catch (NumberFormatException e) {
328 d.findViewById(R.id.details_frame_rate_row)
329 .setVisibility(View.GONE);
330 }
331
332 try {
333 String frame_rate = String.format(
334 activity.getString(R.string.details_fps),
335 Integer.parseInt(
336 retriever.extractMetadata(
337 MediaMetadataRetriever.METADATA_KEY_FRAME_RATE)));
338 ((TextView)d.findViewById(R.id.details_frame_rate_value))
339 .setText(frame_rate);
340 } catch (NumberFormatException e) {
341 d.findViewById(R.id.details_frame_rate_row)
342 .setVisibility(View.GONE);
343 }
344
345 try {
346 long bitRate = Long.parseLong(retriever.extractMetadata(
347 MediaMetadataRetriever.METADATA_KEY_BIT_RATE));
348 String bps;
349 if (bitRate < 1000000) {
350 bps = String.format(
351 activity.getString(R.string.details_kbps),
352 bitRate / 1000);
353 } else {
354 bps = String.format(
355 activity.getString(R.string.details_mbps),
356 ((double) bitRate) / 1000000.0);
357 }
358 ((TextView)d.findViewById(R.id.details_bit_rate_value))
359 .setText(bps);
360 } catch (NumberFormatException e) {
361 d.findViewById(R.id.details_bit_rate_row)
362 .setVisibility(View.GONE);
363 }
364
365 String format = retriever.extractMetadata(
366 MediaMetadataRetriever.METADATA_KEY_VIDEO_FORMAT);
367 ((TextView)d.findViewById(R.id.details_format_value))
368 .setText(format);
369
370 String codec = retriever.extractMetadata(
371 MediaMetadataRetriever.METADATA_KEY_CODEC);
372
373 if (codec == null) {
374 d.findViewById(R.id.details_codec_row).
375 setVisibility(View.GONE);
376 } else {
377 ((TextView)d.findViewById(R.id.details_codec_value))
378 .setText(codec);
379 }
380 } catch(RuntimeException ex) {
381 // Assume this is a corrupt video file.
382 } finally {
383 try {
384 retriever.release();
385 } catch (RuntimeException ex) {
386 // Ignore failures while cleaning up.
387 }
388 }
389 }
390
391 String dimensionsString = String.format(
392 activity.getString(R.string.details_dimension_x),
393 dimensionWidth, dimensionHeight);
394 ((TextView)d.findViewById(R.id.details_resolution_value))
395 .setText(dimensionsString);
396
397 String dateString = "";
398 long dateTaken = image.getDateTaken();
399 if (dateTaken != 0) {
400 java.util.Date date = new java.util.Date(image.getDateTaken());
401 java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat();
402 dateString = dateFormat.format(date);
403
404 ((TextView)d.findViewById(R.id.details_date_taken_value))
405 .setText(dateString);
406 } else {
407 d.findViewById(R.id.details_date_taken_row)
408 .setVisibility(View.GONE);
409 }
410
411 builder.setNeutralButton(R.string.details_ok,
412 new DialogInterface.OnClickListener() {
413 public void onClick(DialogInterface dialog, int which) {
414 dialog.dismiss();
415 }
416 });
417
418 builder.setIcon(android.R.drawable.ic_dialog_info)
419 .setTitle(R.string.details_panel_title)
420 .setView(d)
421 .show();
422
423 }
424 });
425 return true;
426 }
427 });
428 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
429 }
430
431 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
432 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
433 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
434 public boolean onMenuItemClick(MenuItem item) {
435 onInvoke.run(new MenuCallback() {
436 public void run(Uri uri, IImage image) {
437 if (image != null) {
438 Intent intent = new Intent(Intent.ACTION_VIEW,
439 image.fullSizeImageUri());
440 activity.startActivity(intent);
441 }
442 }});
443 return true;
444 }
445 });
446 }
447
448
449 return new MenuItemsResult() {
450 public void gettingReadyToOpen(Menu menu, ImageManager.IImage image) {
451 // protect against null here. this isn't strictly speaking required
452 // but if a client app isn't handling sdcard removal properly it
453 // could happen
454 if (image == null) {
455 return;
456 }
457 boolean readOnly = image.isReadonly();
458 boolean isDrm = image.isDrm();
459 if (Config.LOGV)
460 Log.v(TAG, "readOnly: " + readOnly + "; drm: " + isDrm);
461 for (MenuItem item: requiresWriteAccessItems) {
462 if (Config.LOGV)
463 Log.v(TAG, "item is " + item.toString());
464 item.setVisible(!readOnly);
465 item.setEnabled(!readOnly);
466 }
467 for (MenuItem item: requiresNoDrmAccessItems) {
468 if (Config.LOGV)
469 Log.v(TAG, "item is " + item.toString());
470 item.setVisible(!isDrm);
471 item.setEnabled(!isDrm);
472 }
473 }
474 public void aboutToCall(MenuItem menu, ImageManager.IImage image) {
475 }
476 };
477 }
478
479 static void deletePhoto(Activity activity, Runnable onDelete) {
480 deleteImageImpl(activity, onDelete, true);
481 }
482
483 static void deleteImage(Activity activity, Runnable onDelete, IImage image) {
484 if (image != null) {
485 deleteImageImpl(activity, onDelete, ImageManager.isImage(image));
486 }
487 }
488
489 private static void deleteImageImpl(Activity activity, final Runnable onDelete, boolean isPhoto) {
490 boolean confirm = android.preference.PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("pref_gallery_confirm_delete_key", true);
491 if (!confirm) {
492 if (onDelete != null)
493 onDelete.run();
494 } else {
495 displayDeleteDialog(activity, onDelete, isPhoto);
496 }
497 }
498
499 public static void displayDeleteDialog(Activity activity,
500 final Runnable onDelete, boolean isPhoto) {
501 android.app.AlertDialog.Builder b = new android.app.AlertDialog.Builder(activity);
502 b.setIcon(android.R.drawable.ic_dialog_alert);
503 b.setTitle(R.string.confirm_delete_title);
504 b.setMessage(isPhoto? R.string.confirm_delete_message
505 : R.string.confirm_delete_video_message);
506 b.setPositiveButton(android.R.string.ok, new android.content.DialogInterface.OnClickListener() {
507 public void onClick(android.content.DialogInterface v, int x) {
508 if (onDelete != null)
509 onDelete.run();
510 }
511 });
512 b.setNegativeButton(android.R.string.cancel, new android.content.DialogInterface.OnClickListener() {
513 public void onClick(android.content.DialogInterface v, int x) {
514
515 }
516 });
517 b.create().show();
518 }
519
520 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
521 final boolean switchToVideo) {
522 int group = switchToVideo ? MenuHelper.IMAGE_MODE_ITEM : MenuHelper.VIDEO_MODE_ITEM;
523 int labelId = switchToVideo ? R.string.switch_to_video_lable
524 : R.string.switch_to_camera_lable;
525 int iconId = switchToVideo ? R.drawable.ic_menu_camera_video_view
526 : android.R.drawable.ic_menu_camera;
527 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0,
528 labelId).setOnMenuItemClickListener(
529 new OnMenuItemClickListener() {
530 public boolean onMenuItemClick(MenuItem item) {
531 String action = switchToVideo ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
532 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
533 Intent intent = new Intent(action);
534 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
535 activity.finish();
536 activity.startActivity(intent);
537 return true;
538 }
539 });
540 item.setIcon(iconId);
541 }
542
543 static void gotoStillImageCapture(Activity activity) {
544 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
545 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
546 try {
547 activity.startActivity(intent);
548 } catch (ActivityNotFoundException e) {
549 Log.e(TAG, "Could not start still image capture activity", e);
550 }
551 }
552
553 static void gotoCameraImageGallery(Activity activity) {
554 gotoGallery(activity, R.string.gallery_camera_bucket_name, ImageManager.INCLUDE_IMAGES);
555 }
556
557 static void gotoCameraVideoGallery(Activity activity) {
558 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
559 ImageManager.INCLUDE_VIDEOS);
560 }
561
562 static private void gotoGallery(Activity activity, int windowTitleId, int mediaTypes) {
563 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon().appendQueryParameter("bucketId",
564 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
565 Intent intent = new Intent(Intent.ACTION_VIEW, target);
566 intent.putExtra("windowTitle", activity.getString(windowTitleId));
567 intent.putExtra("mediaTypes", mediaTypes);
568 // Request unspecified so that we match the current camera orientation rather than
569 // matching the "flip orientation" preference.
570 // Disabled because people don't care for it. Also it's
571 // not as compelling now that we have implemented have quick orientation flipping.
572 // intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
573 // android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
574 try {
575 activity.startActivity(intent);
576 } catch (ActivityNotFoundException e) {
577 Log.e(TAG, "Could not start gallery activity", e);
578 }
579 }
580
581 static void addCaptureMenuItems(Menu menu, final Activity activity) {
582
583 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
584 .setOnMenuItemClickListener(
585 new MenuItem.OnMenuItemClickListener() {
586 public boolean onMenuItemClick(MenuItem item) {
587 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
588 try {
589 activity.startActivity(intent);
590 } catch (android.content.ActivityNotFoundException e) {
591 // Ignore exception
592 }
593 return true;
594 }
595 })
596 .setIcon(android.R.drawable.ic_menu_camera);
597
598 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
599 .setOnMenuItemClickListener(
600 new MenuItem.OnMenuItemClickListener() {
601 public boolean onMenuItemClick(MenuItem item) {
602 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
603 try {
604 activity.startActivity(intent);
605 } catch (android.content.ActivityNotFoundException e) {
606 // Ignore exception
607 }
608 return true;
609 }
610 })
611 .setIcon(R.drawable.ic_menu_camera_video_view);
612 }
613 static MenuItem addFlipOrientation(Menu menu, final Activity activity, final SharedPreferences prefs) {
614 // position 41 after rotate
615 // D
616 return menu
617 .add(Menu.CATEGORY_SECONDARY, 304, 41, R.string.flip_orientation)
618 .setOnMenuItemClickListener(
619 new MenuItem.OnMenuItemClickListener() {
620 public boolean onMenuItemClick(MenuItem item) {
621 // Check what our actual orientation is
622 int current = activity.getResources().getConfiguration().orientation;
623 int newOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
624 if (current == Configuration.ORIENTATION_LANDSCAPE) {
625 newOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
626 }
627 SharedPreferences.Editor editor = prefs.edit();
628 editor.putInt("nuorientation", newOrientation);
629 editor.commit();
630 requestOrientation(activity, prefs, true);
631 return true;
632 }
633 })
634 .setIcon(android.R.drawable.ic_menu_always_landscape_portrait);
635 }
636
637 static void requestOrientation(Activity activity, SharedPreferences prefs) {
638 requestOrientation(activity, prefs, false);
639 }
640
641 static private void requestOrientation(Activity activity, SharedPreferences prefs,
642 boolean ignoreIntentExtra) {
643 int req = prefs.getInt("nuorientation",
644 android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
645 // A little trick: use USER instead of UNSPECIFIED, so we ignore the
646 // orientation set by the activity below. It may have forced a landscape
647 // orientation, which the user has now cleared here.
648 if (req == android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
649 req = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
650 }
651 if (! ignoreIntentExtra) {
652 Intent intent = activity.getIntent();
653 req = intent.getIntExtra(MediaStore.EXTRA_SCREEN_ORIENTATION, req);
654 }
655 activity.setRequestedOrientation(req);
656 }
657
658 static void setFlipOrientationEnabled(Activity activity, MenuItem flipItem) {
659 int keyboard = activity.getResources().getConfiguration().hardKeyboardHidden;
660 flipItem.setEnabled(keyboard != android.content.res.Configuration.HARDKEYBOARDHIDDEN_NO);
661 }
662
663 public static String formatDuration(final Activity activity, int durationMs) {
664 int duration = durationMs / 1000;
665 int h = duration / 3600;
666 int m = (duration - h * 3600) / 60;
667 int s = duration - (h * 3600 + m * 60);
668 String durationValue;
669 if (h == 0) {
670 durationValue = String.format(
671 activity.getString(R.string.details_ms), m, s);
672 } else {
673 durationValue = String.format(
674 activity.getString(R.string.details_hms), h, m, s);
675 }
676 return durationValue;
677 }
678
679 public static void showStorageToast(Activity activity) {
680 showStorageToast(activity, calculatePicturesRemaining());
681 }
682
683 public static void showStorageToast(Activity activity, int remaining) {
684 String noStorageText = null;
685
686 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
687 String state = Environment.getExternalStorageState();
688 if (state == Environment.MEDIA_CHECKING) {
689 noStorageText = activity.getString(R.string.preparing_sd);
690 } else {
691 noStorageText = activity.getString(R.string.no_storage);
692 }
693 } else if (remaining < 1) {
694 noStorageText = activity.getString(R.string.not_enough_space);
695 }
696
697 if (noStorageText != null) {
698 Toast.makeText(activity, noStorageText, 5000).show();
699 }
700 }
701
702 public static int calculatePicturesRemaining() {
703 try {
704 if (!ImageManager.hasStorage()) {
705 return NO_STORAGE_ERROR;
706 } else {
707 String storageDirectory = Environment.getExternalStorageDirectory().toString();
708 StatFs stat = new StatFs(storageDirectory);
709 float remaining = ((float)stat.getAvailableBlocks() * (float)stat.getBlockSize()) / 400000F;
710 return (int)remaining;
711 }
712 } catch (Exception ex) {
713 // if we can't stat the filesystem then we don't know how many
714 // pictures are remaining. it might be zero but just leave it
715 // blank since we really don't know.
716 return CANNOT_STAT_ERROR;
717 }
718 }
719}
720