• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.gallery3d.ui;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.nfc.NfcAdapter;
24 import android.os.Handler;
25 import android.view.ActionMode;
26 import android.view.LayoutInflater;
27 import android.view.Menu;
28 import android.view.MenuInflater;
29 import android.view.MenuItem;
30 import android.view.View;
31 import android.widget.Button;
32 import android.widget.PopupMenu.OnMenuItemClickListener;
33 import android.widget.ShareActionProvider;
34 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
35 
36 import com.android.gallery3d.R;
37 import com.android.gallery3d.app.GalleryActionBar;
38 import com.android.gallery3d.app.GalleryActivity;
39 import com.android.gallery3d.common.Utils;
40 import com.android.gallery3d.data.DataManager;
41 import com.android.gallery3d.data.MediaObject;
42 import com.android.gallery3d.data.Path;
43 import com.android.gallery3d.ui.CustomMenu.DropDownMenu;
44 import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
45 import com.android.gallery3d.util.Future;
46 import com.android.gallery3d.util.GalleryUtils;
47 import com.android.gallery3d.util.ThreadPool.Job;
48 import com.android.gallery3d.util.ThreadPool.JobContext;
49 
50 import java.util.ArrayList;
51 
52 public class ActionModeHandler implements ActionMode.Callback {
53     private static final String TAG = "ActionModeHandler";
54     private static final int SUPPORT_MULTIPLE_MASK = MediaObject.SUPPORT_DELETE
55             | MediaObject.SUPPORT_ROTATE | MediaObject.SUPPORT_SHARE
56             | MediaObject.SUPPORT_CACHE | MediaObject.SUPPORT_IMPORT;
57 
58     public interface ActionModeListener {
onActionItemClicked(MenuItem item)59         public boolean onActionItemClicked(MenuItem item);
60     }
61 
62     private final GalleryActivity mActivity;
63     private final MenuExecutor mMenuExecutor;
64     private final SelectionManager mSelectionManager;
65     private final NfcAdapter mNfcAdapter;
66     private Menu mMenu;
67     private DropDownMenu mSelectionMenu;
68     private ActionModeListener mListener;
69     private Future<?> mMenuTask;
70     private final Handler mMainHandler;
71     private ShareActionProvider mShareActionProvider;
72 
ActionModeHandler( GalleryActivity activity, SelectionManager selectionManager)73     public ActionModeHandler(
74             GalleryActivity activity, SelectionManager selectionManager) {
75         mActivity = Utils.checkNotNull(activity);
76         mSelectionManager = Utils.checkNotNull(selectionManager);
77         mMenuExecutor = new MenuExecutor(activity, selectionManager);
78         mMainHandler = new Handler(activity.getMainLooper());
79         mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity.getAndroidContext());
80     }
81 
startActionMode()82     public ActionMode startActionMode() {
83         Activity a = (Activity) mActivity;
84         final ActionMode actionMode = a.startActionMode(this);
85         CustomMenu customMenu = new CustomMenu(a);
86         View customView = LayoutInflater.from(a).inflate(
87                 R.layout.action_mode, null);
88         actionMode.setCustomView(customView);
89         mSelectionMenu = customMenu.addDropDownMenu(
90                 (Button) customView.findViewById(R.id.selection_menu),
91                 R.menu.selection);
92         updateSelectionMenu();
93         customMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
94             @Override
95             public boolean onMenuItemClick(MenuItem item) {
96                 return onActionItemClicked(actionMode, item);
97             }
98         });
99         return actionMode;
100     }
101 
setTitle(String title)102     public void setTitle(String title) {
103         mSelectionMenu.setTitle(title);
104     }
105 
setActionModeListener(ActionModeListener listener)106     public void setActionModeListener(ActionModeListener listener) {
107         mListener = listener;
108     }
109 
110     @Override
onActionItemClicked(ActionMode mode, MenuItem item)111     public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
112         GLRoot root = mActivity.getGLRoot();
113         root.lockRenderThread();
114         try {
115             boolean result;
116             // Give listener a chance to process this command before it's routed to
117             // ActionModeHandler, which handles command only based on the action id.
118             // Sometimes the listener may have more background information to handle
119             // an action command.
120             if (mListener != null) {
121                 result = mListener.onActionItemClicked(item);
122                 if (result) {
123                     mSelectionManager.leaveSelectionMode();
124                     return result;
125                 }
126             }
127             ProgressListener listener = null;
128             String confirmMsg = null;
129             int action = item.getItemId();
130             if (action == R.id.action_import) {
131                 listener = new ImportCompleteListener(mActivity);
132             } else if (item.getItemId() == R.id.action_delete) {
133                 confirmMsg = mActivity.getResources().getQuantityString(
134                         R.plurals.delete_selection, mSelectionManager.getSelectedCount());
135             }
136             mMenuExecutor.onMenuClicked(item, confirmMsg, listener);
137             if (action == R.id.action_select_all) {
138                 updateSupportedOperation();
139                 updateSelectionMenu();
140             }
141         } finally {
142             root.unlockRenderThread();
143         }
144         return true;
145     }
146 
updateSelectionMenu()147     private void updateSelectionMenu() {
148         // update title
149         int count = mSelectionManager.getSelectedCount();
150         String format = mActivity.getResources().getQuantityString(
151                 R.plurals.number_of_items_selected, count);
152         setTitle(String.format(format, count));
153         // For clients who call SelectionManager.selectAll() directly, we need to ensure the
154         // menu status is consistent with selection manager.
155         MenuItem item = mSelectionMenu.findItem(R.id.action_select_all);
156         if (item != null) {
157             if (mSelectionManager.inSelectAllMode()) {
158                 item.setChecked(true);
159                 item.setTitle(R.string.deselect_all);
160             } else {
161                 item.setChecked(false);
162                 item.setTitle(R.string.select_all);
163             }
164         }
165     }
166 
onCreateActionMode(ActionMode mode, Menu menu)167     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
168         MenuInflater inflater = mode.getMenuInflater();
169         inflater.inflate(R.menu.operation, menu);
170 
171         mShareActionProvider = GalleryActionBar.initializeShareActionProvider(menu);
172         OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() {
173             public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
174                 mSelectionManager.leaveSelectionMode();
175                 return false;
176             }
177         };
178 
179         mShareActionProvider.setOnShareTargetSelectedListener(listener);
180         mMenu = menu;
181         return true;
182     }
183 
onDestroyActionMode(ActionMode mode)184     public void onDestroyActionMode(ActionMode mode) {
185         mSelectionManager.leaveSelectionMode();
186     }
187 
onPrepareActionMode(ActionMode mode, Menu menu)188     public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
189         return true;
190     }
191 
192     // Menu options are determined by selection set itself.
193     // We cannot expand it because MenuExecuter executes it based on
194     // the selection set instead of the expanded result.
195     // e.g. LocalImage can be rotated but collections of them (LocalAlbum) can't.
computeMenuOptions(JobContext jc)196     private int computeMenuOptions(JobContext jc) {
197         ArrayList<Path> unexpandedPaths = mSelectionManager.getSelected(false);
198         if (unexpandedPaths.isEmpty()) {
199             // This happens when starting selection mode from overflow menu
200             // (instead of long press a media object)
201             return 0;
202         }
203         int operation = MediaObject.SUPPORT_ALL;
204         DataManager manager = mActivity.getDataManager();
205         int type = 0;
206         for (Path path : unexpandedPaths) {
207             if (jc.isCancelled()) return 0;
208             int support = manager.getSupportedOperations(path);
209             type |= manager.getMediaType(path);
210             operation &= support;
211         }
212 
213         switch (unexpandedPaths.size()) {
214             case 1:
215                 final String mimeType = MenuExecutor.getMimeType(type);
216                 if (!GalleryUtils.isEditorAvailable((Context) mActivity, mimeType)) {
217                     operation &= ~MediaObject.SUPPORT_EDIT;
218                 }
219                 break;
220             default:
221                 operation &= SUPPORT_MULTIPLE_MASK;
222         }
223 
224         return operation;
225     }
226 
227     // Share intent needs to expand the selection set so we can get URI of
228     // each media item
computeSharingIntent(JobContext jc)229     private Intent computeSharingIntent(JobContext jc) {
230         ArrayList<Path> expandedPaths = mSelectionManager.getSelected(true);
231         if (expandedPaths.size() == 0) {
232             if (mNfcAdapter != null) {
233                 mNfcAdapter.setBeamPushUris(null, (Activity)mActivity);
234             }
235             return null;
236         }
237         final ArrayList<Uri> uris = new ArrayList<Uri>();
238         DataManager manager = mActivity.getDataManager();
239         int type = 0;
240         final Intent intent = new Intent();
241         for (Path path : expandedPaths) {
242             if (jc.isCancelled()) return null;
243             int support = manager.getSupportedOperations(path);
244             type |= manager.getMediaType(path);
245 
246             if ((support & MediaObject.SUPPORT_SHARE) != 0) {
247                 uris.add(manager.getContentUri(path));
248             }
249         }
250 
251         final int size = uris.size();
252         if (size > 0) {
253             final String mimeType = MenuExecutor.getMimeType(type);
254             if (size > 1) {
255                 intent.setAction(Intent.ACTION_SEND_MULTIPLE).setType(mimeType);
256                 intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
257             } else {
258                 intent.setAction(Intent.ACTION_SEND).setType(mimeType);
259                 intent.putExtra(Intent.EXTRA_STREAM, uris.get(0));
260             }
261             intent.setType(mimeType);
262             if (mNfcAdapter != null) {
263                 mNfcAdapter.setBeamPushUris(uris.toArray(new Uri[uris.size()]),
264                         (Activity)mActivity);
265             }
266         } else {
267             if (mNfcAdapter != null) {
268                 mNfcAdapter.setBeamPushUris(null, (Activity)mActivity);
269             }
270         }
271 
272         return intent;
273     }
274 
updateSupportedOperation(Path path, boolean selected)275     public void updateSupportedOperation(Path path, boolean selected) {
276         // TODO: We need to improve the performance
277         updateSupportedOperation();
278     }
279 
updateSupportedOperation()280     public void updateSupportedOperation() {
281         // Interrupt previous unfinished task, mMenuTask is only accessed in main thread
282         if (mMenuTask != null) {
283             mMenuTask.cancel();
284         }
285 
286         updateSelectionMenu();
287 
288         // Disable share action until share intent is in good shape
289         final MenuItem item = mShareActionProvider != null ?
290                 mMenu.findItem(R.id.action_share) : null;
291         final boolean supportShare = item != null;
292         if (supportShare) item.setEnabled(false);
293 
294         // Generate sharing intent and update supported operations in the background
295         // The task can take a long time and be canceled in the mean time.
296         mMenuTask = mActivity.getThreadPool().submit(new Job<Void>() {
297             public Void run(final JobContext jc) {
298                 // Pass1: Deal with unexpanded media object list for menu operation.
299                 final int operation = computeMenuOptions(jc);
300 
301                 // Pass2: Deal with expanded media object list for sharing operation.
302                 final Intent intent = supportShare ? computeSharingIntent(jc) : null;
303                 mMainHandler.post(new Runnable() {
304                     public void run() {
305                         mMenuTask = null;
306                         if (!jc.isCancelled()) {
307                             MenuExecutor.updateMenuOperation(mMenu, operation);
308                             if (supportShare) {
309                                 item.setEnabled(true);
310                                 mShareActionProvider.setShareIntent(intent);
311                             }
312                         }
313                     }
314                 });
315                 return null;
316             }
317         });
318     }
319 
pause()320     public void pause() {
321         if (mMenuTask != null) {
322             mMenuTask.cancel();
323             mMenuTask = null;
324         }
325         mMenuExecutor.pause();
326     }
327 
resume()328     public void resume() {
329         if (mSelectionManager.inSelectionMode()) updateSupportedOperation();
330     }
331 }
332