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