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 
17 package com.android.photos;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.provider.MediaStore.Files.FileColumns;
25 import android.util.SparseBooleanArray;
26 import android.view.ActionMode;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.widget.AbsListView.MultiChoiceModeListener;
31 import android.widget.ShareActionProvider;
32 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
33 
34 import com.android.gallery3d.R;
35 import com.android.gallery3d.app.TrimVideo;
36 import com.android.gallery3d.data.MediaObject;
37 import com.android.gallery3d.filtershow.FilterShowActivity;
38 import com.android.gallery3d.filtershow.crop.CropActivity;
39 import com.android.gallery3d.util.GalleryUtils;
40 
41 import java.util.ArrayList;
42 import java.util.List;
43 
44 public class MultiChoiceManager implements MultiChoiceModeListener,
45     OnShareTargetSelectedListener, SelectionManager.SelectedUriSource {
46 
47     public interface Provider {
getMultiChoiceManager()48         public MultiChoiceManager getMultiChoiceManager();
49     }
50 
51     public interface Delegate {
getSelectedItemPositions()52         public SparseBooleanArray getSelectedItemPositions();
getSelectedItemCount()53         public int getSelectedItemCount();
getItemMediaType(Object item)54         public int getItemMediaType(Object item);
getItemSupportedOperations(Object item)55         public int getItemSupportedOperations(Object item);
getSubItemUrisForItem(Object item)56         public ArrayList<Uri> getSubItemUrisForItem(Object item);
getItemUri(Object item)57         public Uri getItemUri(Object item);
getItemAtPosition(int position)58         public Object getItemAtPosition(int position);
getPathForItemAtPosition(int position)59         public Object getPathForItemAtPosition(int position);
deleteItemWithPath(Object itemPath)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 
MultiChoiceManager(Activity activity)71     public MultiChoiceManager(Activity activity) {
72         mContext = activity;
73         mSelectionManager = new SelectionManager(activity);
74     }
75 
setDelegate(Delegate delegate)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
getSelectedShareableUris()87     public ArrayList<Uri> getSelectedShareableUris() {
88         return mSelectedShareableUrisArray;
89     }
90 
updateSelectedTitle(ActionMode mode)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 
getItemMimetype(Object item)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
onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked)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 
updateActionItemVisibilities(Menu menu, int supportedOperations)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 
141         editItem.setVisible((supportedOperations & MediaObject.SUPPORT_EDIT) > 0);
142         deleteItem.setVisible((supportedOperations & MediaObject.SUPPORT_DELETE) > 0);
143         shareItem.setVisible((supportedOperations & MediaObject.SUPPORT_SHARE) > 0);
144         cropItem.setVisible((supportedOperations & MediaObject.SUPPORT_CROP) > 0);
145         trimItem.setVisible((supportedOperations & MediaObject.SUPPORT_TRIM) > 0);
146         muteItem.setVisible((supportedOperations & MediaObject.SUPPORT_MUTE) > 0);
147     }
148 
149     @Override
onCreateActionMode(ActionMode mode, Menu menu)150     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
151         mSelectionManager.setSelectedUriSource(this);
152         mActionMode = mode;
153         MenuInflater inflater = mode.getMenuInflater();
154         inflater.inflate(R.menu.gallery_multiselect, menu);
155         MenuItem menuItem = menu.findItem(R.id.menu_share);
156         mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
157         mShareActionProvider.setOnShareTargetSelectedListener(this);
158         updateSelectedTitle(mode);
159         return true;
160     }
161 
162     @Override
onDestroyActionMode(ActionMode mode)163     public void onDestroyActionMode(ActionMode mode) {
164         // onDestroyActionMode gets called when the share target was selected,
165         // but apparently before the ArrayList is serialized in the intent
166         // so we can't clear the old one here.
167         mSelectedShareableUrisArray = new ArrayList<Uri>();
168         mSelectionManager.onClearSelection();
169         mSelectionManager.setSelectedUriSource(null);
170         mShareActionProvider = null;
171         mActionMode = null;
172     }
173 
174     @Override
onPrepareActionMode(ActionMode mode, Menu menu)175     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
176         updateSelectedTitle(mode);
177         return false;
178     }
179 
180     @Override
onShareTargetSelected(ShareActionProvider provider, Intent intent)181     public boolean onShareTargetSelected(ShareActionProvider provider, Intent intent) {
182         mActionMode.finish();
183         return false;
184     }
185 
186     private static class BulkDeleteTask extends AsyncTask<Void, Void, Void> {
187         private Delegate mDelegate;
188         private List<Object> mPaths;
189 
BulkDeleteTask(Delegate delegate, List<Object> paths)190         public BulkDeleteTask(Delegate delegate, List<Object> paths) {
191             mDelegate = delegate;
192             mPaths = paths;
193         }
194 
195         @Override
doInBackground(Void... ignored)196         protected Void doInBackground(Void... ignored) {
197             for (Object path : mPaths) {
198                 mDelegate.deleteItemWithPath(path);
199             }
200             return null;
201         }
202     }
203 
204     @Override
onActionItemClicked(ActionMode mode, MenuItem item)205     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
206         int actionItemId = item.getItemId();
207         switch (actionItemId) {
208             case R.id.menu_delete:
209                 BulkDeleteTask deleteTask = new BulkDeleteTask(mDelegate,
210                         getPathsForSelectedItems());
211                 deleteTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
212                 mode.finish();
213                 return true;
214             case R.id.menu_edit:
215             case R.id.menu_crop:
216             case R.id.menu_trim:
217             case R.id.menu_mute:
218                 singleItemAction(getSelectedItem(), actionItemId);
219                 mode.finish();
220                 return true;
221             default:
222                 return false;
223         }
224     }
225 
singleItemAction(Object item, int actionItemId)226     private void singleItemAction(Object item, int actionItemId) {
227         Intent intent = new Intent();
228         String mime = getItemMimetype(item);
229         Uri uri = mDelegate.getItemUri(item);
230         switch (actionItemId) {
231             case R.id.menu_edit:
232                 intent.setDataAndType(uri, mime)
233                       .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
234                       .setAction(Intent.ACTION_EDIT);
235                 mContext.startActivity(Intent.createChooser(intent, null));
236                 return;
237             case R.id.menu_crop:
238                 intent.setDataAndType(uri, mime)
239                       .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
240                       .setAction(CropActivity.CROP_ACTION)
241                       .setClass(mContext, FilterShowActivity.class);
242                 mContext.startActivity(intent);
243                 return;
244             case R.id.menu_trim:
245                 intent.setData(uri)
246                       .setClass(mContext, TrimVideo.class);
247                 mContext.startActivity(intent);
248                 return;
249             case R.id.menu_mute:
250                 /* TODO need a way to get the file path of an item
251                 MuteVideo muteVideo = new MuteVideo(filePath,
252                         uri, (Activity) mContext);
253                 muteVideo.muteInBackground();
254                 */
255                 return;
256             default:
257                 return;
258         }
259     }
260 
getPathsForSelectedItems()261     private List<Object> getPathsForSelectedItems() {
262         List<Object> paths = new ArrayList<Object>();
263         SparseBooleanArray selected = mDelegate.getSelectedItemPositions();
264         for (int i = 0; i < selected.size(); i++) {
265             if (selected.valueAt(i)) {
266                 paths.add(mDelegate.getPathForItemAtPosition(i));
267             }
268         }
269         return paths;
270     }
271 
getSelectedItem()272     public Object getSelectedItem() {
273         if (mDelegate.getSelectedItemCount() != 1) {
274             return null;
275         }
276         SparseBooleanArray selected = mDelegate.getSelectedItemPositions();
277         for (int i = 0; i < selected.size(); i++) {
278             if (selected.valueAt(i)) {
279                 return mDelegate.getItemAtPosition(selected.keyAt(i));
280             }
281         }
282         return null;
283     }
284 }
285