blob: aaf5d3974fa084369fc2adc7011deeec74769f2b [file] [log] [blame]
Owen Linf9a0a432011-08-17 22:07:43 +08001/*
2 * Copyright (C) 2010 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.gallery3d.ui;
18
Ray Chen0c1f2c82011-10-05 14:32:58 +080019import android.app.Activity;
Ray Chen67098d12012-04-05 17:25:43 +080020import android.app.AlertDialog;
Ray Chen0c1f2c82011-10-05 14:32:58 +080021import android.app.ProgressDialog;
22import android.content.Context;
Ray Chen67098d12012-04-05 17:25:43 +080023import android.content.DialogInterface;
Owen Lind759b7c2012-05-16 15:32:02 -070024import android.content.DialogInterface.OnCancelListener;
Ray Chen67098d12012-04-05 17:25:43 +080025import android.content.DialogInterface.OnClickListener;
Ray Chen0c1f2c82011-10-05 14:32:58 +080026import android.content.Intent;
27import android.os.Handler;
28import android.os.Message;
29import android.view.Menu;
30import android.view.MenuItem;
Ray Chen0c1f2c82011-10-05 14:32:58 +080031
Owen Linf9a0a432011-08-17 22:07:43 +080032import com.android.gallery3d.R;
33import com.android.gallery3d.app.CropImage;
34import com.android.gallery3d.app.GalleryActivity;
35import com.android.gallery3d.common.Utils;
36import com.android.gallery3d.data.DataManager;
37import com.android.gallery3d.data.MediaItem;
38import com.android.gallery3d.data.MediaObject;
39import com.android.gallery3d.data.Path;
40import com.android.gallery3d.util.Future;
41import com.android.gallery3d.util.GalleryUtils;
42import com.android.gallery3d.util.ThreadPool.Job;
43import com.android.gallery3d.util.ThreadPool.JobContext;
44
Owen Linf9a0a432011-08-17 22:07:43 +080045import java.util.ArrayList;
46
47public class MenuExecutor {
48 @SuppressWarnings("unused")
49 private static final String TAG = "MenuExecutor";
50
51 private static final int MSG_TASK_COMPLETE = 1;
52 private static final int MSG_TASK_UPDATE = 2;
53 private static final int MSG_DO_SHARE = 3;
54
55 public static final int EXECUTION_RESULT_SUCCESS = 1;
56 public static final int EXECUTION_RESULT_FAIL = 2;
57 public static final int EXECUTION_RESULT_CANCEL = 3;
58
59 private ProgressDialog mDialog;
60 private Future<?> mTask;
Chih-Chung Chang6b891c62012-06-07 20:09:13 +080061 // wait the operation to finish when we want to stop it.
62 private boolean mWaitOnStop;
Owen Linf9a0a432011-08-17 22:07:43 +080063
64 private final GalleryActivity mActivity;
65 private final SelectionManager mSelectionManager;
66 private final Handler mHandler;
67
Chih-Chung Chang6b891c62012-06-07 20:09:13 +080068 private static ProgressDialog createProgressDialog(
Owen Linf9a0a432011-08-17 22:07:43 +080069 Context context, int titleId, int progressMax) {
70 ProgressDialog dialog = new ProgressDialog(context);
71 dialog.setTitle(titleId);
72 dialog.setMax(progressMax);
73 dialog.setCancelable(false);
74 dialog.setIndeterminate(false);
75 if (progressMax > 1) {
76 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
77 }
Owen Linf9a0a432011-08-17 22:07:43 +080078 return dialog;
79 }
80
81 public interface ProgressListener {
Owen Lind759b7c2012-05-16 15:32:02 -070082 public void onConfirmDialogShown();
83 public void onConfirmDialogDismissed(boolean confirmed);
Owen Linf9a0a432011-08-17 22:07:43 +080084 public void onProgressUpdate(int index);
85 public void onProgressComplete(int result);
86 }
87
88 public MenuExecutor(
89 GalleryActivity activity, SelectionManager selectionManager) {
90 mActivity = Utils.checkNotNull(activity);
91 mSelectionManager = Utils.checkNotNull(selectionManager);
92 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
93 @Override
94 public void handleMessage(Message message) {
95 switch (message.what) {
96 case MSG_TASK_COMPLETE: {
Ray Chenb2b45182011-08-31 16:37:04 +080097 stopTaskAndDismissDialog();
Owen Linf9a0a432011-08-17 22:07:43 +080098 if (message.obj != null) {
99 ProgressListener listener = (ProgressListener) message.obj;
100 listener.onProgressComplete(message.arg1);
101 }
102 mSelectionManager.leaveSelectionMode();
103 break;
104 }
105 case MSG_TASK_UPDATE: {
106 if (mDialog != null) mDialog.setProgress(message.arg1);
107 if (message.obj != null) {
108 ProgressListener listener = (ProgressListener) message.obj;
109 listener.onProgressUpdate(message.arg1);
110 }
111 break;
112 }
113 case MSG_DO_SHARE: {
114 ((Activity) mActivity).startActivity((Intent) message.obj);
115 break;
116 }
117 }
118 }
119 };
120 }
121
Ray Chenb2b45182011-08-31 16:37:04 +0800122 private void stopTaskAndDismissDialog() {
Owen Linf9a0a432011-08-17 22:07:43 +0800123 if (mTask != null) {
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800124 if (!mWaitOnStop) mTask.cancel();
Owen Linf9a0a432011-08-17 22:07:43 +0800125 mTask.waitDone();
126 mDialog.dismiss();
127 mDialog = null;
128 mTask = null;
129 }
130 }
131
Ray Chenb2b45182011-08-31 16:37:04 +0800132 public void pause() {
133 stopTaskAndDismissDialog();
134 }
135
Owen Linf9a0a432011-08-17 22:07:43 +0800136 private void onProgressUpdate(int index, ProgressListener listener) {
137 mHandler.sendMessage(
138 mHandler.obtainMessage(MSG_TASK_UPDATE, index, 0, listener));
139 }
140
141 private void onProgressComplete(int result, ProgressListener listener) {
142 mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_COMPLETE, result, 0, listener));
143 }
144
Owen Linf9a0a432011-08-17 22:07:43 +0800145 private static void setMenuItemVisibility(
146 Menu menu, int id, boolean visibility) {
147 MenuItem item = menu.findItem(id);
148 if (item != null) item.setVisible(visibility);
149 }
150
151 public static void updateMenuOperation(Menu menu, int supported) {
152 boolean supportDelete = (supported & MediaObject.SUPPORT_DELETE) != 0;
153 boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0;
154 boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0;
155 boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0;
156 boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0;
157 boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0;
158 boolean supportCache = (supported & MediaObject.SUPPORT_CACHE) != 0;
159 boolean supportEdit = (supported & MediaObject.SUPPORT_EDIT) != 0;
160 boolean supportInfo = (supported & MediaObject.SUPPORT_INFO) != 0;
161 boolean supportImport = (supported & MediaObject.SUPPORT_IMPORT) != 0;
162
163 setMenuItemVisibility(menu, R.id.action_delete, supportDelete);
164 setMenuItemVisibility(menu, R.id.action_rotate_ccw, supportRotate);
165 setMenuItemVisibility(menu, R.id.action_rotate_cw, supportRotate);
166 setMenuItemVisibility(menu, R.id.action_crop, supportCrop);
167 setMenuItemVisibility(menu, R.id.action_share, supportShare);
168 setMenuItemVisibility(menu, R.id.action_setas, supportSetAs);
169 setMenuItemVisibility(menu, R.id.action_show_on_map, supportShowOnMap);
170 setMenuItemVisibility(menu, R.id.action_edit, supportEdit);
171 setMenuItemVisibility(menu, R.id.action_details, supportInfo);
172 setMenuItemVisibility(menu, R.id.action_import, supportImport);
173 }
174
175 private Path getSingleSelectedPath() {
176 ArrayList<Path> ids = mSelectionManager.getSelected(true);
177 Utils.assertTrue(ids.size() == 1);
178 return ids.get(0);
179 }
180
Yuli Huangf50ce2b2012-05-02 00:50:56 +0800181 private Intent getIntentBySingleSelectedPath(String action) {
182 DataManager manager = mActivity.getDataManager();
183 Path path = getSingleSelectedPath();
184 String mimeType = getMimeType(manager.getMediaType(path));
185 return new Intent(action).setDataAndType(manager.getContentUri(path), mimeType);
186 }
187
Ray Chen67098d12012-04-05 17:25:43 +0800188 private void onMenuClicked(int action, ProgressListener listener) {
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800189 onMenuClicked(action, listener, false, true);
190 }
191
192 public void onMenuClicked(int action, ProgressListener listener,
193 boolean waitOnStop, boolean showDialog) {
Owen Linf9a0a432011-08-17 22:07:43 +0800194 int title;
Owen Linf9a0a432011-08-17 22:07:43 +0800195 switch (action) {
196 case R.id.action_select_all:
197 if (mSelectionManager.inSelectAllMode()) {
198 mSelectionManager.deSelectAll();
199 } else {
200 mSelectionManager.selectAll();
201 }
Wu-cheng Li57263d32012-04-30 23:49:17 +0800202 return;
Owen Linf9a0a432011-08-17 22:07:43 +0800203 case R.id.action_crop: {
Yuli Huangf50ce2b2012-05-02 00:50:56 +0800204 Intent intent = getIntentBySingleSelectedPath(CropImage.ACTION_CROP);
Owen Linf9a0a432011-08-17 22:07:43 +0800205 ((Activity) mActivity).startActivity(intent);
Wu-cheng Li57263d32012-04-30 23:49:17 +0800206 return;
Owen Linf9a0a432011-08-17 22:07:43 +0800207 }
Yuli Huangf50ce2b2012-05-02 00:50:56 +0800208 case R.id.action_edit: {
209 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_EDIT)
210 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
211 ((Activity) mActivity).startActivity(Intent.createChooser(intent, null));
212 return;
213 }
Owen Linf9a0a432011-08-17 22:07:43 +0800214 case R.id.action_setas: {
Yuli Huangf50ce2b2012-05-02 00:50:56 +0800215 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_ATTACH_DATA)
216 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
217 intent.putExtra("mimeType", intent.getType());
Owen Linf9a0a432011-08-17 22:07:43 +0800218 Activity activity = (Activity) mActivity;
219 activity.startActivity(Intent.createChooser(
220 intent, activity.getString(R.string.set_as)));
Wu-cheng Li57263d32012-04-30 23:49:17 +0800221 return;
Owen Linf9a0a432011-08-17 22:07:43 +0800222 }
Ray Chen67098d12012-04-05 17:25:43 +0800223 case R.id.action_delete:
Owen Linf9a0a432011-08-17 22:07:43 +0800224 title = R.string.delete;
225 break;
226 case R.id.action_rotate_cw:
227 title = R.string.rotate_right;
228 break;
229 case R.id.action_rotate_ccw:
230 title = R.string.rotate_left;
231 break;
232 case R.id.action_show_on_map:
233 title = R.string.show_on_map;
234 break;
Owen Linf9a0a432011-08-17 22:07:43 +0800235 case R.id.action_import:
236 title = R.string.Import;
237 break;
238 default:
Ray Chen67098d12012-04-05 17:25:43 +0800239 return;
Owen Linf9a0a432011-08-17 22:07:43 +0800240 }
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800241 startAction(action, title, listener, waitOnStop, showDialog);
Ray Chen67098d12012-04-05 17:25:43 +0800242 }
243
Owen Lind759b7c2012-05-16 15:32:02 -0700244 private class ConfirmDialogListener implements OnClickListener, OnCancelListener {
245 private final int mActionId;
246 private final ProgressListener mListener;
247
248 public ConfirmDialogListener(int actionId, ProgressListener listener) {
249 mActionId = actionId;
250 mListener = listener;
251 }
252
253 @Override
254 public void onClick(DialogInterface dialog, int which) {
255 if (which == DialogInterface.BUTTON_POSITIVE) {
256 if (mListener != null) {
257 mListener.onConfirmDialogDismissed(true);
258 }
259 onMenuClicked(mActionId, mListener);
260 } else {
261 if (mListener != null) {
262 mListener.onConfirmDialogDismissed(false);
263 }
264 }
265 }
266
267 @Override
268 public void onCancel(DialogInterface dialog) {
269 if (mListener != null) {
270 mListener.onConfirmDialogDismissed(false);
271 }
272 }
273 }
274
Ray Chen9a033b02012-05-15 11:22:21 +0800275 public void onMenuClicked(MenuItem menuItem, String confirmMsg,
Ray Chen67098d12012-04-05 17:25:43 +0800276 final ProgressListener listener) {
277 final int action = menuItem.getItemId();
278
Ray Chen9a033b02012-05-15 11:22:21 +0800279 if (confirmMsg != null) {
Owen Lind759b7c2012-05-16 15:32:02 -0700280 if (listener != null) listener.onConfirmDialogShown();
281 ConfirmDialogListener cdl = new ConfirmDialogListener(action, listener);
Ray Chen67098d12012-04-05 17:25:43 +0800282 new AlertDialog.Builder(mActivity.getAndroidContext())
Ray Chen9a033b02012-05-15 11:22:21 +0800283 .setMessage(confirmMsg)
Owen Lind759b7c2012-05-16 15:32:02 -0700284 .setOnCancelListener(cdl)
285 .setPositiveButton(R.string.ok, cdl)
286 .setNegativeButton(R.string.cancel, cdl)
287 .create().show();
Ray Chen67098d12012-04-05 17:25:43 +0800288 } else {
289 onMenuClicked(action, listener);
290 }
Owen Linf9a0a432011-08-17 22:07:43 +0800291 }
292
293 public void startAction(int action, int title, ProgressListener listener) {
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800294 startAction(action, title, listener, false, true);
295 }
296
297 public void startAction(int action, int title, ProgressListener listener,
298 boolean waitOnStop, boolean showDialog) {
Owen Linf9a0a432011-08-17 22:07:43 +0800299 ArrayList<Path> ids = mSelectionManager.getSelected(false);
Ray Chenb2b45182011-08-31 16:37:04 +0800300 stopTaskAndDismissDialog();
Owen Linf9a0a432011-08-17 22:07:43 +0800301
302 Activity activity = (Activity) mActivity;
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800303 mDialog = createProgressDialog(activity, title, ids.size());
304 if (showDialog) {
305 mDialog.show();
306 }
Owen Linf9a0a432011-08-17 22:07:43 +0800307 MediaOperation operation = new MediaOperation(action, ids, listener);
308 mTask = mActivity.getThreadPool().submit(operation, null);
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800309 mWaitOnStop = waitOnStop;
Owen Linf9a0a432011-08-17 22:07:43 +0800310 }
311
312 public static String getMimeType(int type) {
313 switch (type) {
314 case MediaObject.MEDIA_TYPE_IMAGE :
315 return "image/*";
316 case MediaObject.MEDIA_TYPE_VIDEO :
317 return "video/*";
318 default: return "*/*";
319 }
320 }
321
322 private boolean execute(
323 DataManager manager, JobContext jc, int cmd, Path path) {
324 boolean result = true;
Ray Chenb2b45182011-08-31 16:37:04 +0800325 Log.v(TAG, "Execute cmd: " + cmd + " for " + path);
326 long startTime = System.currentTimeMillis();
327
Owen Linf9a0a432011-08-17 22:07:43 +0800328 switch (cmd) {
Ray Chen67098d12012-04-05 17:25:43 +0800329 case R.id.action_delete:
Owen Linf9a0a432011-08-17 22:07:43 +0800330 manager.delete(path);
331 break;
332 case R.id.action_rotate_cw:
333 manager.rotate(path, 90);
334 break;
335 case R.id.action_rotate_ccw:
336 manager.rotate(path, -90);
337 break;
338 case R.id.action_toggle_full_caching: {
339 MediaObject obj = manager.getMediaObject(path);
340 int cacheFlag = obj.getCacheFlag();
341 if (cacheFlag == MediaObject.CACHE_FLAG_FULL) {
342 cacheFlag = MediaObject.CACHE_FLAG_SCREENNAIL;
343 } else {
344 cacheFlag = MediaObject.CACHE_FLAG_FULL;
345 }
346 obj.cache(cacheFlag);
347 break;
348 }
349 case R.id.action_show_on_map: {
350 MediaItem item = (MediaItem) manager.getMediaObject(path);
351 double latlng[] = new double[2];
352 item.getLatLong(latlng);
353 if (GalleryUtils.isValidLocation(latlng[0], latlng[1])) {
354 GalleryUtils.showOnMap((Context) mActivity, latlng[0], latlng[1]);
355 }
356 break;
357 }
358 case R.id.action_import: {
359 MediaObject obj = manager.getMediaObject(path);
360 result = obj.Import();
361 break;
362 }
Owen Linf9a0a432011-08-17 22:07:43 +0800363 default:
364 throw new AssertionError();
365 }
Ray Chenb2b45182011-08-31 16:37:04 +0800366 Log.v(TAG, "It takes " + (System.currentTimeMillis() - startTime) +
367 " ms to execute cmd for " + path);
Owen Linf9a0a432011-08-17 22:07:43 +0800368 return result;
369 }
370
371 private class MediaOperation implements Job<Void> {
372 private final ArrayList<Path> mItems;
373 private final int mOperation;
374 private final ProgressListener mListener;
375
Chih-Chung Chang6b891c62012-06-07 20:09:13 +0800376 public MediaOperation(int operation, ArrayList<Path> items,
377 ProgressListener listener) {
Owen Linf9a0a432011-08-17 22:07:43 +0800378 mOperation = operation;
379 mItems = items;
380 mListener = listener;
381 }
382
Ahbong Chang78179792012-07-30 11:34:13 +0800383 @Override
Owen Linf9a0a432011-08-17 22:07:43 +0800384 public Void run(JobContext jc) {
385 int index = 0;
386 DataManager manager = mActivity.getDataManager();
387 int result = EXECUTION_RESULT_SUCCESS;
Ray Chenb2b45182011-08-31 16:37:04 +0800388 try {
389 for (Path id : mItems) {
390 if (jc.isCancelled()) {
391 result = EXECUTION_RESULT_CANCEL;
392 break;
393 }
394 if (!execute(manager, jc, mOperation, id)) {
395 result = EXECUTION_RESULT_FAIL;
396 }
397 onProgressUpdate(index++, mListener);
Owen Linf9a0a432011-08-17 22:07:43 +0800398 }
Ray Chenb2b45182011-08-31 16:37:04 +0800399 } catch (Throwable th) {
400 Log.e(TAG, "failed to execute operation " + mOperation
401 + " : " + th);
402 } finally {
403 onProgressComplete(result, mListener);
Owen Linf9a0a432011-08-17 22:07:43 +0800404 }
Owen Linf9a0a432011-08-17 22:07:43 +0800405 return null;
406 }
407 }
408}