blob: 604ef5ded8d544408c6805fe30767678c3cc875a [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;
Owen Lin6795ff12009-06-09 13:39:00 -070024import android.content.Context;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080025import android.content.DialogInterface;
26import android.content.Intent;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080027import android.content.DialogInterface.OnClickListener;
Ray Chen0f0af522009-04-22 13:26:07 +080028import android.location.Address;
29import android.location.Geocoder;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080030import android.media.MediaMetadataRetriever;
31import android.net.Uri;
32import android.os.Environment;
33import android.os.Handler;
34import android.os.StatFs;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080035import android.preference.PreferenceManager;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080036import android.provider.MediaStore;
37import android.provider.MediaStore.Images;
Owen Lin937fc482009-04-14 02:02:51 -070038import android.text.format.Formatter;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080039import android.util.Log;
40import android.view.Menu;
41import android.view.MenuItem;
42import android.view.SubMenu;
43import android.view.View;
Owen Lin059daa32009-05-18 15:31:17 -070044import android.view.MenuItem.OnMenuItemClickListener;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080045import android.widget.ImageView;
46import android.widget.TextView;
47import android.widget.Toast;
48
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070049import java.io.Closeable;
Ray Chen0f0af522009-04-22 13:26:07 +080050import java.io.IOException;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070051import java.text.SimpleDateFormat;
52import java.util.ArrayList;
53import java.util.Date;
Ray Chend5142de2009-05-07 15:24:48 +080054import java.util.HashMap;
Ray Chen0f0af522009-04-22 13:26:07 +080055import java.util.List;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080056
Ray Chend5142de2009-05-07 15:24:48 +080057/**
58 * A utility class to handle various kinds of menu operations.
59 */
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080060public class MenuHelper {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070061 private static final String TAG = "MenuHelper";
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080062
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070063 public static final int GENERIC_ITEM = 1;
64 public static final int IMAGE_SAVING_ITEM = 2;
65 public static final int VIDEO_SAVING_ITEM = 3;
66 public static final int IMAGE_MODE_ITEM = 4;
67 public static final int VIDEO_MODE_ITEM = 5;
68 public static final int MENU_ITEM_MAX = 5;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080069
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070070 public static final int INCLUDE_ALL = 0xFFFFFFFF;
71 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
72 public static final int INCLUDE_SHARE_MENU = (1 << 1);
73 public static final int INCLUDE_SET_MENU = (1 << 2);
74 public static final int INCLUDE_CROP_MENU = (1 << 3);
75 public static final int INCLUDE_DELETE_MENU = (1 << 4);
76 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
77 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080078 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080079
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070080 public static final int MENU_SWITCH_CAMERA_MODE = 0;
81 public static final int MENU_CAPTURE_PICTURE = 1;
82 public static final int MENU_CAPTURE_VIDEO = 2;
83 public static final int MENU_IMAGE_SHARE = 10;
84 public static final int MENU_IMAGE_SET = 14;
85 public static final int MENU_IMAGE_SET_WALLPAPER = 15;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070086 public static final int MENU_IMAGE_CROP = 18;
87 public static final int MENU_IMAGE_ROTATE = 19;
88 public static final int MENU_IMAGE_ROTATE_LEFT = 20;
89 public static final int MENU_IMAGE_ROTATE_RIGHT = 21;
90 public static final int MENU_IMAGE_TOSS = 22;
91 public static final int MENU_VIDEO_PLAY = 23;
92 public static final int MENU_VIDEO_SHARE = 24;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080093
94 public static final int NO_STORAGE_ERROR = -1;
95 public static final int CANNOT_STAT_ERROR = -2;
Ray Chen0f0af522009-04-22 13:26:07 +080096 public static final String EMPTY_STRING = "";
Ray Chend5142de2009-05-07 15:24:48 +080097 public static final String JPEG_MIME_TYPE = "image/jpeg";
Ray Chen0a5a78c2009-04-24 14:13:01 +080098 // valid range is -180f to +180f
99 public static final float INVALID_LATLNG = 255f;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800100
101 /** Activity result code used to report crop results.
102 */
103 public static final int RESULT_COMMON_MENU_CROP = 490;
104
105 public interface MenuItemsResult {
Owen Lin101d5282009-04-03 16:20:08 -0700106 public void gettingReadyToOpen(Menu menu, IImage image);
107 public void aboutToCall(MenuItem item, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800108 }
109
110 public interface MenuInvoker {
111 public void run(MenuCallback r);
112 }
113
114 public interface MenuCallback {
Owen Lin101d5282009-04-03 16:20:08 -0700115 public void run(Uri uri, IImage image);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800116 }
117
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700118 public static void closeSilently(Closeable c) {
119 if (c != null) {
120 try {
121 c.close();
122 } catch (Throwable e) {
123 // ignore
124 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800125 }
126 }
127
Owen Lin101d5282009-04-03 16:20:08 -0700128 public static long getImageFileSize(IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800129 java.io.InputStream data = image.fullSizeImageData();
The Android Open Source Project9c9be2e2009-03-05 14:34:37 -0800130 if (data == null) return -1;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800131 try {
132 return data.available();
133 } catch (java.io.IOException ex) {
134 return -1;
135 } finally {
136 closeSilently(data);
137 }
138 }
139
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700140 // This is a hack before we find a solution to pass a permission to other
141 // applications. See bug #1735149.
142 // Checks if the URI starts with "content://mms".
143 public static boolean isMMSUri(Uri uri) {
144 return (uri != null) &&
145 uri.getScheme().equals("content") &&
146 uri.getAuthority().equals("mms");
147 }
Owen Lin937fc482009-04-14 02:02:51 -0700148
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700149 public static void enableShareMenuItem(Menu menu, boolean enabled) {
150 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
151 if (item != null) {
152 item.setVisible(enabled);
153 item.setEnabled(enabled);
154 }
155 }
156
Ray Chen0f0af522009-04-22 13:26:07 +0800157 private static void setDetailsValue(View d, String text, int valueId) {
158 ((TextView) d.findViewById(valueId)).setText(text);
159 }
160
161 private static void hideDetailsRow(View d, int rowId) {
162 d.findViewById(rowId).setVisibility(View.GONE);
163 }
164
Ray Chend5142de2009-05-07 15:24:48 +0800165 private static float[] getLatLng(HashMap<String, String> exifData) {
166 if (exifData == null) {
167 return null;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800168 }
169
Ray Chend5142de2009-05-07 15:24:48 +0800170 String latValue = exifData.get(ExifInterface.TAG_GPS_LATITUDE);
171 String latRef = exifData.get(ExifInterface.TAG_GPS_LATITUDE_REF);
172 String lngValue = exifData.get(ExifInterface.TAG_GPS_LONGITUDE);
173 String lngRef = exifData.get(ExifInterface.TAG_GPS_LONGITUDE_REF);
174 float[] latlng = null;
175
176 if (latValue != null && latRef != null
177 && lngValue != null && lngRef != null) {
178 latlng = new float[2];
179 latlng[0] = ExifInterface.convertRationalLatLonToFloat(
180 latValue, latRef);
181 latlng[1] = ExifInterface.convertRationalLatLonToFloat(
182 lngValue, lngRef);
Ray Chen0f0af522009-04-22 13:26:07 +0800183 }
Ray Chend5142de2009-05-07 15:24:48 +0800184
185 return latlng;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800186 }
187
Ray Chend5142de2009-05-07 15:24:48 +0800188 private static void setLatLngDetails(View d, Activity context,
189 HashMap<String, String> exifData) {
190 float[] latlng = getLatLng(exifData);
191 if (latlng != null) {
192 setDetailsValue(d, String.valueOf(latlng[0]),
193 R.id.details_latitude_value);
194 setDetailsValue(d, String.valueOf(latlng[1]),
195 R.id.details_longitude_value);
196 setReverseGeocodingDetails(d, context, latlng[0], latlng[1]);
197 } else {
198 hideDetailsRow(d, R.id.details_latitude_row);
199 hideDetailsRow(d, R.id.details_longitude_row);
200 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800201 }
Ray Chen0f0af522009-04-22 13:26:07 +0800202 }
203
204 private static void setReverseGeocodingDetails(View d, Activity context,
205 float lat, float lng) {
206 // Fill in reverse-geocoded address
207 String value = EMPTY_STRING;
Ray Chen0a5a78c2009-04-24 14:13:01 +0800208 if (lat == INVALID_LATLNG || lng == INVALID_LATLNG) {
209 hideDetailsRow(d, R.id.details_location_row);
210 return;
211 }
212
Ray Chen0f0af522009-04-22 13:26:07 +0800213 try {
214 Geocoder geocoder = new Geocoder(context);
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800215 List<Address> address = geocoder.getFromLocation(lat, lng, 1);
216 StringBuilder sb = new StringBuilder();
217 for (Address addr : address) {
218 int index = addr.getMaxAddressLineIndex();
219 sb.append(addr.getAddressLine(index));
Ray Chen0f0af522009-04-22 13:26:07 +0800220 }
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800221 value = sb.toString();
Ray Chen0f0af522009-04-22 13:26:07 +0800222 } catch (IOException ex) {
223 // Ignore this exception.
224 value = EMPTY_STRING;
225 Log.e(TAG, "Geocoder exception: ", ex);
226 } catch (RuntimeException ex) {
227 // Ignore this exception.
228 value = EMPTY_STRING;
229 Log.e(TAG, "Geocoder exception: ", ex);
230 }
231 if (value != EMPTY_STRING) {
232 setDetailsValue(d, value, R.id.details_location_value);
233 } else {
234 hideDetailsRow(d, R.id.details_location_row);
235 }
236 }
237
Ray Chend5142de2009-05-07 15:24:48 +0800238 private static HashMap<String, String> getExifData(IImage image) {
239 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
240 return null;
241 }
242
243 ExifInterface exif = new ExifInterface(image.getDataPath());
244 HashMap<String, String> exifData = null;
245 if (exif != null) {
246 exifData = exif.getAttributes();
247 }
248 return exifData;
249 }
Ray Chen0a5a78c2009-04-24 14:13:01 +0800250 // Called when "Show on Maps" is clicked.
251 // Displays image location on Google Maps for further operations.
252 private static boolean onShowMapClicked(MenuInvoker onInvoke,
253 final Handler handler,
254 final Activity activity) {
255 onInvoke.run(new MenuCallback() {
256 public void run(Uri u, IImage image) {
257 if (image == null) {
258 return;
259 }
Ray Chend5142de2009-05-07 15:24:48 +0800260 float[] latlng = getLatLng(getExifData(image));
261 if (latlng == null) {
Ray Chen0a5a78c2009-04-24 14:13:01 +0800262 handler.post(new Runnable() {
263 public void run() {
264 Toast.makeText(activity,
265 R.string.no_location_image,
266 Toast.LENGTH_SHORT).show();
267 }
268 });
269 return;
270 }
271
272 // Can't use geo:latitude,longitude because it only centers
273 // the MapView to specified location, but we need a bubble
274 // for further operations (routing to/from).
275 // The q=(lat, lng) syntax is suggested by geo-team.
276 String uri = "http://maps.google.com/maps?f=q&" +
Ray Chend5142de2009-05-07 15:24:48 +0800277 "q=(" + latlng[0] + "," + latlng[1] + ")";
Ray Chen0a5a78c2009-04-24 14:13:01 +0800278 activity.startActivity(new Intent(
279 android.content.Intent.ACTION_VIEW,
280 Uri.parse(uri)));
281 }
282 });
283 return true;
284 }
285
Ray Chend5142de2009-05-07 15:24:48 +0800286 private static void hideExifInformation(View d) {
287 hideDetailsRow(d, R.id.details_resolution_row);
288 hideDetailsRow(d, R.id.details_make_row);
289 hideDetailsRow(d, R.id.details_model_row);
290 hideDetailsRow(d, R.id.details_whitebalance_row);
291 hideDetailsRow(d, R.id.details_latitude_row);
292 hideDetailsRow(d, R.id.details_longitude_row);
293 hideDetailsRow(d, R.id.details_location_row);
294 }
295
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800296 private static void showExifInformation(IImage image, View d,
297 Activity activity) {
Ray Chend5142de2009-05-07 15:24:48 +0800298 HashMap<String, String> exifData = getExifData(image);
299 if (exifData == null) {
300 hideExifInformation(d);
301 return;
302 }
303
304 String value = exifData.get(ExifInterface.TAG_MAKE);
305 if (value != null) {
306 setDetailsValue(d, value, R.id.details_make_value);
307 } else {
308 hideDetailsRow(d, R.id.details_make_row);
309 }
310
311 value = exifData.get(ExifInterface.TAG_MODEL);
312 if (value != null) {
313 setDetailsValue(d, value, R.id.details_model_value);
314 } else {
315 hideDetailsRow(d, R.id.details_model_row);
316 }
317
318 value = exifData.get(ExifInterface.TAG_WHITE_BALANCE);
319 if (value != null) {
320 value = ExifInterface.whiteBalanceToString(
321 Integer.parseInt(value));
322 }
323 if (value != null && value != EMPTY_STRING) {
324 setDetailsValue(d, value, R.id.details_whitebalance_value);
325 } else {
326 hideDetailsRow(d, R.id.details_whitebalance_row);
327 }
328
329 setLatLngDetails(d, activity, exifData);
330 }
331
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700332 // Called when "Details" is clicked.
333 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700334 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800335 final Handler handler,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800336 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700337 onInvoke.run(new MenuCallback() {
338 public void run(Uri u, IImage image) {
339 if (image == null) {
340 return;
341 }
342
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800343 final AlertDialog.Builder builder =
344 new AlertDialog.Builder(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700345
346 final View d = View.inflate(activity, R.layout.detailsview,
347 null);
348
349 ImageView imageView = (ImageView) d.findViewById(
350 R.id.details_thumbnail_image);
351 imageView.setImageBitmap(image.miniThumbBitmap());
352
353 TextView textView = (TextView) d.findViewById(
354 R.id.details_image_title);
355 textView.setText(image.getDisplayName());
356
357 long length = getImageFileSize(image);
Owen Lin937fc482009-04-14 02:02:51 -0700358 String lengthString = length < 0
Ray Chen0f0af522009-04-22 13:26:07 +0800359 ? EMPTY_STRING
Owen Lin937fc482009-04-14 02:02:51 -0700360 : Formatter.formatFileSize(activity, length);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700361 ((TextView) d
362 .findViewById(R.id.details_file_size_value))
363 .setText(lengthString);
364
365 int dimensionWidth = 0;
366 int dimensionHeight = 0;
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800367 if (ImageManager.isImage(image)) {
Ray Chen0f0af522009-04-22 13:26:07 +0800368 // getWidth is much slower than reading from EXIF
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700369 dimensionWidth = image.getWidth();
370 dimensionHeight = image.getHeight();
371 d.findViewById(R.id.details_duration_row)
372 .setVisibility(View.GONE);
373 d.findViewById(R.id.details_frame_rate_row)
374 .setVisibility(View.GONE);
375 d.findViewById(R.id.details_bit_rate_row)
376 .setVisibility(View.GONE);
377 d.findViewById(R.id.details_format_row)
378 .setVisibility(View.GONE);
379 d.findViewById(R.id.details_codec_row)
380 .setVisibility(View.GONE);
381 } else {
382 MediaMetadataRetriever retriever
383 = new MediaMetadataRetriever();
384 try {
385 retriever.setMode(MediaMetadataRetriever
386 .MODE_GET_METADATA_ONLY);
387 retriever.setDataSource(image.getDataPath());
388 try {
389 dimensionWidth = Integer.parseInt(
390 retriever.extractMetadata(
391 MediaMetadataRetriever
392 .METADATA_KEY_VIDEO_WIDTH));
393 dimensionHeight = Integer.parseInt(
394 retriever.extractMetadata(
395 MediaMetadataRetriever
396 .METADATA_KEY_VIDEO_HEIGHT));
397 } catch (NumberFormatException e) {
398 dimensionWidth = 0;
399 dimensionHeight = 0;
400 }
401
402 try {
403 int durationMs = Integer.parseInt(
404 retriever.extractMetadata(
405 MediaMetadataRetriever
406 .METADATA_KEY_DURATION));
407 String durationValue = formatDuration(
408 activity, durationMs);
409 ((TextView) d.findViewById(
410 R.id.details_duration_value))
411 .setText(durationValue);
412 } catch (NumberFormatException e) {
413 d.findViewById(
414 R.id.details_frame_rate_row)
415 .setVisibility(View.GONE);
416 }
417
418 try {
419 String frameRate = String.format(
420 activity.getString(R.string.details_fps),
421 Integer.parseInt(
422 retriever.extractMetadata(
423 MediaMetadataRetriever
424 .METADATA_KEY_FRAME_RATE)));
425 ((TextView) d.findViewById(
426 R.id.details_frame_rate_value))
427 .setText(frameRate);
428 } catch (NumberFormatException e) {
429 d.findViewById(
430 R.id.details_frame_rate_row)
431 .setVisibility(View.GONE);
432 }
433
434 try {
435 long bitRate = Long.parseLong(
436 retriever.extractMetadata(
437 MediaMetadataRetriever
438 .METADATA_KEY_BIT_RATE));
439 String bps;
440 if (bitRate < 1000000) {
441 bps = String.format(
442 activity.getString(
443 R.string.details_kbps),
444 bitRate / 1000);
445 } else {
446 bps = String.format(
447 activity.getString(
448 R.string.details_mbps),
Owen Lin937fc482009-04-14 02:02:51 -0700449 (bitRate) / 1000000.0);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700450 }
451 ((TextView) d.findViewById(
452 R.id.details_bit_rate_value))
453 .setText(bps);
454 } catch (NumberFormatException e) {
455 d.findViewById(R.id.details_bit_rate_row)
456 .setVisibility(View.GONE);
457 }
458
459 String format = retriever.extractMetadata(
460 MediaMetadataRetriever
461 .METADATA_KEY_VIDEO_FORMAT);
462 ((TextView) d.findViewById(
463 R.id.details_format_value))
464 .setText(format);
465
466 String codec = retriever.extractMetadata(
467 MediaMetadataRetriever.METADATA_KEY_CODEC);
Ray Chen0f0af522009-04-22 13:26:07 +0800468 if (codec != null) {
469 setDetailsValue(d, codec, R.id.details_codec_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700470 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800471 hideDetailsRow(d, R.id.details_codec_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700472 }
Ray Chen0f0af522009-04-22 13:26:07 +0800473
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700474 } catch (RuntimeException ex) {
475 // Assume this is a corrupt video file.
476 } finally {
477 try {
478 retriever.release();
479 } catch (RuntimeException ex) {
480 // Ignore failures while cleaning up.
481 }
482 }
483 }
484
Ray Chen0f0af522009-04-22 13:26:07 +0800485 String value = null;
486 if (dimensionWidth > 0 && dimensionHeight > 0) {
487 value = String.format(
488 activity.getString(R.string.details_dimension_x),
489 dimensionWidth, dimensionHeight);
Ray Chen0f0af522009-04-22 13:26:07 +0800490 }
Ray Chend5142de2009-05-07 15:24:48 +0800491
Ray Chen0f0af522009-04-22 13:26:07 +0800492 if (value != null) {
493 setDetailsValue(d, value, R.id.details_resolution_value);
494 } else {
495 hideDetailsRow(d, R.id.details_resolution_row);
496 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700497
Ray Chen0f0af522009-04-22 13:26:07 +0800498 value = EMPTY_STRING;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700499 long dateTaken = image.getDateTaken();
500 if (dateTaken != 0) {
501 Date date = new Date(image.getDateTaken());
502 SimpleDateFormat dateFormat = new SimpleDateFormat();
Ray Chen0f0af522009-04-22 13:26:07 +0800503 value = dateFormat.format(date);
504 }
505 if (value != EMPTY_STRING) {
506 setDetailsValue(d, value, R.id.details_date_taken_value);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700507 } else {
Ray Chen0f0af522009-04-22 13:26:07 +0800508 hideDetailsRow(d, R.id.details_date_taken_row);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700509 }
510
Ray Chend5142de2009-05-07 15:24:48 +0800511 // Show more EXIF header details for JPEG images.
512 if (JPEG_MIME_TYPE.equals(image.getMimeType())) {
513 showExifInformation(image, d, activity);
514 } else {
515 hideExifInformation(d);
516 }
517
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700518 builder.setNeutralButton(R.string.details_ok,
519 new DialogInterface.OnClickListener() {
520 public void onClick(DialogInterface dialog,
521 int which) {
522 dialog.dismiss();
523 }
524 });
525
Ray Chen993105a2009-04-10 02:11:35 -0700526 handler.post(
527 new Runnable() {
528 public void run() {
Chih-Chung Chang6b270502009-04-29 11:57:06 +0800529 builder.setIcon(
530 android.R.drawable.ic_dialog_info)
Ray Chen993105a2009-04-10 02:11:35 -0700531 .setTitle(R.string.details_panel_title)
532 .setView(d)
533 .show();
534 }
535 });
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700536 }
537 });
538 return true;
539 }
540
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700541 // Called when "Rotate left" or "Rotate right" is clicked.
542 private static boolean onRotateClicked(MenuInvoker onInvoke,
543 final int degree) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700544 onInvoke.run(new MenuCallback() {
545 public void run(Uri u, IImage image) {
546 if (image == null || image.isReadonly()) {
547 return;
548 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700549 image.rotateImageBy(degree);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700550 }
551 });
552 return true;
553 }
554
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700555 // Called when "Crop" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700556 private static boolean onCropClicked(MenuInvoker onInvoke,
557 final Activity activity) {
558 onInvoke.run(new MenuCallback() {
559 public void run(Uri u, IImage image) {
560 if (u == null) {
561 return;
562 }
563
564 Intent cropIntent = new Intent();
565 cropIntent.setClass(activity, CropImage.class);
566 cropIntent.setData(u);
567 activity.startActivityForResult(cropIntent,
568 RESULT_COMMON_MENU_CROP);
569 }
570 });
571 return true;
572 }
573
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700574 // Called when "Set as" is clicked.
575 private static boolean onSetAsClicked(MenuInvoker onInvoke,
576 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700577 onInvoke.run(new MenuCallback() {
578 public void run(Uri u, IImage image) {
579 if (u == null || image == null) {
580 return;
581 }
582
Chih-Chung Changbb187782009-07-02 16:46:30 +0800583 Intent intent = Util.createSetAsIntent(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700584 activity.startActivity(Intent.createChooser(intent,
585 activity.getText(R.string.setImage)));
586 }
587 });
588 return true;
589 }
590
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700591 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700592 private static boolean onImageShareClicked(MenuInvoker onInvoke,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800593 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700594 onInvoke.run(new MenuCallback() {
595 public void run(Uri u, IImage image) {
596 if (image == null) return;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700597
598 Intent intent = new Intent();
599 intent.setAction(Intent.ACTION_SEND);
600 String mimeType = image.getMimeType();
601 intent.setType(mimeType);
602 intent.putExtra(Intent.EXTRA_STREAM, u);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800603 boolean isImage = ImageManager.isImage(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700604 try {
605 activity.startActivity(Intent.createChooser(intent,
606 activity.getText(isImage
607 ? R.string.sendImage
608 : R.string.sendVideo)));
609 } catch (android.content.ActivityNotFoundException ex) {
610 Toast.makeText(activity, isImage
611 ? R.string.no_way_to_share_image
612 : R.string.no_way_to_share_video,
613 Toast.LENGTH_SHORT).show();
614 }
615 }
616 });
617 return true;
618 }
619
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700620 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700621 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
622 final Activity activity) {
623 onInvoke.run(new MenuCallback() {
624 public void run(Uri uri, IImage image) {
625 if (image != null) {
626 Intent intent = new Intent(Intent.ACTION_VIEW,
627 image.fullSizeImageUri());
628 activity.startActivity(intent);
629 }
630 }});
631 return true;
632 }
633
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800634 // Called when "Delete" is clicked.
635 private static boolean onDeleteClicked(MenuInvoker onInvoke,
636 final Activity activity, final Runnable onDelete) {
637 onInvoke.run(new MenuCallback() {
638 public void run(Uri uri, IImage image) {
639 if (image != null) {
640 deleteImage(activity, onDelete, image);
641 }
642 }});
643 return true;
644 }
645
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800646 static MenuItemsResult addImageMenuItems(
647 Menu menu,
648 int inclusions,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800649 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700650 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800651 final Runnable onDelete,
652 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700653 final ArrayList<MenuItem> requiresWriteAccessItems =
654 new ArrayList<MenuItem>();
655 final ArrayList<MenuItem> requiresNoDrmAccessItems =
656 new ArrayList<MenuItem>();
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800657 final ArrayList<MenuItem> requiresImageItems =
658 new ArrayList<MenuItem>();
659 final ArrayList<MenuItem> requiresVideoItems =
660 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800661
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800662 if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700663 SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM,
664 MENU_IMAGE_ROTATE, 40, R.string.rotate)
665 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800666 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700667 // since the items within the submenu won't be shown anyway. This
668 // is really a framework bug in that it shouldn't show the submenu
669 // if the submenu has no visible items.
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800670 MenuItem rotateLeft = rotateSubmenu.add(0, MENU_IMAGE_ROTATE_LEFT,
671 50, R.string.rotate_left)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800672 .setOnMenuItemClickListener(
673 new MenuItem.OnMenuItemClickListener() {
674 public boolean onMenuItemClick(MenuItem item) {
675 return onRotateClicked(onInvoke, -90);
676 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800677 }).setAlphabeticShortcut('l');
678
679 MenuItem rotateRight = rotateSubmenu.add(0, MENU_IMAGE_ROTATE_RIGHT,
680 60, R.string.rotate_right)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800681 .setOnMenuItemClickListener(
682 new MenuItem.OnMenuItemClickListener() {
683 public boolean onMenuItemClick(MenuItem item) {
684 return onRotateClicked(onInvoke, 90);
685 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800686 }).setAlphabeticShortcut('r');
687
688 requiresWriteAccessItems.add(rotateSubmenu.getItem());
689 requiresWriteAccessItems.add(rotateLeft);
690 requiresWriteAccessItems.add(rotateRight);
691
692 requiresImageItems.add(rotateSubmenu.getItem());
693 requiresImageItems.add(rotateLeft);
694 requiresImageItems.add(rotateRight);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800695 }
696
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800697 if ((inclusions & INCLUDE_CROP_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800698 MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700699 R.string.camera_crop);
700 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
701 autoCrop.setOnMenuItemClickListener(
702 new MenuItem.OnMenuItemClickListener() {
703 public boolean onMenuItemClick(MenuItem item) {
704 return onCropClicked(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 requiresWriteAccessItems.add(autoCrop);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800708 requiresImageItems.add(autoCrop);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800709 }
710
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800711 if ((inclusions & INCLUDE_SET_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700712 MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75,
713 R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800714 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700715 setMenu.setOnMenuItemClickListener(
716 new MenuItem.OnMenuItemClickListener() {
717 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700718 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800719 }
720 });
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800721 requiresImageItems.add(setMenu);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800722 }
723
724 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800725 MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
726 R.string.camera_share).setOnMenuItemClickListener(
727 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700728 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800729 return onImageShareClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800730 }
731 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800732 item1.setIcon(android.R.drawable.ic_menu_share);
733 MenuItem item = item1;
734 requiresNoDrmAccessItems.add(item);
735 }
736
737 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700738 MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS,
739 70, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800740 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700741 deleteItem.setOnMenuItemClickListener(
742 new MenuItem.OnMenuItemClickListener() {
743 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800744 return onDeleteClicked(onInvoke, activity,
745 onDelete);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700746 }
747 })
748 .setAlphabeticShortcut('d')
749 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800750 }
751
752 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700753 MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details)
754 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800755 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800756 return onDetailsClicked(onInvoke, handler, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800757 }
758 });
759 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
760 }
761
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800762 if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) {
763 MenuItem showOnMapItem = menu.add(0, 0, 80, R.string.show_on_map)
Ray Chen0a5a78c2009-04-24 14:13:01 +0800764 .setOnMenuItemClickListener(
765 new MenuItem.OnMenuItemClickListener() {
766 public boolean onMenuItemClick(MenuItem item) {
767 return onShowMapClicked(onInvoke,
768 handler, activity);
769 }
Ray Chen1e99d7c2009-05-13 13:55:29 +0800770 }).setIcon(R.drawable.ic_menu_3d_globe);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800771 requiresImageItems.add(showOnMapItem);
Ray Chen0a5a78c2009-04-24 14:13:01 +0800772 }
773
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800774 if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) {
775 MenuItem videoPlayItem = menu.add(VIDEO_SAVING_ITEM,
776 MENU_VIDEO_PLAY, 0, R.string.video_play)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700777 .setOnMenuItemClickListener(
778 new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800779 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700780 return onViewPlayClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800781 }
782 });
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800783 videoPlayItem.setIcon(
784 com.android.internal.R.drawable.ic_menu_play_clip);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800785 requiresVideoItems.add(videoPlayItem);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800786 }
787
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800788 return new MenuItemsResult() {
Owen Lin101d5282009-04-03 16:20:08 -0700789 public void gettingReadyToOpen(Menu menu, IImage image) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700790 // protect against null here. this isn't strictly speaking
791 // required but if a client app isn't handling sdcard removal
792 // properly it could happen
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800793 if (image == null) {
794 return;
795 }
Chih-Chung Chang2b82c4a2009-04-14 18:00:10 +0800796
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800797 ArrayList<MenuItem> enableList = new ArrayList<MenuItem>();
798 ArrayList<MenuItem> disableList = new ArrayList<MenuItem>();
799 ArrayList<MenuItem> list;
800
801 list = image.isReadonly() ? disableList : enableList;
802 list.addAll(requiresWriteAccessItems);
803
804 list = image.isDrm() ? disableList : enableList;
805 list.addAll(requiresNoDrmAccessItems);
806
807 list = ImageManager.isImage(image) ? enableList : disableList;
808 list.addAll(requiresImageItems);
809
810 list = ImageManager.isVideo(image) ? enableList : disableList;
811 list.addAll(requiresVideoItems);
812
813 for (MenuItem item : enableList) {
814 item.setVisible(true);
815 item.setEnabled(true);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700816 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800817
818 for (MenuItem item : disableList) {
819 item.setVisible(false);
820 item.setEnabled(false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800821 }
822 }
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700823
824 // must override abstract method
Owen Lin101d5282009-04-03 16:20:08 -0700825 public void aboutToCall(MenuItem menu, IImage image) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800826 }
827 };
828 }
829
830 static void deletePhoto(Activity activity, Runnable onDelete) {
Ray Chen7638a542009-03-24 20:37:45 -0700831 deleteImpl(activity, onDelete, true);
832 }
Owen Lin095afa12009-03-24 23:41:27 -0700833
Ray Chen7638a542009-03-24 20:37:45 -0700834 static void deleteVideo(Activity activity, Runnable onDelete) {
835 deleteImpl(activity, onDelete, false);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800836 }
837
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800838 static void deleteImage(
839 Activity activity, Runnable onDelete, IImage image) {
840 deleteImpl(activity, onDelete, ImageManager.isImage(image));
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800841 }
842
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800843 static void deleteImpl(
844 Activity activity, Runnable onDelete, boolean isImage) {
845 boolean needConfirm = PreferenceManager
846 .getDefaultSharedPreferences(activity)
847 .getBoolean("pref_gallery_confirm_delete_key", true);
848 if (!needConfirm) {
849 if (onDelete != null) onDelete.run();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800850 } else {
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800851 String title = activity.getString(R.string.confirm_delete_title);
852 String message = activity.getString(isImage
853 ? R.string.confirm_delete_message
854 : R.string.confirm_delete_video_message);
855 confirmAction(activity, title, message, onDelete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800856 }
857 }
858
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +0800859 public static void deleteMultiple(Context context, Runnable action) {
860 boolean needConfirm = PreferenceManager
861 .getDefaultSharedPreferences(context)
862 .getBoolean("pref_gallery_confirm_delete_key", true);
863 if (!needConfirm) {
864 if (action != null) action.run();
865 } else {
866 String title = context.getString(R.string.confirm_delete_title);
867 String message = context.getString(
868 R.string.confirm_delete_multiple_message);
869 confirmAction(context, title, message, action);
870 }
871 }
872
873 public static void confirmAction(Context context, String title,
874 String message, final Runnable action) {
875 OnClickListener listener = new OnClickListener() {
876 public void onClick(DialogInterface dialog, int which) {
877 switch (which) {
878 case DialogInterface.BUTTON_POSITIVE:
879 if (action != null) action.run();
880 }
881 }
882 };
883 new AlertDialog.Builder(context)
884 .setIcon(android.R.drawable.ic_dialog_alert)
885 .setTitle(title)
886 .setMessage(message)
887 .setPositiveButton(android.R.string.ok, listener)
888 .setNegativeButton(android.R.string.cancel, listener)
889 .create()
890 .show();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800891 }
892
Owen Lin3283e042009-06-26 11:59:58 -0700893 static void addSwitchModeMenuItem(
894 Menu menu, final Activity activity, boolean switchToVideo) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700895 int group = switchToVideo
896 ? MenuHelper.IMAGE_MODE_ITEM
897 : MenuHelper.VIDEO_MODE_ITEM;
898 int labelId = switchToVideo
899 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800900 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700901 int iconId = switchToVideo
902 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800903 : android.R.drawable.ic_menu_camera;
Owen Lin3283e042009-06-26 11:59:58 -0700904 final String action = switchToVideo
905 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
906 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700907 MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0, labelId)
908 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
Owen Lin3283e042009-06-26 11:59:58 -0700909 public boolean onMenuItemClick(MenuItem item) {
910 startCameraActivity(activity, action);
911 return true;
912 }
913 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800914 item.setIcon(iconId);
915 }
916
Owen Lin3283e042009-06-26 11:59:58 -0700917 private static void startCameraActivity(Activity activity, String action) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700918 Intent intent = new Intent(action);
919 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
920 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
Chih-Chung Changcd65be32009-06-09 13:51:29 +0800921
922 // Keep the camera instance for a while.
923 // This avoids re-opening the camera and saves time.
924 CameraHolder.instance().keep();
925
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700926 activity.startActivity(intent);
Owen Lin059daa32009-05-18 15:31:17 -0700927 }
928
929 public static void gotoVideoMode(Activity activity) {
Owen Lin3283e042009-06-26 11:59:58 -0700930 startCameraActivity(activity, MediaStore.INTENT_ACTION_VIDEO_CAMERA);
Owen Lin059daa32009-05-18 15:31:17 -0700931 }
932
Owen Lin3283e042009-06-26 11:59:58 -0700933 public static void gotoCameraMode(Activity activity) {
934 startCameraActivity(
935 activity, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800936 }
937
Owen Lin3283e042009-06-26 11:59:58 -0700938 public static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700939 gotoGallery(activity, R.string.gallery_camera_bucket_name,
940 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800941 }
942
Owen Lin3283e042009-06-26 11:59:58 -0700943 public static void gotoCameraVideoGallery(Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800944 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
945 ImageManager.INCLUDE_VIDEOS);
946 }
947
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700948 private static void gotoGallery(Activity activity, int windowTitleId,
949 int mediaTypes) {
950 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
951 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800952 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
953 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700954 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800955 intent.putExtra("windowTitle", activity.getString(windowTitleId));
956 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700957
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800958 try {
959 activity.startActivity(intent);
960 } catch (ActivityNotFoundException e) {
961 Log.e(TAG, "Could not start gallery activity", e);
962 }
963 }
964
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700965 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800966 menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700967 .setOnMenuItemClickListener(
968 new MenuItem.OnMenuItemClickListener() {
969 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700970 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700971 }
972 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700973 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800974
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700975 private static boolean onCapturePictureClicked(Activity activity) {
976 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
977 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
978 try {
979 activity.startActivity(intent);
980 } catch (android.content.ActivityNotFoundException e) {
981 // Ignore exception
982 }
983 return true;
984 }
985
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700986 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800987 menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700988 .setOnMenuItemClickListener(
989 new MenuItem.OnMenuItemClickListener() {
990 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700991 return onCaptureVideoClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700992 }
993 }).setIcon(R.drawable.ic_menu_camera_video_view);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800994 }
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700995
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700996 private static boolean onCaptureVideoClicked(Activity activity) {
997 Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
998 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
999 try {
1000 activity.startActivity(intent);
1001 } catch (android.content.ActivityNotFoundException e) {
1002 // Ignore exception
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001003 }
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001004 return true;
1005 }
1006
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -07001007 public static void addCaptureMenuItems(Menu menu, final Activity activity) {
1008 addCapturePictureMenuItems(menu, activity);
1009 addCaptureVideoMenuItems(menu, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001010 }
1011
Owen Lin6795ff12009-06-09 13:39:00 -07001012 public static String formatDuration(final Context context,
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001013 int durationMs) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001014 int duration = durationMs / 1000;
1015 int h = duration / 3600;
1016 int m = (duration - h * 3600) / 60;
1017 int s = duration - (h * 3600 + m * 60);
1018 String durationValue;
1019 if (h == 0) {
1020 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -07001021 context.getString(R.string.details_ms), m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001022 } else {
1023 durationValue = String.format(
Owen Lin6795ff12009-06-09 13:39:00 -07001024 context.getString(R.string.details_hms), h, m, s);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001025 }
1026 return durationValue;
1027 }
1028
1029 public static void showStorageToast(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001030 showStorageToast(activity, calculatePicturesRemaining());
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001031 }
1032
1033 public static void showStorageToast(Activity activity, int remaining) {
1034 String noStorageText = null;
1035
1036 if (remaining == MenuHelper.NO_STORAGE_ERROR) {
1037 String state = Environment.getExternalStorageState();
1038 if (state == Environment.MEDIA_CHECKING) {
1039 noStorageText = activity.getString(R.string.preparing_sd);
1040 } else {
1041 noStorageText = activity.getString(R.string.no_storage);
1042 }
1043 } else if (remaining < 1) {
1044 noStorageText = activity.getString(R.string.not_enough_space);
1045 }
1046
1047 if (noStorageText != null) {
1048 Toast.makeText(activity, noStorageText, 5000).show();
1049 }
1050 }
1051
1052 public static int calculatePicturesRemaining() {
1053 try {
1054 if (!ImageManager.hasStorage()) {
1055 return NO_STORAGE_ERROR;
1056 } else {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001057 String storageDirectory =
1058 Environment.getExternalStorageDirectory().toString();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001059 StatFs stat = new StatFs(storageDirectory);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -07001060 float remaining = ((float) stat.getAvailableBlocks()
1061 * (float) stat.getBlockSize()) / 400000F;
1062 return (int) remaining;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -08001063 }
1064 } catch (Exception ex) {
1065 // if we can't stat the filesystem then we don't know how many
1066 // pictures are remaining. it might be zero but just leave it
1067 // blank since we really don't know.
1068 return CANNOT_STAT_ERROR;
1069 }
1070 }
1071}