[multi-part] Make context menu match spec.
* Update context menu items in DirectoryFragment as defined in PRD
* Fix selection by right click in pickers
* Fix several bugs caused by selection in pickers
Functionalities are not done. OpenWith and OpenInNewWindow is not
yet supported.
Bug: 31495650
Bug: 31345822
Change-Id: I7be3169e2ee69971c0e86aa8b5e1078b15eb2960
diff --git a/res/menu/container_context_menu.xml b/res/menu/container_context_menu.xml
new file mode 100644
index 0000000..08feae6
--- /dev/null
+++ b/res/menu/container_context_menu.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Context menu used when right clicks on empty area of recycler view or empty view. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <group
+ android:id="@+id/menu_clipboard_group">
+ <item
+ android:id="@+id/menu_select_all"
+ android:title="@string/menu_select_all" />
+
+ <item
+ android:id="@+id/menu_paste_from_clipboard"
+ android:title="@string/menu_paste_from_clipboard" />
+ </group>
+
+ <group
+ android:id="@+id/menu_modifier_group">
+ <item
+ android:id="@+id/menu_create_dir"
+ android:title="@string/menu_create_dir" />
+ </group>
+</menu>
\ No newline at end of file
diff --git a/res/menu/context_menu.xml b/res/menu/context_menu.xml
deleted file mode 100644
index abac167..0000000
--- a/res/menu/context_menu.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item
- android:id="@+id/menu_cut_to_clipboard"
- android:title="@string/menu_cut_to_clipboard" />
- <item
- android:id="@+id/menu_copy_to_clipboard"
- android:title="@string/menu_copy_to_clipboard" />
- <item
- android:id="@+id/menu_paste_from_clipboard"
- android:title="@string/menu_paste_from_clipboard" />
-
- <item
- android:id="@+id/menu_paste_into_folder"
- android:title="@string/menu_paste_into_folder" />
-</menu>
diff --git a/res/menu/dir_context_menu.xml b/res/menu/dir_context_menu.xml
new file mode 100644
index 0000000..8c0772d
--- /dev/null
+++ b/res/menu/dir_context_menu.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Context menu used when user right clicks on a folder with a selection that doesn't have files.
+ Selection may be empty. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group
+ android:id="@+id/menu_open_group">
+ <item
+ android:id="@+id/menu_open_in_new_window"
+ android:title="@string/menu_open_in_new_window" />
+ </group>
+
+ <group
+ android:id="@+id/menu_clipboard_group">
+ <item
+ android:id="@+id/menu_cut_to_clipboard"
+ android:title="@string/menu_cut_to_clipboard" />
+ <item
+ android:id="@+id/menu_copy_to_clipboard"
+ android:title="@string/menu_copy_to_clipboard" />
+ <item
+ android:id="@+id/menu_paste_into_folder"
+ android:title="@string/menu_paste_into_folder" />
+ </group>
+
+ <group
+ android:id="@+id/menu_modifier_group">
+ <item
+ android:id="@+id/menu_rename"
+ android:title="@string/menu_rename" />
+ <item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu_delete" />
+ </group>
+</menu>
\ No newline at end of file
diff --git a/res/menu/file_context_menu.xml b/res/menu/file_context_menu.xml
new file mode 100644
index 0000000..16d36bc
--- /dev/null
+++ b/res/menu/file_context_menu.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Context menu used when user right clicks on a file with a selection that doesn't have folders.
+ The selection may be empty. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <group
+ android:id="@+id/menu_open_group">
+ <item
+ android:id="@+id/menu_share"
+ android:title="@string/menu_share" />
+ <item
+ android:id="@+id/menu_open"
+ android:title="@string/menu_open" />
+ <item
+ android:id="@+id/menu_open_with"
+ android:title="@string/menu_open_with" />
+ </group>
+
+ <group
+ android:id="@+id/menu_clipboard_group">
+ <item
+ android:id="@+id/menu_cut_to_clipboard"
+ android:title="@string/menu_cut_to_clipboard" />
+ <item
+ android:id="@+id/menu_copy_to_clipboard"
+ android:title="@string/menu_copy_to_clipboard" />
+ </group>
+
+ <group
+ android:id="@+id/menu_modifier_group">
+ <item
+ android:id="@+id/menu_rename"
+ android:title="@string/menu_rename" />
+ <item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu_delete" />
+ </group>
+</menu>
diff --git a/res/menu/mixed_context_menu.xml b/res/menu/mixed_context_menu.xml
new file mode 100644
index 0000000..9f17b22
--- /dev/null
+++ b/res/menu/mixed_context_menu.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Context menu used when user right clicks with a selection mixed with both folders and docs. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <group
+ android:id="@+id/menu_clipboard_group">
+ <item
+ android:id="@+id/menu_cut_to_clipboard"
+ android:title="@string/menu_cut_to_clipboard" />
+ <item
+ android:id="@+id/menu_copy_to_clipboard"
+ android:title="@string/menu_copy_to_clipboard" />
+ </group>
+
+ <group
+ android:id="@+id/menu_modifier_group">
+ <item
+ android:id="@+id/menu_delete"
+ android:title="@string/menu_delete" />
+ </group>
+</menu>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 52ad855..6032316 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -32,8 +32,6 @@
<string name="menu_grid">Grid view</string>
<!-- Menu item that switches view to show documents as a list [CHAR LIMIT=24] -->
<string name="menu_list">List view</string>
- <!-- Menu item that switches the criteria with which documents are sorted [CHAR LIMIT=24] -->
- <string name="menu_sort">Sort by</string>
<!-- Menu item that enters a mode to search for documents [CHAR LIMIT=24] -->
<string name="menu_search">Search</string>
<!-- Menu item that opens settings/options for a device (like an SD card). [CHAR LIMIT=24] -->
@@ -41,6 +39,10 @@
<!-- Menu item title that opens the selected documents [CHAR LIMIT=24] -->
<string name="menu_open">Open</string>
+ <!-- Menu item title that shows a chooser to user to pick the app to open the selected documents. [CHAR LIMIT=24] -->
+ <string name="menu_open_with">Open with</string>
+ <!-- Menu item title that opens a doc in new window. [CHAR LIMIT=24] -->
+ <string name="menu_open_in_new_window">Open in new window</string>
<!-- Menu item title that saves the current document [CHAR LIMIT=24] -->
<string name="menu_save">Save</string>
<!-- Menu item title that shares the selected documents [CHAR LIMIT=24] -->
@@ -53,6 +55,8 @@
<string name="menu_copy">Copy to\u2026</string>
<!-- Menu item title that moves the selected documents [CHAR LIMIT=24] -->
<string name="menu_move">Move to\u2026</string>
+ <!-- Menu item that renames the selected document [CHAR LIMIT=24] -->
+ <string name="menu_rename">Rename</string>
<!-- Menu item title that creates a new window in the activity [CHAR LIMIT=24] -->
<string name="menu_new_window">New window</string>
@@ -203,8 +207,6 @@
</plurals>
<!-- Toast shown when a user tries to paste files into an unsupported location. -->
<string name="clipboard_files_cannot_paste">Cannot paste the selected files in this location.</string>
- <!-- Menu item that renames the selected document [CHAR LIMIT=24] -->
- <string name="menu_rename">Rename</string>
<!-- Toast shown when renaming document failed with an error [CHAR LIMIT=48] -->
<string name="rename_error">Failed to rename document</string>
<!-- Context Menu item that ejects the root selected [CHAR LIMIT=24] -->
diff --git a/src/com/android/documentsui/DocumentsMenuManager.java b/src/com/android/documentsui/DocumentsMenuManager.java
index 035268e..6e19178 100644
--- a/src/com/android/documentsui/DocumentsMenuManager.java
+++ b/src/com/android/documentsui/DocumentsMenuManager.java
@@ -59,7 +59,7 @@
}
@Override
- void updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails) {
+ void updateSelectAll(MenuItem selectAll) {
selectAll.setVisible(mState.allowMultiple);
}
@@ -70,8 +70,18 @@
}
@Override
- void updateOpen(MenuItem open, SelectionDetails selectionDetails) {
+ void updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails) {
+ updateOpen(open, selectionDetails);
+ }
+
+ @Override
+ void updateOpenInContextMenu(MenuItem open, SelectionDetails selectionDetails) {
+ updateOpen(open, selectionDetails);
+ }
+
+ private void updateOpen(MenuItem open, SelectionDetails selectionDetails) {
open.setVisible(mState.action == ACTION_GET_CONTENT
|| mState.action == ACTION_OPEN);
+ open.setEnabled(selectionDetails.size() > 0);
}
}
diff --git a/src/com/android/documentsui/FilesMenuManager.java b/src/com/android/documentsui/FilesMenuManager.java
index cff1f0a..973c730 100644
--- a/src/com/android/documentsui/FilesMenuManager.java
+++ b/src/com/android/documentsui/FilesMenuManager.java
@@ -58,6 +58,24 @@
}
@Override
+ void updateOpenInContextMenu(MenuItem open, SelectionDetails selectionDetails) {
+ open.setEnabled(selectionDetails.size() == 1
+ && !selectionDetails.containsPartialFiles());
+ }
+
+ @Override
+ void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) {
+ openWith.setEnabled(selectionDetails.size() == 1
+ && !selectionDetails.containsPartialFiles());
+ }
+
+ @Override
+ void updateOpenInNewWindow(MenuItem openInNewWindow, SelectionDetails selectionDetails) {
+ openInNewWindow.setEnabled(selectionDetails.size() == 1
+ && !selectionDetails.containsPartialFiles());
+ }
+
+ @Override
void updateMoveTo(MenuItem moveTo, SelectionDetails selectionDetails) {
moveTo.setVisible(true);
moveTo.setEnabled(!selectionDetails.containsPartialFiles() && selectionDetails.canDelete());
@@ -75,7 +93,7 @@
}
@Override
- void updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails) {
+ void updateSelectAll(MenuItem selectAll) {
selectAll.setVisible(true);
}
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java
index 974a5bf..e9daa9f 100644
--- a/src/com/android/documentsui/MenuManager.java
+++ b/src/com/android/documentsui/MenuManager.java
@@ -20,6 +20,7 @@
import android.view.MenuItem;
import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.dirlist.DirectoryFragment;
public abstract class MenuManager {
@@ -31,20 +32,20 @@
mState = displayState;
}
- /** @See DirectoryFragment.SelectionModeListener#updateActionMenu */
+ /** @see ActionModeController */
public void updateActionMenu(Menu menu, SelectionDetails selection) {
- updateOpen(menu.findItem(R.id.menu_open), selection);
+ updateOpenInActionMode(menu.findItem(R.id.menu_open), selection);
updateDelete(menu.findItem(R.id.menu_delete), selection);
updateShare(menu.findItem(R.id.menu_share), selection);
updateRename(menu.findItem(R.id.menu_rename), selection);
- updateSelectAll(menu.findItem(R.id.menu_select_all), selection);
+ updateSelectAll(menu.findItem(R.id.menu_select_all));
updateMoveTo(menu.findItem(R.id.menu_move_to), selection);
updateCopyTo(menu.findItem(R.id.menu_copy_to), selection);
Menus.disableHiddenItems(menu);
}
- /** @See Activity#onPrepareOptionsMenu */
+ /** @see BaseActivity#onPrepareOptionsMenu */
public void updateOptionMenu(Menu menu, DirectoryDetails directoryDetails) {
updateCreateDir(menu.findItem(R.id.menu_create_dir), directoryDetails);
updateSettings(menu.findItem(R.id.menu_settings), directoryDetails);
@@ -57,76 +58,92 @@
Menus.disableHiddenItems(menu);
}
- /** @See DirectoryFragment.onCreateContextMenu
+ /**
+ * @see DirectoryFragment#onCreateContextMenu
*
- * Called when user tries to generate a context menu anchored to a file.
- * */
- public void updateContextMenuForFile(
- Menu menu,
- SelectionDetails selectionDetails,
- DirectoryDetails directoryDetails) {
+ * Called when user tries to generate a context menu anchored to a file when the selection
+ * doesn't contain any folder.
+ *
+ * @param selectionDetails
+ * containsFiles may return false because this may be called when user right clicks on an
+ * unselectable item in pickers
+ */
+ public void updateContextMenuForFiles(Menu menu, SelectionDetails selectionDetails) {
+ assert(selectionDetails != null);
+
+ MenuItem share = menu.findItem(R.id.menu_share);
+ MenuItem open = menu.findItem(R.id.menu_open);
+ MenuItem openWith = menu.findItem(R.id.menu_open_with);
+ MenuItem rename = menu.findItem(R.id.menu_rename);
+
+ updateShare(share, selectionDetails);
+ updateOpenInContextMenu(open, selectionDetails);
+ updateOpenWith(openWith, selectionDetails);
+ updateRename(rename, selectionDetails);
+
+ updateContextMenu(menu, selectionDetails);
+ }
+
+ /**
+ * @see DirectoryFragment#onCreateContextMenu
+ *
+ * Called when user tries to generate a context menu anchored to a folder when the selection
+ * doesn't contain any file.
+ *
+ * @param selectionDetails
+ * containDirectories may return false because this may be called when user right clicks on
+ * an unselectable item in pickers
+ */
+ public void updateContextMenuForDirs(Menu menu, SelectionDetails selectionDetails) {
+ assert(selectionDetails != null);
+
+ MenuItem openInNewWindow = menu.findItem(R.id.menu_open_in_new_window);
+ MenuItem rename = menu.findItem(R.id.menu_rename);
+ MenuItem pasteInto = menu.findItem(R.id.menu_paste_into_folder);
+
+ updateOpenInNewWindow(openInNewWindow, selectionDetails);
+ updateRename(rename, selectionDetails);
+ updatePasteInto(pasteInto, selectionDetails);
+
+ updateContextMenu(menu, selectionDetails);
+ }
+
+ /**
+ * @see DirectoryFragment#onCreateContextMenu
+ *
+ * Update shared context menu items of both files and folders context menus.
+ */
+ public void updateContextMenu(Menu menu, SelectionDetails selectionDetails) {
assert(selectionDetails != null);
MenuItem cut = menu.findItem(R.id.menu_cut_to_clipboard);
MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
- MenuItem pasteInto = menu.findItem(R.id.menu_paste_into_folder);
MenuItem delete = menu.findItem(R.id.menu_delete);
- MenuItem rename = menu.findItem(R.id.menu_rename);
- copy.setEnabled(!selectionDetails.containsPartialFiles());
- cut.setEnabled(
- !selectionDetails.containsPartialFiles() && selectionDetails.canDelete());
- updatePasteInto(pasteInto, selectionDetails);
- updateRename(rename, selectionDetails);
- updateDelete(delete, selectionDetails);
-
- updateContextMenu(menu, directoryDetails);
+ final boolean canCopy =
+ selectionDetails.size() > 0 && !selectionDetails.containsPartialFiles();
+ final boolean canDelete = selectionDetails.canDelete();
+ cut.setEnabled(canCopy && canDelete);
+ copy.setEnabled(canCopy);
+ delete.setEnabled(canDelete);
}
- /** @See DirectoryFragment.onCreateContextMenu
+ /**
+ * @see DirectoryFragment#onCreateContextMenu
*
* Called when user tries to generate a context menu anchored to an empty pane.
- * */
+ */
public void updateContextMenuForContainer(Menu menu, DirectoryDetails directoryDetails) {
- MenuItem cut = menu.findItem(R.id.menu_cut_to_clipboard);
- MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
- MenuItem pasteInto = menu.findItem(R.id.menu_paste_into_folder);
- MenuItem delete = menu.findItem(R.id.menu_delete);
- MenuItem rename = menu.findItem(R.id.menu_rename);
-
- cut.setEnabled(false);
- copy.setEnabled(false);
- pasteInto.setEnabled(false);
- rename.setEnabled(false);
- delete.setEnabled(false);
-
- updateContextMenu(menu, directoryDetails);
- }
-
- private void updateContextMenu(Menu menu, DirectoryDetails directoryDetails) {
-
- MenuItem cut = menu.findItem(R.id.menu_cut_to_clipboard);
- MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard);
MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard);
- MenuItem pasteInto = menu.findItem(R.id.menu_paste_into_folder);
- MenuItem delete = menu.findItem(R.id.menu_delete);
- MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+ MenuItem selectAll = menu.findItem(R.id.menu_select_all);
- updateCreateDir(createDir, directoryDetails);
- paste.setEnabled(directoryDetails.hasItemsToPaste());
-
- //Cut, Copy and Delete should always be visible
- cut.setVisible(true);
- copy.setVisible(true);
- delete.setVisible(true);
-
- // PasteInto should only show if it is enabled. If it's not enabled, Paste shows (regardless
- // of whether it is enabled or not).
- // Paste then hides itself whenever PasteInto is enabled/visible
- pasteInto.setVisible(pasteInto.isEnabled());
- paste.setVisible(!pasteInto.isVisible());
+ paste.setEnabled(directoryDetails.hasItemsToPaste() && directoryDetails.canCreateDoc());
+ updateSelectAll(selectAll);
}
+ /**
+ * @see RootsFragment#onCreateContextMenu
+ */
public void updateRootContextMenu(Menu menu, RootInfo root) {
MenuItem settings = menu.findItem(R.id.menu_settings);
MenuItem eject = menu.findItem(R.id.menu_eject_root);
@@ -162,10 +179,18 @@
newWindow.setVisible(false);
}
- void updateOpen(MenuItem open, SelectionDetails selectionDetails) {
+ void updateOpenInActionMode(MenuItem open, SelectionDetails selectionDetails) {
open.setVisible(false);
}
+ void updateOpenWith(MenuItem openWith, SelectionDetails selectionDetails) {
+ openWith.setVisible(false);
+ }
+
+ void updateOpenInNewWindow(MenuItem openInNewWindow, SelectionDetails selectionDetails) {
+ openInNewWindow.setVisible(false);
+ }
+
void updateShare(MenuItem share, SelectionDetails selectionDetails) {
share.setVisible(false);
}
@@ -187,10 +212,11 @@
}
void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) {
- pasteInto.setEnabled(false);
+ pasteInto.setVisible(false);
}
- abstract void updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails);
+ abstract void updateOpenInContextMenu(MenuItem open, SelectionDetails selectionDetails);
+ abstract void updateSelectAll(MenuItem selectAll);
abstract void updateCreateDir(MenuItem createDir, DirectoryDetails directoryDetails);
/**
@@ -199,6 +225,10 @@
public interface SelectionDetails {
boolean containsDirectories();
+ boolean containsFiles();
+
+ int size();
+
boolean containsPartialFiles();
// TODO: Update these to express characteristics instead of answering concrete questions,
@@ -229,6 +259,10 @@
return false;
}
+ public boolean canCreateDoc() {
+ return isInRecents() ? false : mActivity.getCurrentDirectory().isCreateSupported();
+ }
+
public boolean isInRecents() {
return mActivity.getCurrentDirectory() == null;
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index b6a0009..4965ead 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -55,7 +55,6 @@
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
@@ -296,7 +295,7 @@
? MultiSelectManager.MODE_MULTIPLE
: MultiSelectManager.MODE_SINGLE,
this::canSetSelectionState);
- mSelectionMetadata = new SelectionMetadata(mSelectionMgr, mModel::getItem);
+ mSelectionMetadata = new SelectionMetadata(mModel::getItem);
mSelectionMgr.addItemCallback(mSelectionMetadata);
mModel.addUpdateListener(mAdapter);
@@ -438,23 +437,47 @@
View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.context_menu, menu);
+ final MenuInflater inflater = getActivity().getMenuInflater();
- menu.add(Menu.NONE, R.id.menu_create_dir, Menu.NONE, R.string.menu_create_dir);
- menu.add(Menu.NONE, R.id.menu_delete, Menu.NONE, R.string.menu_delete);
- menu.add(Menu.NONE, R.id.menu_rename, Menu.NONE, R.string.menu_rename);
-
- boolean mouseOverFile = !(v == mRecView || v == mEmptyView);
- if (mouseOverFile) {
- mMenuManager.updateContextMenuForFile(
- menu,
- mSelectionMetadata,
- getBaseActivity().getDirectoryDetails());
- } else {
- mMenuManager.updateContextMenuForContainer(
- menu, getBaseActivity().getDirectoryDetails());
+ final String modelId = getModelId(v);
+ if (modelId == null) {
+ inflater.inflate(R.menu.container_context_menu, menu);
+ mMenuManager.updateContextMenuForContainer(
+ menu, getBaseActivity().getDirectoryDetails());
+ return;
}
+
+ final boolean hasDir = mSelectionMetadata.containsDirectories();
+ final boolean hasFile = mSelectionMetadata.containsFiles();
+ if (!hasDir && !hasFile) {
+ // User triggered a context menu on a doc without any selection. This is a legitimate
+ // case in pickers while user right clicks on an unselectable item.
+ final String mimeType = DocumentInfo.getCursorString(
+ mModel.getItem(modelId), Document.COLUMN_MIME_TYPE);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ inflater.inflate(R.menu.dir_context_menu, menu);
+ mMenuManager.updateContextMenuForDirs(menu, mSelectionMetadata);
+ } else {
+ inflater.inflate(R.menu.file_context_menu, menu);
+ mMenuManager.updateContextMenuForFiles(menu, mSelectionMetadata);
+ }
+ return;
+ }
+
+ if (!hasDir) {
+ inflater.inflate(R.menu.file_context_menu, menu);
+ mMenuManager.updateContextMenuForFiles(menu, mSelectionMetadata);
+ return;
+ }
+
+ if (!hasFile) {
+ inflater.inflate(R.menu.dir_context_menu, menu);
+ mMenuManager.updateContextMenuForDirs(menu, mSelectionMetadata);
+ return;
+ }
+
+ inflater.inflate(R.menu.mixed_context_menu, menu);
+ mMenuManager.updateContextMenu(menu, mSelectionMetadata);
}
@Override
@@ -523,16 +546,6 @@
return false;
}
- public void onDisplayStateChanged() {
- updateDisplayState();
- }
-
- public void onSortOrderChanged() {
- // Sort order is implemented as a sorting wrapper around directory
- // results. So when sort order changes, we force a reload of the directory.
- getLoaderManager().restartLoader(LOADER_ID, null, this);
- }
-
public void onViewModeChanged() {
// Mode change is just visual change; no need to kick loader.
updateDisplayState();
@@ -696,7 +709,12 @@
// Model must be accessed in UI thread, since underlying cursor is not threadsafe.
List<DocumentInfo> docs = mModel.getDocuments(selected);
- BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
+ BaseActivity activity = getBaseActivity();
+ if (docs.size() > 1) {
+ activity.onDocumentsPicked(docs);
+ } else {
+ activity.onDocumentPicked(docs.get(0), mModel);
+ }
}
private void shareDocuments(final Selection selected) {
diff --git a/src/com/android/documentsui/dirlist/MultiSelectManager.java b/src/com/android/documentsui/dirlist/MultiSelectManager.java
index 780c564..49eaa37 100644
--- a/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -212,7 +212,10 @@
private boolean setItemsSelectedQuietly(Iterable<String> ids, boolean selected) {
boolean changed = false;
for (String id: ids) {
- final boolean itemChanged = selected ? mSelection.add(id) : mSelection.remove(id);
+ final boolean itemChanged =
+ selected
+ ? canSetState(id, true) && mSelection.add(id)
+ : canSetState(id, false) && mSelection.remove(id);
if (itemChanged) {
notifyItemStateChanged(id, selected);
}
diff --git a/src/com/android/documentsui/dirlist/SelectionMetadata.java b/src/com/android/documentsui/dirlist/SelectionMetadata.java
index 7dbcacb..6a289f2 100644
--- a/src/com/android/documentsui/dirlist/SelectionMetadata.java
+++ b/src/com/android/documentsui/dirlist/SelectionMetadata.java
@@ -35,19 +35,18 @@
private static final String TAG = "SelectionMetadata";
- private final MultiSelectManager mSelectionMgr;
private final Function<String, Cursor> mDocFinder;
+ private int mDirectoryCount = 0;
+ private int mFileCount = 0;
+
// Partial files are files that haven't been fully downloaded.
private int mPartialCount = 0;
- private int mDirectoryCount = 0;
private int mWritableDirectoryCount = 0;
private int mNoDeleteCount = 0;
private int mNoRenameCount = 0;
- SelectionMetadata(
- MultiSelectManager selectionMgr, Function<String, Cursor> docFinder) {
- mSelectionMgr = selectionMgr;
+ SelectionMetadata(Function<String, Cursor> docFinder) {
mDocFinder = docFinder;
}
@@ -60,23 +59,27 @@
return;
}
+ final int delta = selected ? 1 : -1;
+
final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
if (MimePredicate.isDirectoryType(mimeType)) {
- mDirectoryCount += selected ? 1 : -1;
+ mDirectoryCount += delta;
+ } else {
+ mFileCount += delta;
}
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if ((docFlags & Document.FLAG_PARTIAL) != 0) {
- mPartialCount += selected ? 1 : -1;
+ mPartialCount += delta;
}
if ((docFlags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0) {
- mWritableDirectoryCount += selected ? 1 : -1;
+ mWritableDirectoryCount += delta;
}
if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) {
- mNoDeleteCount += selected ? 1 : -1;
+ mNoDeleteCount += delta;
}
if ((docFlags & Document.FLAG_SUPPORTS_RENAME) == 0) {
- mNoRenameCount += selected ? 1 : -1;
+ mNoRenameCount += delta;
}
}
@@ -86,23 +89,32 @@
}
@Override
+ public boolean containsFiles() {
+ return mFileCount > 0;
+ }
+
+ @Override
+ public int size() {
+ return mDirectoryCount + mFileCount;
+ }
+
+ @Override
public boolean containsPartialFiles() {
return mPartialCount > 0;
}
@Override
public boolean canDelete() {
- return mNoDeleteCount == 0;
+ return size() > 0 && mNoDeleteCount == 0;
}
@Override
public boolean canRename() {
- return mNoRenameCount == 0 && mSelectionMgr.getSelection().size() == 1;
+ return mNoRenameCount == 0 && size() == 1;
}
@Override
public boolean canPasteInto() {
- return mDirectoryCount == 1 && mWritableDirectoryCount == 1
- && mSelectionMgr.getSelection().size() == 1;
+ return mDirectoryCount == 1 && mWritableDirectoryCount == 1 && size() == 1;
}
}
diff --git a/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java b/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java
index 5272e43..f08e915 100644
--- a/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java
+++ b/tests/src/com/android/documentsui/DocumentsMenuManagerTest.java
@@ -43,6 +43,8 @@
private TestMenu testMenu;
private TestMenuItem open;
+ private TestMenuItem openInNewWindow;
+ private TestMenuItem openWith;
private TestMenuItem share;
private TestMenuItem delete;
private TestMenuItem rename;
@@ -68,6 +70,8 @@
public void setUp() {
testMenu = TestMenu.create();
open = testMenu.findItem(R.id.menu_open);
+ openInNewWindow = testMenu.findItem(R.id.menu_open_in_new_window);
+ openWith = testMenu.findItem(R.id.menu_open_with);
share = testMenu.findItem(R.id.menu_share);
delete = testMenu.findItem(R.id.menu_delete);
rename = testMenu.findItem(R.id.menu_rename);
@@ -149,8 +153,8 @@
@Test
public void testOptionMenu_canCreateDirectory() {
- directoryDetails.canCreateDirectory = true;
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ directoryDetails.canCreateDirectory = true;
mgr.updateOptionMenu(testMenu, directoryDetails);
createDir.assertEnabled();
@@ -169,8 +173,8 @@
@Test
public void testOptionMenu_inRecents() {
- directoryDetails.isInRecents = true;
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ directoryDetails.isInRecents = true;
mgr.updateOptionMenu(testMenu, directoryDetails);
grid.assertInvisible();
@@ -181,27 +185,22 @@
public void testContextMenu_EmptyArea() {
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
mgr.updateContextMenuForContainer(testMenu, directoryDetails);
- cut.assertVisible();
- copy.assertVisible();
- cut.assertDisabled();
- copy.assertDisabled();
+ selectAll.assertVisible();
paste.assertVisible();
- pasteInto.assertInvisible();
createDir.assertVisible();
- delete.assertVisible();
}
@Test
public void testContextMenu_OnFile() {
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
- mgr.updateContextMenuForFile(testMenu, selectionDetails, directoryDetails);
+ mgr.updateContextMenuForFiles(testMenu, selectionDetails);
+ // We don't want share in pickers.
+ share.assertInvisible();
+ // We don't want openWith in pickers.
+ openWith.assertInvisible();
cut.assertVisible();
copy.assertVisible();
- paste.assertVisible();
- pasteInto.assertInvisible();
- paste.assertDisabled();
rename.assertInvisible();
- createDir.assertVisible();
delete.assertVisible();
}
@@ -209,15 +208,14 @@
public void testContextMenu_OnDirectory() {
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
selectionDetails.canPasteInto = true;
- mgr.updateContextMenuForFile(testMenu, selectionDetails, directoryDetails);
+ mgr.updateContextMenuForDirs(testMenu, selectionDetails);
+ // We don't want openInNewWindow in pickers
+ openInNewWindow.assertInvisible();
cut.assertVisible();
copy.assertVisible();
// Doesn't matter if directory is selected, we don't want pasteInto for DocsActivity
- paste.assertVisible();
pasteInto.assertInvisible();
- pasteInto.assertDisabled();
rename.assertInvisible();
- createDir.assertVisible();
delete.assertVisible();
}
@@ -232,8 +230,8 @@
@Test
public void testRootContextMenu_hasRootSettings() {
- testRootInfo.flags = Root.FLAG_HAS_SETTINGS;
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ testRootInfo.flags = Root.FLAG_HAS_SETTINGS;
mgr.updateRootContextMenu(testMenu, testRootInfo);
settings.assertInvisible();
@@ -241,8 +239,8 @@
@Test
public void testRootContextMenu_canEject() {
- testRootInfo.flags = Root.FLAG_SUPPORTS_EJECT;
DocumentsMenuManager mgr = new DocumentsMenuManager(testSearchManager, state);
+ testRootInfo.flags = Root.FLAG_SUPPORTS_EJECT;
mgr.updateRootContextMenu(testMenu, testRootInfo);
eject.assertInvisible();
diff --git a/tests/src/com/android/documentsui/FilesMenuManagerTest.java b/tests/src/com/android/documentsui/FilesMenuManagerTest.java
index 3924ece..cbec819 100644
--- a/tests/src/com/android/documentsui/FilesMenuManagerTest.java
+++ b/tests/src/com/android/documentsui/FilesMenuManagerTest.java
@@ -40,6 +40,7 @@
private TestMenu testMenu;
private TestMenuItem rename;
+ private TestMenuItem selectAll;
private TestMenuItem moveTo;
private TestMenuItem copyTo;
private TestMenuItem share;
@@ -47,6 +48,9 @@
private TestMenuItem createDir;
private TestMenuItem settings;
private TestMenuItem newWindow;
+ private TestMenuItem open;
+ private TestMenuItem openWith;
+ private TestMenuItem openInNewWindow;
private TestMenuItem cut;
private TestMenuItem copy;
private TestMenuItem paste;
@@ -63,6 +67,7 @@
public void setUp() {
testMenu = TestMenu.create();
rename = testMenu.findItem(R.id.menu_rename);
+ selectAll = testMenu.findItem(R.id.menu_select_all);
moveTo = testMenu.findItem(R.id.menu_move_to);
copyTo = testMenu.findItem(R.id.menu_copy_to);
share = testMenu.findItem(R.id.menu_share);
@@ -70,6 +75,9 @@
createDir = testMenu.findItem(R.id.menu_create_dir);
settings = testMenu.findItem(R.id.menu_settings);
newWindow = testMenu.findItem(R.id.menu_new_window);
+ open = testMenu.findItem(R.id.menu_open);
+ openWith = testMenu.findItem(R.id.menu_open_with);
+ openInNewWindow = testMenu.findItem(R.id.menu_open_in_new_window);
cut = testMenu.findItem(R.id.menu_cut_to_clipboard);
copy = testMenu.findItem(R.id.menu_copy_to_clipboard);
paste = testMenu.findItem(R.id.menu_paste_from_clipboard);
@@ -197,60 +205,79 @@
public void testContextMenu_EmptyArea() {
FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
mgr.updateContextMenuForContainer(testMenu, directoryDetails);
+
+ selectAll.assertVisible();
+ paste.assertVisible();
+ createDir.assertVisible();
+ }
+
+ @Test
+ public void testContextMenu_OnFile() {
+ selectionDetails.size = 1;
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
+ mgr.updateContextMenuForFiles(testMenu, selectionDetails);
+ open.assertVisible();
+ open.assertEnabled();
+ openWith.assertVisible();
+ openWith.assertEnabled();
cut.assertVisible();
copy.assertVisible();
- cut.assertDisabled();
- copy.assertDisabled();
- paste.assertVisible();
- pasteInto.assertInvisible();
+ rename.assertVisible();
createDir.assertVisible();
delete.assertVisible();
}
@Test
- public void testContextMenu_OnFile() {
+ public void testContextMenu_OnMultipleFiles() {
FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
- mgr.updateContextMenuForFile(testMenu, selectionDetails, directoryDetails);
- cut.assertVisible();
- copy.assertVisible();
- paste.assertVisible();
- pasteInto.assertInvisible();
- paste.assertDisabled();
- rename.assertVisible();
- createDir.assertVisible();
- delete.assertVisible();
+ selectionDetails.size = 3;
+ mgr.updateContextMenuForFiles(testMenu, selectionDetails);
+ open.assertVisible();
+ open.assertDisabled();
+ openWith.assertVisible();
+ openWith.assertDisabled();
}
@Test
public void testContextMenu_OnWritableDirectory() {
FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
+ selectionDetails.size = 1;
selectionDetails.canPasteInto = true;
- mgr.updateContextMenuForFile(testMenu, selectionDetails, directoryDetails);
+ mgr.updateContextMenuForDirs(testMenu, selectionDetails);
+ openInNewWindow.assertVisible();
+ openInNewWindow.assertEnabled();
cut.assertVisible();
copy.assertVisible();
- paste.assertInvisible();
pasteInto.assertVisible();
pasteInto.assertEnabled();
rename.assertVisible();
- createDir.assertVisible();
delete.assertVisible();
}
@Test
public void testContextMenu_OnNonWritableDirectory() {
FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
+ selectionDetails.size = 1;
selectionDetails.canPasteInto = false;
- mgr.updateContextMenuForFile(testMenu, selectionDetails, directoryDetails);
+ mgr.updateContextMenuForDirs(testMenu, selectionDetails);
+ openInNewWindow.assertVisible();
+ openInNewWindow.assertEnabled();
cut.assertVisible();
copy.assertVisible();
- paste.assertVisible();
- pasteInto.assertInvisible();
+ pasteInto.assertVisible();
pasteInto.assertDisabled();
rename.assertVisible();
- createDir.assertVisible();
delete.assertVisible();
}
+ @Test
+ public void testContextMenu_OnMultipleDirectories() {
+ FilesMenuManager mgr = new FilesMenuManager(testSearchManager, state);
+ selectionDetails.size = 3;
+ mgr.updateContextMenuForDirs(testMenu, selectionDetails);
+ openInNewWindow.assertVisible();
+ openInNewWindow.assertDisabled();
+ }
@Test
public void testRootContextMenu() {
diff --git a/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
index 1c7b863..fef5672 100644
--- a/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
+++ b/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java
@@ -29,6 +29,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -87,6 +88,24 @@
}
@Test
+ public void testSetItemsSelected() {
+ mManager.setItemsSelected(getStringIds(6, 7, 8), true);
+
+ mSelection.assertRangeSelected(6, 8);
+ }
+
+ @Test
+ public void testSetItemsSelected_SkipUnselectableItem() {
+ mIgnored.add(ITEMS.get(7));
+
+ mManager.setItemsSelected(getStringIds(6, 7, 8), true);
+
+ mSelection.assertSelected(6);
+ mSelection.assertNotSelected(7);
+ mSelection.assertSelected(8);
+ }
+
+ @Test
public void testRangeSelection() {
mManager.startRangeSelection(15);
mManager.snapRangeSelection(19);
@@ -283,4 +302,12 @@
return ids;
}
+
+ private static Iterable<String> getStringIds(int... ids) {
+ List<String> stringIds = new ArrayList<>(ids.length);
+ for (int id : ids) {
+ stringIds.add(ITEMS.get(id));
+ }
+ return stringIds;
+ }
}
diff --git a/tests/src/com/android/documentsui/testing/TestMenu.java b/tests/src/com/android/documentsui/testing/TestMenu.java
index 718b2dc..d61db11 100644
--- a/tests/src/com/android/documentsui/testing/TestMenu.java
+++ b/tests/src/com/android/documentsui/testing/TestMenu.java
@@ -36,7 +36,10 @@
private SparseArray<TestMenuItem> items = new SparseArray<>();
public static TestMenu create() {
- return create(R.id.menu_open,
+ return create(
+ R.id.menu_open,
+ R.id.menu_open_in_new_window,
+ R.id.menu_open_with,
R.id.menu_rename,
R.id.menu_move_to,
R.id.menu_copy_to,
@@ -62,7 +65,7 @@
menu.items = new SparseArray<>();
for (int id : ids) {
TestMenuItem item = TestMenuItem.create(id);
- menu.addMenuItem(id, item);
+ menu.addMenuItem(id, item);
}
return menu;
}
diff --git a/tests/src/com/android/documentsui/testing/TestMenuItem.java b/tests/src/com/android/documentsui/testing/TestMenuItem.java
index 6314b7b..110617b 100644
--- a/tests/src/com/android/documentsui/testing/TestMenuItem.java
+++ b/tests/src/com/android/documentsui/testing/TestMenuItem.java
@@ -43,6 +43,10 @@
final TestMenuItem mockMenuItem = Mockito.mock(TestMenuItem.class,
Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS));
+ // By default all menu items are enabled and visible.
+ mockMenuItem.enabled = true;
+ mockMenuItem.visible = true;
+
return mockMenuItem;
}
diff --git a/tests/src/com/android/documentsui/testing/TestSelectionDetails.java b/tests/src/com/android/documentsui/testing/TestSelectionDetails.java
index 795ef7a..e7f9b6c 100644
--- a/tests/src/com/android/documentsui/testing/TestSelectionDetails.java
+++ b/tests/src/com/android/documentsui/testing/TestSelectionDetails.java
@@ -23,10 +23,12 @@
*/
public class TestSelectionDetails implements SelectionDetails {
+ public int size;
public boolean canRename;
public boolean canDelete;
public boolean containPartial;
public boolean containDirectories;
+ public boolean containFiles;
public boolean canPasteInto;
@Override
@@ -35,6 +37,11 @@
}
@Override
+ public boolean containsFiles() {
+ return containFiles;
+ }
+
+ @Override
public boolean containsDirectories() {
return containDirectories;
}
@@ -53,4 +60,9 @@
public boolean canPasteInto() {
return canPasteInto;
}
+
+ @Override
+ public int size() {
+ return size;
+ }
}
\ No newline at end of file