blob: 9a66675989b0f1c6c26ed7f9d18ba91765aabefc [file] [log] [blame]
Owen Lina2fba682011-08-17 22:07:43 +08001/*
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
Ray Chen875423f2011-10-05 14:32:58 +080019import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.net.Uri;
23import android.os.Handler;
24import android.view.ActionMode;
25import android.view.LayoutInflater;
26import android.view.Menu;
27import android.view.MenuInflater;
28import android.view.MenuItem;
29import android.view.View;
30import android.widget.Button;
Ray Chen875423f2011-10-05 14:32:58 +080031import android.widget.PopupMenu.OnMenuItemClickListener;
Owen Lin73a04ff2012-03-14 17:27:24 +080032import android.widget.ShareActionProvider;
Ray Chen875423f2011-10-05 14:32:58 +080033import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
34
Owen Lina2fba682011-08-17 22:07:43 +080035import com.android.gallery3d.R;
36import com.android.gallery3d.app.GalleryActionBar;
37import com.android.gallery3d.app.GalleryActivity;
38import com.android.gallery3d.common.Utils;
39import com.android.gallery3d.data.DataManager;
40import com.android.gallery3d.data.MediaObject;
41import com.android.gallery3d.data.Path;
42import com.android.gallery3d.ui.CustomMenu.DropDownMenu;
43import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
44import com.android.gallery3d.util.Future;
45import com.android.gallery3d.util.GalleryUtils;
46import com.android.gallery3d.util.ThreadPool.Job;
47import com.android.gallery3d.util.ThreadPool.JobContext;
48
Owen Lina2fba682011-08-17 22:07:43 +080049import 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;
Ray Chen875423f2011-10-05 14:32:58 +080068 private final Handler mMainHandler;
Owen Lina2fba682011-08-17 22:07:43 +080069 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);
Ray Chenafa492c2011-09-19 12:35:51 +080089 updateSelectionMenu();
Owen Lina2fba682011-08-17 22:07:43 +080090 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();
Ray Chenafa492c2011-09-19 12:35:51 +0800122 updateSelectionMenu();
Owen Lina2fba682011-08-17 22:07:43 +0800123 }
124 return result;
125 }
126
Ray Chenafa492c2011-09-19 12:35:51 +0800127 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));
Ray Chendf483f42011-09-10 15:29:51 +0800133 // 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
Owen Lina2fba682011-08-17 22:07:43 +0800147 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);
Ray Chend2e19e12011-08-24 15:18:31 +0800152 OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() {
153 public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
154 mSelectionManager.leaveSelectionMode();
155 return false;
156 }
157 };
Owen Lina2fba682011-08-17 22:07:43 +0800158
Ray Chend2e19e12011-08-24 15:18:31 +0800159 mShareActionProvider.setOnShareTargetSelectedListener(listener);
Owen Lina2fba682011-08-17 22:07:43 +0800160 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
Ray Chen571c9e12011-09-14 15:12:29 +0800172 // 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.
Ray Chen60bcd2f2012-02-20 13:32:47 +0800176 private int computeMenuOptions(JobContext jc) {
177 ArrayList<Path> unexpandedPaths = mSelectionManager.getSelected(false);
178 if (unexpandedPaths.isEmpty()) {
179 // This happens when starting selection mode from overflow menu
180 // (instead of long press a media object)
181 return 0;
182 }
Owen Lina2fba682011-08-17 22:07:43 +0800183 int operation = MediaObject.SUPPORT_ALL;
184 DataManager manager = mActivity.getDataManager();
Owen Lina2fba682011-08-17 22:07:43 +0800185 int type = 0;
Ray Chen60bcd2f2012-02-20 13:32:47 +0800186 for (Path path : unexpandedPaths) {
187 if (jc.isCancelled()) return 0;
Owen Lina2fba682011-08-17 22:07:43 +0800188 int support = manager.getSupportedOperations(path);
189 type |= manager.getMediaType(path);
190 operation &= support;
Owen Lina2fba682011-08-17 22:07:43 +0800191 }
Owen Lina2fba682011-08-17 22:07:43 +0800192
Ray Chen60bcd2f2012-02-20 13:32:47 +0800193 switch (unexpandedPaths.size()) {
194 case 1:
195 final String mimeType = MenuExecutor.getMimeType(type);
196 if (!GalleryUtils.isEditorAvailable((Context) mActivity, mimeType)) {
197 operation &= ~MediaObject.SUPPORT_EDIT;
198 }
199 break;
200 default:
201 operation &= SUPPORT_MULTIPLE_MASK;
Owen Lina2fba682011-08-17 22:07:43 +0800202 }
203
Ray Chen60bcd2f2012-02-20 13:32:47 +0800204 return operation;
Ray Chen571c9e12011-09-14 15:12:29 +0800205 }
Owen Lina2fba682011-08-17 22:07:43 +0800206
Ray Chen571c9e12011-09-14 15:12:29 +0800207 // Share intent needs to expand the selection set so we can get URI of
208 // each media item
Ray Chen60bcd2f2012-02-20 13:32:47 +0800209 private Intent computeSharingIntent(JobContext jc) {
210 ArrayList<Path> expandedPaths = mSelectionManager.getSelected(true);
211 if (expandedPaths.size() == 0) return null;
Ray Chen571c9e12011-09-14 15:12:29 +0800212 final ArrayList<Uri> uris = new ArrayList<Uri>();
Ray Chen571c9e12011-09-14 15:12:29 +0800213 DataManager manager = mActivity.getDataManager();
214 int type = 0;
Ray Chen571c9e12011-09-14 15:12:29 +0800215 final Intent intent = new Intent();
Ray Chen60bcd2f2012-02-20 13:32:47 +0800216 for (Path path : expandedPaths) {
217 if (jc.isCancelled()) return null;
Ray Chen571c9e12011-09-14 15:12:29 +0800218 int support = manager.getSupportedOperations(path);
219 type |= manager.getMediaType(path);
220
221 if ((support & MediaObject.SUPPORT_SHARE) != 0) {
222 uris.add(manager.getContentUri(path));
223 }
224 }
225
226 final int size = uris.size();
227 if (size > 0) {
228 final String mimeType = MenuExecutor.getMimeType(type);
229 if (size > 1) {
230 intent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(mimeType);
231 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
232 } else {
233 intent.setAction(Intent.ACTION_SEND).setType(mimeType);
234 intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
235 }
236 intent.setType(mimeType);
Ray Chen571c9e12011-09-14 15:12:29 +0800237 }
Ray Chen60bcd2f2012-02-20 13:32:47 +0800238
239 return intent;
Owen Lina2fba682011-08-17 22:07:43 +0800240 }
241
242 public void updateSupportedOperation(Path path, boolean selected) {
243 // TODO: We need to improve the performance
244 updateSupportedOperation();
245 }
246
247 public void updateSupportedOperation() {
Ray Chen60bcd2f2012-02-20 13:32:47 +0800248 // Interrupt previous unfinished task, mMenuTask is only accessed in main thread
Owen Lina2fba682011-08-17 22:07:43 +0800249 if (mMenuTask != null) {
250 mMenuTask.cancel();
251 }
252
253 // Disable share action until share intent is in good shape
Ray Chen60bcd2f2012-02-20 13:32:47 +0800254 final MenuItem item = mShareActionProvider != null ?
255 mMenu.findItem(R.id.action_share) : null;
256 final boolean supportShare = item != null;
257 if (supportShare) item.setEnabled(false);
Owen Lina2fba682011-08-17 22:07:43 +0800258
259 // Generate sharing intent and update supported operations in the background
Ray Chen60bcd2f2012-02-20 13:32:47 +0800260 // The task can take a long time and be canceled in the mean time.
Owen Lina2fba682011-08-17 22:07:43 +0800261 mMenuTask = mActivity.getThreadPool().submit(new Job<Void>() {
Ray Chen60bcd2f2012-02-20 13:32:47 +0800262 public Void run(final JobContext jc) {
263 // Pass1: Deal with unexpanded media object list for menu operation.
264 final int operation = computeMenuOptions(jc);
265
266 // Pass2: Deal with expanded media object list for sharing operation.
267 final Intent intent = supportShare ? computeSharingIntent(jc) : null;
268 mMainHandler.post(new Runnable() {
269 public void run() {
270 mMenuTask = null;
271 if (!jc.isCancelled()) {
272 MenuExecutor.updateMenuOperation(mMenu, operation);
273 if (supportShare) {
274 item.setEnabled(true);
275 mShareActionProvider.setShareIntent(intent);
276 }
277 }
278 }
279 });
Owen Lina2fba682011-08-17 22:07:43 +0800280 return null;
281 }
282 });
283 }
284
285 public void pause() {
286 if (mMenuTask != null) {
287 mMenuTask.cancel();
288 mMenuTask = null;
289 }
290 mMenuExecutor.pause();
291 }
292
293 public void resume() {
Ray Chen875423f2011-10-05 14:32:58 +0800294 if (mSelectionManager.inSelectionMode()) updateSupportedOperation();
Owen Lina2fba682011-08-17 22:07:43 +0800295 }
296}