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