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.picker;
18 
19 import static com.android.documentsui.base.State.ACTION_CREATE;
20 import static com.android.documentsui.base.State.ACTION_GET_CONTENT;
21 import static com.android.documentsui.base.State.ACTION_OPEN;
22 
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.database.MatrixCursor;
27 import android.provider.DocumentsContract.Document;
28 import android.provider.DocumentsContract.Root;
29 
30 import androidx.test.filters.SmallTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.documentsui.DirectoryResult;
34 import com.android.documentsui.Model;
35 import com.android.documentsui.R;
36 import com.android.documentsui.base.DocumentInfo;
37 import com.android.documentsui.base.RootInfo;
38 import com.android.documentsui.base.State;
39 import com.android.documentsui.roots.RootCursorWrapper;
40 import com.android.documentsui.testing.TestDirectoryDetails;
41 import com.android.documentsui.testing.TestFeatures;
42 import com.android.documentsui.testing.TestMenu;
43 import com.android.documentsui.testing.TestMenuItem;
44 import com.android.documentsui.testing.TestSearchViewManager;
45 import com.android.documentsui.testing.TestSelectionDetails;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 
51 @RunWith(AndroidJUnit4.class)
52 @SmallTest
53 public final class MenuManagerTest {
54 
55     private TestMenu testMenu;
56 
57     /* Directory Context Menu items */
58     private TestMenuItem dirShare;
59     private TestMenuItem dirOpen;
60     private TestMenuItem dirOpenWith;
61     private TestMenuItem dirCutToClipboard;
62     private TestMenuItem dirCopyToClipboard;
63     private TestMenuItem dirPasteFromClipboard;
64     private TestMenuItem dirCreateDir;
65     private TestMenuItem dirSelectAll;
66     private TestMenuItem mDirDeselectAll;
67     private TestMenuItem dirRename;
68     private TestMenuItem dirDelete;
69     private TestMenuItem dirViewInOwner;
70     private TestMenuItem dirOpenInNewWindow;
71     private TestMenuItem dirPasteIntoFolder;
72 
73     /* Root List Context Menu items */
74     private TestMenuItem rootEjectRoot;
75     private TestMenuItem rootOpenInNewWindow;
76     private TestMenuItem rootPasteIntoFolder;
77     private TestMenuItem rootSettings;
78 
79     /* Action Mode menu items */
80     private TestMenuItem actionModeOpen;
81     private TestMenuItem actionModeOpenWith;
82     private TestMenuItem actionModeSelect;
83     private TestMenuItem actionModeShare;
84     private TestMenuItem actionModeDelete;
85     private TestMenuItem actionModeSelectAll;
86     private TestMenuItem mActionModeDeselectAll;
87     private TestMenuItem actionModeCopyTo;
88     private TestMenuItem actionModeExtractTo;
89     private TestMenuItem actionModeMoveTo;
90     private TestMenuItem actionModeCompress;
91     private TestMenuItem actionModeRename;
92     private TestMenuItem actionModeViewInOwner;
93     private TestMenuItem actionModeSort;
94 
95     /* Option Menu items */
96     private TestMenuItem optionSearch;
97     private TestMenuItem optionDebug;
98     private TestMenuItem optionNewWindow;
99     private TestMenuItem optionCreateDir;
100     private TestMenuItem optionSelectAll;
101     private TestMenuItem optionSettings;
102     private TestMenuItem optionSort;
103     private TestMenuItem mOptionLauncher;
104     private TestMenuItem mOptionShowHiddenFiles;
105 
106     private TestMenuItem subOptionGrid;
107     private TestMenuItem subOptionList;
108 
109     private TestSelectionDetails selectionDetails;
110     private TestDirectoryDetails dirDetails;
111     private TestSearchViewManager testSearchManager;
112     private State state = new State();
113     private RootInfo testRootInfo;
114     private DocumentInfo testDocInfo;
115     private MenuManager mgr;
116 
117     private int mFilesCount;
118 
119     @Before
setUp()120     public void setUp() {
121         testMenu = TestMenu.create();
122         dirShare = testMenu.findItem(R.id.dir_menu_share);
123         dirOpen = testMenu.findItem(R.id.dir_menu_open);
124         dirOpenWith = testMenu.findItem(R.id.dir_menu_open_with);
125         dirCutToClipboard = testMenu.findItem(R.id.dir_menu_cut_to_clipboard);
126         dirCopyToClipboard = testMenu.findItem(R.id.dir_menu_copy_to_clipboard);
127         dirPasteFromClipboard = testMenu.findItem(R.id.dir_menu_paste_from_clipboard);
128         dirCreateDir = testMenu.findItem(R.id.dir_menu_create_dir);
129         dirSelectAll = testMenu.findItem(R.id.dir_menu_select_all);
130         mDirDeselectAll = testMenu.findItem(R.id.dir_menu_deselect_all);
131         dirRename = testMenu.findItem(R.id.dir_menu_rename);
132         dirDelete = testMenu.findItem(R.id.dir_menu_delete);
133         dirViewInOwner = testMenu.findItem(R.id.dir_menu_view_in_owner);
134         dirOpenInNewWindow = testMenu.findItem(R.id.dir_menu_open_in_new_window);
135         dirPasteIntoFolder = testMenu.findItem(R.id.dir_menu_paste_into_folder);
136 
137         rootEjectRoot = testMenu.findItem(R.id.root_menu_eject_root);
138         rootOpenInNewWindow = testMenu.findItem(R.id.root_menu_open_in_new_window);
139         rootPasteIntoFolder = testMenu.findItem(R.id.root_menu_paste_into_folder);
140         rootSettings = testMenu.findItem(R.id.root_menu_settings);
141 
142         actionModeOpenWith = testMenu.findItem(R.id.action_menu_open_with);
143         actionModeSelect = testMenu.findItem(R.id.action_menu_select);
144         actionModeShare = testMenu.findItem(R.id.action_menu_share);
145         actionModeDelete = testMenu.findItem(R.id.action_menu_delete);
146         actionModeSelectAll = testMenu.findItem(R.id.action_menu_select_all);
147         mActionModeDeselectAll = testMenu.findItem(R.id.action_menu_deselect_all);
148         actionModeCopyTo = testMenu.findItem(R.id.action_menu_copy_to);
149         actionModeExtractTo = testMenu.findItem(R.id.action_menu_extract_to);
150         actionModeMoveTo = testMenu.findItem(R.id.action_menu_move_to);
151         actionModeCompress = testMenu.findItem(R.id.action_menu_compress);
152         actionModeRename = testMenu.findItem(R.id.action_menu_rename);
153         actionModeViewInOwner = testMenu.findItem(R.id.action_menu_view_in_owner);
154         actionModeSort = testMenu.findItem(R.id.action_menu_sort);
155 
156         optionSearch = testMenu.findItem(R.id.option_menu_search);
157         optionDebug = testMenu.findItem(R.id.option_menu_debug);
158         optionNewWindow = testMenu.findItem(R.id.option_menu_new_window);
159         optionCreateDir = testMenu.findItem(R.id.option_menu_create_dir);
160         optionSelectAll = testMenu.findItem(R.id.option_menu_select_all);
161         optionSettings = testMenu.findItem(R.id.option_menu_settings);
162         optionSort = testMenu.findItem(R.id.option_menu_sort);
163         mOptionLauncher = testMenu.findItem(R.id.option_menu_launcher);
164         mOptionShowHiddenFiles = testMenu.findItem(R.id.option_menu_show_hidden_files);
165 
166         // Menu actions on root title row.
167         subOptionGrid = testMenu.findItem(R.id.sub_menu_grid);
168         subOptionList = testMenu.findItem(R.id.sub_menu_list);
169 
170         selectionDetails = new TestSelectionDetails();
171         dirDetails = new TestDirectoryDetails();
172         testSearchManager = new TestSearchViewManager();
173         mgr = new MenuManager(testSearchManager, state, dirDetails, this::getFilesCount);
174         selectionDetails.size = 1;
175         mFilesCount = 10;
176 
177         testRootInfo = new RootInfo();
178         testDocInfo = new DocumentInfo();
179         state.action = ACTION_CREATE;
180         state.allowMultiple = true;
181     }
182 
getFilesCount()183     private int getFilesCount() {
184         return mFilesCount;
185     }
186 
187     @Test
testActionMenu()188     public void testActionMenu() {
189         mgr.updateActionMenu(testMenu, selectionDetails);
190         actionModeSelect.assertDisabledAndInvisible();
191         actionModeDelete.assertDisabledAndInvisible();
192         actionModeShare.assertDisabledAndInvisible();
193         actionModeRename.assertDisabledAndInvisible();
194         actionModeSelectAll.assertEnabledAndVisible();
195         mActionModeDeselectAll.assertDisabledAndInvisible();
196         actionModeViewInOwner.assertDisabledAndInvisible();
197         actionModeSort.assertEnabledAndVisible();
198     }
199 
200     @Test
testActionMenu_selectAction()201     public void testActionMenu_selectAction() {
202         state.action = ACTION_OPEN;
203         mgr.updateActionMenu(testMenu, selectionDetails);
204 
205         actionModeSelect.assertEnabledAndVisible();
206     }
207 
208     @Test
testActionMenu_selectActionTitle()209     public void testActionMenu_selectActionTitle() {
210         state.action = ACTION_OPEN;
211         mgr.updateActionMenu(testMenu, selectionDetails);
212 
213         actionModeSelect.assertTitle(R.string.menu_select);
214     }
215 
216     @Test
testActionMenu_getContentAction()217     public void testActionMenu_getContentAction() {
218         state.action = ACTION_GET_CONTENT;
219         mgr.updateActionMenu(testMenu, selectionDetails);
220 
221         actionModeSelect.assertEnabledAndVisible();
222     }
223 
224     @Test
testActionMenu_getContentActionTitle()225     public void testActionMenu_getContentActionTitle() {
226         state.action = ACTION_GET_CONTENT;
227         mgr.updateActionMenu(testMenu, selectionDetails);
228 
229         actionModeSelect.assertTitle(R.string.menu_select);
230     }
231 
232     @Test
testActionMenu_notAllowMultiple()233     public void testActionMenu_notAllowMultiple() {
234         state.allowMultiple = false;
235         mgr.updateActionMenu(testMenu, selectionDetails);
236 
237         actionModeSelectAll.assertDisabledAndInvisible();
238         mActionModeDeselectAll.assertDisabledAndInvisible();
239     }
240 
241     @Test
testActionMenu_AllowMultiple()242     public void testActionMenu_AllowMultiple() {
243         state.allowMultiple = true;
244         mgr.updateActionMenu(testMenu, selectionDetails);
245 
246         actionModeSelectAll.assertEnabledAndVisible();
247         mActionModeDeselectAll.assertDisabledAndInvisible();
248     }
249 
250     @Test
testActionMenu_CanDeselectAll()251     public void testActionMenu_CanDeselectAll() {
252         state.allowMultiple = true;
253         selectionDetails.size = 1;
254         mFilesCount = 1;
255 
256         mgr.updateActionMenu(testMenu, selectionDetails);
257 
258         actionModeSelectAll.assertDisabledAndInvisible();
259         mActionModeDeselectAll.assertEnabledAndVisible();
260     }
261 
262     @Test
testOptionMenu()263     public void testOptionMenu() {
264         mgr.updateOptionMenu(testMenu);
265 
266         optionCreateDir.assertDisabledAndInvisible();
267         optionDebug.assertDisabledAndInvisible();
268         optionSort.assertEnabledAndVisible();
269         mOptionLauncher.assertDisabledAndInvisible();
270         mOptionShowHiddenFiles.assertEnabledAndVisible();
271         assertTrue(testSearchManager.showMenuCalled());
272     }
273 
274     @Test
testOptionMenu_notPicking()275     public void testOptionMenu_notPicking() {
276         state.action = ACTION_OPEN;
277         state.derivedMode = State.MODE_LIST;
278         mgr.updateOptionMenu(testMenu);
279         mgr.updateSubMenu(testMenu);
280 
281         optionCreateDir.assertDisabledAndInvisible();
282         subOptionGrid.assertEnabledAndVisible();
283         subOptionList.assertDisabledAndInvisible();
284         assertFalse(testSearchManager.showMenuCalled());
285     }
286 
287     @Test
testOptionMenu_canCreateDirectory()288     public void testOptionMenu_canCreateDirectory() {
289         dirDetails.canCreateDirectory = true;
290         mgr.updateOptionMenu(testMenu);
291 
292         optionCreateDir.assertEnabledAndVisible();
293     }
294 
295     @Test
testOptionMenu_inRecents()296     public void testOptionMenu_inRecents() {
297         dirDetails.isInRecents = true;
298         mgr.updateOptionMenu(testMenu);
299         mgr.updateSubMenu(testMenu);
300 
301         subOptionGrid.assertDisabledAndInvisible();
302         subOptionList.assertDisabledAndInvisible();
303     }
304 
305 
306     @Test
testOptionMenu_onlyContainer()307     public void testOptionMenu_onlyContainer() {
308         state.allowMultiple = true;
309         mgr.updateModel(getTestModel(true));
310         mgr.updateOptionMenu(testMenu);
311 
312         optionSelectAll.assertDisabledAndInvisible();
313     }
314 
315     @Test
testOptionMenu_containerAndFile()316     public void testOptionMenu_containerAndFile() {
317         state.allowMultiple = true;
318         mgr.updateModel(getTestModel(false));
319         mgr.updateOptionMenu(testMenu);
320 
321         optionSelectAll.assertEnabledAndVisible();
322     }
323 
324     @Test
testContextMenu_EmptyArea()325     public void testContextMenu_EmptyArea() {
326         dirDetails.hasItemsToPaste = false;
327         dirDetails.canCreateDoc = false;
328         dirDetails.canCreateDirectory = false;
329 
330         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
331 
332         dirSelectAll.assertEnabledAndVisible();
333         mDirDeselectAll.assertDisabledAndInvisible();
334         dirPasteFromClipboard.assertDisabledAndInvisible();
335         dirCreateDir.assertDisabledAndInvisible();
336     }
337 
338     @Test
testContextMenu_EmptyArea_NoItemToPaste()339     public void testContextMenu_EmptyArea_NoItemToPaste() {
340         dirDetails.hasItemsToPaste = false;
341         dirDetails.canCreateDoc = true;
342 
343         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
344 
345         dirSelectAll.assertEnabledAndVisible();
346         dirPasteFromClipboard.assertDisabledAndInvisible();
347         dirCreateDir.assertDisabledAndInvisible();
348     }
349 
350     @Test
testContextMenu_EmptyArea_CantCreateDoc()351     public void testContextMenu_EmptyArea_CantCreateDoc() {
352         dirDetails.hasItemsToPaste = true;
353         dirDetails.canCreateDoc = false;
354 
355         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
356 
357         dirSelectAll.assertEnabledAndVisible();
358         dirPasteFromClipboard.assertDisabledAndInvisible();
359         dirCreateDir.assertDisabledAndInvisible();
360     }
361 
362     @Test
testContextMenu_EmptyArea_canPaste()363     public void testContextMenu_EmptyArea_canPaste() {
364         dirDetails.hasItemsToPaste = true;
365         dirDetails.canCreateDoc = true;
366 
367         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
368 
369         dirSelectAll.assertEnabledAndVisible();
370         dirPasteFromClipboard.assertEnabledAndVisible();
371         dirCreateDir.assertDisabledAndInvisible();
372     }
373 
374     @Test
testContextMenu_EmptyArea_CanCreateDirectory()375     public void testContextMenu_EmptyArea_CanCreateDirectory() {
376         dirDetails.canCreateDirectory = true;
377 
378         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
379 
380         dirSelectAll.assertEnabledAndVisible();
381         dirPasteFromClipboard.assertDisabledAndInvisible();
382         dirCreateDir.assertEnabledAndVisible();
383     }
384 
385     @Test
testContextMenu_EmptyArea_CanDeselectAll()386     public void testContextMenu_EmptyArea_CanDeselectAll() {
387         selectionDetails.size = 1;
388         mFilesCount = 1;
389 
390         mgr.updateContextMenuForContainer(testMenu, selectionDetails);
391 
392         dirSelectAll.assertDisabledAndInvisible();
393         mDirDeselectAll.assertEnabledAndVisible();
394     }
395 
396     @Test
testContextMenu_OnFile()397     public void testContextMenu_OnFile() {
398         mgr.updateContextMenuForFiles(testMenu, selectionDetails);
399         // We don't want share in pickers.
400         dirShare.assertDisabledAndInvisible();
401         // We don't want openWith in pickers.
402         dirOpenWith.assertDisabledAndInvisible();
403         dirCutToClipboard.assertDisabledAndInvisible();
404         dirCopyToClipboard.assertEnabledAndVisible();
405         dirRename.assertDisabledAndInvisible();
406         dirDelete.assertDisabledAndInvisible();
407     }
408 
409     @Test
testContextMenu_OnDirectory()410     public void testContextMenu_OnDirectory() {
411         selectionDetails.canPasteInto = true;
412         mgr.updateContextMenuForDirs(testMenu, selectionDetails);
413         // We don't want openInNewWindow in pickers
414         dirOpenInNewWindow.assertDisabledAndInvisible();
415         dirCutToClipboard.assertDisabledAndInvisible();
416         dirCopyToClipboard.assertEnabledAndVisible();
417         // Doesn't matter if directory is selected, we don't want pasteInto for PickerActivity
418         dirPasteIntoFolder.assertDisabledAndInvisible();
419         dirRename.assertDisabledAndInvisible();
420         dirDelete.assertDisabledAndInvisible();
421     }
422 
423     @Test
testContextMenu_OnMixedDocs()424     public void testContextMenu_OnMixedDocs() {
425         selectionDetails.containDirectories = true;
426         selectionDetails.containFiles = true;
427         selectionDetails.size = 2;
428         selectionDetails.canDelete = true;
429         mgr.updateContextMenu(testMenu, selectionDetails);
430         dirCutToClipboard.assertEnabledAndVisible();
431         dirCopyToClipboard.assertEnabledAndVisible();
432         dirDelete.assertEnabledAndVisible();
433     }
434 
435     @Test
testContextMenu_OnMixedDocs_hasPartialFile()436     public void testContextMenu_OnMixedDocs_hasPartialFile() {
437         selectionDetails.containDirectories = true;
438         selectionDetails.containFiles = true;
439         selectionDetails.size = 2;
440         selectionDetails.containPartial = true;
441         selectionDetails.canDelete = true;
442         mgr.updateContextMenu(testMenu, selectionDetails);
443         dirCutToClipboard.assertDisabledAndInvisible();
444         dirCopyToClipboard.assertDisabledAndInvisible();
445         dirDelete.assertEnabledAndVisible();
446     }
447 
448     @Test
testContextMenu_OnMixedDocs_hasUndeletableFile()449     public void testContextMenu_OnMixedDocs_hasUndeletableFile() {
450         selectionDetails.containDirectories = true;
451         selectionDetails.containFiles = true;
452         selectionDetails.size = 2;
453         selectionDetails.canDelete = false;
454         mgr.updateContextMenu(testMenu, selectionDetails);
455         dirCutToClipboard.assertDisabledAndInvisible();
456         dirCopyToClipboard.assertEnabledAndVisible();
457         dirDelete.assertDisabledAndInvisible();
458     }
459 
460     @Test
testRootContextMenu()461     public void testRootContextMenu() {
462         mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo);
463 
464         rootEjectRoot.assertDisabledAndInvisible();
465         rootOpenInNewWindow.assertDisabledAndInvisible();
466         rootPasteIntoFolder.assertDisabledAndInvisible();
467         rootSettings.assertDisabledAndInvisible();
468     }
469 
470     @Test
testRootContextMenu_hasRootSettings()471     public void testRootContextMenu_hasRootSettings() {
472         testRootInfo.flags = Root.FLAG_HAS_SETTINGS;
473         mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo);
474 
475         rootSettings.assertDisabledAndInvisible();
476     }
477 
478     @Test
testRootContextMenu_nonWritableRoot()479     public void testRootContextMenu_nonWritableRoot() {
480         dirDetails.hasItemsToPaste = true;
481         mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo);
482 
483         rootPasteIntoFolder.assertDisabledAndInvisible();
484     }
485 
486     @Test
testRootContextMenu_nothingToPaste()487     public void testRootContextMenu_nothingToPaste() {
488         testRootInfo.flags = Root.FLAG_SUPPORTS_CREATE;
489         dirDetails.hasItemsToPaste = false;
490         mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo);
491 
492         rootPasteIntoFolder.assertDisabledAndInvisible();
493     }
494 
495     @Test
testRootContextMenu_canEject()496     public void testRootContextMenu_canEject() {
497         testRootInfo.flags = Root.FLAG_SUPPORTS_EJECT;
498         mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo);
499 
500         rootEjectRoot.assertDisabledAndInvisible();
501     }
502 
getTestModel(boolean onlyDirectory)503     private Model getTestModel(boolean onlyDirectory) {
504         String[] COLUMNS = new String[]{
505                 RootCursorWrapper.COLUMN_AUTHORITY,
506                 Document.COLUMN_DOCUMENT_ID,
507                 Document.COLUMN_FLAGS,
508                 Document.COLUMN_DISPLAY_NAME,
509                 Document.COLUMN_SIZE,
510                 Document.COLUMN_LAST_MODIFIED,
511                 Document.COLUMN_MIME_TYPE
512         };
513         MatrixCursor c = new MatrixCursor(COLUMNS);
514         for (int i = 0; i < 3; ++i) {
515             MatrixCursor.RowBuilder row = c.newRow();
516             row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i));
517             row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
518         }
519 
520         if (!onlyDirectory) {
521             MatrixCursor.RowBuilder row = c.newRow();
522             row.add(Document.COLUMN_DOCUMENT_ID, "4");
523             row.add(Document.COLUMN_MIME_TYPE, "image/jpg");
524         }
525 
526         DirectoryResult r = new DirectoryResult();
527         r.setCursor(c);
528         Model model = new Model(new TestFeatures());
529         model.update(r);
530 
531         return model;
532     }
533 }
534