blob: 3a3a88174c4145cb5e8d443614ea75b1de4c2fd0 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.camera;
import com.android.gallery.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
import android.location.Geocoder;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.StatFs;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.text.format.Formatter;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.camera.gallery.IImage;
import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* A utility class to handle various kinds of menu operations.
*/
public class MenuHelper {
private static final String TAG = "MenuHelper";
public static final int INCLUDE_ALL = 0xFFFFFFFF;
public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
public static final int INCLUDE_SHARE_MENU = (1 << 1);
public static final int INCLUDE_SET_MENU = (1 << 2);
public static final int INCLUDE_CROP_MENU = (1 << 3);
public static final int INCLUDE_DELETE_MENU = (1 << 4);
public static final int INCLUDE_ROTATE_MENU = (1 << 5);
public static final int INCLUDE_DETAILS_MENU = (1 << 6);
public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
public static final int MENU_IMAGE_SHARE = 1;
public static final int MENU_IMAGE_SHOWMAP = 2;
public static final int POSITION_SWITCH_CAMERA_MODE = 1;
public static final int POSITION_GOTO_GALLERY = 2;
public static final int POSITION_VIEWPLAY = 3;
public static final int POSITION_CAPTURE_PICTURE = 4;
public static final int POSITION_CAPTURE_VIDEO = 5;
public static final int POSITION_IMAGE_SHARE = 6;
public static final int POSITION_IMAGE_ROTATE = 7;
public static final int POSITION_IMAGE_TOSS = 8;
public static final int POSITION_IMAGE_CROP = 9;
public static final int POSITION_IMAGE_SET = 10;
public static final int POSITION_DETAILS = 11;
public static final int POSITION_SHOWMAP = 12;
public static final int POSITION_SLIDESHOW = 13;
public static final int POSITION_MULTISELECT = 14;
public static final int POSITION_CAMERA_SETTING = 15;
public static final int POSITION_GALLERY_SETTING = 16;
public static final int NO_STORAGE_ERROR = -1;
public static final int CANNOT_STAT_ERROR = -2;
public static final String EMPTY_STRING = "";
public static final String JPEG_MIME_TYPE = "image/jpeg";
// valid range is -180f to +180f
public static final float INVALID_LATLNG = 255f;
/** Activity result code used to report crop results.
*/
public static final int RESULT_COMMON_MENU_CROP = 490;
public interface MenuItemsResult {
public void gettingReadyToOpen(Menu menu, IImage image);
public void aboutToCall(MenuItem item, IImage image);
}
public interface MenuInvoker {
public void run(MenuCallback r);
}
public interface MenuCallback {
public void run(Uri uri, IImage image);
}
public static void closeSilently(Closeable c) {
if (c != null) {
try {
c.close();
} catch (Throwable e) {
// ignore
}
}
}
public static long getImageFileSize(IImage image) {
java.io.InputStream data = image.fullSizeImageData();
if (data == null) return -1;
try {
return data.available();
} catch (java.io.IOException ex) {
return -1;
} finally {
closeSilently(data);
}
}
// This is a hack before we find a solution to pass a permission to other
// applications. See bug #1735149, #1836138.
// Checks if the URI is on our whitelist:
// content://media/... (MediaProvider)
// file:///sdcard/... (Browser download)
public static boolean isWhiteListUri(Uri uri) {
if (uri == null) return false;
String scheme = uri.getScheme();
String authority = uri.getAuthority();
if (scheme.equals("content") && authority.equals("media")) {
return true;
}
if (scheme.equals("file")) {
List<String> p = uri.getPathSegments();
if (p.size() >= 1 && p.get(0).equals("sdcard")) {
return true;
}
}
return false;
}
public static void enableShareMenuItem(Menu menu, boolean enabled) {
MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
if (item != null) {
item.setVisible(enabled);
item.setEnabled(enabled);
}
}
public static boolean hasLatLngData(IImage image) {
ExifInterface exif = getExif(image);
if (exif == null) return false;
float latlng[] = new float[2];
return exif.getLatLong(latlng);
}
public static void enableShowOnMapMenuItem(Menu menu, boolean enabled) {
MenuItem item = menu.findItem(MENU_IMAGE_SHOWMAP);
if (item != null) {
item.setEnabled(enabled);
}
}
private static void setDetailsValue(View d, String text, int valueId) {
((TextView) d.findViewById(valueId)).setText(text);
}
private static void hideDetailsRow(View d, int rowId) {
d.findViewById(rowId).setVisibility(View.GONE);
}
private static class UpdateLocationCallback implements
ReverseGeocoderTask.Callback {
WeakReference<View> mView;
public UpdateLocationCallback(WeakReference<View> view) {
mView = view;
}
public void onComplete(String location) {
// View d is per-thread data, so when setDetailsValue is
// executed by UI thread, it doesn't matter whether the
// details dialog is dismissed or not.
View view = mView.get();
if (view == null) return;
if (!location.equals(MenuHelper.EMPTY_STRING)) {
MenuHelper.setDetailsValue(view, location,
R.id.details_location_value);
} else {
MenuHelper.hideDetailsRow(view, R.id.details_location_row);
}
}
}
private static void setLatLngDetails(final View d, Activity context,
ExifInterface exif) {
float[] latlng = new float[2];
if (exif.getLatLong(latlng)) {
setDetailsValue(d, String.valueOf(latlng[0]),
R.id.details_latitude_value);
setDetailsValue(d, String.valueOf(latlng[1]),
R.id.details_longitude_value);
if (latlng[0] == INVALID_LATLNG || latlng[1] == INVALID_LATLNG) {
hideDetailsRow(d, R.id.details_latitude_row);
hideDetailsRow(d, R.id.details_longitude_row);
hideDetailsRow(d, R.id.details_location_row);
return;
}
UpdateLocationCallback cb = new UpdateLocationCallback(
new WeakReference<View>(d));
Geocoder geocoder = new Geocoder(context);
new ReverseGeocoderTask(geocoder, latlng, cb).execute();
} else {
hideDetailsRow(d, R.id.details_latitude_row);
hideDetailsRow(d, R.id.details_longitude_row);
hideDetailsRow(d, R.id.details_location_row);
}
}
private static ExifInterface getExif(IImage image) {
if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
return null;
}
try {
return new ExifInterface(image.getDataPath());
} catch (IOException ex) {
Log.e(TAG, "cannot read exif", ex);
return null;
}
}
// Called when "Show on Maps" is clicked.
// Displays image location on Google Maps for further operations.
private static boolean onShowMapClicked(MenuInvoker onInvoke,
final Handler handler,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (image == null) {
return;
}
boolean ok = false;
ExifInterface exif = getExif(image);
float latlng[] = null;
if (exif != null) {
latlng = new float[2];
if (exif.getLatLong(latlng)) {
ok = true;
}
}
if (!ok) {
handler.post(new Runnable() {
public void run() {
Toast.makeText(activity,
R.string.no_location_image,
Toast.LENGTH_SHORT).show();
}
});
return;
}
// Can't use geo:latitude,longitude because it only centers
// the MapView to specified location, but we need a bubble
// for further operations (routing to/from).
// The q=(lat, lng) syntax is suggested by geo-team.
String uri = "http://maps.google.com/maps?f=q&" +
"q=(" + latlng[0] + "," + latlng[1] + ")";
activity.startActivity(new Intent(
android.content.Intent.ACTION_VIEW,
Uri.parse(uri)));
}
});
return true;
}
private static void hideExifInformation(View d) {
hideDetailsRow(d, R.id.details_resolution_row);
hideDetailsRow(d, R.id.details_make_row);
hideDetailsRow(d, R.id.details_model_row);
hideDetailsRow(d, R.id.details_whitebalance_row);
hideDetailsRow(d, R.id.details_latitude_row);
hideDetailsRow(d, R.id.details_longitude_row);
hideDetailsRow(d, R.id.details_location_row);
}
private static void showExifInformation(IImage image, View d,
Activity activity) {
ExifInterface exif = getExif(image);
if (exif == null) {
hideExifInformation(d);
return;
}
String value = exif.getAttribute(ExifInterface.TAG_MAKE);
if (value != null) {
setDetailsValue(d, value, R.id.details_make_value);
} else {
hideDetailsRow(d, R.id.details_make_row);
}
value = exif.getAttribute(ExifInterface.TAG_MODEL);
if (value != null) {
setDetailsValue(d, value, R.id.details_model_value);
} else {
hideDetailsRow(d, R.id.details_model_row);
}
value = getWhiteBalanceString(exif);
if (value != null && !value.equals(EMPTY_STRING)) {
setDetailsValue(d, value, R.id.details_whitebalance_value);
} else {
hideDetailsRow(d, R.id.details_whitebalance_row);
}
setLatLngDetails(d, activity, exif);
}
/**
* Returns a human-readable string describing the white balance value. Returns empty
* string if there is no white balance value or it is not recognized.
*/
private static String getWhiteBalanceString(ExifInterface exif) {
int whitebalance = exif.getAttributeInt(ExifInterface.TAG_WHITE_BALANCE, -1);
if (whitebalance == -1) return "";
switch (whitebalance) {
case ExifInterface.WHITEBALANCE_AUTO:
return "Auto";
case ExifInterface.WHITEBALANCE_MANUAL:
return "Manual";
default:
return "";
}
}
// Called when "Details" is clicked.
// Displays detailed information about the image/video.
private static boolean onDetailsClicked(MenuInvoker onInvoke,
final Handler handler,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (image == null) {
return;
}
final AlertDialog.Builder builder =
new AlertDialog.Builder(activity);
final View d = View.inflate(activity, R.layout.detailsview,
null);
ImageView imageView = (ImageView) d.findViewById(
R.id.details_thumbnail_image);
imageView.setImageBitmap(image.miniThumbBitmap());
TextView textView = (TextView) d.findViewById(
R.id.details_image_title);
textView.setText(image.getTitle());
long length = getImageFileSize(image);
String lengthString = length < 0
? EMPTY_STRING
: Formatter.formatFileSize(activity, length);
((TextView) d
.findViewById(R.id.details_file_size_value))
.setText(lengthString);
d.findViewById(R.id.details_frame_rate_row)
.setVisibility(View.GONE);
d.findViewById(R.id.details_bit_rate_row)
.setVisibility(View.GONE);
d.findViewById(R.id.details_format_row)
.setVisibility(View.GONE);
d.findViewById(R.id.details_codec_row)
.setVisibility(View.GONE);
int dimensionWidth = 0;
int dimensionHeight = 0;
if (ImageManager.isImage(image)) {
// getWidth is much slower than reading from EXIF
dimensionWidth = image.getWidth();
dimensionHeight = image.getHeight();
d.findViewById(R.id.details_duration_row)
.setVisibility(View.GONE);
}
String value = null;
if (dimensionWidth > 0 && dimensionHeight > 0) {
value = String.format(
activity.getString(R.string.details_dimension_x),
dimensionWidth, dimensionHeight);
}
if (value != null) {
setDetailsValue(d, value, R.id.details_resolution_value);
} else {
hideDetailsRow(d, R.id.details_resolution_row);
}
value = EMPTY_STRING;
long dateTaken = image.getDateTaken();
if (dateTaken != 0) {
Date date = new Date(image.getDateTaken());
SimpleDateFormat dateFormat = new SimpleDateFormat();
value = dateFormat.format(date);
}
if (value != EMPTY_STRING) {
setDetailsValue(d, value, R.id.details_date_taken_value);
} else {
hideDetailsRow(d, R.id.details_date_taken_row);
}
// Show more EXIF header details for JPEG images.
if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
showExifInformation(image, d, activity);
} else {
hideExifInformation(d);
}
builder.setNeutralButton(R.string.details_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
dialog.dismiss();
}
});
handler.post(
new Runnable() {
public void run() {
builder.setIcon(
android.R.drawable.ic_dialog_info)
.setTitle(R.string.details_panel_title)
.setView(d)
.show();
}
});
}
});
return true;
}
// Called when "Rotate left" or "Rotate right" is clicked.
private static boolean onRotateClicked(MenuInvoker onInvoke,
final int degree) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (image == null || image.isReadonly()) {
return;
}
image.rotateImageBy(degree);
}
});
return true;
}
// Called when "Crop" is clicked.
private static boolean onCropClicked(MenuInvoker onInvoke,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (u == null) {
return;
}
Intent cropIntent = new Intent(
"com.android.camera.action.CROP");
cropIntent.setData(u);
activity.startActivityForResult(
cropIntent, RESULT_COMMON_MENU_CROP);
}
});
return true;
}
// Called when "Set as" is clicked.
private static boolean onSetAsClicked(MenuInvoker onInvoke,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (u == null || image == null) {
return;
}
Intent intent = Util.createSetAsIntent(image);
activity.startActivity(Intent.createChooser(intent,
activity.getText(R.string.setImage)));
}
});
return true;
}
// Called when "Share" is clicked.
private static boolean onImageShareClicked(MenuInvoker onInvoke,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri u, IImage image) {
if (image == null) return;
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
String mimeType = image.getMimeType();
intent.setType(mimeType);
intent.putExtra(Intent.EXTRA_STREAM, u);
boolean isImage = ImageManager.isImage(image);
try {
activity.startActivity(Intent.createChooser(intent,
activity.getText(isImage
? R.string.sendImage
: R.string.sendVideo)));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(activity, isImage
? R.string.no_way_to_share_image
: R.string.no_way_to_share_video,
Toast.LENGTH_SHORT).show();
}
}
});
return true;
}
// Called when "Play" is clicked.
private static boolean onViewPlayClicked(MenuInvoker onInvoke,
final Activity activity) {
onInvoke.run(new MenuCallback() {
public void run(Uri uri, IImage image) {
if (image != null) {
Intent intent = new Intent(Intent.ACTION_VIEW,
image.fullSizeImageUri());
activity.startActivity(intent);
}
}});
return true;
}
// Called when "Delete" is clicked.
private static boolean onDeleteClicked(MenuInvoker onInvoke,
final Activity activity, final Runnable onDelete) {
onInvoke.run(new MenuCallback() {
public void run(Uri uri, IImage image) {
if (image != null) {
deleteImage(activity, onDelete, image);
}
}});
return true;
}
static MenuItemsResult addImageMenuItems(
Menu menu,
int inclusions,
final Activity activity,
final Handler handler,
final Runnable onDelete,
final MenuInvoker onInvoke) {
final ArrayList<MenuItem> requiresWriteAccessItems =
new ArrayList<MenuItem>();
final ArrayList<MenuItem> requiresNoDrmAccessItems =
new ArrayList<MenuItem>();
final ArrayList<MenuItem> requiresImageItems =
new ArrayList<MenuItem>();
final ArrayList<MenuItem> requiresVideoItems =
new ArrayList<MenuItem>();
if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
SubMenu rotateSubmenu = menu.addSubMenu(Menu.NONE, Menu.NONE,
POSITION_IMAGE_ROTATE, R.string.rotate)
.setIcon(android.R.drawable.ic_menu_rotate);
// Don't show the rotate submenu if the item at hand is read only
// since the items within the submenu won't be shown anyway. This
// is really a framework bug in that it shouldn't show the submenu
// if the submenu has no visible items.
MenuItem rotateLeft = rotateSubmenu.add(R.string.rotate_left)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onRotateClicked(onInvoke, -90);
}
}).setAlphabeticShortcut('l');
MenuItem rotateRight = rotateSubmenu.add(R.string.rotate_right)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onRotateClicked(onInvoke, 90);
}
}).setAlphabeticShortcut('r');
requiresWriteAccessItems.add(rotateSubmenu.getItem());
requiresWriteAccessItems.add(rotateLeft);
requiresWriteAccessItems.add(rotateRight);
requiresImageItems.add(rotateSubmenu.getItem());
requiresImageItems.add(rotateLeft);
requiresImageItems.add(rotateRight);
}
if ((inclusions & INCLUDE_CROP_MENU) != 0) {
MenuItem autoCrop = menu.add(Menu.NONE, Menu.NONE,
POSITION_IMAGE_CROP, R.string.camera_crop);
autoCrop.setIcon(android.R.drawable.ic_menu_crop);
autoCrop.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onCropClicked(onInvoke, activity);
}
});
requiresWriteAccessItems.add(autoCrop);
requiresImageItems.add(autoCrop);
}
if ((inclusions & INCLUDE_SET_MENU) != 0) {
MenuItem setMenu = menu.add(Menu.NONE, Menu.NONE,
POSITION_IMAGE_SET, R.string.camera_set);
setMenu.setIcon(android.R.drawable.ic_menu_set_as);
setMenu.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onSetAsClicked(onInvoke, activity);
}
});
requiresImageItems.add(setMenu);
}
if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
MenuItem item1 = menu.add(Menu.NONE, MENU_IMAGE_SHARE,
POSITION_IMAGE_SHARE, R.string.camera_share)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onImageShareClicked(onInvoke, activity);
}
});
item1.setIcon(android.R.drawable.ic_menu_share);
MenuItem item = item1;
requiresNoDrmAccessItems.add(item);
}
if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
MenuItem deleteItem = menu.add(Menu.NONE, Menu.NONE,
POSITION_IMAGE_TOSS, R.string.camera_toss);
requiresWriteAccessItems.add(deleteItem);
deleteItem.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onDeleteClicked(onInvoke, activity,
onDelete);
}
})
.setAlphabeticShortcut('d')
.setIcon(android.R.drawable.ic_menu_delete);
}
if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
MenuItem detailsMenu = menu.add(Menu.NONE, Menu.NONE,
POSITION_DETAILS, R.string.details)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onDetailsClicked(onInvoke, handler, activity);
}
});
detailsMenu.setIcon(R.drawable.ic_menu_view_details);
}
if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) {
MenuItem showOnMapItem = menu.add(Menu.NONE, MENU_IMAGE_SHOWMAP,
POSITION_SHOWMAP, R.string.show_on_map);
showOnMapItem.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onShowMapClicked(onInvoke,
handler, activity);
}
}).setIcon(R.drawable.ic_menu_3d_globe);
requiresImageItems.add(showOnMapItem);
}
if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) {
MenuItem videoPlayItem = menu.add(Menu.NONE, Menu.NONE,
POSITION_VIEWPLAY, R.string.video_play)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onViewPlayClicked(onInvoke, activity);
}
});
videoPlayItem.setIcon(
com.android.internal.R.drawable.ic_menu_play_clip);
requiresVideoItems.add(videoPlayItem);
}
return new MenuItemsResult() {
public void gettingReadyToOpen(Menu menu, IImage image) {
// protect against null here. this isn't strictly speaking
// required but if a client app isn't handling sdcard removal
// properly it could happen
if (image == null) {
return;
}
ArrayList<MenuItem> enableList = new ArrayList<MenuItem>();
ArrayList<MenuItem> disableList = new ArrayList<MenuItem>();
ArrayList<MenuItem> list;
list = image.isReadonly() ? disableList : enableList;
list.addAll(requiresWriteAccessItems);
list = image.isDrm() ? disableList : enableList;
list.addAll(requiresNoDrmAccessItems);
list = ImageManager.isImage(image) ? enableList : disableList;
list.addAll(requiresImageItems);
list = ImageManager.isVideo(image) ? enableList : disableList;
list.addAll(requiresVideoItems);
for (MenuItem item : enableList) {
item.setVisible(true);
item.setEnabled(true);
}
for (MenuItem item : disableList) {
item.setVisible(false);
item.setEnabled(false);
}
}
// must override abstract method
public void aboutToCall(MenuItem menu, IImage image) {
}
};
}
static void deletePhoto(Activity activity, Runnable onDelete) {
deleteImpl(activity, onDelete, true);
}
static void deleteImage(
Activity activity, Runnable onDelete, IImage image) {
deleteImpl(activity, onDelete, ImageManager.isImage(image));
}
static void deleteImpl(
Activity activity, Runnable onDelete, boolean isImage) {
boolean needConfirm = PreferenceManager
.getDefaultSharedPreferences(activity)
.getBoolean("pref_gallery_confirm_delete_key", true);
if (!needConfirm) {
if (onDelete != null) onDelete.run();
} else {
String title = activity.getString(R.string.confirm_delete_title);
String message = activity.getString(isImage
? R.string.confirm_delete_message
: R.string.confirm_delete_video_message);
confirmAction(activity, title, message, onDelete);
}
}
public static void deleteMultiple(Context context, Runnable action) {
boolean needConfirm = PreferenceManager
.getDefaultSharedPreferences(context)
.getBoolean("pref_gallery_confirm_delete_key", true);
if (!needConfirm) {
if (action != null) action.run();
} else {
String title = context.getString(R.string.confirm_delete_title);
String message = context.getString(
R.string.confirm_delete_multiple_message);
confirmAction(context, title, message, action);
}
}
public static void confirmAction(Context context, String title,
String message, final Runnable action) {
OnClickListener listener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
if (action != null) action.run();
}
}
};
new AlertDialog.Builder(context)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(title)
.setMessage(message)
.setPositiveButton(android.R.string.ok, listener)
.setNegativeButton(android.R.string.cancel, listener)
.create()
.show();
}
static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_PICTURE,
R.string.capture_picture)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onCapturePictureClicked(activity);
}
}).setIcon(android.R.drawable.ic_menu_camera);
}
private static boolean onCapturePictureClicked(Activity activity) {
Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
activity.startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
// Ignore exception
}
return true;
}
static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_VIDEO,
R.string.capture_video)
.setOnMenuItemClickListener(
new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
return onCaptureVideoClicked(activity);
}
}).setIcon(R.drawable.ic_menu_camera_video_view);
}
private static boolean onCaptureVideoClicked(Activity activity) {
Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
activity.startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
// Ignore exception
}
return true;
}
public static void addCaptureMenuItems(Menu menu, final Activity activity) {
addCapturePictureMenuItems(menu, activity);
addCaptureVideoMenuItems(menu, activity);
}
public static String formatDuration(final Context context,
int durationMs) {
int duration = durationMs / 1000;
int h = duration / 3600;
int m = (duration - h * 3600) / 60;
int s = duration - (h * 3600 + m * 60);
String durationValue;
if (h == 0) {
durationValue = String.format(
context.getString(R.string.details_ms), m, s);
} else {
durationValue = String.format(
context.getString(R.string.details_hms), h, m, s);
}
return durationValue;
}
public static void showStorageToast(Activity activity) {
showStorageToast(activity, calculatePicturesRemaining());
}
public static void showStorageToast(Activity activity, int remaining) {
String noStorageText = null;
if (remaining == MenuHelper.NO_STORAGE_ERROR) {
String state = Environment.getExternalStorageState();
if (state == Environment.MEDIA_CHECKING) {
noStorageText = activity.getString(R.string.preparing_sd);
} else {
noStorageText = activity.getString(R.string.no_storage);
}
} else if (remaining < 1) {
noStorageText = activity.getString(R.string.not_enough_space);
}
if (noStorageText != null) {
Toast.makeText(activity, noStorageText, 5000).show();
}
}
public static int calculatePicturesRemaining() {
try {
if (!ImageManager.hasStorage()) {
return NO_STORAGE_ERROR;
} else {
String storageDirectory =
Environment.getExternalStorageDirectory().toString();
StatFs stat = new StatFs(storageDirectory);
float remaining = ((float) stat.getAvailableBlocks()
* (float) stat.getBlockSize()) / 400000F;
return (int) remaining;
}
} catch (Exception ex) {
// if we can't stat the filesystem then we don't know how many
// pictures are remaining. it might be zero but just leave it
// blank since we really don't know.
return CANNOT_STAT_ERROR;
}
}
}