ActionModeHandler.java revision 67098d1a72fd04e2af06d3a5939cde28c65f70d9
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 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; 31import android.widget.PopupMenu.OnMenuItemClickListener; 32import android.widget.ShareActionProvider; 33import android.widget.ShareActionProvider.OnShareTargetSelectedListener; 34 35import 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 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 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 @Override 92 public boolean onMenuItemClick(MenuItem item) { 93 return onActionItemClicked(actionMode, item); 94 } 95 }); 96 return actionMode; 97 } 98 99 public void setTitle(String title) { 100 mSelectionMenu.setTitle(title); 101 } 102 103 public void setActionModeListener(ActionModeListener listener) { 104 mListener = listener; 105 } 106 107 @Override 108 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 109 GLRoot root = mActivity.getGLRoot(); 110 root.lockRenderThread(); 111 try { 112 boolean result; 113 // Give listener a chance to process this command before it's routed to 114 // ActionModeHandler, which handles command only based on the action id. 115 // Sometimes the listener may have more background information to handle 116 // an action command. 117 if (mListener != null) { 118 result = mListener.onActionItemClicked(item); 119 if (result) { 120 mSelectionManager.leaveSelectionMode(); 121 return result; 122 } 123 } 124 ProgressListener listener = null; 125 boolean needsConfirm = false; 126 int action = item.getItemId(); 127 if (action == R.id.action_import) { 128 listener = new ImportCompleteListener(mActivity); 129 } else if (item.getItemId() == R.id.action_delete) { 130 needsConfirm = true; 131 } 132 mMenuExecutor.onMenuClicked(item, needsConfirm, listener); 133 if (action == R.id.action_select_all) { 134 updateSupportedOperation(); 135 updateSelectionMenu(); 136 } 137 } finally { 138 root.unlockRenderThread(); 139 } 140 return true; 141 } 142 143 private void updateSelectionMenu() { 144 // update title 145 int count = mSelectionManager.getSelectedCount(); 146 String format = mActivity.getResources().getQuantityString( 147 R.plurals.number_of_items_selected, count); 148 setTitle(String.format(format, count)); 149 // For clients who call SelectionManager.selectAll() directly, we need to ensure the 150 // menu status is consistent with selection manager. 151 MenuItem item = mSelectionMenu.findItem(R.id.action_select_all); 152 if (item != null) { 153 if (mSelectionManager.inSelectAllMode()) { 154 item.setChecked(true); 155 item.setTitle(R.string.deselect_all); 156 } else { 157 item.setChecked(false); 158 item.setTitle(R.string.select_all); 159 } 160 } 161 } 162 163 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 164 MenuInflater inflater = mode.getMenuInflater(); 165 inflater.inflate(R.menu.operation, menu); 166 167 mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu); 168 OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() { 169 public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) { 170 mSelectionManager.leaveSelectionMode(); 171 return false; 172 } 173 }; 174 175 mShareActionProvider.setOnShareTargetSelectedListener(listener); 176 mMenu = menu; 177 return true; 178 } 179 180 public void onDestroyActionMode(ActionMode mode) { 181 mSelectionManager.leaveSelectionMode(); 182 } 183 184 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 185 return true; 186 } 187 188 // Menu options are determined by selection set itself. 189 // We cannot expand it because MenuExecuter executes it based on 190 // the selection set instead of the expanded result. 191 // e.g. LocalImage can be rotated but collections of them (LocalAlbum) can't. 192 private int computeMenuOptions(JobContext jc) { 193 ArrayList<Path> unexpandedPaths = mSelectionManager.getSelected(false); 194 if (unexpandedPaths.isEmpty()) { 195 // This happens when starting selection mode from overflow menu 196 // (instead of long press a media object) 197 return 0; 198 } 199 int operation = MediaObject.SUPPORT_ALL; 200 DataManager manager = mActivity.getDataManager(); 201 int type = 0; 202 for (Path path : unexpandedPaths) { 203 if (jc.isCancelled()) return 0; 204 int support = manager.getSupportedOperations(path); 205 type |= manager.getMediaType(path); 206 operation &= support; 207 } 208 209 switch (unexpandedPaths.size()) { 210 case 1: 211 final String mimeType = MenuExecutor.getMimeType(type); 212 if (!GalleryUtils.isEditorAvailable((Context) mActivity, mimeType)) { 213 operation &= ~MediaObject.SUPPORT_EDIT; 214 } 215 break; 216 default: 217 operation &= SUPPORT_MULTIPLE_MASK; 218 } 219 220 return operation; 221 } 222 223 // Share intent needs to expand the selection set so we can get URI of 224 // each media item 225 private Intent computeSharingIntent(JobContext jc) { 226 ArrayList<Path> expandedPaths = mSelectionManager.getSelected(true); 227 if (expandedPaths.size() == 0) return null; 228 final ArrayList<Uri> uris = new ArrayList<Uri>(); 229 DataManager manager = mActivity.getDataManager(); 230 int type = 0; 231 final Intent intent = new Intent(); 232 for (Path path : expandedPaths) { 233 if (jc.isCancelled()) return null; 234 int support = manager.getSupportedOperations(path); 235 type |= manager.getMediaType(path); 236 237 if ((support & MediaObject.SUPPORT_SHARE) != 0) { 238 uris.add(manager.getContentUri(path)); 239 } 240 } 241 242 final int size = uris.size(); 243 if (size > 0) { 244 final String mimeType = MenuExecutor.getMimeType(type); 245 if (size > 1) { 246 intent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(mimeType); 247 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); 248 } else { 249 intent.setAction(Intent.ACTION_SEND).setType(mimeType); 250 intent.putExtra(Intent.EXTRA_STREAM, uris.get(0)); 251 } 252 intent.setType(mimeType); 253 } 254 255 return intent; 256 } 257 258 public void updateSupportedOperation(Path path, boolean selected) { 259 // TODO: We need to improve the performance 260 updateSupportedOperation(); 261 } 262 263 public void updateSupportedOperation() { 264 // Interrupt previous unfinished task, mMenuTask is only accessed in main thread 265 if (mMenuTask != null) { 266 mMenuTask.cancel(); 267 } 268 269 // Disable share action until share intent is in good shape 270 final MenuItem item = mShareActionProvider != null ? 271 mMenu.findItem(R.id.action_share) : null; 272 final boolean supportShare = item != null; 273 if (supportShare) item.setEnabled(false); 274 275 // Generate sharing intent and update supported operations in the background 276 // The task can take a long time and be canceled in the mean time. 277 mMenuTask = mActivity.getThreadPool().submit(new Job<Void>() { 278 public Void run(final JobContext jc) { 279 // Pass1: Deal with unexpanded media object list for menu operation. 280 final int operation = computeMenuOptions(jc); 281 282 // Pass2: Deal with expanded media object list for sharing operation. 283 final Intent intent = supportShare ? computeSharingIntent(jc) : null; 284 mMainHandler.post(new Runnable() { 285 public void run() { 286 mMenuTask = null; 287 if (!jc.isCancelled()) { 288 MenuExecutor.updateMenuOperation(mMenu, operation); 289 if (supportShare) { 290 item.setEnabled(true); 291 mShareActionProvider.setShareIntent(intent); 292 } 293 } 294 } 295 }); 296 return null; 297 } 298 }); 299 } 300 301 public void pause() { 302 if (mMenuTask != null) { 303 mMenuTask.cancel(); 304 mMenuTask = null; 305 } 306 mMenuExecutor.pause(); 307 } 308 309 public void resume() { 310 if (mSelectionManager.inSelectionMode()) updateSupportedOperation(); 311 } 312} 313