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.files;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.net.Uri;
22 import android.view.KeyEvent;
23 import android.view.KeyboardShortcutGroup;
24 import android.view.KeyboardShortcutInfo;
25 import android.view.Menu;
26 import android.view.MenuInflater;
27 import android.view.MenuItem;
28 import android.view.View;
29 
30 import androidx.fragment.app.Fragment;
31 import androidx.recyclerview.selection.SelectionTracker;
32 
33 import com.android.documentsui.R;
34 import com.android.documentsui.base.DocumentInfo;
35 import com.android.documentsui.base.Features;
36 import com.android.documentsui.base.Lookup;
37 import com.android.documentsui.base.LookupApplicationName;
38 import com.android.documentsui.base.Menus;
39 import com.android.documentsui.base.RootInfo;
40 import com.android.documentsui.base.Shared;
41 import com.android.documentsui.base.State;
42 import com.android.documentsui.base.UserId;
43 import com.android.documentsui.queries.SearchViewManager;
44 
45 import java.util.List;
46 import java.util.function.IntFunction;
47 import java.util.function.IntSupplier;
48 
49 public final class MenuManager extends com.android.documentsui.MenuManager {
50 
51     private final Features mFeatures;
52     private final Context mContext;
53     private final SelectionTracker<String> mSelectionManager;
54     private final Lookup<String, Uri> mUriLookup;
55     private final LookupApplicationName mAppNameLookup;
56 
MenuManager( Features features, SearchViewManager searchManager, State displayState, DirectoryDetails dirDetails, Context context, SelectionTracker<String> selectionManager, LookupApplicationName appNameLookup, Lookup<String, Uri> uriLookup, IntSupplier filesCountSupplier)57     public MenuManager(
58             Features features,
59             SearchViewManager searchManager,
60             State displayState,
61             DirectoryDetails dirDetails,
62             Context context,
63             SelectionTracker<String> selectionManager,
64             LookupApplicationName appNameLookup,
65             Lookup<String, Uri> uriLookup,
66             IntSupplier filesCountSupplier) {
67 
68         super(searchManager, displayState, dirDetails, filesCountSupplier);
69 
70         mFeatures = features;
71         mContext = context;
72         mSelectionManager = selectionManager;
73         mAppNameLookup = appNameLookup;
74         mUriLookup = uriLookup;
75     }
76 
77     @Override
updateKeyboardShortcutsMenu( List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier)78     public void updateKeyboardShortcutsMenu(
79             List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier) {
80         KeyboardShortcutGroup group = new KeyboardShortcutGroup(
81                 stringSupplier.apply(R.string.app_label));
82         group.addItem(new KeyboardShortcutInfo(
83                 stringSupplier.apply(R.string.menu_cut_to_clipboard), KeyEvent.KEYCODE_X,
84                 KeyEvent.META_CTRL_ON));
85         group.addItem(new KeyboardShortcutInfo(
86                 stringSupplier.apply(R.string.menu_copy_to_clipboard), KeyEvent.KEYCODE_C,
87                 KeyEvent.META_CTRL_ON));
88         group.addItem(new KeyboardShortcutInfo(
89                 stringSupplier.apply(R.string.menu_paste_from_clipboard), KeyEvent.KEYCODE_V,
90                 KeyEvent.META_CTRL_ON));
91         group.addItem(new KeyboardShortcutInfo(
92                 stringSupplier.apply(R.string.menu_create_dir), KeyEvent.KEYCODE_E,
93                 KeyEvent.META_CTRL_ON));
94         group.addItem(new KeyboardShortcutInfo(
95                 stringSupplier.apply(R.string.menu_select_all), KeyEvent.KEYCODE_A,
96                 KeyEvent.META_CTRL_ON));
97         group.addItem(new KeyboardShortcutInfo(
98                 stringSupplier.apply(R.string.menu_new_window), KeyEvent.KEYCODE_N,
99                 KeyEvent.META_CTRL_ON));
100         data.add(group);
101     }
102 
103     @Override
showContextMenu(Fragment f, View v, float x, float y)104     public void showContextMenu(Fragment f, View v, float x, float y) {
105         // Register context menu here so long-press doesn't trigger this context floating menu.
106         f.registerForContextMenu(v);
107         v.showContextMenu(x, y);
108         f.unregisterForContextMenu(v);
109     }
110 
111     @Override
inflateContextMenuForContainer( Menu menu, MenuInflater inflater, SelectionDetails selectionDetails)112     public void inflateContextMenuForContainer(
113             Menu menu, MenuInflater inflater, SelectionDetails selectionDetails) {
114         inflater.inflate(R.menu.container_context_menu, menu);
115         updateContextMenuForContainer(menu, selectionDetails);
116     }
117 
118     @Override
inflateContextMenuForDocs( Menu menu, MenuInflater inflater, SelectionDetails selectionDetails)119     public void inflateContextMenuForDocs(
120             Menu menu, MenuInflater inflater, SelectionDetails selectionDetails) {
121         final boolean hasDir = selectionDetails.containsDirectories();
122         final boolean hasFile = selectionDetails.containsFiles();
123 
124         assert(hasDir || hasFile);
125         if (!hasDir) {
126             inflater.inflate(R.menu.file_context_menu, menu);
127             updateContextMenuForFiles(menu, selectionDetails);
128             return;
129         }
130 
131         if (!hasFile) {
132             inflater.inflate(R.menu.dir_context_menu, menu);
133             updateContextMenuForDirs(menu, selectionDetails);
134             return;
135         }
136 
137         inflater.inflate(R.menu.mixed_context_menu, menu);
138         updateContextMenu(menu, selectionDetails);
139     }
140 
141     @Override
updateSettings(MenuItem settings, RootInfo root)142     protected void updateSettings(MenuItem settings, RootInfo root) {
143         Menus.setEnabledAndVisible(settings, root.hasSettings());
144     }
145 
146     @Override
updateEject(MenuItem eject, RootInfo root)147     protected void updateEject(MenuItem eject, RootInfo root) {
148         Menus.setEnabledAndVisible(eject, root.supportsEject() && !root.ejecting);
149     }
150 
151     @Override
updateSettings(MenuItem settings)152     protected void updateSettings(MenuItem settings) {
153         boolean enabled = mDirDetails.hasRootSettings();
154         Menus.setEnabledAndVisible(settings, enabled);
155     }
156 
157     @Override
updateNewWindow(MenuItem newWindow)158     protected void updateNewWindow(MenuItem newWindow) {
159         Menus.setEnabledAndVisible(newWindow, true);
160     }
161 
162     @Override
updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails)163     protected void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) {
164         Menus.setEnabledAndVisible(openWith, selectionDetails.canOpenWith());
165     }
166 
167     @Override
updateOpenInNewWindow( MenuItem openInNewWindow, SelectionDetails selectionDetails)168     protected void updateOpenInNewWindow(
169             MenuItem openInNewWindow, SelectionDetails selectionDetails) {
170         Menus.setEnabledAndVisible(openInNewWindow, selectionDetails.size() == 1
171             && !selectionDetails.containsPartialFiles());
172     }
173 
174     @Override
updateOpenInNewWindow(MenuItem openInNewWindow, RootInfo root)175     protected void updateOpenInNewWindow(MenuItem openInNewWindow, RootInfo root) {
176         assert(openInNewWindow.isVisible() && openInNewWindow.isEnabled());
177     }
178 
179     @Override
updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails)180     protected void updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails) {
181         Menus.setEnabledAndVisible(moveTo,
182                 !selectionDetails.containsPartialFiles() && selectionDetails.canDelete());
183     }
184 
185     @Override
updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails)186     protected void updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails) {
187         Menus.setEnabledAndVisible(copyTo, !selectionDetails.containsPartialFiles()
188                 && !selectionDetails.canExtract());
189     }
190 
191     @Override
updateCompress(MenuItem compress, SelectionDetails selectionDetails)192     protected void updateCompress(MenuItem compress, SelectionDetails selectionDetails) {
193         final boolean readOnly = !mDirDetails.canCreateDoc();
194         Menus.setEnabledAndVisible(compress, mFeatures.isArchiveCreationEnabled()
195                 && !readOnly
196                 && !selectionDetails.containsPartialFiles()
197                 && !selectionDetails.canExtract());
198     }
199 
200     @Override
updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails)201     protected void updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails) {
202         boolean enabled = selectionDetails.canExtract();
203         Menus.setEnabledAndVisible(extractTo, enabled);
204     }
205 
206     @Override
updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails)207     protected void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) {
208         Menus.setEnabledAndVisible(pasteInto,
209                 selectionDetails.canPasteInto() && mDirDetails.hasItemsToPaste());
210     }
211 
212     @Override
updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo)213     protected void updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo) {
214         Menus.setEnabledAndVisible(pasteInto, root.supportsCreate()
215                 && docInfo != null
216                 && docInfo.isCreateSupported()
217                 && mDirDetails.hasItemsToPaste());
218     }
219 
220     @Override
updateSelectAll(MenuItem selectAll)221     protected void updateSelectAll(MenuItem selectAll) {
222         Menus.setEnabledAndVisible(selectAll, true);
223     }
224 
225     @Override
updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails)226     protected void updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails) {
227         final boolean visible = selectionDetails.size() < mFilesCountSupplier.getAsInt();
228         Menus.setEnabledAndVisible(selectAll, visible);
229     }
230 
231     @Override
232     protected void updateDeselectAll(MenuItem deselectAll, SelectionDetails selectionDetails) {
233         final boolean visible = selectionDetails.size() == mFilesCountSupplier.getAsInt();
234         Menus.setEnabledAndVisible(deselectAll, visible);
235     }
236 
237     @Override
238     protected void updateCreateDir(MenuItem createDir) {
239         Menus.setEnabledAndVisible(createDir, mDirDetails.canCreateDirectory());
240     }
241 
242     @Override
243     protected void updateShare(MenuItem share, SelectionDetails selectionDetails) {
244         boolean enabled = !selectionDetails.containsDirectories()
245                 && !selectionDetails.containsPartialFiles()
246                 && !selectionDetails.canExtract();
247         Menus.setEnabledAndVisible(share, enabled);
248     }
249 
250     @Override
251     protected void updateDelete(MenuItem delete, SelectionDetails selectionDetails) {
252         boolean enabled = selectionDetails.canDelete();
253         Menus.setEnabledAndVisible(delete, enabled);
254     }
255 
256     @Override
257     protected void updateRename(MenuItem rename, SelectionDetails selectionDetails) {
258         Menus.setEnabledAndVisible(rename,
259                 !selectionDetails.containsPartialFiles() && selectionDetails.canRename());
260     }
261 
262     @Override
263     protected void updateInspect(MenuItem inspect) {
264         boolean visible = mFeatures.isInspectorEnabled();
265         Menus.setEnabledAndVisible(inspect, visible && mDirDetails.canInspectDirectory());
266     }
267 
268     @Override
269     protected void updateInspect(MenuItem inspect, SelectionDetails selectionDetails) {
270         boolean visible = mFeatures.isInspectorEnabled() && selectionDetails.size() <= 1;
271         Menus.setEnabledAndVisible(inspect, visible);
272     }
273 
274     @Override
275     protected void updateViewInOwner(MenuItem view, SelectionDetails selectionDetails) {
276         if (selectionDetails.canViewInOwner() &&
277                 mSelectionManager.getSelection().iterator().hasNext()) {
278             Menus.setEnabledAndVisible(view, true);
279             Resources res = mContext.getResources();
280             String selectedModelId = mSelectionManager.getSelection().iterator().next();
281             Uri selectedUri = mUriLookup.lookup(selectedModelId);
282             String appName = mAppNameLookup.getApplicationName(UserId.DEFAULT_USER,
283                     selectedUri.getAuthority());
284             String title = res.getString(R.string.menu_view_in_owner, appName);
285             view.setTitle(title);
286         }
287         else {
288             Menus.setEnabledAndVisible(view, false);
289         }
290     }
291 
292     @Override
293     protected void updateLauncher(MenuItem launcher) {
294         Menus.setEnabledAndVisible(launcher, mState.debugMode);
295         launcher.setTitle(Shared.isLauncherEnabled(mContext)
296                 ? "Hide launcher icon" : "Show launcher icon");
297     }
298 }
299