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         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
onCreateActionMode(ActionMode mode, Menu menu)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
onDestroyActionMode(ActionMode mode)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
onPrepareActionMode(ActionMode mode, Menu menu)177     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
178         updateSelectedTitle(mode);
179         return false;
180     }
181 
182     @Override
onShareTargetSelected(ShareActionProvider provider, Intent intent)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 
BulkDeleteTask(Delegate delegate, List<Object> paths)192         public BulkDeleteTask(Delegate delegate, List<Object> paths) {
193             mDelegate = delegate;
194             mPaths = paths;
195         }
196 
197         @Override
doInBackground(Void... ignored)198         protected Void doInBackground(Void... ignored) {
199             for (Object path : mPaths) {
200                 mDelegate.deleteItemWithPath(path);
201             }
202             return null;
203         }
204     }
205 
206     @Override
onActionItemClicked(ActionMode mode, MenuItem item)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 
singleItemAction(Object item, int actionItemId)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 
getPathsForSelectedItems()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 
getSelectedItem()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