Home | History | Annotate | Download | only in ui
      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