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