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.CropImage;
     34 import com.android.gallery3d.app.GalleryActivity;
     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.util.Future;
     41 import com.android.gallery3d.util.GalleryUtils;
     42 import com.android.gallery3d.util.ThreadPool.Job;
     43 import com.android.gallery3d.util.ThreadPool.JobContext;
     44 
     45 import java.util.ArrayList;
     46 
     47 public 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;
     61     // wait the operation to finish when we want to stop it.
     62     private boolean mWaitOnStop;
     63 
     64     private final GalleryActivity mActivity;
     65     private final SelectionManager mSelectionManager;
     66     private final Handler mHandler;
     67 
     68     private static ProgressDialog createProgressDialog(
     69             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         }
     78         return dialog;
     79     }
     80 
     81     public interface ProgressListener {
     82         public void onConfirmDialogShown();
     83         public void onConfirmDialogDismissed(boolean confirmed);
     84         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: {
     97                         stopTaskAndDismissDialog();
     98                         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 
    122     private void stopTaskAndDismissDialog() {
    123         if (mTask != null) {
    124             if (!mWaitOnStop) mTask.cancel();
    125             mTask.waitDone();
    126             mDialog.dismiss();
    127             mDialog = null;
    128             mTask = null;
    129         }
    130     }
    131 
    132     public void pause() {
    133         stopTaskAndDismissDialog();
    134     }
    135 
    136     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 
    145     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 
    181     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 
    188     private void onMenuClicked(int action, ProgressListener listener) {
    189         onMenuClicked(action, listener, false, true);
    190     }
    191 
    192     public void onMenuClicked(int action, ProgressListener listener,
    193             boolean waitOnStop, boolean showDialog) {
    194         int title;
    195         switch (action) {
    196             case R.id.action_select_all:
    197                 if (mSelectionManager.inSelectAllMode()) {
    198                     mSelectionManager.deSelectAll();
    199                 } else {
    200                     mSelectionManager.selectAll();
    201                 }
    202                 return;
    203             case R.id.action_crop: {
    204                 Intent intent = getIntentBySingleSelectedPath(CropImage.ACTION_CROP);
    205                 ((Activity) mActivity).startActivity(intent);
    206                 return;
    207             }
    208             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             }
    214             case R.id.action_setas: {
    215                 Intent intent = getIntentBySingleSelectedPath(Intent.ACTION_ATTACH_DATA)
    216                         .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    217                 intent.putExtra("mimeType", intent.getType());
    218                 Activity activity = (Activity) mActivity;
    219                 activity.startActivity(Intent.createChooser(
    220                         intent, activity.getString(R.string.set_as)));
    221                 return;
    222             }
    223             case R.id.action_delete:
    224                 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;
    235             case R.id.action_import:
    236                 title = R.string.Import;
    237                 break;
    238             default:
    239                 return;
    240         }
    241         startAction(action, title, listener, waitOnStop, showDialog);
    242     }
    243 
    244     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 
    275     public void onMenuClicked(MenuItem menuItem, String confirmMsg,
    276             final ProgressListener listener) {
    277         final int action = menuItem.getItemId();
    278 
    279         if (confirmMsg != null) {
    280             if (listener != null) listener.onConfirmDialogShown();
    281             ConfirmDialogListener cdl = new ConfirmDialogListener(action, listener);
    282             new AlertDialog.Builder(mActivity.getAndroidContext())
    283                     .setMessage(confirmMsg)
    284                     .setOnCancelListener(cdl)
    285                     .setPositiveButton(R.string.ok, cdl)
    286                     .setNegativeButton(R.string.cancel, cdl)
    287                     .create().show();
    288         } else {
    289             onMenuClicked(action, listener);
    290         }
    291     }
    292 
    293     public void startAction(int action, int title, ProgressListener listener) {
    294         startAction(action, title, listener, false, true);
    295     }
    296 
    297     public void startAction(int action, int title, ProgressListener listener,
    298             boolean waitOnStop, boolean showDialog) {
    299         ArrayList<Path> ids = mSelectionManager.getSelected(false);
    300         stopTaskAndDismissDialog();
    301 
    302         Activity activity = (Activity) mActivity;
    303         mDialog = createProgressDialog(activity, title, ids.size());
    304         if (showDialog) {
    305             mDialog.show();
    306         }
    307         MediaOperation operation = new MediaOperation(action, ids, listener);
    308         mTask = mActivity.getThreadPool().submit(operation, null);
    309         mWaitOnStop = waitOnStop;
    310     }
    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;
    325         Log.v(TAG, "Execute cmd: " + cmd + " for " + path);
    326         long startTime = System.currentTimeMillis();
    327 
    328         switch (cmd) {
    329             case R.id.action_delete:
    330                 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             }
    363             default:
    364                 throw new AssertionError();
    365         }
    366         Log.v(TAG, "It takes " + (System.currentTimeMillis() - startTime) +
    367                 " ms to execute cmd for " + path);
    368         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 
    376         public MediaOperation(int operation, ArrayList<Path> items,
    377                 ProgressListener listener) {
    378             mOperation = operation;
    379             mItems = items;
    380             mListener = listener;
    381         }
    382 
    383         public Void run(JobContext jc) {
    384             int index = 0;
    385             DataManager manager = mActivity.getDataManager();
    386             int result = EXECUTION_RESULT_SUCCESS;
    387             try {
    388                 for (Path id : mItems) {
    389                     if (jc.isCancelled()) {
    390                         result = EXECUTION_RESULT_CANCEL;
    391                         break;
    392                     }
    393                     if (!execute(manager, jc, mOperation, id)) {
    394                         result = EXECUTION_RESULT_FAIL;
    395                     }
    396                     onProgressUpdate(index++, mListener);
    397                 }
    398             } catch (Throwable th) {
    399                 Log.e(TAG, "failed to execute operation " + mOperation
    400                         + " : " + th);
    401             } finally {
    402                onProgressComplete(result, mListener);
    403             }
    404             return null;
    405         }
    406     }
    407 }
    408