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