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