• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.documentsui;
18 
19 import android.app.Fragment;
20 import android.view.KeyboardShortcutGroup;
21 import android.view.Menu;
22 import android.view.MenuInflater;
23 import android.view.MenuItem;
24 import android.view.View;
25 
26 import com.android.documentsui.base.DocumentInfo;
27 import com.android.documentsui.base.Menus;
28 import com.android.documentsui.base.RootInfo;
29 import com.android.documentsui.base.State;
30 import com.android.documentsui.dirlist.DirectoryFragment;
31 import com.android.documentsui.queries.SearchViewManager;
32 import com.android.documentsui.sidebar.RootsFragment;
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import java.util.List;
36 import java.util.function.IntFunction;
37 
38 public abstract class MenuManager {
39 
40     final protected SearchViewManager mSearchManager;
41     final protected State mState;
42     final protected DirectoryDetails mDirDetails;
43 
MenuManager( SearchViewManager searchManager, State displayState, DirectoryDetails dirDetails)44     public MenuManager(
45             SearchViewManager searchManager,
46             State displayState,
47             DirectoryDetails dirDetails) {
48         mSearchManager = searchManager;
49         mState = displayState;
50         mDirDetails = dirDetails;
51     }
52 
53     /** @see ActionModeController */
updateActionMenu(Menu menu, SelectionDetails selection)54     public void updateActionMenu(Menu menu, SelectionDetails selection) {
55         updateOpenInActionMode(menu.findItem(R.id.action_menu_open), selection);
56         updateOpenWith(menu.findItem(R.id.action_menu_open_with), selection);
57         updateDelete(menu.findItem(R.id.action_menu_delete), selection);
58         updateShare(menu.findItem(R.id.action_menu_share), selection);
59         updateRename(menu.findItem(R.id.action_menu_rename), selection);
60         updateSelectAll(menu.findItem(R.id.action_menu_select_all));
61         updateMoveTo(menu.findItem(R.id.action_menu_move_to), selection);
62         updateCopyTo(menu.findItem(R.id.action_menu_copy_to), selection);
63         updateCompress(menu.findItem(R.id.action_menu_compress), selection);
64         updateExtractTo(menu.findItem(R.id.action_menu_extract_to), selection);
65         updateInspect(menu.findItem(R.id.action_menu_inspect), selection);
66         updateViewInOwner(menu.findItem(R.id.action_menu_view_in_owner), selection);
67 
68         Menus.disableHiddenItems(menu);
69     }
70 
71     /** @see BaseActivity#onPrepareOptionsMenu */
updateOptionMenu(Menu menu)72     public void updateOptionMenu(Menu menu) {
73         updateCreateDir(menu.findItem(R.id.option_menu_create_dir));
74         updateSettings(menu.findItem(R.id.option_menu_settings));
75         updateSelectAll(menu.findItem(R.id.option_menu_select_all));
76         updateNewWindow(menu.findItem(R.id.option_menu_new_window));
77         updateModePicker(menu.findItem(R.id.option_menu_grid),
78                 menu.findItem(R.id.option_menu_list));
79         updateAdvanced(menu.findItem(R.id.option_menu_advanced));
80         updateDebug(menu.findItem(R.id.option_menu_debug));
81         updateInspect(menu.findItem(R.id.option_menu_inspect));
82         Menus.disableHiddenItems(menu);
83     }
84 
85     /**
86      * Called when we needs {@link MenuManager} to ask Android to show context menu for us.
87      * {@link MenuManager} can choose to defeat this request.
88      *
89      * {@link #inflateContextMenuForDocs} and {@link #inflateContextMenuForContainer} are called
90      * afterwards when Android asks us to provide the content of context menus, so they're not
91      * correct locations to suppress context menus.
92      */
showContextMenu(Fragment f, View v, float x, float y)93     public void showContextMenu(Fragment f, View v, float x, float y) {
94         // Pickers don't have any context menu at this moment.
95     }
96 
inflateContextMenuForContainer(Menu menu, MenuInflater inflater)97     public void inflateContextMenuForContainer(Menu menu, MenuInflater inflater) {
98         throw new UnsupportedOperationException("Pickers don't allow context menu.");
99     }
100 
inflateContextMenuForDocs( Menu menu, MenuInflater inflater, SelectionDetails selectionDetails)101     public void inflateContextMenuForDocs(
102             Menu menu, MenuInflater inflater, SelectionDetails selectionDetails) {
103         throw new UnsupportedOperationException("Pickers don't allow context menu.");
104     }
105 
106     /**
107      * @see DirectoryFragment#onCreateContextMenu
108      *
109      * Called when user tries to generate a context menu anchored to a file when the selection
110      * doesn't contain any folder.
111      *
112      * @param selectionDetails
113      *      containsFiles may return false because this may be called when user right clicks on an
114      *      unselectable item in pickers
115      */
116     @VisibleForTesting
updateContextMenuForFiles(Menu menu, SelectionDetails selectionDetails)117     public void updateContextMenuForFiles(Menu menu, SelectionDetails selectionDetails) {
118         assert selectionDetails != null;
119 
120         MenuItem share = menu.findItem(R.id.dir_menu_share);
121         MenuItem open = menu.findItem(R.id.dir_menu_open);
122         MenuItem openWith = menu.findItem(R.id.dir_menu_open_with);
123         MenuItem rename = menu.findItem(R.id.dir_menu_rename);
124         MenuItem viewInOwner = menu.findItem(R.id.dir_menu_view_in_owner);
125 
126         updateShare(share, selectionDetails);
127         updateOpenInContextMenu(open, selectionDetails);
128         updateOpenWith(openWith, selectionDetails);
129         updateRename(rename, selectionDetails);
130         updateViewInOwner(viewInOwner, selectionDetails);
131 
132         updateContextMenu(menu, selectionDetails);
133     }
134 
135     /**
136      * @see DirectoryFragment#onCreateContextMenu
137      *
138      * Called when user tries to generate a context menu anchored to a folder when the selection
139      * doesn't contain any file.
140      *
141      * @param selectionDetails
142      *      containDirectories may return false because this may be called when user right clicks on
143      *      an unselectable item in pickers
144      */
145     @VisibleForTesting
updateContextMenuForDirs(Menu menu, SelectionDetails selectionDetails)146     public void updateContextMenuForDirs(Menu menu, SelectionDetails selectionDetails) {
147         assert selectionDetails != null;
148 
149         MenuItem openInNewWindow = menu.findItem(R.id.dir_menu_open_in_new_window);
150         MenuItem rename = menu.findItem(R.id.dir_menu_rename);
151         MenuItem pasteInto = menu.findItem(R.id.dir_menu_paste_into_folder);
152 
153         updateOpenInNewWindow(openInNewWindow, selectionDetails);
154         updateRename(rename, selectionDetails);
155         updatePasteInto(pasteInto, selectionDetails);
156 
157         updateContextMenu(menu, selectionDetails);
158     }
159 
160     /**
161      * @see DirectoryFragment#onCreateContextMenu
162      *
163      * Update shared context menu items of both files and folders context menus.
164      */
165     @VisibleForTesting
updateContextMenu(Menu menu, SelectionDetails selectionDetails)166     public void updateContextMenu(Menu menu, SelectionDetails selectionDetails) {
167         assert selectionDetails != null;
168 
169         MenuItem cut = menu.findItem(R.id.dir_menu_cut_to_clipboard);
170         MenuItem copy = menu.findItem(R.id.dir_menu_copy_to_clipboard);
171         MenuItem delete = menu.findItem(R.id.dir_menu_delete);
172         MenuItem inspect = menu.findItem(R.id.dir_menu_inspect);
173 
174         final boolean canCopy =
175                 selectionDetails.size() > 0 && !selectionDetails.containsPartialFiles();
176         final boolean canDelete = selectionDetails.canDelete();
177         cut.setEnabled(canCopy && canDelete);
178         copy.setEnabled(canCopy);
179         delete.setEnabled(canDelete);
180 
181         inspect.setEnabled(selectionDetails.size() == 1);
182     }
183 
184     /**
185      * @see DirectoryFragment#onCreateContextMenu
186      *
187      * Called when user tries to generate a context menu anchored to an empty pane.
188      */
189     @VisibleForTesting
updateContextMenuForContainer(Menu menu)190     public void updateContextMenuForContainer(Menu menu) {
191         MenuItem paste = menu.findItem(R.id.dir_menu_paste_from_clipboard);
192         MenuItem selectAll = menu.findItem(R.id.dir_menu_select_all);
193         MenuItem createDir = menu.findItem(R.id.dir_menu_create_dir);
194         MenuItem inspect = menu.findItem(R.id.dir_menu_inspect);
195 
196         paste.setEnabled(mDirDetails.hasItemsToPaste() && mDirDetails.canCreateDoc());
197         updateSelectAll(selectAll);
198         updateCreateDir(createDir);
199         updateInspect(inspect);
200     }
201 
202     /**
203      * @see RootsFragment#onCreateContextMenu
204      */
updateRootContextMenu(Menu menu, RootInfo root, DocumentInfo docInfo)205     public void updateRootContextMenu(Menu menu, RootInfo root, DocumentInfo docInfo) {
206         MenuItem eject = menu.findItem(R.id.root_menu_eject_root);
207         MenuItem pasteInto = menu.findItem(R.id.root_menu_paste_into_folder);
208         MenuItem openInNewWindow = menu.findItem(R.id.root_menu_open_in_new_window);
209         MenuItem settings = menu.findItem(R.id.root_menu_settings);
210 
211         updateEject(eject, root);
212         updatePasteInto(pasteInto, root, docInfo);
213         updateOpenInNewWindow(openInNewWindow, root);
214         updateSettings(settings, root);
215     }
216 
updateKeyboardShortcutsMenu( List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier)217     public abstract void updateKeyboardShortcutsMenu(
218             List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier);
219 
updateModePicker(MenuItem grid, MenuItem list)220     protected void updateModePicker(MenuItem grid, MenuItem list) {
221         grid.setVisible(mState.derivedMode != State.MODE_GRID);
222         list.setVisible(mState.derivedMode != State.MODE_LIST);
223     }
224 
updateAdvanced(MenuItem advanced)225     protected void updateAdvanced(MenuItem advanced) {
226         advanced.setVisible(mState.showDeviceStorageOption);
227         advanced.setTitle(mState.showDeviceStorageOption && mState.showAdvanced
228                 ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
229     }
230 
updateDebug(MenuItem debug)231     protected void updateDebug(MenuItem debug) {
232         debug.setVisible(mState.debugMode);
233     }
234 
updateSettings(MenuItem settings)235     protected void updateSettings(MenuItem settings) {
236         settings.setVisible(false);
237     }
238 
updateSettings(MenuItem settings, RootInfo root)239     protected void updateSettings(MenuItem settings, RootInfo root) {
240         settings.setVisible(false);
241     }
242 
updateEject(MenuItem eject, RootInfo root)243     protected void updateEject(MenuItem eject, RootInfo root) {
244         eject.setVisible(false);
245     }
246 
updateNewWindow(MenuItem newWindow)247     protected void updateNewWindow(MenuItem newWindow) {
248         newWindow.setVisible(false);
249     }
250 
updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails)251     protected void updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails) {
252         open.setVisible(false);
253     }
254 
updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails)255     protected void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) {
256         openWith.setVisible(false);
257     }
258 
updateOpenInNewWindow( MenuItem openInNewWindow, SelectionDetails selectionDetails)259     protected void updateOpenInNewWindow(
260             MenuItem openInNewWindow, SelectionDetails selectionDetails) {
261         openInNewWindow.setVisible(false);
262     }
263 
updateOpenInNewWindow( MenuItem openInNewWindow, RootInfo root)264     protected void updateOpenInNewWindow(
265             MenuItem openInNewWindow, RootInfo root) {
266         openInNewWindow.setVisible(false);
267     }
268 
updateShare(MenuItem share, SelectionDetails selectionDetails)269     protected void updateShare(MenuItem share, SelectionDetails selectionDetails) {
270         share.setVisible(false);
271     }
272 
updateDelete(MenuItem delete, SelectionDetails selectionDetails)273     protected void updateDelete(MenuItem delete, SelectionDetails selectionDetails) {
274         delete.setVisible(false);
275     }
276 
updateRename(MenuItem rename, SelectionDetails selectionDetails)277     protected void updateRename(MenuItem rename, SelectionDetails selectionDetails) {
278         rename.setVisible(false);
279     }
280 
281     /**
282      * This method is called for standard activity option menu as opposed
283      * to when there is a selection.
284      */
updateInspect(MenuItem inspector)285     protected void updateInspect(MenuItem inspector) {
286         inspector.setVisible(false);
287     }
288 
289     /**
290      * This method is called for action mode, when a selection exists.
291      */
updateInspect(MenuItem inspect, SelectionDetails selectionDetails)292     protected void updateInspect(MenuItem inspect, SelectionDetails selectionDetails) {
293         inspect.setVisible(false);
294     }
295 
updateViewInOwner(MenuItem view, SelectionDetails selectionDetails)296     protected void updateViewInOwner(MenuItem view, SelectionDetails selectionDetails) {
297         view.setVisible(false);
298     }
299 
updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails)300     protected void updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails) {
301         moveTo.setVisible(false);
302     }
303 
updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails)304     protected void updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails) {
305         copyTo.setVisible(false);
306     }
307 
updateCompress(MenuItem compress, SelectionDetails selectionDetails)308     protected void updateCompress(MenuItem compress, SelectionDetails selectionDetails) {
309         compress.setVisible(false);
310     }
311 
updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails)312     protected void updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails) {
313         extractTo.setVisible(false);
314     }
315 
updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails)316     protected void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) {
317         pasteInto.setVisible(false);
318     }
319 
updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo)320     protected void updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo) {
321         pasteInto.setVisible(false);
322     }
323 
updateOpenInContextMenu( MenuItem open, SelectionDetails selectionDetails)324     protected abstract void updateOpenInContextMenu(
325             MenuItem open, SelectionDetails selectionDetails);
updateSelectAll(MenuItem selectAll)326     protected abstract void updateSelectAll(MenuItem selectAll);
updateCreateDir(MenuItem createDir)327     protected abstract void updateCreateDir(MenuItem createDir);
328 
329     /**
330      * Access to meta data about the selection.
331      */
332     public interface SelectionDetails {
containsDirectories()333         boolean containsDirectories();
334 
containsFiles()335         boolean containsFiles();
336 
size()337         int size();
338 
containsPartialFiles()339         boolean containsPartialFiles();
340 
containsFilesInArchive()341         boolean containsFilesInArchive();
342 
343         // TODO: Update these to express characteristics instead of answering concrete questions,
344         // since the answer to those questions is (or can be) activity specific.
canDelete()345         boolean canDelete();
346 
canRename()347         boolean canRename();
348 
canPasteInto()349         boolean canPasteInto();
350 
canExtract()351         boolean canExtract();
352 
canOpenWith()353         boolean canOpenWith();
354 
canViewInOwner()355         boolean canViewInOwner();
356     }
357 
358     public static class DirectoryDetails {
359         private final BaseActivity mActivity;
360 
DirectoryDetails(BaseActivity activity)361         public DirectoryDetails(BaseActivity activity) {
362             mActivity = activity;
363         }
364 
hasRootSettings()365         public boolean hasRootSettings() {
366             return mActivity.getCurrentRoot().hasSettings();
367         }
368 
hasItemsToPaste()369         public boolean hasItemsToPaste() {
370             return false;
371         }
372 
canCreateDoc()373         public boolean canCreateDoc() {
374             return isInRecents() ? false : mActivity.getCurrentDirectory().isCreateSupported();
375         }
376 
isInRecents()377         public boolean isInRecents() {
378             return mActivity.getCurrentDirectory() == null;
379         }
380 
canCreateDirectory()381         public boolean canCreateDirectory() {
382             return mActivity.canCreateDirectory();
383         }
384     }
385 }
386