ActionModeHandler.java revision 9b54e8c645d14748b7dd75e25b4db9e97e3415a9
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
17package com.android.gallery3d.ui;
18
19import com.android.gallery3d.R;
20import com.android.gallery3d.app.GalleryActionBar;
21import com.android.gallery3d.app.GalleryActivity;
22import com.android.gallery3d.common.Utils;
23import com.android.gallery3d.data.DataManager;
24import com.android.gallery3d.data.MediaObject;
25import com.android.gallery3d.data.Path;
26import com.android.gallery3d.ui.CustomMenu.DropDownMenu;
27import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
28import com.android.gallery3d.util.Future;
29import com.android.gallery3d.util.GalleryUtils;
30import com.android.gallery3d.util.ThreadPool.Job;
31import com.android.gallery3d.util.ThreadPool.JobContext;
32
33import android.app.Activity;
34import android.content.Context;
35import android.content.Intent;
36import android.net.Uri;
37import android.os.Handler;
38import android.view.ActionMode;
39import android.view.LayoutInflater;
40import android.view.Menu;
41import android.view.MenuInflater;
42import android.view.MenuItem;
43import android.view.View;
44import android.widget.Button;
45import android.widget.PopupMenu.OnMenuItemClickListener;
46import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
47import android.widget.ShareActionProvider;
48
49import java.util.ArrayList;
50
51public 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 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        if (paths.size() == 0) return;
179
180        int operation = MediaObject.SUPPORT_ALL;
181        DataManager manager = mActivity.getDataManager();
182        int type = 0;
183        for (Path path : paths) {
184            if (jc.isCancelled()) return;
185            int support = manager.getSupportedOperations(path);
186            type |= manager.getMediaType(path);
187            operation &= support;
188        }
189
190        final String mimeType = MenuExecutor.getMimeType(type);
191        if (paths.size() == 1) {
192            if (!GalleryUtils.isEditorAvailable((Context) mActivity, mimeType)) {
193                operation &= ~MediaObject.SUPPORT_EDIT;
194            }
195        } else {
196            operation &= SUPPORT_MULTIPLE_MASK;
197        }
198
199        final int supportedOperation = operation;
200
201        mMainHandler.post(new Runnable() {
202            @Override
203            public void run() {
204                mMenuTask = null;
205                MenuExecutor.updateMenuOperation(mMenu, supportedOperation);
206            }
207        });
208    }
209
210    // Share intent needs to expand the selection set so we can get URI of
211    // each media item
212    private void updateSharingIntent(JobContext jc) {
213        if (mShareActionProvider == null) return;
214        ArrayList<Path> paths = mSelectionManager.getSelected(true);
215        if (paths.size() == 0) return;
216
217        final ArrayList<Uri> uris = new ArrayList<Uri>();
218
219        DataManager manager = mActivity.getDataManager();
220        int type = 0;
221
222        final Intent intent = new Intent();
223        for (Path path : paths) {
224            int support = manager.getSupportedOperations(path);
225            type |= manager.getMediaType(path);
226
227            if ((support & MediaObject.SUPPORT_SHARE) != 0) {
228                uris.add(manager.getContentUri(path));
229            }
230        }
231
232        final int size = uris.size();
233        if (size > 0) {
234            final String mimeType = MenuExecutor.getMimeType(type);
235            if (size > 1) {
236                intent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(mimeType);
237                intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
238            } else {
239                intent.setAction(Intent.ACTION_SEND).setType(mimeType);
240                intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
241            }
242            intent.setType(mimeType);
243
244            mMainHandler.post(new Runnable() {
245                @Override
246                public void run() {
247                    Log.v(TAG, "Sharing intent is ready: action = " + intent.getAction());
248                    mShareActionProvider.setShareIntent(intent);
249                }
250            });
251        }
252    }
253
254    public void updateSupportedOperation(Path path, boolean selected) {
255        // TODO: We need to improve the performance
256        updateSupportedOperation();
257    }
258
259    public void updateSupportedOperation() {
260        if (mMenuTask != null) {
261            mMenuTask.cancel();
262        }
263
264        // Disable share action until share intent is in good shape
265        if (mShareActionProvider != null) {
266            Log.v(TAG, "Disable sharing until intent is ready");
267            mShareActionProvider.setShareIntent(null);
268        }
269
270        // Generate sharing intent and update supported operations in the background
271        mMenuTask = mActivity.getThreadPool().submit(new Job<Void>() {
272            public Void run(JobContext jc) {
273                updateMenuOptions(jc);
274                updateSharingIntent(jc);
275                return null;
276            }
277        });
278    }
279
280    public void pause() {
281        if (mMenuTask != null) {
282            mMenuTask.cancel();
283            mMenuTask = null;
284        }
285        mMenuExecutor.pause();
286    }
287
288    public void resume() {
289        updateSupportedOperation();
290    }
291}
292