MenuExecutor.java revision ef3cc52f473f15534f00b61b55b53e93931fef97
1/* 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 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.ProgressDialog; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.DialogInterface.OnCancelListener; 25import android.content.DialogInterface.OnClickListener; 26import android.content.Intent; 27import android.os.Handler; 28import android.os.Message; 29import android.view.Menu; 30import android.view.MenuItem; 31 32import com.android.gallery3d.R; 33import com.android.gallery3d.app.AbstractGalleryActivity; 34import com.android.gallery3d.common.Utils; 35import com.android.gallery3d.data.DataManager; 36import com.android.gallery3d.data.MediaItem; 37import com.android.gallery3d.data.MediaObject; 38import com.android.gallery3d.data.Path; 39import com.android.gallery3d.filtershow.crop.CropActivity; 40import com.android.gallery3d.util.Future; 41import com.android.gallery3d.util.GalleryUtils; 42import com.android.gallery3d.util.PrintJob; 43import com.android.gallery3d.util.ThreadPool.Job; 44import com.android.gallery3d.util.ThreadPool.JobContext; 45 46import java.util.ArrayList; 47 48public class MenuExecutor { 49 @SuppressWarnings("unused") 50 private static final String TAG = "MenuExecutor"; 51 52 private static final int MSG_TASK_COMPLETE = 1; 53 private static final int MSG_TASK_UPDATE = 2; 54 private static final int MSG_TASK_START = 3; 55 private static final int MSG_DO_SHARE = 4; 56 57 public static final int EXECUTION_RESULT_SUCCESS = 1; 58 public static final int EXECUTION_RESULT_FAIL = 2; 59 public static final int EXECUTION_RESULT_CANCEL = 3; 60 61 private ProgressDialog mDialog; 62 private Future<?> mTask; 63 // wait the operation to finish when we want to stop it. 64 private boolean mWaitOnStop; 65 private boolean mPaused; 66 67 private final AbstractGalleryActivity mActivity; 68 private final SelectionManager mSelectionManager; 69 private final Handler mHandler; 70 71 private static ProgressDialog createProgressDialog( 72 Context context, int titleId, int progressMax) { 73 ProgressDialog dialog = new ProgressDialog(context); 74 dialog.setTitle(titleId); 75 dialog.setMax(progressMax); 76 dialog.setCancelable(false); 77 dialog.setIndeterminate(false); 78 if (progressMax > 1) { 79 dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 80 } 81 return dialog; 82 } 83 84 public interface ProgressListener { 85 public void onConfirmDialogShown(); 86 public void onConfirmDialogDismissed(boolean confirmed); 87 public void onProgressStart(); 88 public void onProgressUpdate(int index); 89 public void onProgressComplete(int result); 90 } 91 92 public MenuExecutor( 93 AbstractGalleryActivity activity, SelectionManager selectionManager) { 94 mActivity = Utils.checkNotNull(activity); 95 mSelectionManager = Utils.checkNotNull(selectionManager); 96 mHandler = new SynchronizedHandler(mActivity.getGLRoot()) { 97 @Override 98 public void handleMessage(Message message) { 99 switch (message.what) { 100 case MSG_TASK_START: { 101 if (message.obj != null) { 102 ProgressListener listener = (ProgressListener) message.obj; 103 listener.onProgressStart(); 104 } 105 break; 106 } 107 case MSG_TASK_COMPLETE: { 108 stopTaskAndDismissDialog(); 109 if (message.obj != null) { 110 ProgressListener listener = (ProgressListener) message.obj; 111 listener.onProgressComplete(message.arg1); 112 } 113 mSelectionManager.leaveSelectionMode(); 114 break; 115 } 116 case MSG_TASK_UPDATE: { 117 if (mDialog != null && !mPaused) mDialog.setProgress(message.arg1); 118 if (message.obj != null) { 119 ProgressListener listener = (ProgressListener) message.obj; 120 listener.onProgressUpdate(message.arg1); 121 } 122 break; 123 } 124 case MSG_DO_SHARE: { 125 ((Activity) mActivity).startActivity((Intent) message.obj); 126 break; 127 } 128 } 129 } 130 }; 131 } 132 133 private void stopTaskAndDismissDialog() { 134 if (mTask != null) { 135 if (!mWaitOnStop) mTask.cancel(); 136 if (mDialog != null && mDialog.isShowing()) mDialog.dismiss(); 137 mDialog = null; 138 mTask = null; 139 } 140 } 141 142 public void resume() { 143 mPaused = false; 144 if (mDialog != null) mDialog.show(); 145 } 146 147 public void pause() { 148 mPaused = true; 149 if (mDialog != null && mDialog.isShowing()) mDialog.hide(); 150 } 151 152 public void destroy() { 153 stopTaskAndDismissDialog(); 154 } 155 156 private void onProgressUpdate(int index, ProgressListener listener) { 157 mHandler.sendMessage( 158 mHandler.obtainMessage(MSG_TASK_UPDATE, index, 0, listener)); 159 } 160 161 private void onProgressStart(ProgressListener listener) { 162 mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_START, listener)); 163 } 164 165 private void onProgressComplete(int result, ProgressListener listener) { 166 mHandler.sendMessage(mHandler.obtainMessage(MSG_TASK_COMPLETE, result, 0, listener)); 167 } 168 169 public static void updateMenuOperation(Menu menu, int supported) { 170 boolean supportDelete = (supported & MediaObject.SUPPORT_DELETE) != 0; 171 boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0; 172 boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0; 173 boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0; 174 boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0; 175 boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0; 176 boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0; 177 boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0; 178 boolean supportCache = (supported & MediaObject.SUPPORT_CACHE) != 0; 179 boolean supportEdit = (supported & MediaObject.SUPPORT_EDIT) != 0; 180 boolean supportInfo = (supported & MediaObject.SUPPORT_INFO) != 0; 181 boolean supportPrint = (supported & MediaObject.SUPPORT_PRINT) != 0; 182 supportPrint &= PrintJob.systemSupportsPrint(); 183 184 setMenuItemVisible(menu, R.id.action_delete, supportDelete); 185 setMenuItemVisible(menu, R.id.action_rotate_ccw, supportRotate); 186 setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate); 187 setMenuItemVisible(menu, R.id.action_crop, supportCrop); 188 setMenuItemVisible(menu, R.id.action_trim, supportTrim); 189 setMenuItemVisible(menu, R.id.action_mute, supportMute); 190 // Hide panorama until call to updateMenuForPanorama corrects it 191 setMenuItemVisible(menu, R.id.action_share_panorama, false); 192 setMenuItemVisible(menu, R.id.action_share, supportShare); 193 setMenuItemVisible(menu, R.id.action_setas, supportSetAs); 194 setMenuItemVisible(menu, R.id.action_show_on_map, supportShowOnMap); 195 setMenuItemVisible(menu, R.id.action_edit, supportEdit); 196 // setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit); 197 setMenuItemVisible(menu, R.id.action_details, supportInfo); 198 setMenuItemVisible(menu, R.id.print, supportPrint); 199 } 200 201 public static void updateMenuForPanorama(Menu menu, boolean shareAsPanorama360, 202 boolean disablePanorama360Options) { 203 setMenuItemVisible(menu, R.id.action_share_panorama, shareAsPanorama360); 204 if (disablePanorama360Options) { 205 setMenuItemVisible(menu, R.id.action_rotate_ccw, false); 206 setMenuItemVisible(menu, R.id.action_rotate_cw, false); 207 } 208 } 209 210 private static void setMenuItemVisible(Menu menu, int itemId, boolean visible) { 211 MenuItem item = menu.findItem(itemId); 212 if (item != null) item.setVisible(visible); 213 } 214 215 private Path getSingleSelectedPath() { 216 ArrayList<Path> ids = mSelectionManager.getSelected(true); 217 Utils.assertTrue(ids.size() == 1); 218 return ids.get(0); 219 } 220 221 private Intent getIntentBySingleSelectedPath(String action) { 222 DataManager manager = mActivity.getDataManager(); 223 Path path = getSingleSelectedPath(); 224 String mimeType = getMimeType(manager.getMediaType(path)); 225 return new Intent(action).setDataAndType(manager.getContentUri(path), mimeType); 226 } 227 228 private void onMenuClicked(int action, ProgressListener listener) { 229 onMenuClicked(action, listener, false, true); 230 } 231 232 public void onMenuClicked(int action, ProgressListener listener, 233 boolean waitOnStop, boolean showDialog) { 234 int title; 235 switch (action) { 236 case R.id.action_select_all: 237 if (mSelectionManager.inSelectAllMode()) { 238 mSelectionManager.deSelectAll(); 239 } else { 240 mSelectionManager.selectAll(); 241 } 242 return; 243 case R.id.action_crop: { 244 Intent intent = getIntentBySingleSelectedPath(CropActivity.CROP_ACTION); 245 ((Activity) mActivity).startActivity(intent); 246 return; 247 } 248 case R.id.action_edit: { 249 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_EDIT) 250 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 251 ((Activity) mActivity).startActivity(Intent.createChooser(intent, null)); 252 return; 253 } 254 case R.id.action_setas: { 255 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_ATTACH_DATA) 256 .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 257 intent.putExtra("mimeType", intent.getType()); 258 Activity activity = mActivity; 259 activity.startActivity(Intent.createChooser( 260 intent, activity.getString(R.string.set_as))); 261 return; 262 } 263 case R.id.action_delete: 264 title = R.string.delete; 265 break; 266 case R.id.action_rotate_cw: 267 title = R.string.rotate_right; 268 break; 269 case R.id.action_rotate_ccw: 270 title = R.string.rotate_left; 271 break; 272 case R.id.action_show_on_map: 273 title = R.string.show_on_map; 274 break; 275 default: 276 return; 277 } 278 startAction(action, title, listener, waitOnStop, showDialog); 279 } 280 281 private class ConfirmDialogListener implements OnClickListener, OnCancelListener { 282 private final int mActionId; 283 private final ProgressListener mListener; 284 285 public ConfirmDialogListener(int actionId, ProgressListener listener) { 286 mActionId = actionId; 287 mListener = listener; 288 } 289 290 @Override 291 public void onClick(DialogInterface dialog, int which) { 292 if (which == DialogInterface.BUTTON_POSITIVE) { 293 if (mListener != null) { 294 mListener.onConfirmDialogDismissed(true); 295 } 296 onMenuClicked(mActionId, mListener); 297 } else { 298 if (mListener != null) { 299 mListener.onConfirmDialogDismissed(false); 300 } 301 } 302 } 303 304 @Override 305 public void onCancel(DialogInterface dialog) { 306 if (mListener != null) { 307 mListener.onConfirmDialogDismissed(false); 308 } 309 } 310 } 311 312 public void onMenuClicked(MenuItem menuItem, String confirmMsg, 313 final ProgressListener listener) { 314 final int action = menuItem.getItemId(); 315 316 if (confirmMsg != null) { 317 if (listener != null) listener.onConfirmDialogShown(); 318 ConfirmDialogListener cdl = new ConfirmDialogListener(action, listener); 319 new AlertDialog.Builder(mActivity.getAndroidContext()) 320 .setMessage(confirmMsg) 321 .setOnCancelListener(cdl) 322 .setPositiveButton(R.string.ok, cdl) 323 .setNegativeButton(R.string.cancel, cdl) 324 .create().show(); 325 } else { 326 onMenuClicked(action, listener); 327 } 328 } 329 330 public void startAction(int action, int title, ProgressListener listener) { 331 startAction(action, title, listener, false, true); 332 } 333 334 public void startAction(int action, int title, ProgressListener listener, 335 boolean waitOnStop, boolean showDialog) { 336 ArrayList<Path> ids = mSelectionManager.getSelected(false); 337 stopTaskAndDismissDialog(); 338 339 Activity activity = mActivity; 340 if (showDialog) { 341 mDialog = createProgressDialog(activity, title, ids.size()); 342 mDialog.show(); 343 } else { 344 mDialog = null; 345 } 346 MediaOperation operation = new MediaOperation(action, ids, listener); 347 mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null); 348 mWaitOnStop = waitOnStop; 349 } 350 351 public void startSingleItemAction(int action, Path targetPath) { 352 ArrayList<Path> ids = new ArrayList<Path>(1); 353 ids.add(targetPath); 354 mDialog = null; 355 MediaOperation operation = new MediaOperation(action, ids, null); 356 mTask = mActivity.getBatchServiceThreadPoolIfAvailable().submit(operation, null); 357 mWaitOnStop = false; 358 } 359 360 public static String getMimeType(int type) { 361 switch (type) { 362 case MediaObject.MEDIA_TYPE_IMAGE : 363 return GalleryUtils.MIME_TYPE_IMAGE; 364 case MediaObject.MEDIA_TYPE_VIDEO : 365 return GalleryUtils.MIME_TYPE_VIDEO; 366 default: return GalleryUtils.MIME_TYPE_ALL; 367 } 368 } 369 370 private boolean execute( 371 DataManager manager, JobContext jc, int cmd, Path path) { 372 boolean result = true; 373 Log.v(TAG, "Execute cmd: " + cmd + " for " + path); 374 long startTime = System.currentTimeMillis(); 375 376 switch (cmd) { 377 case R.id.action_delete: 378 manager.delete(path); 379 break; 380 case R.id.action_rotate_cw: 381 manager.rotate(path, 90); 382 break; 383 case R.id.action_rotate_ccw: 384 manager.rotate(path, -90); 385 break; 386 case R.id.action_toggle_full_caching: { 387 MediaObject obj = manager.getMediaObject(path); 388 int cacheFlag = obj.getCacheFlag(); 389 if (cacheFlag == MediaObject.CACHE_FLAG_FULL) { 390 cacheFlag = MediaObject.CACHE_FLAG_SCREENNAIL; 391 } else { 392 cacheFlag = MediaObject.CACHE_FLAG_FULL; 393 } 394 obj.cache(cacheFlag); 395 break; 396 } 397 case R.id.action_show_on_map: { 398 MediaItem item = (MediaItem) manager.getMediaObject(path); 399 double latlng[] = new double[2]; 400 item.getLatLong(latlng); 401 if (GalleryUtils.isValidLocation(latlng[0], latlng[1])) { 402 GalleryUtils.showOnMap(mActivity, latlng[0], latlng[1]); 403 } 404 break; 405 } 406 default: 407 throw new AssertionError(); 408 } 409 Log.v(TAG, "It takes " + (System.currentTimeMillis() - startTime) + 410 " ms to execute cmd for " + path); 411 return result; 412 } 413 414 private class MediaOperation implements Job<Void> { 415 private final ArrayList<Path> mItems; 416 private final int mOperation; 417 private final ProgressListener mListener; 418 419 public MediaOperation(int operation, ArrayList<Path> items, 420 ProgressListener listener) { 421 mOperation = operation; 422 mItems = items; 423 mListener = listener; 424 } 425 426 @Override 427 public Void run(JobContext jc) { 428 int index = 0; 429 DataManager manager = mActivity.getDataManager(); 430 int result = EXECUTION_RESULT_SUCCESS; 431 try { 432 onProgressStart(mListener); 433 for (Path id : mItems) { 434 if (jc.isCancelled()) { 435 result = EXECUTION_RESULT_CANCEL; 436 break; 437 } 438 if (!execute(manager, jc, mOperation, id)) { 439 result = EXECUTION_RESULT_FAIL; 440 } 441 onProgressUpdate(index++, mListener); 442 } 443 } catch (Throwable th) { 444 Log.e(TAG, "failed to execute operation " + mOperation 445 + " : " + th); 446 } finally { 447 onProgressComplete(result, mListener); 448 } 449 return null; 450 } 451 } 452} 453