1/* 2 * Copyright (C) 2013 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.photos; 18 19import android.app.Activity; 20import android.content.Context; 21import android.content.Intent; 22import android.net.Uri; 23import android.os.AsyncTask; 24import android.provider.MediaStore.Files.FileColumns; 25import android.util.SparseBooleanArray; 26import android.view.ActionMode; 27import android.view.Menu; 28import android.view.MenuInflater; 29import android.view.MenuItem; 30import android.widget.AbsListView.MultiChoiceModeListener; 31import android.widget.ShareActionProvider; 32import android.widget.ShareActionProvider.OnShareTargetSelectedListener; 33 34import com.android.gallery3d.R; 35import com.android.gallery3d.app.TrimVideo; 36import com.android.gallery3d.data.MediaObject; 37import com.android.gallery3d.filtershow.FilterShowActivity; 38import com.android.gallery3d.filtershow.crop.CropActivity; 39import com.android.gallery3d.util.GalleryUtils; 40 41import java.util.ArrayList; 42import java.util.List; 43 44public class MultiChoiceManager implements MultiChoiceModeListener, 45 OnShareTargetSelectedListener, SelectionManager.SelectedUriSource { 46 47 public interface Provider { 48 public MultiChoiceManager getMultiChoiceManager(); 49 } 50 51 public interface Delegate { 52 public SparseBooleanArray getSelectedItemPositions(); 53 public int getSelectedItemCount(); 54 public int getItemMediaType(Object item); 55 public int getItemSupportedOperations(Object item); 56 public ArrayList<Uri> getSubItemUrisForItem(Object item); 57 public Uri getItemUri(Object item); 58 public Object getItemAtPosition(int position); 59 public Object getPathForItemAtPosition(int position); 60 public void deleteItemWithPath(Object itemPath); 61 } 62 63 private SelectionManager mSelectionManager; 64 private ShareActionProvider mShareActionProvider; 65 private ActionMode mActionMode; 66 private Context mContext; 67 private Delegate mDelegate; 68 69 private ArrayList<Uri> mSelectedShareableUrisArray = new ArrayList<Uri>(); 70 71 public MultiChoiceManager(Activity activity) { 72 mContext = activity; 73 mSelectionManager = new SelectionManager(activity); 74 } 75 76 public void setDelegate(Delegate delegate) { 77 if (mDelegate == delegate) { 78 return; 79 } 80 if (mActionMode != null) { 81 mActionMode.finish(); 82 } 83 mDelegate = delegate; 84 } 85 86 @Override 87 public ArrayList<Uri> getSelectedShareableUris() { 88 return mSelectedShareableUrisArray; 89 } 90 91 private void updateSelectedTitle(ActionMode mode) { 92 int count = mDelegate.getSelectedItemCount(); 93 mode.setTitle(mContext.getResources().getQuantityString( 94 R.plurals.number_of_items_selected, count, count)); 95 } 96 97 private String getItemMimetype(Object item) { 98 int type = mDelegate.getItemMediaType(item); 99 if (type == FileColumns.MEDIA_TYPE_IMAGE) { 100 return GalleryUtils.MIME_TYPE_IMAGE; 101 } else if (type == FileColumns.MEDIA_TYPE_VIDEO) { 102 return GalleryUtils.MIME_TYPE_VIDEO; 103 } else { 104 return GalleryUtils.MIME_TYPE_ALL; 105 } 106 } 107 108 @Override 109 public void onItemCheckedStateChanged(ActionMode mode, int position, long id, 110 boolean checked) { 111 updateSelectedTitle(mode); 112 Object item = mDelegate.getItemAtPosition(position); 113 114 int supported = mDelegate.getItemSupportedOperations(item); 115 116 if ((supported & MediaObject.SUPPORT_SHARE) > 0) { 117 ArrayList<Uri> subItems = mDelegate.getSubItemUrisForItem(item); 118 if (checked) { 119 mSelectedShareableUrisArray.addAll(subItems); 120 } else { 121 mSelectedShareableUrisArray.removeAll(subItems); 122 } 123 } 124 125 mSelectionManager.onItemSelectedStateChanged(mShareActionProvider, 126 mDelegate.getItemMediaType(item), 127 supported, 128 checked); 129 updateActionItemVisibilities(mode.getMenu(), 130 mSelectionManager.getSupportedOperations()); 131 } 132 133 private void updateActionItemVisibilities(Menu menu, int supportedOperations) { 134 MenuItem editItem = menu.findItem(R.id.menu_edit); 135 MenuItem deleteItem = menu.findItem(R.id.menu_delete); 136 MenuItem shareItem = menu.findItem(R.id.menu_share); 137 MenuItem cropItem = menu.findItem(R.id.menu_crop); 138 MenuItem trimItem = menu.findItem(R.id.menu_trim); 139 MenuItem muteItem = menu.findItem(R.id.menu_mute); 140 MenuItem setAsItem = menu.findItem(R.id.menu_set_as); 141 142 editItem.setVisible((supportedOperations & MediaObject.SUPPORT_EDIT) > 0); 143 deleteItem.setVisible((supportedOperations & MediaObject.SUPPORT_DELETE) > 0); 144 shareItem.setVisible((supportedOperations & MediaObject.SUPPORT_SHARE) > 0); 145 cropItem.setVisible((supportedOperations & MediaObject.SUPPORT_CROP) > 0); 146 trimItem.setVisible((supportedOperations & MediaObject.SUPPORT_TRIM) > 0); 147 muteItem.setVisible((supportedOperations & MediaObject.SUPPORT_MUTE) > 0); 148 setAsItem.setVisible((supportedOperations & MediaObject.SUPPORT_SETAS) > 0); 149 } 150 151 @Override 152 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 153 mSelectionManager.setSelectedUriSource(this); 154 mActionMode = mode; 155 MenuInflater inflater = mode.getMenuInflater(); 156 inflater.inflate(R.menu.gallery_multiselect, menu); 157 MenuItem menuItem = menu.findItem(R.id.menu_share); 158 mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider(); 159 mShareActionProvider.setOnShareTargetSelectedListener(this); 160 updateSelectedTitle(mode); 161 return true; 162 } 163 164 @Override 165 public void onDestroyActionMode(ActionMode mode) { 166 // onDestroyActionMode gets called when the share target was selected, 167 // but apparently before the ArrayList is serialized in the intent 168 // so we can't clear the old one here. 169 mSelectedShareableUrisArray = new ArrayList<Uri>(); 170 mSelectionManager.onClearSelection(); 171 mSelectionManager.setSelectedUriSource(null); 172 mShareActionProvider = null; 173 mActionMode = null; 174 } 175 176 @Override 177 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 178 updateSelectedTitle(mode); 179 return false; 180 } 181 182 @Override 183 public boolean onShareTargetSelected(ShareActionProvider provider, Intent intent) { 184 mActionMode.finish(); 185 return false; 186 } 187 188 private static class BulkDeleteTask extends AsyncTask<Void, Void, Void> { 189 private Delegate mDelegate; 190 private List<Object> mPaths; 191 192 public BulkDeleteTask(Delegate delegate, List<Object> paths) { 193 mDelegate = delegate; 194 mPaths = paths; 195 } 196 197 @Override 198 protected Void doInBackground(Void... ignored) { 199 for (Object path : mPaths) { 200 mDelegate.deleteItemWithPath(path); 201 } 202 return null; 203 } 204 } 205 206 @Override 207 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 208 int actionItemId = item.getItemId(); 209 switch (actionItemId) { 210 case R.id.menu_delete: 211 BulkDeleteTask deleteTask = new BulkDeleteTask(mDelegate, 212 getPathsForSelectedItems()); 213 deleteTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 214 mode.finish(); 215 return true; 216 case R.id.menu_edit: 217 case R.id.menu_crop: 218 case R.id.menu_trim: 219 case R.id.menu_mute: 220 case R.id.menu_set_as: 221 singleItemAction(getSelectedItem(), actionItemId); 222 mode.finish(); 223 return true; 224 default: 225 return false; 226 } 227 } 228 229 private void singleItemAction(Object item, int actionItemId) { 230 Intent intent = new Intent(); 231 String mime = getItemMimetype(item); 232 Uri uri = mDelegate.getItemUri(item); 233 switch (actionItemId) { 234 case R.id.menu_edit: 235 intent.setDataAndType(uri, mime) 236 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 237 .setAction(Intent.ACTION_EDIT); 238 mContext.startActivity(Intent.createChooser(intent, null)); 239 return; 240 case R.id.menu_crop: 241 intent.setDataAndType(uri, mime) 242 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 243 .setAction(CropActivity.CROP_ACTION) 244 .setClass(mContext, FilterShowActivity.class); 245 mContext.startActivity(intent); 246 return; 247 case R.id.menu_trim: 248 intent.setData(uri) 249 .setClass(mContext, TrimVideo.class); 250 mContext.startActivity(intent); 251 return; 252 case R.id.menu_mute: 253 /* TODO need a way to get the file path of an item 254 MuteVideo muteVideo = new MuteVideo(filePath, 255 uri, (Activity) mContext); 256 muteVideo.muteInBackground(); 257 */ 258 return; 259 case R.id.menu_set_as: 260 intent.setDataAndType(uri, mime) 261 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 262 .setAction(Intent.ACTION_ATTACH_DATA) 263 .putExtra("mimeType", mime); 264 mContext.startActivity(Intent.createChooser( 265 intent, mContext.getString(R.string.set_as))); 266 return; 267 default: 268 return; 269 } 270 } 271 272 private List<Object> getPathsForSelectedItems() { 273 List<Object> paths = new ArrayList<Object>(); 274 SparseBooleanArray selected = mDelegate.getSelectedItemPositions(); 275 for (int i = 0; i < selected.size(); i++) { 276 if (selected.valueAt(i)) { 277 paths.add(mDelegate.getPathForItemAtPosition(i)); 278 } 279 } 280 return paths; 281 } 282 283 public Object getSelectedItem() { 284 if (mDelegate.getSelectedItemCount() != 1) { 285 return null; 286 } 287 SparseBooleanArray selected = mDelegate.getSelectedItemPositions(); 288 for (int i = 0; i < selected.size(); i++) { 289 if (selected.valueAt(i)) { 290 return mDelegate.getItemAtPosition(selected.keyAt(i)); 291 } 292 } 293 return null; 294 } 295} 296