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