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 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 */ 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 updateViewInOwner(menu.findItem(R.id.action_menu_view_in_owner), selection); 66 updateInspector(menu.findItem(R.id.action_menu_inspector), selection); 67 68 Menus.disableHiddenItems(menu); 69 } 70 71 /** @see BaseActivity#onPrepareOptionsMenu */ 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 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 */ 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 97 public void inflateContextMenuForContainer(Menu menu, MenuInflater inflater) { 98 throw new UnsupportedOperationException("Pickers don't allow context menu."); 99 } 100 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 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 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 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 173 final boolean canCopy = 174 selectionDetails.size() > 0 && !selectionDetails.containsPartialFiles(); 175 final boolean canDelete = selectionDetails.canDelete(); 176 cut.setEnabled(canCopy && canDelete); 177 copy.setEnabled(canCopy); 178 delete.setEnabled(canDelete); 179 } 180 181 /** 182 * @see DirectoryFragment#onCreateContextMenu 183 * 184 * Called when user tries to generate a context menu anchored to an empty pane. 185 */ 186 @VisibleForTesting 187 public void updateContextMenuForContainer(Menu menu) { 188 MenuItem paste = menu.findItem(R.id.dir_menu_paste_from_clipboard); 189 MenuItem selectAll = menu.findItem(R.id.dir_menu_select_all); 190 MenuItem createDir = menu.findItem(R.id.dir_menu_create_dir); 191 192 paste.setEnabled(mDirDetails.hasItemsToPaste() && mDirDetails.canCreateDoc()); 193 updateSelectAll(selectAll); 194 updateCreateDir(createDir); 195 } 196 197 /** 198 * @see RootsFragment#onCreateContextMenu 199 */ 200 public void updateRootContextMenu(Menu menu, RootInfo root, DocumentInfo docInfo) { 201 MenuItem eject = menu.findItem(R.id.root_menu_eject_root); 202 MenuItem pasteInto = menu.findItem(R.id.root_menu_paste_into_folder); 203 MenuItem openInNewWindow = menu.findItem(R.id.root_menu_open_in_new_window); 204 MenuItem settings = menu.findItem(R.id.root_menu_settings); 205 206 updateEject(eject, root); 207 updatePasteInto(pasteInto, root, docInfo); 208 updateOpenInNewWindow(openInNewWindow, root); 209 updateSettings(settings, root); 210 } 211 212 public abstract void updateKeyboardShortcutsMenu( 213 List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier); 214 215 protected void updateModePicker(MenuItem grid, MenuItem list) { 216 grid.setVisible(mState.derivedMode != State.MODE_GRID); 217 list.setVisible(mState.derivedMode != State.MODE_LIST); 218 } 219 220 protected void updateAdvanced(MenuItem advanced) { 221 advanced.setVisible(mState.showDeviceStorageOption); 222 advanced.setTitle(mState.showDeviceStorageOption && mState.showAdvanced 223 ? R.string.menu_advanced_hide : R.string.menu_advanced_show); 224 } 225 226 protected void updateDebug(MenuItem debug) { 227 debug.setVisible(mState.debugMode); 228 } 229 230 protected void updateSettings(MenuItem settings) { 231 settings.setVisible(false); 232 } 233 234 protected void updateSettings(MenuItem settings, RootInfo root) { 235 settings.setVisible(false); 236 } 237 238 protected void updateEject(MenuItem eject, RootInfo root) { 239 eject.setVisible(false); 240 } 241 242 protected void updateNewWindow(MenuItem newWindow) { 243 newWindow.setVisible(false); 244 } 245 246 protected void updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails) { 247 open.setVisible(false); 248 } 249 250 protected void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) { 251 openWith.setVisible(false); 252 } 253 254 protected void updateOpenInNewWindow( 255 MenuItem openInNewWindow, SelectionDetails selectionDetails) { 256 openInNewWindow.setVisible(false); 257 } 258 259 protected void updateOpenInNewWindow( 260 MenuItem openInNewWindow, RootInfo root) { 261 openInNewWindow.setVisible(false); 262 } 263 264 protected void updateShare(MenuItem share, SelectionDetails selectionDetails) { 265 share.setVisible(false); 266 } 267 268 protected void updateDelete(MenuItem delete, SelectionDetails selectionDetails) { 269 delete.setVisible(false); 270 } 271 272 protected void updateRename(MenuItem rename, SelectionDetails selectionDetails) { 273 rename.setVisible(false); 274 } 275 276 protected void updateInspector(MenuItem properties, SelectionDetails selectionDetails) { 277 properties.setVisible(false); 278 } 279 280 protected void updateViewInOwner(MenuItem view, SelectionDetails selectionDetails) { 281 view.setVisible(false); 282 } 283 284 protected void updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails) { 285 moveTo.setVisible(false); 286 } 287 288 protected void updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails) { 289 copyTo.setVisible(false); 290 } 291 292 protected void updateCompress(MenuItem compress, SelectionDetails selectionDetails) { 293 compress.setVisible(false); 294 } 295 296 protected void updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails) { 297 extractTo.setVisible(false); 298 } 299 300 protected void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) { 301 pasteInto.setVisible(false); 302 } 303 304 protected void updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo) { 305 pasteInto.setVisible(false); 306 } 307 308 protected abstract void updateOpenInContextMenu( 309 MenuItem open, SelectionDetails selectionDetails); 310 protected abstract void updateSelectAll(MenuItem selectAll); 311 protected abstract void updateCreateDir(MenuItem createDir); 312 313 /** 314 * Access to meta data about the selection. 315 */ 316 public interface SelectionDetails { 317 boolean containsDirectories(); 318 319 boolean containsFiles(); 320 321 int size(); 322 323 boolean containsPartialFiles(); 324 325 boolean containsFilesInArchive(); 326 327 // TODO: Update these to express characteristics instead of answering concrete questions, 328 // since the answer to those questions is (or can be) activity specific. 329 boolean canDelete(); 330 331 boolean canRename(); 332 333 boolean canPasteInto(); 334 335 boolean canExtract(); 336 337 boolean canOpenWith(); 338 339 boolean canViewInOwner(); 340 } 341 342 public static class DirectoryDetails { 343 private final BaseActivity mActivity; 344 345 public DirectoryDetails(BaseActivity activity) { 346 mActivity = activity; 347 } 348 349 public boolean hasRootSettings() { 350 return mActivity.getCurrentRoot().hasSettings(); 351 } 352 353 public boolean hasItemsToPaste() { 354 return false; 355 } 356 357 public boolean canCreateDoc() { 358 return isInRecents() ? false : mActivity.getCurrentDirectory().isCreateSupported(); 359 } 360 361 public boolean isInRecents() { 362 return mActivity.getCurrentDirectory() == null; 363 } 364 365 public boolean canCreateDirectory() { 366 return mActivity.canCreateDirectory(); 367 } 368 } 369 } 370