2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.ui;
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.ProgressDialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.DialogInterface.OnCancelListener;
25 import android.content.DialogInterface.OnClickListener;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.support.v4.print.PrintHelper;
30 import android.view.Menu;
31 import android.view.MenuItem;
33 import com.android.gallery3d.R;
34 import com.android.gallery3d.app.AbstractGalleryActivity;
35 import com.android.gallery3d.common.Utils;
36 import com.android.gallery3d.data.DataManager;
37 import com.android.gallery3d.data.MediaItem;
38 import com.android.gallery3d.data.MediaObject;
39 import com.android.gallery3d.data.Path;
40 import com.android.gallery3d.filtershow.crop.CropActivity;
41 import com.android.gallery3d.util.Future;
42 import com.android.gallery3d.util.GalleryUtils;
43 import com.android.gallery3d.util.ThreadPool.Job;
44 import com.android.gallery3d.util.ThreadPool.JobContext;
46 import java.util.ArrayList;
48 public class MenuExecutor {
49 private static final String TAG = "MenuExecutor";
51 private static final int MSG_TASK_COMPLETE = 1;
52 private static final int MSG_TASK_UPDATE = 2;
53 private static final int MSG_TASK_START = 3;
54 private static final int MSG_DO_SHARE = 4;
56 public static final int EXECUTION_RESULT_SUCCESS = 1;
57 public static final int EXECUTION_RESULT_FAIL = 2;
58 public static final int EXECUTION_RESULT_CANCEL = 3;
60 private ProgressDialog mDialog;
61 private Future<?> mTask;
62 // wait the operation to finish when we want to stop it.
63 private boolean mWaitOnStop;
64 private boolean mPaused;
66 private final AbstractGalleryActivity mActivity;
67 private final SelectionManager mSelectionManager;
68 private final Handler mHandler;
70 private static ProgressDialog createProgressDialog(
71 Context context, int titleId, int progressMax) {
72 ProgressDialog dialog = new ProgressDialog(context);
73 dialog.setTitle(titleId);
74 dialog.setMax(progressMax);
75 dialog.setCancelable(false);
76 dialog.setIndeterminate(false);
77 if (progressMax > 1) {
78 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
83 public interface ProgressListener {
84 public void onConfirmDialogShown();
85 public void onConfirmDialogDismissed(boolean confirmed);
86 public void onProgressStart();
87 public void onProgressUpdate(int index);
88 public void onProgressComplete(int result);
92 AbstractGalleryActivity activity, SelectionManager selectionManager) {
93 mActivity = Utils.checkNotNull(activity);
94 mSelectionManager = Utils.checkNotNull(selectionManager);
95 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
97 public void handleMessage(Message message) {
98 switch (message.what) {
99 case MSG_TASK_START: {
100 if (message.obj != null) {
101 ProgressListener listener = (ProgressListener) message.obj;
102 listener.onProgressStart();
106 case MSG_TASK_COMPLETE: {
107 stopTaskAndDismissDialog();
108 if (message.obj != null) {
109 ProgressListener listener = (ProgressListener) message.obj;
110 listener.onProgressComplete(message.arg1);
112 mSelectionManager.leaveSelectionMode();
115 case MSG_TASK_UPDATE: {
116 if (mDialog != null && !mPaused) mDialog.setProgress(message.arg1);
117 if (message.obj != null) {
118 ProgressListener listener = (ProgressListener) message.obj;
119 listener.onProgressUpdate(message.arg1);
124 ((Activity) mActivity).startActivity((Intent) message.obj);
132 private void stopTaskAndDismissDialog() {
134 if (!mWaitOnStop) mTask.cancel();
135 if (mDialog != null && mDialog.isShowing()) mDialog.dismiss();
141 public void resume() {
143 if (mDialog != null) mDialog.show();
146 public void pause() {
148 if (mDialog != null && mDialog.isShowing()) mDialog.hide();
151 public void destroy() {
152 stopTaskAndDismissDialog();
155 private void onProgressUpdate(int index, ProgressListener listener) {
156 mHandler.sendMessage(
157 mHandler.obtainMessage(MSG_TASK_UPDATE, index, 0, listener));
160 private void onProgressStart(ProgressListener listener) {
161 mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_START, listener));
164 private void onProgressComplete(int result, ProgressListener listener) {
165 mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_COMPLETE, result, 0, listener));
168 public static void updateMenuOperation(Menu menu, int supported) {
169 boolean supportDelete = (supported & MediaObject.SUPPORT_DELETE) != 0;
170 boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
171 boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
172 boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0;
173 boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0;
174 boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
175 boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
176 boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
177 boolean supportCache = (supported & MediaObject.SUPPORT_CACHE) != 0;
178 boolean supportEdit = (supported & MediaObject.SUPPORT_EDIT) != 0;
179 boolean supportInfo = (supported & MediaObject.SUPPORT_INFO) != 0;
180 boolean supportPrint = (supported & MediaObject.SUPPORT_PRINT) != 0;
181 supportPrint &= PrintHelper.systemSupportsPrint();
183 setMenuItemVisible(menu, R.id.action_delete, supportDelete);
184 setMenuItemVisible(menu, R.id.action_rotate_ccw, supportRotate);
185 setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate);
186 setMenuItemVisible(menu, R.id.action_crop, supportCrop);
187 setMenuItemVisible(menu, R.id.action_trim, supportTrim);
188 setMenuItemVisible(menu, R.id.action_mute, supportMute);
189 // Hide panorama until call to updateMenuForPanorama corrects it
190 setMenuItemVisible(menu, R.id.action_share_panorama, false);
191 setMenuItemVisible(menu, R.id.action_share, supportShare);
192 setMenuItemVisible(menu, R.id.action_setas, supportSetAs);
193 setMenuItemVisible(menu, R.id.action_show_on_map, supportShowOnMap);
194 setMenuItemVisible(menu, R.id.action_edit, supportEdit);
195 // setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit);
196 setMenuItemVisible(menu, R.id.action_details, supportInfo);
197 setMenuItemVisible(menu, R.id.print, supportPrint);
200 public static void updateMenuForPanorama(Menu menu, boolean shareAsPanorama360,
201 boolean disablePanorama360Options) {
202 setMenuItemVisible(menu, R.id.action_share_panorama, shareAsPanorama360);
203 if (disablePanorama360Options) {
204 setMenuItemVisible(menu, R.id.action_rotate_ccw, false);
205 setMenuItemVisible(menu, R.id.action_rotate_cw, false);
209 private static void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
210 MenuItem item = menu.findItem(itemId);
211 if (item != null) item.setVisible(visible);
214 private Path getSingleSelectedPath() {
215 ArrayList<Path> ids = mSelectionManager.getSelected(true);
216 Utils.assertTrue(ids.size() == 1);
220 private Intent getIntentBySingleSelectedPath(String action) {
221 DataManager manager = mActivity.getDataManager();
222 Path path = getSingleSelectedPath();
223 String mimeType = getMimeType(manager.getMediaType(path));
224 return new Intent(action).setDataAndType(manager.getContentUri(path), mimeType);
227 private void onMenuClicked(int action, ProgressListener listener) {
228 onMenuClicked(action, listener, false, true);
231 public void onMenuClicked(int action, ProgressListener listener,
232 boolean waitOnStop, boolean showDialog) {
235 case R.id.action_select_all:
236 if (mSelectionManager.inSelectAllMode()) {
237 mSelectionManager.deSelectAll();
239 mSelectionManager.selectAll();
242 case R.id.action_crop: {
243 Intent intent = getIntentBySingleSelectedPath(CropActivity.CROP_ACTION);
244 ((Activity) mActivity).startActivity(intent);
247 case R.id.action_edit: {
248 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_EDIT)
249 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
250 ((Activity) mActivity).startActivity(Intent.createChooser(intent, null));
253 case R.id.action_setas: {
254 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_ATTACH_DATA)
255 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
256 intent.putExtra("mimeType", intent.getType());
257 Activity activity = mActivity;
258 activity.startActivity(Intent.createChooser(
259 intent, activity.getString(R.string.set_as)));
262 case R.id.action_delete:
263 title = R.string.delete;
265 case R.id.action_rotate_cw:
266 title = R.string.rotate_right;
268 case R.id.action_rotate_ccw:
269 title = R.string.rotate_left;
271 case R.id.action_show_on_map:
272 title = R.string.show_on_map;
277 startAction(action, title, listener, waitOnStop, showDialog);
280 private class ConfirmDialogListener implements OnClickListener, OnCancelListener {
281 private final int mActionId;
282 private final ProgressListener mListener;
284 public ConfirmDialogListener(int actionId, ProgressListener listener) {
285 mActionId = actionId;
286 mListener = listener;
290 public void onClick(DialogInterface dialog, int which) {
291 if (which == DialogInterface.BUTTON_POSITIVE) {
292 if (mListener != null) {
293 mListener.onConfirmDialogDismissed(true);
295 onMenuClicked(mActionId, mListener);
297 if (mListener != null) {
298 mListener.onConfirmDialogDismissed(false);
304 public void onCancel(DialogInterface dialog) {
305 if (mListener != null) {
306 mListener.onConfirmDialogDismissed(false);
311 public void onMenuClicked(MenuItem menuItem, String confirmMsg,
312 final ProgressListener listener) {
313 final int action = menuItem.getItemId();
315 if (confirmMsg != null) {
316 if (listener != null) listener.onConfirmDialogShown();
317 ConfirmDialogListener cdl = new ConfirmDialogListener(action, listener);
318 new AlertDialog.Builder(mActivity.getAndroidContext())
319 .setMessage(confirmMsg)
320 .setOnCancelListener(cdl)
321 .setPositiveButton(R.string.ok, cdl)
322 .setNegativeButton(R.string.cancel, cdl)
325 onMenuClicked(action, listener);
329 public void startAction(int action, int title, ProgressListener listener) {
330 startAction(action, title, listener, false, true);
333 public void startAction(int action, int title, ProgressListener listener,
334 boolean waitOnStop, boolean showDialog) {
335 ArrayList<Path> ids = mSelectionManager.getSelected(false);
336 stopTaskAndDismissDialog();
338 Activity activity = mActivity;
340 mDialog = createProgressDialog(activity, title, ids.size());
345 MediaOperation operation = new MediaOperation(action, ids, listener);
346 mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null);
347 mWaitOnStop = waitOnStop;
350 public void startSingleItemAction(int action, Path targetPath) {
351 ArrayList<Path> ids = new ArrayList<Path>(1);
354 MediaOperation operation = new MediaOperation(action, ids, null);
355 mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null);
359 public static String getMimeType(int type) {
361 case MediaObject.MEDIA_TYPE_IMAGE :
362 return GalleryUtils.MIME_TYPE_IMAGE;
363 case MediaObject.MEDIA_TYPE_VIDEO :
364 return GalleryUtils.MIME_TYPE_VIDEO;
365 default: return GalleryUtils.MIME_TYPE_ALL;
369 private boolean execute(
370 DataManager manager, JobContext jc, int cmd, Path path) {
371 boolean result = true;
372 Log.v(TAG, "Execute cmd: " + cmd + " for " + path);
373 long startTime = System.currentTimeMillis();
376 case R.id.action_delete:
377 manager.delete(path);
379 case R.id.action_rotate_cw:
380 manager.rotate(path, 90);
382 case R.id.action_rotate_ccw:
383 manager.rotate(path, -90);
385 case R.id.action_toggle_full_caching: {
386 MediaObject obj = manager.getMediaObject(path);
387 int cacheFlag = obj.getCacheFlag();
388 if (cacheFlag == MediaObject.CACHE_FLAG_FULL) {
389 cacheFlag = MediaObject.CACHE_FLAG_SCREENNAIL;
391 cacheFlag = MediaObject.CACHE_FLAG_FULL;
393 obj.cache(cacheFlag);
396 case R.id.action_show_on_map: {
397 MediaItem item = (MediaItem) manager.getMediaObject(path);
398 double latlng[] = new double[2];
399 item.getLatLong(latlng);
400 if (GalleryUtils.isValidLocation(latlng[0], latlng[1])) {
401 GalleryUtils.showOnMap(mActivity, latlng[0], latlng[1]);
406 throw new AssertionError();
408 Log.v(TAG, "It takes " + (System.currentTimeMillis() - startTime) +
409 " ms to execute cmd for " + path);
413 private class MediaOperation implements Job<Void> {
414 private final ArrayList<Path> mItems;
415 private final int mOperation;
416 private final ProgressListener mListener;
418 public MediaOperation(int operation, ArrayList<Path> items,
419 ProgressListener listener) {
420 mOperation = operation;
422 mListener = listener;
426 public Void run(JobContext jc) {
428 DataManager manager = mActivity.getDataManager();
429 int result = EXECUTION_RESULT_SUCCESS;
431 onProgressStart(mListener);
432 for (Path id : mItems) {
433 if (jc.isCancelled()) {
434 result = EXECUTION_RESULT_CANCEL;
437 if (!execute(manager, jc, mOperation, id)) {
438 result = EXECUTION_RESULT_FAIL;
440 onProgressUpdate(index++, mListener);
442 } catch (Throwable th) {
443 Log.e(TAG, "failed to execute operation " + mOperation
446 onProgressComplete(result, mListener);