blob: 429bd61ed8f768a72cc905e1096ad663612bfe16 [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
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080019import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.ActivityNotFoundException;
Owen Lin6795ff12009-06-09 13:39:00 -070022import android.content.Context;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080023import android.content.DialogInterface;
24import android.content.Intent;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080025import android.content.DialogInterface.OnClickListener;
Ray Chen0f0af522009-04-22 13:26:07 +080026import android.location.Geocoder;
repo synca274cf12009-07-06 15:05:23 +080027import android.media.ExifInterface;
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;
Chih-Chung Chang0e48fe62009-06-26 19:23:56 +080033import android.preference.PreferenceManager;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080034import android.provider.MediaStore;
35import android.provider.MediaStore.Images;
Owen Lin937fc482009-04-14 02:02:51 -070036import android.text.format.Formatter;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080037import android.util.Log;
38import android.view.Menu;
39import android.view.MenuItem;
40import android.view.SubMenu;
41import android.view.View;
Owen Lin059daa32009-05-18 15:31:17 -070042import android.view.MenuItem.OnMenuItemClickListener;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080043import android.widget.ImageView;
44import android.widget.TextView;
45import android.widget.Toast;
46
Owen Line594b192009-08-13 18:04:45 +080047import com.android.camera.gallery.IImage;
48
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -070049import java.io.Closeable;
repo syncaa7075c2009-06-24 14:59:48 +080050import java.lang.ref.WeakReference;
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 INCLUDE_ALL = 0xFFFFFFFF;
64 public static final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
65 public static final int INCLUDE_SHARE_MENU = (1 << 1);
66 public static final int INCLUDE_SET_MENU = (1 << 2);
67 public static final int INCLUDE_CROP_MENU = (1 << 3);
68 public static final int INCLUDE_DELETE_MENU = (1 << 4);
69 public static final int INCLUDE_ROTATE_MENU = (1 << 5);
70 public static final int INCLUDE_DETAILS_MENU = (1 << 6);
Ray Chen0a5a78c2009-04-24 14:13:01 +080071 public static final int INCLUDE_SHOWMAP_MENU = (1 << 7);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080072
Chih-Chung Changd1890832009-09-08 13:32:52 +080073 public static final int MENU_IMAGE_SHARE = 1;
74 public static final int MENU_IMAGE_SHOWMAP = 2;
75
76 public static final int POSITION_SWITCH_CAMERA_MODE = 1;
77 public static final int POSITION_GOTO_GALLERY = 2;
78 public static final int POSITION_VIEWPLAY = 3;
79 public static final int POSITION_CAPTURE_PICTURE = 4;
80 public static final int POSITION_CAPTURE_VIDEO = 5;
81 public static final int POSITION_IMAGE_SHARE = 6;
82 public static final int POSITION_IMAGE_ROTATE = 7;
83 public static final int POSITION_IMAGE_TOSS = 8;
84 public static final int POSITION_IMAGE_CROP = 9;
85 public static final int POSITION_IMAGE_SET = 10;
86 public static final int POSITION_DETAILS = 11;
87 public static final int POSITION_SHOWMAP = 12;
88 public static final int POSITION_SLIDESHOW = 13;
89 public static final int POSITION_MULTISELECT = 14;
90 public static final int POSITION_CAMERA_SETTING = 15;
91 public static final int POSITION_GALLERY_SETTING = 16;
The Android Open Source Projectb64d3452009-03-03 19:32:20 -080092
93 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
Chih-Chung Changb2a97652009-07-10 18:39:49 +0800140 // applications. See bug #1735149, #1836138.
141 // Checks if the URI is on our whitelist:
142 // content://media/... (MediaProvider)
143 // file:///sdcard/... (Browser download)
144 public static boolean isWhiteListUri(Uri uri) {
145 if (uri == null) return false;
146
147 String scheme = uri.getScheme();
148 String authority = uri.getAuthority();
149
150 if (scheme.equals("content") && authority.equals("media")) {
151 return true;
152 }
153
154 if (scheme.equals("file")) {
155 List<String> p = uri.getPathSegments();
156
157 if (p.size() >= 1 && p.get(0).equals("sdcard")) {
158 return true;
159 }
160 }
161
162 return false;
Chih-Chung Chang47bf4ce2009-03-25 01:51:03 -0700163 }
Owen Lin937fc482009-04-14 02:02:51 -0700164
Chih-Chung Changeb9d8a22009-03-27 16:07:25 -0700165 public static void enableShareMenuItem(Menu menu, boolean enabled) {
166 MenuItem item = menu.findItem(MENU_IMAGE_SHARE);
167 if (item != null) {
168 item.setVisible(enabled);
169 item.setEnabled(enabled);
170 }
171 }
172
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800173 public static boolean hasLatLngData(IImage image) {
174 return ExifInterface.getLatLng(getExifData(image)) != null;
175 }
Chih-Chung Changd1890832009-09-08 13:32:52 +0800176
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800177 public static void enableShowOnMapMenuItem(Menu menu, boolean enabled) {
178 MenuItem item = menu.findItem(MENU_IMAGE_SHOWMAP);
179 if (item != null) {
180 item.setEnabled(enabled);
181 }
182 }
183
Ray Chen0f0af522009-04-22 13:26:07 +0800184 private static void setDetailsValue(View d, String text, int valueId) {
185 ((TextView) d.findViewById(valueId)).setText(text);
186 }
187
188 private static void hideDetailsRow(View d, int rowId) {
189 d.findViewById(rowId).setVisibility(View.GONE);
190 }
191
repo syncaa7075c2009-06-24 14:59:48 +0800192 private static class UpdateLocationCallback implements
193 ReverseGeocoderTask.Callback {
194 WeakReference<View> mView;
195
196 public UpdateLocationCallback(WeakReference<View> view) {
197 mView = view;
198 }
199
200 public void onComplete(String location) {
201 // View d is per-thread data, so when setDetailsValue is
202 // executed by UI thread, it doesn't matter whether the
203 // details dialog is dismissed or not.
204 View view = mView.get();
205 if (view == null) return;
Chih-Chung Chang9b93bcb2009-09-04 19:46:14 +0800206 if (!location.equals(MenuHelper.EMPTY_STRING)) {
repo syncaa7075c2009-06-24 14:59:48 +0800207 MenuHelper.setDetailsValue(view, location,
208 R.id.details_location_value);
209 } else {
210 MenuHelper.hideDetailsRow(view, R.id.details_location_row);
211 }
212 }
213 }
214
215 private static void setLatLngDetails(final View d, Activity context,
Ray Chend5142de2009-05-07 15:24:48 +0800216 HashMap<String, String> exifData) {
repo synca274cf12009-07-06 15:05:23 +0800217 float[] latlng = ExifInterface.getLatLng(exifData);
Ray Chend5142de2009-05-07 15:24:48 +0800218 if (latlng != null) {
219 setDetailsValue(d, String.valueOf(latlng[0]),
220 R.id.details_latitude_value);
221 setDetailsValue(d, String.valueOf(latlng[1]),
repo syncaa7075c2009-06-24 14:59:48 +0800222 R.id.details_longitude_value);
223
224 if (latlng[0] == INVALID_LATLNG || latlng[1] == INVALID_LATLNG) {
225 hideDetailsRow(d, R.id.details_latitude_row);
226 hideDetailsRow(d, R.id.details_longitude_row);
227 hideDetailsRow(d, R.id.details_location_row);
228 return;
229 }
230
231 UpdateLocationCallback cb = new UpdateLocationCallback(
232 new WeakReference<View>(d));
233 Geocoder geocoder = new Geocoder(context);
234 new ReverseGeocoderTask(geocoder, latlng, cb).execute();
Ray Chend5142de2009-05-07 15:24:48 +0800235 } else {
236 hideDetailsRow(d, R.id.details_latitude_row);
237 hideDetailsRow(d, R.id.details_longitude_row);
238 hideDetailsRow(d, R.id.details_location_row);
Ray Chen0f0af522009-04-22 13:26:07 +0800239 }
Ray Chen0f0af522009-04-22 13:26:07 +0800240 }
241
Ray Chend5142de2009-05-07 15:24:48 +0800242 private static HashMap<String, String> getExifData(IImage image) {
243 if (!JPEG_MIME_TYPE.equals(image.getMimeType())) {
244 return null;
245 }
246
repo synca274cf12009-07-06 15:05:23 +0800247 return ExifInterface.loadExifData(image.getDataPath());
Ray Chend5142de2009-05-07 15:24:48 +0800248 }
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 }
repo synca274cf12009-07-06 15:05:23 +0800259 float[] latlng = ExifInterface.getLatLng(getExifData(image));
Ray Chend5142de2009-05-07 15:24:48 +0800260 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
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800295 private static void showExifInformation(IImage image, View d,
296 Activity activity) {
Ray Chend5142de2009-05-07 15:24:48 +0800297 HashMap<String, String> exifData = getExifData(image);
298 if (exifData == null) {
299 hideExifInformation(d);
300 return;
301 }
302
303 String value = exifData.get(ExifInterface.TAG_MAKE);
304 if (value != null) {
305 setDetailsValue(d, value, R.id.details_make_value);
306 } else {
307 hideDetailsRow(d, R.id.details_make_row);
308 }
309
310 value = exifData.get(ExifInterface.TAG_MODEL);
311 if (value != null) {
312 setDetailsValue(d, value, R.id.details_model_value);
313 } else {
314 hideDetailsRow(d, R.id.details_model_row);
315 }
316
317 value = exifData.get(ExifInterface.TAG_WHITE_BALANCE);
318 if (value != null) {
319 value = ExifInterface.whiteBalanceToString(
320 Integer.parseInt(value));
321 }
322 if (value != null && value != EMPTY_STRING) {
323 setDetailsValue(d, value, R.id.details_whitebalance_value);
324 } else {
325 hideDetailsRow(d, R.id.details_whitebalance_row);
326 }
327
328 setLatLngDetails(d, activity, exifData);
329 }
330
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700331 // Called when "Details" is clicked.
332 // Displays detailed information about the image/video.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700333 private static boolean onDetailsClicked(MenuInvoker onInvoke,
Ray Chen0a5a78c2009-04-24 14:13:01 +0800334 final Handler handler,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800335 final Activity activity) {
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;
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800366 if (ImageManager.isImage(image)) {
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 Changbb187782009-07-02 16:46:30 +0800582 Intent intent = Util.createSetAsIntent(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700583 activity.startActivity(Intent.createChooser(intent,
584 activity.getText(R.string.setImage)));
585 }
586 });
587 return true;
588 }
589
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700590 // Called when "Share" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700591 private static boolean onImageShareClicked(MenuInvoker onInvoke,
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800592 final Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700593 onInvoke.run(new MenuCallback() {
594 public void run(Uri u, IImage image) {
595 if (image == null) return;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700596
597 Intent intent = new Intent();
598 intent.setAction(Intent.ACTION_SEND);
599 String mimeType = image.getMimeType();
600 intent.setType(mimeType);
601 intent.putExtra(Intent.EXTRA_STREAM, u);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800602 boolean isImage = ImageManager.isImage(image);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700603 try {
604 activity.startActivity(Intent.createChooser(intent,
605 activity.getText(isImage
606 ? R.string.sendImage
607 : R.string.sendVideo)));
608 } catch (android.content.ActivityNotFoundException ex) {
609 Toast.makeText(activity, isImage
610 ? R.string.no_way_to_share_image
611 : R.string.no_way_to_share_video,
612 Toast.LENGTH_SHORT).show();
613 }
614 }
615 });
616 return true;
617 }
618
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700619 // Called when "Play" is clicked.
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700620 private static boolean onViewPlayClicked(MenuInvoker onInvoke,
621 final Activity activity) {
622 onInvoke.run(new MenuCallback() {
623 public void run(Uri uri, IImage image) {
624 if (image != null) {
625 Intent intent = new Intent(Intent.ACTION_VIEW,
626 image.fullSizeImageUri());
627 activity.startActivity(intent);
628 }
629 }});
630 return true;
631 }
632
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800633 // Called when "Delete" is clicked.
634 private static boolean onDeleteClicked(MenuInvoker onInvoke,
635 final Activity activity, final Runnable onDelete) {
636 onInvoke.run(new MenuCallback() {
637 public void run(Uri uri, IImage image) {
638 if (image != null) {
639 deleteImage(activity, onDelete, image);
640 }
641 }});
642 return true;
643 }
644
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800645 static MenuItemsResult addImageMenuItems(
646 Menu menu,
647 int inclusions,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800648 final Activity activity,
Ray Chen993105a2009-04-10 02:11:35 -0700649 final Handler handler,
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800650 final Runnable onDelete,
651 final MenuInvoker onInvoke) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700652 final ArrayList<MenuItem> requiresWriteAccessItems =
653 new ArrayList<MenuItem>();
654 final ArrayList<MenuItem> requiresNoDrmAccessItems =
655 new ArrayList<MenuItem>();
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800656 final ArrayList<MenuItem> requiresImageItems =
657 new ArrayList<MenuItem>();
658 final ArrayList<MenuItem> requiresVideoItems =
659 new ArrayList<MenuItem>();
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800660
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800661 if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800662 SubMenu rotateSubmenu = menu.addSubMenu(Menu.NONE, Menu.NONE,
663 POSITION_IMAGE_ROTATE, R.string.rotate)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700664 .setIcon(android.R.drawable.ic_menu_rotate);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800665 // Don't show the rotate submenu if the item at hand is read only
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700666 // since the items within the submenu won't be shown anyway. This
667 // is really a framework bug in that it shouldn't show the submenu
668 // if the submenu has no visible items.
Chih-Chung Changd1890832009-09-08 13:32:52 +0800669 MenuItem rotateLeft = rotateSubmenu.add(R.string.rotate_left)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800670 .setOnMenuItemClickListener(
671 new MenuItem.OnMenuItemClickListener() {
672 public boolean onMenuItemClick(MenuItem item) {
673 return onRotateClicked(onInvoke, -90);
674 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800675 }).setAlphabeticShortcut('l');
676
Chih-Chung Changd1890832009-09-08 13:32:52 +0800677 MenuItem rotateRight = rotateSubmenu.add(R.string.rotate_right)
Chih-Chung Changf6e6d2b2009-05-14 19:19:17 +0800678 .setOnMenuItemClickListener(
679 new MenuItem.OnMenuItemClickListener() {
680 public boolean onMenuItemClick(MenuItem item) {
681 return onRotateClicked(onInvoke, 90);
682 }
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800683 }).setAlphabeticShortcut('r');
684
685 requiresWriteAccessItems.add(rotateSubmenu.getItem());
686 requiresWriteAccessItems.add(rotateLeft);
687 requiresWriteAccessItems.add(rotateRight);
688
689 requiresImageItems.add(rotateSubmenu.getItem());
690 requiresImageItems.add(rotateLeft);
691 requiresImageItems.add(rotateRight);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800692 }
693
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800694 if ((inclusions & INCLUDE_CROP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800695 MenuItem autoCrop = menu.add(Menu.NONE, Menu.NONE,
696 POSITION_IMAGE_CROP, R.string.camera_crop);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700697 autoCrop.setIcon(android.R.drawable.ic_menu_crop);
698 autoCrop.setOnMenuItemClickListener(
699 new MenuItem.OnMenuItemClickListener() {
700 public boolean onMenuItemClick(MenuItem item) {
701 return onCropClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800702 }
703 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800704 requiresWriteAccessItems.add(autoCrop);
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800705 requiresImageItems.add(autoCrop);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800706 }
707
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800708 if ((inclusions & INCLUDE_SET_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800709 MenuItem setMenu = menu.add(Menu.NONE, Menu.NONE,
710 POSITION_IMAGE_SET, R.string.camera_set);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800711 setMenu.setIcon(android.R.drawable.ic_menu_set_as);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700712 setMenu.setOnMenuItemClickListener(
713 new MenuItem.OnMenuItemClickListener() {
714 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700715 return onSetAsClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800716 }
717 });
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800718 requiresImageItems.add(setMenu);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800719 }
720
721 if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800722 MenuItem item1 = menu.add(Menu.NONE, MENU_IMAGE_SHARE,
723 POSITION_IMAGE_SHARE, R.string.camera_share)
724 .setOnMenuItemClickListener(
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800725 new MenuItem.OnMenuItemClickListener() {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700726 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800727 return onImageShareClicked(onInvoke, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800728 }
729 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800730 item1.setIcon(android.R.drawable.ic_menu_share);
731 MenuItem item = item1;
732 requiresNoDrmAccessItems.add(item);
733 }
734
735 if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800736 MenuItem deleteItem = menu.add(Menu.NONE, Menu.NONE,
737 POSITION_IMAGE_TOSS, R.string.camera_toss);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800738 requiresWriteAccessItems.add(deleteItem);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700739 deleteItem.setOnMenuItemClickListener(
740 new MenuItem.OnMenuItemClickListener() {
741 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang91acfc92009-07-06 15:37:24 +0800742 return onDeleteClicked(onInvoke, activity,
743 onDelete);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700744 }
745 })
746 .setAlphabeticShortcut('d')
747 .setIcon(android.R.drawable.ic_menu_delete);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800748 }
749
750 if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800751 MenuItem detailsMenu = menu.add(Menu.NONE, Menu.NONE,
752 POSITION_DETAILS, R.string.details)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700753 .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800754 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800755 return onDetailsClicked(onInvoke, handler, activity);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800756 }
757 });
758 detailsMenu.setIcon(R.drawable.ic_menu_view_details);
759 }
760
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800761 if ((inclusions & INCLUDE_SHOWMAP_MENU) != 0) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800762 MenuItem showOnMapItem = menu.add(Menu.NONE, MENU_IMAGE_SHOWMAP,
763 POSITION_SHOWMAP, R.string.show_on_map);
Chih-Chung Chang120bf582009-09-07 18:51:47 +0800764 showOnMapItem.setOnMenuItemClickListener(
Ray Chen0a5a78c2009-04-24 14:13:01 +0800765 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) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800775 MenuItem videoPlayItem = menu.add(Menu.NONE, Menu.NONE,
776 POSITION_VIEWPLAY, 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);
repo synca274cf12009-07-06 15:05:23 +0800812
Chih-Chung Chang35f8af02009-07-01 14:52:24 +0800813 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 labelId = switchToVideo
896 ? R.string.switch_to_video_lable
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800897 : R.string.switch_to_camera_lable;
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700898 int iconId = switchToVideo
899 ? R.drawable.ic_menu_camera_video_view
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800900 : android.R.drawable.ic_menu_camera;
Owen Lin3283e042009-06-26 11:59:58 -0700901 final String action = switchToVideo
902 ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
903 : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
Chih-Chung Changd1890832009-09-08 13:32:52 +0800904 MenuItem item = menu.add(Menu.NONE, Menu.NONE,
905 POSITION_SWITCH_CAMERA_MODE, labelId)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700906 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
Owen Lin3283e042009-06-26 11:59:58 -0700907 public boolean onMenuItemClick(MenuItem item) {
908 startCameraActivity(activity, action);
909 return true;
910 }
911 });
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800912 item.setIcon(iconId);
913 }
914
Owen Lin3283e042009-06-26 11:59:58 -0700915 private static void startCameraActivity(Activity activity, String action) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700916 Intent intent = new Intent(action);
917 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
918 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
Chih-Chung Changcd65be32009-06-09 13:51:29 +0800919
920 // Keep the camera instance for a while.
921 // This avoids re-opening the camera and saves time.
922 CameraHolder.instance().keep();
923
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700924 activity.startActivity(intent);
Owen Lin059daa32009-05-18 15:31:17 -0700925 }
926
927 public static void gotoVideoMode(Activity activity) {
Owen Lin3283e042009-06-26 11:59:58 -0700928 startCameraActivity(activity, MediaStore.INTENT_ACTION_VIDEO_CAMERA);
Owen Lin059daa32009-05-18 15:31:17 -0700929 }
930
Owen Lin3283e042009-06-26 11:59:58 -0700931 public static void gotoCameraMode(Activity activity) {
932 startCameraActivity(
933 activity, MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800934 }
935
Owen Lin3283e042009-06-26 11:59:58 -0700936 public static void gotoCameraImageGallery(Activity activity) {
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700937 gotoGallery(activity, R.string.gallery_camera_bucket_name,
938 ImageManager.INCLUDE_IMAGES);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800939 }
940
Owen Lin3283e042009-06-26 11:59:58 -0700941 public static void gotoCameraVideoGallery(Activity activity) {
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800942 gotoGallery(activity, R.string.gallery_camera_videos_bucket_name,
943 ImageManager.INCLUDE_VIDEOS);
944 }
945
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700946 private static void gotoGallery(Activity activity, int windowTitleId,
947 int mediaTypes) {
948 Uri target = Images.Media.INTERNAL_CONTENT_URI.buildUpon()
949 .appendQueryParameter("bucketId",
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800950 ImageManager.CAMERA_IMAGE_BUCKET_ID).build();
951 Intent intent = new Intent(Intent.ACTION_VIEW, target);
The Android Open Source Projecte3f45162009-03-11 12:11:58 -0700952 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800953 intent.putExtra("windowTitle", activity.getString(windowTitleId));
954 intent.putExtra("mediaTypes", mediaTypes);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700955
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800956 try {
957 activity.startActivity(intent);
958 } catch (ActivityNotFoundException e) {
959 Log.e(TAG, "Could not start gallery activity", e);
960 }
961 }
962
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700963 static void addCapturePictureMenuItems(Menu menu, final Activity activity) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800964 menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_PICTURE,
965 R.string.capture_picture)
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700966 .setOnMenuItemClickListener(
967 new MenuItem.OnMenuItemClickListener() {
968 public boolean onMenuItemClick(MenuItem item) {
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700969 return onCapturePictureClicked(activity);
Chih-Chung Chang9ae6df02009-04-08 01:51:45 -0700970 }
971 }).setIcon(android.R.drawable.ic_menu_camera);
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700972 }
The Android Open Source Projectb64d3452009-03-03 19:32:20 -0800973
Chih-Chung Chang272c3fd2009-04-08 05:14:37 -0700974 private static boolean onCapturePictureClicked(Activity activity) {
975 Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
976 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
977 try {
978 activity.startActivity(intent);
979 } catch (android.content.ActivityNotFoundException e) {
980 // Ignore exception
981 }
982 return true;
983 }
984
The Android Open Source Projectde365d82009-03-18 17:39:48 -0700985 static void addCaptureVideoMenuItems(Menu menu, final Activity activity) {
Chih-Chung Changd1890832009-09-08 13:32:52 +0800986 menu.add(Menu.NONE, Menu.NONE, POSITION_CAPTURE_VIDEO,
987 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}