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.content.Context;
     21 import android.content.Intent;
     22 import android.net.Uri;
     23 import android.os.Handler;
     24 import android.view.ActionMode;
     25 import android.view.LayoutInflater;
     26 import android.view.Menu;
     27 import android.view.MenuInflater;
     28 import android.view.MenuItem;
     29 import android.view.View;
     30 import android.widget.Button;
     31 import android.widget.ShareActionProvider;
     32 import android.widget.PopupMenu.OnMenuItemClickListener;
     33 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
     34 
     35 import com.android.gallery3d.R;
     36 import com.android.gallery3d.app.GalleryActionBar;
     37 import com.android.gallery3d.app.GalleryActivity;
     38 import com.android.gallery3d.common.Utils;
     39 import com.android.gallery3d.data.DataManager;
     40 import com.android.gallery3d.data.MediaObject;
     41 import com.android.gallery3d.data.Path;
     42 import com.android.gallery3d.ui.CustomMenu.DropDownMenu;
     43 import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
     44 import com.android.gallery3d.util.Future;
     45 import com.android.gallery3d.util.GalleryUtils;
     46 import com.android.gallery3d.util.ThreadPool.Job;
     47 import com.android.gallery3d.util.ThreadPool.JobContext;
     48 
     49 import java.util.ArrayList;
     50 
     51 public class ActionModeHandler implements ActionMode.Callback {
     52     private static final String TAG = "ActionModeHandler";
     53     private static final int SUPPORT_MULTIPLE_MASK = MediaObject.SUPPORT_DELETE
     54             | MediaObject.SUPPORT_ROTATE | MediaObject.SUPPORT_SHARE
     55             | MediaObject.SUPPORT_CACHE | MediaObject.SUPPORT_IMPORT;
     56 
     57     public interface ActionModeListener {
     58         public boolean onActionItemClicked(MenuItem item);
     59     }
     60 
     61     private final GalleryActivity mActivity;
     62     private final MenuExecutor mMenuExecutor;
     63     private final SelectionManager mSelectionManager;
     64     private Menu mMenu;
     65     private DropDownMenu mSelectionMenu;
     66     private ActionModeListener mListener;
     67     private Future<?> mMenuTask;
     68     private final Handler mMainHandler;
     69     private ShareActionProvider mShareActionProvider;
     70 
     71     public ActionModeHandler(
     72             GalleryActivity activity, SelectionManager selectionManager) {
     73         mActivity = Utils.checkNotNull(activity);
     74         mSelectionManager = Utils.checkNotNull(selectionManager);
     75         mMenuExecutor = new MenuExecutor(activity, selectionManager);
     76         mMainHandler = new Handler(activity.getMainLooper());
     77     }
     78 
     79     public ActionMode startActionMode() {
     80         Activity a = (Activity) mActivity;
     81         final ActionMode actionMode = a.startActionMode(this);
     82         CustomMenu customMenu = new CustomMenu(a);
     83         View customView = LayoutInflater.from(a).inflate(
     84                 R.layout.action_mode, null);
     85         actionMode.setCustomView(customView);
     86         mSelectionMenu = customMenu.addDropDownMenu(
     87                 (Button) customView.findViewById(R.id.selection_menu),
     88                 R.menu.selection);
     89         updateSelectionMenu();
     90         customMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
     91             public boolean onMenuItemClick(MenuItem item) {
     92                 return onActionItemClicked(actionMode, item);
     93             }
     94         });
     95         return actionMode;
     96     }
     97 
     98     public void setTitle(String title) {
     99         mSelectionMenu.setTitle(title);
    100     }
    101 
    102     public void setActionModeListener(ActionModeListener listener) {
    103         mListener = listener;
    104     }
    105 
    106     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    107         boolean result;
    108         if (mListener != null) {
    109             result = mListener.onActionItemClicked(item);
    110             if (result) {
    111                 mSelectionManager.leaveSelectionMode();
    112                 return result;
    113             }
    114         }
    115         ProgressListener listener = null;
    116         if (item.getItemId() == R.id.action_import) {
    117             listener = new ImportCompleteListener(mActivity);
    118         }
    119         result = mMenuExecutor.onMenuClicked(item, listener);
    120         if (item.getItemId() == R.id.action_select_all) {
    121             updateSupportedOperation();
    122             updateSelectionMenu();
    123         }
    124         return result;
    125     }
    126 
    127     private void updateSelectionMenu() {
    128         // update title
    129         int count = mSelectionManager.getSelectedCount();
    130         String format = mActivity.getResources().getQuantityString(
    131                 R.plurals.number_of_items_selected, count);
    132         setTitle(String.format(format, count));
    133         // For clients who call SelectionManager.selectAll() directly, we need to ensure the
    134         // menu status is consistent with selection manager.
    135         MenuItem item = mSelectionMenu.findItem(R.id.action_select_all);
    136         if (item != null) {
    137             if (mSelectionManager.inSelectAllMode()) {
    138                 item.setChecked(true);
    139                 item.setTitle(R.string.deselect_all);
    140             } else {
    141                 item.setChecked(false);
    142                 item.setTitle(R.string.select_all);
    143             }
    144         }
    145     }
    146 
    147     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    148         MenuInflater inflater = mode.getMenuInflater();
    149         inflater.inflate(R.menu.operation, menu);
    150 
    151         mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu);
    152         OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() {
    153             public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
    154                 mSelectionManager.leaveSelectionMode();
    155                 return false;
    156             }
    157         };
    158 
    159         mShareActionProvider.setOnShareTargetSelectedListener(listener);
    160         mMenu = menu;
    161         return true;
    162     }
    163 
    164     public void onDestroyActionMode(ActionMode mode) {
    165         mSelectionManager.leaveSelectionMode();
    166     }
    167 
    168     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    169         return true;
    170     }
    171 
    172     // Menu options are determined by selection set itself.
    173     // We cannot expand it because MenuExecuter executes it based on
    174     // the selection set instead of the expanded result.
    175     // e.g. LocalImage can be rotated but collections of them (LocalAlbum) can't.
    176     private void updateMenuOptions(JobContext jc) {
    177         ArrayList<Path> paths = mSelectionManager.getSelected(false);
    178 
    179         int operation = MediaObject.SUPPORT_ALL;
    180         DataManager manager = mActivity.getDataManager();
    181         int type = 0;
    182         for (Path path : paths) {
    183             if (jc.isCancelled()) return;
    184             int support = manager.getSupportedOperations(path);
    185             type |= manager.getMediaType(path);
    186             operation &= support;
    187         }
    188 
    189         final String mimeType = MenuExecutor.getMimeType(type);
    190         if (paths.size() == 0) {
    191             operation = 0;
    192         } else if (paths.size() == 1) {
    193             if (!GalleryUtils.isEditorAvailable((Context) mActivity, mimeType)) {
    194                 operation &= ~MediaObject.SUPPORT_EDIT;
    195             }
    196         } else {
    197             operation &= SUPPORT_MULTIPLE_MASK;
    198         }
    199 
    200         final int supportedOperation = operation;
    201 
    202         mMainHandler.post(new Runnable() {
    203             @Override
    204             public void run() {
    205                 mMenuTask = null;
    206                 MenuExecutor.updateMenuOperation(mMenu, supportedOperation);
    207             }
    208         });
    209     }
    210 
    211     // Share intent needs to expand the selection set so we can get URI of
    212     // each media item
    213     private void updateSharingIntent(JobContext jc) {
    214         if (mShareActionProvider == null) return;
    215         ArrayList<Path> paths = mSelectionManager.getSelected(true);
    216         if (paths.size() == 0) return;
    217 
    218         final ArrayList<Uri> uris = new ArrayList<Uri>();
    219 
    220         DataManager manager = mActivity.getDataManager();
    221         int type = 0;
    222 
    223         final Intent intent = new Intent();
    224         for (Path path : paths) {
    225             int support = manager.getSupportedOperations(path);
    226             type |= manager.getMediaType(path);
    227 
    228             if ((support & MediaObject.SUPPORT_SHARE) != 0) {
    229                 uris.add(manager.getContentUri(path));
    230             }
    231         }
    232 
    233         final int size = uris.size();
    234         if (size > 0) {
    235             final String mimeType = MenuExecutor.getMimeType(type);
    236             if (size > 1) {
    237                 intent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(mimeType);
    238                 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
    239             } else {
    240                 intent.setAction(Intent.ACTION_SEND).setType(mimeType);
    241                 intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
    242             }
    243             intent.setType(mimeType);
    244 
    245             mMainHandler.post(new Runnable() {
    246                 @Override
    247                 public void run() {
    248                     Log.v(TAG, "Sharing intent is ready: action = " + intent.getAction());
    249                     mShareActionProvider.setShareIntent(intent);
    250                 }
    251             });
    252         }
    253     }
    254 
    255     public void updateSupportedOperation(Path path, boolean selected) {
    256         // TODO: We need to improve the performance
    257         updateSupportedOperation();
    258     }
    259 
    260     public void updateSupportedOperation() {
    261         if (mMenuTask != null) {
    262             mMenuTask.cancel();
    263         }
    264 
    265         // Disable share action until share intent is in good shape
    266         if (mShareActionProvider != null) {
    267             Log.v(TAG, "Disable sharing until intent is ready");
    268             mShareActionProvider.setShareIntent(null);
    269         }
    270 
    271         // Generate sharing intent and update supported operations in the background
    272         mMenuTask = mActivity.getThreadPool().submit(new Job<Void>() {
    273             public Void run(JobContext jc) {
    274                 updateMenuOptions(jc);
    275                 updateSharingIntent(jc);
    276                 return null;
    277             }
    278         });
    279     }
    280 
    281     public void pause() {
    282         if (mMenuTask != null) {
    283             mMenuTask.cancel();
    284             mMenuTask = null;
    285         }
    286         mMenuExecutor.pause();
    287     }
    288 
    289     public void resume() {
    290         if (mSelectionManager.inSelectionMode()) updateSupportedOperation();
    291     }
    292 }
    293