blob: 81c9404a672a45b6d18e4a75c86492694f4fc60b [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;
Ray Chen0f0af522009-04-22 13:26:07 +080026import android.location.Address;
27import android.location.Geocoder;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080028import 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;
Owen Lin937fc482009-04-14 02:02:51 -070035import android.text.format.Formatter;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080036import android.util.Log;
37import android.view.Menu;
38import android.view.MenuItem;
39import android.view.SubMenu;
40import android.view.View;
Owen Lin059daa32009-05-18 15:31:17 -070041import android.view.MenuItem.OnMenuItemClickListener;
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;
Ray Chen0f0af522009-04-22 13:26:07 +080047import java.io.IOException;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070048import java.text.SimpleDateFormat;
49import java.util.ArrayList;
50import java.util.Date;
Ray Chend5142de2009-05-07 15:24:48 +080051import java.util.HashMap;
Ray Chen0f0af522009-04-22 13:26:07 +080052import java.util.List;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080053
Ray Chend5142de2009-05-07 15:24:48 +080054/**
55 * A utility class to handle various kinds of menu operations.
56 */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080057public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070058 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080059
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070060 public static final int GENERIC_ITEM = 1;
61 public static final int IMAGE_SAVING_ITEM = 2;
62 public static final int VIDEO_SAVING_ITEM = 3;
63 public static final int IMAGE_MODE_ITEM = 4;
64 public static final int VIDEO_MODE_ITEM = 5;
65 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080066
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070067 public static final int INCLUDE_ALL = 0xFFFFFFFF;
68 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
69 public static final int INCLUDE_SHARE_MENU = (1 << 1);
70 public static final int INCLUDE_SET_MENU = (1 << 2);
71 public static final int INCLUDE_CROP_MENU = (1 << 3);
72 public static final int INCLUDE_DELETE_MENU = (1 << 4);
73 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
74 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080075 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080076
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070077 public static final int MENU_SWITCH_CAMERA_MODE = 0;
78 public static final int MENU_CAPTURE_PICTURE = 1;
79 public static final int MENU_CAPTURE_VIDEO = 2;
80 public static final int MENU_IMAGE_SHARE = 10;
81 public static final int MENU_IMAGE_SET = 14;
82 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070083 public static final int MENU_IMAGE_CROP = 18;
84 public static final int MENU_IMAGE_ROTATE = 19;
85 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
86 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
87 public static final int MENU_IMAGE_TOSS = 22;
88 public static final int MENU_VIDEO_PLAY = 23;
89 public static final int MENU_VIDEO_SHARE = 24;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080090
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070091 private static final long SHARE_FILE_LENGTH_LIMIT = 3L * 1024L * 1024L;
The Android Open Source Projecte3f45162009-03-11 12:11:58 -070092
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080093 public static final int NO_STORAGE_ERROR = -1;
94 public static final int CANNOT_STAT_ERROR = -2;
Ray Chen0f0af522009-04-22 13:26:07 +080095 public static final String EMPTY_STRING = "";
Ray Chend5142de2009-05-07 15:24:48 +080096 public static final String JPEG_MIME_TYPE = "image/jpeg";
Ray Chen0a5a78c2009-04-24 14:13:01 +080097 // valid range is -180f to +180f
98 public static final float INVALID_LATLNG = 255f;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080099
100 /** Activity result code used to report crop results.
101 */
102 public static final int RESULT_COMMON_MENU_CROP = 490;
103
104 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700105 public void gettingReadyToOpen(Menu menu, IImage image);
106 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800107 }
108
109 public interface MenuInvoker {
110 public void run(MenuCallback r);
111 }
112
113 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700114 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800115 }
116
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700117 public static void closeSilently(Closeable c) {
118 if (c != null) {
119 try {
120 c.close();
121 } catch (Throwable e) {
122 // ignore
123 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800124 }
125 }
126
Owen Lin101d5282009-04-03 16:20:08 -0700127 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800128 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800129 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800130 try {
131 return data.available();
132 } catch (java.io.IOException ex) {
133 return -1;
134 } finally {
135 closeSilently(data);
136 }
137 }
138
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700139 // This is a hack before we find a solution to pass a permission to other
140 // applications. See bug #1735149.
141 // Checks if the URI starts with "content://mms".
142 public static boolean isMMSUri(Uri uri) {
143 return (uri != null) &&
144 uri.getScheme().equals("content") &&
145 uri.getAuthority().equals("mms");
146 }
Owen Lin937fc482009-04-14 02:02:51 -0700147
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700148 public static void enableShareMenuItem(Menu menu, boolean enabled) {
149 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
150 if (item != null) {
151 item.setVisible(enabled);
152 item.setEnabled(enabled);
153 }
154 }
155
Ray Chen0f0af522009-04-22 13:26:07 +0800156 private static void setDetailsValue(View d, String text, int valueId) {
157 ((TextView) d.findViewById(valueId)).setText(text);
158 }
159
160 private static void hideDetailsRow(View d, int rowId) {
161 d.findViewById(rowId).setVisibility(View.GONE);
162 }
163
Ray Chend5142de2009-05-07 15:24:48 +0800164 private static float[] getLatLng(HashMap<String, String> exifData) {
165 if (exifData == null) {
166 return null;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800167 }
168
Ray Chend5142de2009-05-07 15:24:48 +0800169 String latValue = exifData.get(ExifInterface.TAG_GPS_LATITUDE);
170 String latRef = exifData.get(ExifInterface.TAG_GPS_LATITUDE_REF);
171 String lngValue = exifData.get(ExifInterface.TAG_GPS_LONGITUDE);
172 String lngRef = exifData.get(ExifInterface.TAG_GPS_LONGITUDE_REF);
173 float[] latlng = null;
174
175 if (latValue != null && latRef != null
176 && lngValue != null && lngRef != null) {
177 latlng = new float[2];
178 latlng[0] = ExifInterface.convertRationalLatLonToFloat(
179 latValue, latRef);
180 latlng[1] = ExifInterface.convertRationalLatLonToFloat(
181 lngValue, lngRef);
Ray Chen0f0af522009-04-22 13:26:07 +0800182 }
Ray Chend5142de2009-05-07 15:24:48 +0800183
184 return latlng;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800185 }
186
Ray Chend5142de2009-05-07 15:24:48 +0800187 private static void setLatLngDetails(View d, Activity context,
188 HashMap<String, String> exifData) {
189 float[] latlng = getLatLng(exifData);
190 if (latlng != null) {
191 setDetailsValue(d, String.valueOf(latlng[0]),
192 R.id.details_latitude_value);
193 setDetailsValue(d, String.valueOf(latlng[1]),
194 R.id.details_longitude_value);
195 setReverseGeocodingDetails(d, context, latlng[0], latlng[1]);
196 } else {
197 hideDetailsRow(d, R.id.details_latitude_row);
198 hideDetailsRow(d, R.id.details_longitude_row);
199 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800200 }
Ray Chen0f0af522009-04-22 13:26:07 +0800201 }
202
203 private static void setReverseGeocodingDetails(View d, Activity context,
204 float lat, float lng) {
205 // Fill in reverse-geocoded address
206 String value = EMPTY_STRING;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800207 if (lat == INVALID_LATLNG || lng == INVALID_LATLNG) {
208 hideDetailsRow(d, R.id.details_location_row);
209 return;
210 }
211
Ray Chen0f0af522009-04-22 13:26:07 +0800212 try {
213 Geocoder geocoder = new Geocoder(context);
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800214 List<Address> address = geocoder.getFromLocation(lat, lng, 1);
215 StringBuilder sb = new StringBuilder();
216 for (Address addr : address) {
217 int index = addr.getMaxAddressLineIndex();
218 sb.append(addr.getAddressLine(index));
Ray Chen0f0af522009-04-22 13:26:07 +0800219 }
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800220 value = sb.toString();
Ray Chen0f0af522009-04-22 13:26:07 +0800221 } catch (IOException ex) {
222 // Ignore this exception.
223 value = EMPTY_STRING;
224 Log.e(TAG, "Geocoder exception: ", ex);
225 } catch (RuntimeException ex) {
226 // Ignore this exception.
227 value = EMPTY_STRING;
228 Log.e(TAG, "Geocoder exception: ", ex);
229 }
230 if (value != EMPTY_STRING) {
231 setDetailsValue(d, value, R.id.details_location_value);
232 } else {
233 hideDetailsRow(d, R.id.details_location_row);
234 }
235 }
236
Ray Chend5142de2009-05-07 15:24:48 +0800237 private static HashMap<String, String> getExifData(IImage image) {
238 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
239 return null;
240 }
241
242 ExifInterface exif = new ExifInterface(image.getDataPath());
243 HashMap<String, String> exifData = null;
244 if (exif != null) {
245 exifData = exif.getAttributes();
246 }
247 return exifData;
248 }
Ray Chen0a5a78c2009-04-24 14:13:01 +0800249 // Called when "Show on Maps" is clicked.
250 // Displays image location on Google Maps for further operations.
251 private static boolean onShowMapClicked(MenuInvoker onInvoke,
252 final Handler handler,
253 final Activity activity) {
254 onInvoke.run(new MenuCallback() {
255 public void run(Uri u, IImage image) {
256 if (image == null) {
257 return;
258 }
Ray Chend5142de2009-05-07 15:24:48 +0800259 float[] latlng = getLatLng(getExifData(image));
260 if (latlng == null) {
Ray Chen0a5a78c2009-04-24 14:13:01 +0800261 handler.post(new Runnable() {
262 public void run() {
263 Toast.makeText(activity,
264 R.string.no_location_image,
265 Toast.LENGTH_SHORT).show();
266 }
267 });
268 return;
269 }
270
271 // Can't use geo:latitude,longitude because it only centers
272 // the MapView to specified location, but we need a bubble
273 // for further operations (routing to/from).
274 // The q=(lat, lng) syntax is suggested by geo-team.
275 String uri = "http://maps.google.com/maps?f=q&" +
Ray Chend5142de2009-05-07 15:24:48 +0800276 "q=(" + latlng[0] + "," + latlng[1] + ")";
Ray Chen0a5a78c2009-04-24 14:13:01 +0800277 activity.startActivity(new Intent(
278 android.content.Intent.ACTION_VIEW,
279 Uri.parse(uri)));
280 }
281 });
282 return true;
283 }
284
Ray Chend5142de2009-05-07 15:24:48 +0800285 private static void hideExifInformation(View d) {
286 hideDetailsRow(d, R.id.details_resolution_row);
287 hideDetailsRow(d, R.id.details_make_row);
288 hideDetailsRow(d, R.id.details_model_row);
289 hideDetailsRow(d, R.id.details_whitebalance_row);
290 hideDetailsRow(d, R.id.details_latitude_row);
291 hideDetailsRow(d, R.id.details_longitude_row);
292 hideDetailsRow(d, R.id.details_location_row);
293 }
294
295 private static void showExifInformation(IImage image, View d, Activity activity) {
296 HashMap<String, String> exifData = getExifData(image);
297 if (exifData == null) {
298 hideExifInformation(d);
299 return;
300 }
301
302 String value = exifData.get(ExifInterface.TAG_MAKE);
303 if (value != null) {
304 setDetailsValue(d, value, R.id.details_make_value);
305 } else {
306 hideDetailsRow(d, R.id.details_make_row);
307 }
308
309 value = exifData.get(ExifInterface.TAG_MODEL);
310 if (value != null) {
311 setDetailsValue(d, value, R.id.details_model_value);
312 } else {
313 hideDetailsRow(d, R.id.details_model_row);
314 }
315
316 value = exifData.get(ExifInterface.TAG_WHITE_BALANCE);
317 if (value != null) {
318 value = ExifInterface.whiteBalanceToString(
319 Integer.parseInt(value));
320 }
321 if (value != null && value != EMPTY_STRING) {
322 setDetailsValue(d, value, R.id.details_whitebalance_value);
323 } else {
324 hideDetailsRow(d, R.id.details_whitebalance_row);
325 }
326
327 setLatLngDetails(d, activity, exifData);
328 }
329
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700330 // Called when "Details" is clicked.
331 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700332 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800333 final Handler handler,
334 final Activity activity,
335 final boolean isImage) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700336 onInvoke.run(new MenuCallback() {
337 public void run(Uri u, IImage image) {
338 if (image == null) {
339 return;
340 }
341
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800342 final AlertDialog.Builder builder =
343 new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700344
345 final View d = View.inflate(activity, R.layout.detailsview,
346 null);
347
348 ImageView imageView = (ImageView) d.findViewById(
349 R.id.details_thumbnail_image);
350 imageView.setImageBitmap(image.miniThumbBitmap());
351
352 TextView textView = (TextView) d.findViewById(
353 R.id.details_image_title);
354 textView.setText(image.getDisplayName());
355
356 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700357 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800358 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700359 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700360 ((TextView) d
361 .findViewById(R.id.details_file_size_value))
362 .setText(lengthString);
363
364 int dimensionWidth = 0;
365 int dimensionHeight = 0;
366 if (isImage) {
Ray Chen0f0af522009-04-22 13:26:07 +0800367 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700368 dimensionWidth = image.getWidth();
369 dimensionHeight = image.getHeight();
370 d.findViewById(R.id.details_duration_row)
371 .setVisibility(View.GONE);
372 d.findViewById(R.id.details_frame_rate_row)
373 .setVisibility(View.GONE);
374 d.findViewById(R.id.details_bit_rate_row)
375 .setVisibility(View.GONE);
376 d.findViewById(R.id.details_format_row)
377 .setVisibility(View.GONE);
378 d.findViewById(R.id.details_codec_row)
379 .setVisibility(View.GONE);
380 } else {
381 MediaMetadataRetriever retriever
382 = new MediaMetadataRetriever();
383 try {
384 retriever.setMode(MediaMetadataRetriever
385 .MODE_GET_METADATA_ONLY);
386 retriever.setDataSource(image.getDataPath());
387 try {
388 dimensionWidth = Integer.parseInt(
389 retriever.extractMetadata(
390 MediaMetadataRetriever
391 .METADATA_KEY_VIDEO_WIDTH));
392 dimensionHeight = Integer.parseInt(
393 retriever.extractMetadata(
394 MediaMetadataRetriever
395 .METADATA_KEY_VIDEO_HEIGHT));
396 } catch (NumberFormatException e) {
397 dimensionWidth = 0;
398 dimensionHeight = 0;
399 }
400
401 try {
402 int durationMs = Integer.parseInt(
403 retriever.extractMetadata(
404 MediaMetadataRetriever
405 .METADATA_KEY_DURATION));
406 String durationValue = formatDuration(
407 activity, durationMs);
408 ((TextView) d.findViewById(
409 R.id.details_duration_value))
410 .setText(durationValue);
411 } catch (NumberFormatException e) {
412 d.findViewById(
413 R.id.details_frame_rate_row)
414 .setVisibility(View.GONE);
415 }
416
417 try {
418 String frameRate = String.format(
419 activity.getString(R.string.details_fps),
420 Integer.parseInt(
421 retriever.extractMetadata(
422 MediaMetadataRetriever
423 .METADATA_KEY_FRAME_RATE)));
424 ((TextView) d.findViewById(
425 R.id.details_frame_rate_value))
426 .setText(frameRate);
427 } catch (NumberFormatException e) {
428 d.findViewById(
429 R.id.details_frame_rate_row)
430 .setVisibility(View.GONE);
431 }
432
433 try {
434 long bitRate = Long.parseLong(
435 retriever.extractMetadata(
436 MediaMetadataRetriever
437 .METADATA_KEY_BIT_RATE));
438 String bps;
439 if (bitRate < 1000000) {
440 bps = String.format(
441 activity.getString(
442 R.string.details_kbps),
443 bitRate / 1000);
444 } else {
445 bps = String.format(
446 activity.getString(
447 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700448 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700449 }
450 ((TextView) d.findViewById(
451 R.id.details_bit_rate_value))
452 .setText(bps);
453 } catch (NumberFormatException e) {
454 d.findViewById(R.id.details_bit_rate_row)
455 .setVisibility(View.GONE);
456 }
457
458 String format = retriever.extractMetadata(
459 MediaMetadataRetriever
460 .METADATA_KEY_VIDEO_FORMAT);
461 ((TextView) d.findViewById(
462 R.id.details_format_value))
463 .setText(format);
464
465 String codec = retriever.extractMetadata(
466 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800467 if (codec != null) {
468 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700469 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800470 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700471 }
Ray Chen0f0af522009-04-22 13:26:07 +0800472
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700473 } catch (RuntimeException ex) {
474 // Assume this is a corrupt video file.
475 } finally {
476 try {
477 retriever.release();
478 } catch (RuntimeException ex) {
479 // Ignore failures while cleaning up.
480 }
481 }
482 }
483
Ray Chen0f0af522009-04-22 13:26:07 +0800484 String value = null;
485 if (dimensionWidth > 0 && dimensionHeight > 0) {
486 value = String.format(
487 activity.getString(R.string.details_dimension_x),
488 dimensionWidth, dimensionHeight);
Ray Chen0f0af522009-04-22 13:26:07 +0800489 }
Ray Chend5142de2009-05-07 15:24:48 +0800490
Ray Chen0f0af522009-04-22 13:26:07 +0800491 if (value != null) {
492 setDetailsValue(d, value, R.id.details_resolution_value);
493 } else {
494 hideDetailsRow(d, R.id.details_resolution_row);
495 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700496
Ray Chen0f0af522009-04-22 13:26:07 +0800497 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700498 long dateTaken = image.getDateTaken();
499 if (dateTaken != 0) {
500 Date date = new Date(image.getDateTaken());
501 SimpleDateFormat dateFormat = new SimpleDateFormat();
Ray Chen0f0af522009-04-22 13:26:07 +0800502 value = dateFormat.format(date);
503 }
504 if (value != EMPTY_STRING) {
505 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700506 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800507 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700508 }
509
Ray Chend5142de2009-05-07 15:24:48 +0800510 // Show more EXIF header details for JPEG images.
511 if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
512 showExifInformation(image, d, activity);
513 } else {
514 hideExifInformation(d);
515 }
516
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700517 builder.setNeutralButton(R.string.details_ok,
518 new DialogInterface.OnClickListener() {
519 public void onClick(DialogInterface dialog,
520 int which) {
521 dialog.dismiss();
522 }
523 });
524
Ray Chen993105a2009-04-10 02:11:35 -0700525 handler.post(
526 new Runnable() {
527 public void run() {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800528 builder.setIcon(
529 android.R.drawable.ic_dialog_info)
Ray Chen993105a2009-04-10 02:11:35 -0700530 .setTitle(R.string.details_panel_title)
531 .setView(d)
532 .show();
533 }
534 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700535 }
536 });
537 return true;
538 }
539
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700540 // Called when "Rotate left" or "Rotate right" is clicked.
541 private static boolean onRotateClicked(MenuInvoker onInvoke,
542 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700543 onInvoke.run(new MenuCallback() {
544 public void run(Uri u, IImage image) {
545 if (image == null || image.isReadonly()) {
546 return;
547 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700548 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700549 }
550 });
551 return true;
552 }
553
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700554 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700555 private static boolean onCropClicked(MenuInvoker onInvoke,
556 final Activity activity) {
557 onInvoke.run(new MenuCallback() {
558 public void run(Uri u, IImage image) {
559 if (u == null) {
560 return;
561 }
562
563 Intent cropIntent = new Intent();
564 cropIntent.setClass(activity, CropImage.class);
565 cropIntent.setData(u);
566 activity.startActivityForResult(cropIntent,
567 RESULT_COMMON_MENU_CROP);
568 }
569 });
570 return true;
571 }
572
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700573 // Called when "Set as" is clicked.
574 private static boolean onSetAsClicked(MenuInvoker onInvoke,
575 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700576 onInvoke.run(new MenuCallback() {
577 public void run(Uri u, IImage image) {
578 if (u == null || image == null) {
579 return;
580 }
581
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700582 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
583 intent.setDataAndType(u, image.getMimeType());
584 intent.putExtra("mimeType", image.getMimeType());
585 activity.startActivity(Intent.createChooser(intent,
586 activity.getText(R.string.setImage)));
587 }
588 });
589 return true;
590 }
591
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700592 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700593 private static boolean onImageShareClicked(MenuInvoker onInvoke,
594 final Activity activity, final boolean isImage) {
595 onInvoke.run(new MenuCallback() {
596 public void run(Uri u, IImage image) {
597 if (image == null) return;
598 if (!isImage && getImageFileSize(image)
599 > SHARE_FILE_LENGTH_LIMIT) {
600 Toast.makeText(activity,
601 R.string.too_large_to_attach,
602 Toast.LENGTH_LONG).show();
603 return;
604 }
605
606 Intent intent = new Intent();
607 intent.setAction(Intent.ACTION_SEND);
608 String mimeType = image.getMimeType();
609 intent.setType(mimeType);
610 intent.putExtra(Intent.EXTRA_STREAM, u);
611 boolean isImage = ImageManager.isImageMimeType(mimeType);
612 try {
613 activity.startActivity(Intent.createChooser(intent,
614 activity.getText(isImage
615 ? R.string.sendImage
616 : R.string.sendVideo)));
617 } catch (android.content.ActivityNotFoundException ex) {
618 Toast.makeText(activity, isImage
619 ? R.string.no_way_to_share_image
620 : R.string.no_way_to_share_video,
621 Toast.LENGTH_SHORT).show();
622 }
623 }
624 });
625 return true;
626 }
627
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700628 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700629 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
630 final Activity activity) {
631 onInvoke.run(new MenuCallback() {
632 public void run(Uri uri, IImage image) {
633 if (image != null) {
634 Intent intent = new Intent(Intent.ACTION_VIEW,
635 image.fullSizeImageUri());
636 activity.startActivity(intent);
637 }
638 }});
639 return true;
640 }
641
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800642 static MenuItemsResult addImageMenuItems(
643 Menu menu,
644 int inclusions,
645 final boolean isImage,
646 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700647 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800648 final Runnable onDelete,
649 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700650 final ArrayList<MenuItem> requiresWriteAccessItems =
651 new ArrayList<MenuItem>();
652 final ArrayList<MenuItem> requiresNoDrmAccessItems =
653 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800654
655 if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700656 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
657 MENU_IMAGE_ROTATE, 40, R.string.rotate)
658 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800659 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700660 // since the items within the submenu won't be shown anyway. This
661 // is really a framework bug in that it shouldn't show the submenu
662 // if the submenu has no visible items.
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800663 requiresWriteAccessItems.add(rotateSubmenu.getItem());
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800664 requiresWriteAccessItems.add(
665 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT, 50,
666 R.string.rotate_left)
667 .setOnMenuItemClickListener(
668 new MenuItem.OnMenuItemClickListener() {
669 public boolean onMenuItemClick(MenuItem item) {
670 return onRotateClicked(onInvoke, -90);
671 }
672 }).setAlphabeticShortcut('l'));
673 requiresWriteAccessItems.add(
674 rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT, 60,
675 R.string.rotate_right)
676 .setOnMenuItemClickListener(
677 new MenuItem.OnMenuItemClickListener() {
678 public boolean onMenuItemClick(MenuItem item) {
679 return onRotateClicked(onInvoke, 90);
680 }
681 }).setAlphabeticShortcut('r'));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800682 }
683
684 if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
685 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700686 R.string.camera_crop);
687 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
688 autoCrop.setOnMenuItemClickListener(
689 new MenuItem.OnMenuItemClickListener() {
690 public boolean onMenuItemClick(MenuItem item) {
691 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800692 }
693 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800694 requiresWriteAccessItems.add(autoCrop);
695 }
696
697 if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700698 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
699 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800700 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700701 setMenu.setOnMenuItemClickListener(
702 new MenuItem.OnMenuItemClickListener() {
703 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700704 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800705 }
706 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800707 }
708
709 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800710 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
711 R.string.camera_share).setOnMenuItemClickListener(
712 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700713 public boolean onMenuItemClick(MenuItem item) {
714 return onImageShareClicked(onInvoke, activity,
715 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800716 }
717 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800718 item1.setIcon(android.R.drawable.ic_menu_share);
719 MenuItem item = item1;
720 requiresNoDrmAccessItems.add(item);
721 }
722
723 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700724 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
725 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800726 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700727 deleteItem.setOnMenuItemClickListener(
728 new MenuItem.OnMenuItemClickListener() {
729 public boolean onMenuItemClick(MenuItem item) {
730 deleteImpl(activity, onDelete, isImage);
731 return true;
732 }
733 })
734 .setAlphabeticShortcut('d')
735 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800736 }
737
738 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700739 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
740 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800741 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800742 return onDetailsClicked(onInvoke, handler, activity,
743 isImage);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800744 }
745 });
746 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
747 }
748
Ray Chen0a5a78c2009-04-24 14:13:01 +0800749 if ((isImage) && ((inclusions & INCLUDE_SHOWMAP_MENU) != 0)) {
750 menu.add(0, 0, 80, R.string.show_on_map)
751 .setOnMenuItemClickListener(
752 new MenuItem.OnMenuItemClickListener() {
753 public boolean onMenuItemClick(MenuItem item) {
754 return onShowMapClicked(onInvoke,
755 handler, activity);
756 }
Ray Chen1e99d7c2009-05-13 13:55:29 +0800757 }).setIcon(R.drawable.ic_menu_3d_globe);
Ray Chen0a5a78c2009-04-24 14:13:01 +0800758 }
759
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800760 if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
761 menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700762 .setOnMenuItemClickListener(
763 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800764 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700765 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800766 }
767 });
768 }
769
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800770 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700771 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700772 // protect against null here. this isn't strictly speaking
773 // required but if a client app isn't handling sdcard removal
774 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800775 if (image == null) {
776 return;
777 }
778 boolean readOnly = image.isReadonly();
779 boolean isDrm = image.isDrm();
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800780
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700781 for (MenuItem item : requiresWriteAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700782 item.setVisible(!readOnly);
783 item.setEnabled(!readOnly);
784 }
785 for (MenuItem item : requiresNoDrmAccessItems) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700786 item.setVisible(!isDrm);
787 item.setEnabled(!isDrm);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800788 }
789 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700790
791 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700792 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800793 }
794 };
795 }
796
797 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700798 deleteImpl(activity, onDelete, true);
799 }
Owen Lin095afa12009-03-24 23:41:27 -0700800
Ray Chen7638a542009-03-24 20:37:45 -0700801 static void deleteVideo(Activity activity, Runnable onDelete) {
802 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800803 }
804
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700805 static void deleteImage(Activity activity, Runnable onDelete,
806 IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800807 if (image != null) {
Ray Chen7638a542009-03-24 20:37:45 -0700808 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800809 }
810 }
811
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700812 private static void deleteImpl(Activity activity, final Runnable onDelete,
813 boolean isPhoto) {
814 boolean confirm = android.preference.PreferenceManager
815 .getDefaultSharedPreferences(activity)
816 .getBoolean("pref_gallery_confirm_delete_key", true);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800817 if (!confirm) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700818 if (onDelete != null) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800819 onDelete.run();
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700820 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800821 } else {
822 displayDeleteDialog(activity, onDelete, isPhoto);
823 }
824 }
825
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700826 private static void displayDeleteDialog(Activity activity,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800827 final Runnable onDelete, boolean isPhoto) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700828 android.app.AlertDialog.Builder b =
829 new android.app.AlertDialog.Builder(activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800830 b.setIcon(android.R.drawable.ic_dialog_alert);
831 b.setTitle(R.string.confirm_delete_title);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700832 b.setMessage(isPhoto
833 ? R.string.confirm_delete_message
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800834 : R.string.confirm_delete_video_message);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700835 b.setPositiveButton(android.R.string.ok,
836 new android.content.DialogInterface.OnClickListener() {
837 public void onClick(android.content.DialogInterface v,
838 int x) {
839 if (onDelete != null) {
840 onDelete.run();
841 }
842 }
843 });
844 b.setNegativeButton(android.R.string.cancel,
845 new android.content.DialogInterface.OnClickListener() {
846 public void onClick(android.content.DialogInterface v,
847 int x) {
848 }
849 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800850 b.create().show();
851 }
852
853 static void addSwitchModeMenuItem(Menu menu, final Activity activity,
854 final boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700855 int group = switchToVideo
856 ? MenuHelper.IMAGE_MODE_ITEM
857 : MenuHelper.VIDEO_MODE_ITEM;
858 int labelId = switchToVideo
859 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800860 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700861 int iconId = switchToVideo
862 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800863 : android.R.drawable.ic_menu_camera;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700864 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
865 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
866 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700867 return onSwitchModeClicked(activity, switchToVideo);
868 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700869 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800870 item.setIcon(iconId);
871 }
872
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700873 private static boolean onSwitchModeClicked(Activity activity,
874 boolean switchToVideo) {
875 String action = switchToVideo
876 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
877 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
878 Intent intent = new Intent(action);
879 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
880 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
881 activity.startActivity(intent);
882 return true;
883 }
884
Owen Lin059daa32009-05-18 15:31:17 -0700885 public static void gotoCameraMode(Activity activity) {
886 onSwitchModeClicked(activity, false);
887 }
888
889 public static void gotoVideoMode(Activity activity) {
890 onSwitchModeClicked(activity, true);
891 }
892
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800893 static void gotoStillImageCapture(Activity activity) {
894 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
895 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
896 try {
897 activity.startActivity(intent);
898 } catch (ActivityNotFoundException e) {
899 Log.e(TAG, "Could not start still image capture activity", e);
900 }
901 }
902
903 static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700904 gotoGallery(activity, R.string.gallery_camera_bucket_name,
905 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800906 }
907
908 static void gotoCameraVideoGallery(Activity activity) {
909 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
910 ImageManager.INCLUDE_VIDEOS);
911 }
912
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700913 private static void gotoGallery(Activity activity, int windowTitleId,
914 int mediaTypes) {
915 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
916 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800917 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
918 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700919 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800920 intent.putExtra("windowTitle", activity.getString(windowTitleId));
921 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700922
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800923 try {
924 activity.startActivity(intent);
925 } catch (ActivityNotFoundException e) {
926 Log.e(TAG, "Could not start gallery activity", e);
927 }
928 }
929
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700930 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800931 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700932 .setOnMenuItemClickListener(
933 new MenuItem.OnMenuItemClickListener() {
934 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700935 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700936 }
937 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700938 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800939
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700940 private static boolean onCapturePictureClicked(Activity activity) {
941 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
942 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
943 try {
944 activity.startActivity(intent);
945 } catch (android.content.ActivityNotFoundException e) {
946 // Ignore exception
947 }
948 return true;
949 }
950
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700951 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800952 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700953 .setOnMenuItemClickListener(
954 new MenuItem.OnMenuItemClickListener() {
955 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700956 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700957 }
958 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800959 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700960
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700961 private static boolean onCaptureVideoClicked(Activity activity) {
962 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
963 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
964 try {
965 activity.startActivity(intent);
966 } catch (android.content.ActivityNotFoundException e) {
967 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700968 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700969 return true;
970 }
971
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700972 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
973 addCapturePictureMenuItems(menu, activity);
974 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800975 }
976
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700977 public static String formatDuration(final Activity activity,
978 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800979 int duration = durationMs / 1000;
980 int h = duration / 3600;
981 int m = (duration - h * 3600) / 60;
982 int s = duration - (h * 3600 + m * 60);
983 String durationValue;
984 if (h == 0) {
985 durationValue = String.format(
986 activity.getString(R.string.details_ms), m, s);
987 } else {
988 durationValue = String.format(
989 activity.getString(R.string.details_hms), h, m, s);
990 }
991 return durationValue;
992 }
993
994 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700995 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800996 }
997
998 public static void showStorageToast(Activity activity, int remaining) {
999 String noStorageText = null;
1000
1001 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
1002 String state = Environment.getExternalStorageState();
1003 if (state == Environment.MEDIA_CHECKING) {
1004 noStorageText = activity.getString(R.string.preparing_sd);
1005 } else {
1006 noStorageText = activity.getString(R.string.no_storage);
1007 }
1008 } else if (remaining < 1) {
1009 noStorageText = activity.getString(R.string.not_enough_space);
1010 }
1011
1012 if (noStorageText != null) {
1013 Toast.makeText(activity, noStorageText, 5000).show();
1014 }
1015 }
1016
1017 public static int calculatePicturesRemaining() {
1018 try {
1019 if (!ImageManager.hasStorage()) {
1020 return NO_STORAGE_ERROR;
1021 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001022 String storageDirectory =
1023 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001024 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001025 float remaining = ((float) stat.getAvailableBlocks()
1026 * (float) stat.getBlockSize()) / 400000F;
1027 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001028 }
1029 } catch (Exception ex) {
1030 // if we can't stat the filesystem then we don't know how many
1031 // pictures are remaining. it might be zero but just leave it
1032 // blank since we really don't know.
1033 return CANNOT_STAT_ERROR;
1034 }
1035 }
1036}