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 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 */ 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 */ 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 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 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 */ 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 217 public abstract void updateKeyboardShortcutsMenu( 218 List<KeyboardShortcutGroup> data, IntFunction<String> stringSupplier); 219 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 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 231 protected void updateDebug(MenuItem debug) { 232 debug.setVisible(mState.debugMode); 233 } 234 235 protected void updateSettings(MenuItem settings) { 236 settings.setVisible(false); 237 } 238 239 protected void updateSettings(MenuItem settings, RootInfo root) { 240 settings.setVisible(false); 241 } 242 243 protected void updateEject(MenuItem eject, RootInfo root) { 244 eject.setVisible(false); 245 } 246 247 protected void updateNewWindow(MenuItem newWindow) { 248 newWindow.setVisible(false); 249 } 250 251 protected void updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails) { 252 open.setVisible(false); 253 } 254 255 protected void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) { 256 openWith.setVisible(false); 257 } 258 259 protected void updateOpenInNewWindow( 260 MenuItem openInNewWindow, SelectionDetails selectionDetails) { 261 openInNewWindow.setVisible(false); 262 } 263 264 protected void updateOpenInNewWindow( 265 MenuItem openInNewWindow, RootInfo root) { 266 openInNewWindow.setVisible(false); 267 } 268 269 protected void updateShare(MenuItem share, SelectionDetails selectionDetails) { 270 share.setVisible(false); 271 } 272 273 protected void updateDelete(MenuItem delete, SelectionDetails selectionDetails) { 274 delete.setVisible(false); 275 } 276 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 */ 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 */ 292 protected void updateInspect(MenuItem inspect, SelectionDetails selectionDetails) { 293 inspect.setVisible(false); 294 } 295 296 protected void updateViewInOwner(MenuItem view, SelectionDetails selectionDetails) { 297 view.setVisible(false); 298 } 299 300 protected void updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails) { 301 moveTo.setVisible(false); 302 } 303 304 protected void updateCopyTo(MenuItem copyTo, SelectionDetails selectionDetails) { 305 copyTo.setVisible(false); 306 } 307 308 protected void updateCompress(MenuItem compress, SelectionDetails selectionDetails) { 309 compress.setVisible(false); 310 } 311 312 protected void updateExtractTo(MenuItem extractTo, SelectionDetails selectionDetails) { 313 extractTo.setVisible(false); 314 } 315 316 protected void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) { 317 pasteInto.setVisible(false); 318 } 319 320 protected void updatePasteInto(MenuItem pasteInto, RootInfo root, DocumentInfo docInfo) { 321 pasteInto.setVisible(false); 322 } 323 324 protected abstract void updateOpenInContextMenu( 325 MenuItem open, SelectionDetails selectionDetails); 326 protected abstract void updateSelectAll(MenuItem selectAll); 327 protected abstract void updateCreateDir(MenuItem createDir); 328 329 /** 330 * Access to meta data about the selection. 331 */ 332 public interface SelectionDetails { 333 boolean containsDirectories(); 334 335 boolean containsFiles(); 336 337 int size(); 338 339 boolean containsPartialFiles(); 340 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. 345 boolean canDelete(); 346 347 boolean canRename(); 348 349 boolean canPasteInto(); 350 351 boolean canExtract(); 352 353 boolean canOpenWith(); 354 355 boolean canViewInOwner(); 356 } 357 358 public static class DirectoryDetails { 359 private final BaseActivity mActivity; 360 361 public DirectoryDetails(BaseActivity activity) { 362 mActivity = activity; 363 } 364 365 public boolean hasRootSettings() { 366 return mActivity.getCurrentRoot().hasSettings(); 367 } 368 369 public boolean hasItemsToPaste() { 370 return false; 371 } 372 373 public boolean canCreateDoc() { 374 return isInRecents() ? false : mActivity.getCurrentDirectory().isCreateSupported(); 375 } 376 377 public boolean isInRecents() { 378 return mActivity.getCurrentDirectory() == null; 379 } 380 381 public boolean canCreateDirectory() { 382 return mActivity.canCreateDirectory(); 383 } 384 } 385 } 386