diff options
3 files changed, 91 insertions, 60 deletions
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java index 2f202e7b976b..859763b833f0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java +++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java @@ -17,6 +17,7 @@ package com.android.documentsui; import android.annotation.Nullable; +import android.provider.DocumentsContract.Document; import com.android.documentsui.model.DocumentInfo; import com.android.internal.util.Predicate; @@ -99,4 +100,8 @@ public class MimePredicate implements Predicate<DocumentInfo> { public static boolean isApkType(@Nullable String mimeType) { return APK_TYPE.equals(mimeType); } + + public static boolean isDirectoryType(@Nullable String mimeType) { + return Document.MIME_TYPE_DIR.equals(mimeType); + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 312acb593922..7e3e96798c7b 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -62,7 +62,6 @@ import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; -import android.util.TypedValue; import android.view.ActionMode; import android.view.DragEvent; import android.view.GestureDetector; @@ -89,6 +88,7 @@ import com.android.documentsui.Events.MotionInputEvent; import com.android.documentsui.Menus; import com.android.documentsui.MessageBar; import com.android.documentsui.Metrics; +import com.android.documentsui.MimePredicate; import com.android.documentsui.R; import com.android.documentsui.RecentsLoader; import com.android.documentsui.RootsCache; @@ -103,6 +103,7 @@ import com.android.documentsui.model.RootInfo; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperationService.OpType; import com.android.documentsui.services.FileOperations; + import com.google.common.collect.Lists; import java.lang.annotation.Retention; @@ -110,7 +111,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; @@ -458,13 +458,17 @@ public class DirectoryFragment extends Fragment * ActionMode when there is a selection, canceling it when there is no selection, * and clearing selection when action mode is explicitly exited by the user. */ - private final class SelectionModeListener - implements MultiSelectManager.Callback, ActionMode.Callback { + private final class SelectionModeListener implements MultiSelectManager.Callback, + ActionMode.Callback, FragmentTuner.SelectionDetails { private Selection mSelected = new Selection(); - private int mNoCopyCount = 0; + + // Partial files are files that haven't been fully downloaded. + private int mPartialCount = 0; + private int mDirectoryCount = 0; private int mNoDeleteCount = 0; - private int mNoRenameCount = -1; + private int mNoRenameCount = 0; + private Menu mMenu; @Override @@ -506,14 +510,19 @@ public class DirectoryFragment extends Fragment // TODO: Should this be happening in onSelectionChanged? Technically this callback is // triggered on "silent" selection updates (i.e. we might be reacting to unfinalized // selection changes here) + final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); + if (MimePredicate.isDirectoryType(mimeType)) { + mDirectoryCount += selected ? 1 : -1; + } + final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if ((docFlags & Document.FLAG_PARTIAL) != 0) { - mNoCopyCount += selected ? 1 : -1; + mPartialCount += selected ? 1 : -1; } if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) { mNoDeleteCount += selected ? 1 : -1; } - if ((docFlags & Document.FLAG_SUPPORTS_RENAME) != 0) { + if ((docFlags & Document.FLAG_SUPPORTS_RENAME) == 0) { mNoRenameCount += selected ? 1 : -1; } } @@ -552,8 +561,11 @@ public class DirectoryFragment extends Fragment // clear selection mSelectionManager.clearSelection(); mSelected.clear(); + + mDirectoryCount = 0; + mPartialCount = 0; mNoDeleteCount = 0; - mNoRenameCount = -1; + mNoRenameCount = 0; // Re-enable TalkBack for the toolbars, as they are no longer covered by action mode. final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar); @@ -601,24 +613,29 @@ public class DirectoryFragment extends Fragment return true; } - boolean canCopySelection() { - return mNoCopyCount == 0; + @Override + public boolean containsDirectories() { + return mDirectoryCount > 0; } - boolean canDeleteSelection() { + @Override + public boolean containsPartialFiles() { + return mPartialCount > 0; + } + + @Override + public boolean canDelete() { return mNoDeleteCount == 0; } - boolean canRenameSelection() { + @Override + public boolean canRename() { return mNoRenameCount == 0 && mSelectionManager.getSelection().size() == 1; } private void updateActionMenu() { assert(mMenu != null); - - // Delegate update logic to our owning action, since specialized logic is desired. - mTuner.updateActionMenu( - mMenu, mType, canCopySelection(), canDeleteSelection(), canRenameSelection()); + mTuner.updateActionMenu(mMenu, this); Menus.disableHiddenItems(mMenu); } @@ -1174,7 +1191,7 @@ public class DirectoryFragment extends Fragment view.setOnDragListener(mOnDragListener); } - if (mTuner.allowDragNDrop()) { + if (mTuner.dragAndDropEnabled()) { // Make all items draggable. view.setOnLongClickListener(onLongClickListener); } @@ -1761,7 +1778,7 @@ public class DirectoryFragment extends Fragment mRoot.authority, mRoot.rootId, mQuery) : DocumentsContract.buildChildDocumentsUri( mDocument.authority, mDocument.documentId); - if (mTuner.enableManagedMode()) { + if (mTuner.managedModeEnabled()) { contentsUri = DocumentsContract.setManageMode(contentsUri); } return new DirectoryLoader( diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java index 9e2b86a8cea9..7b0510be644e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java @@ -58,9 +58,6 @@ public abstract class FragmentTuner { } } - public abstract void updateActionMenu( - Menu menu, @ResultType int dirType, - boolean canCopy, boolean canDelete, boolean canRename); // Subtly different from isDocumentEnabled. The reason may be illuminated as follows. // A folder is enabled such that it may be double clicked, even in settings @@ -73,18 +70,23 @@ public abstract class FragmentTuner { return true; } - abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch); - /** * When managed mode is enabled, active downloads will be visible in the UI. * Presumably this should only be true when in the downloads directory. */ - abstract boolean enableManagedMode(); + boolean managedModeEnabled() { + return false; + } /** * Whether drag n' drop is allowed in this context */ - abstract boolean allowDragNDrop(); + boolean dragAndDropEnabled() { + return false; + } + + abstract void updateActionMenu(Menu menu, SelectionDetails selection); + abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch); /** * Provides support for Platform specific specializations of DirectoryFragment. @@ -105,7 +107,7 @@ public abstract class FragmentTuner { return false; } - if (isDirectory(docMimeType)) { + if (MimePredicate.isDirectoryType(docMimeType)) { return false; } @@ -121,9 +123,9 @@ public abstract class FragmentTuner { } @Override - public boolean isDocumentEnabled(String docMimeType, int docFlags) { + public boolean isDocumentEnabled(String mimeType, int docFlags) { // Directories are always enabled. - if (isDirectory(docMimeType)) { + if (MimePredicate.isDirectoryType(mimeType)) { return true; } @@ -141,13 +143,11 @@ public abstract class FragmentTuner { } } - return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType); + return MimePredicate.mimeMatches(mState.acceptMimes, mimeType); } @Override - public void updateActionMenu( - Menu menu, @ResultType int dirType, - boolean canCopy, boolean canDelete, boolean canRename) { + public void updateActionMenu(Menu menu, SelectionDetails selection) { MenuItem open = menu.findItem(R.id.menu_open); MenuItem share = menu.findItem(R.id.menu_share); @@ -155,8 +155,8 @@ public abstract class FragmentTuner { MenuItem rename = menu.findItem(R.id.menu_rename); MenuItem selectAll = menu.findItem(R.id.menu_select_all); - open.setVisible(mState.action == ACTION_GET_CONTENT || - mState.action == ACTION_OPEN); + open.setVisible(mState.action == ACTION_GET_CONTENT + || mState.action == ACTION_OPEN); share.setVisible(false); delete.setVisible(false); rename.setVisible(false); @@ -191,16 +191,6 @@ public abstract class FragmentTuner { } mModelPreviousLoaded = true; } - - @Override - public boolean enableManagedMode() { - return false; - } - - @Override - public boolean allowDragNDrop() { - return false; - } } /** @@ -217,29 +207,39 @@ public abstract class FragmentTuner { } @Override - public void updateActionMenu( - Menu menu, @ResultType int dirType, - boolean canCopy, boolean canDelete, boolean canRename) { + public void updateActionMenu(Menu menu, SelectionDetails selection) { + + menu.findItem(R.id.menu_open).setVisible(false); // "open" is never used in Files. + // Commands accessible only via keyboard... MenuItem copy = menu.findItem(R.id.menu_copy_to_clipboard); MenuItem paste = menu.findItem(R.id.menu_paste_from_clipboard); - copy.setEnabled(canCopy); + // Commands visible in the UI... MenuItem rename = menu.findItem(R.id.menu_rename); MenuItem moveTo = menu.findItem(R.id.menu_move_to); MenuItem copyTo = menu.findItem(R.id.menu_copy_to); + MenuItem share = menu.findItem(R.id.menu_share); + MenuItem delete = menu.findItem(R.id.menu_delete); + + // copy is not visible, keyboard only + copy.setEnabled(!selection.containsPartialFiles()); + // Commands usually on action-bar, so we always manage visibility. + share.setVisible(!selection.containsDirectories() && !selection.containsPartialFiles()); + delete.setVisible(selection.canDelete()); + + share.setEnabled(!selection.containsDirectories() && !selection.containsPartialFiles()); + delete.setEnabled(selection.canDelete()); + + // Commands always in overflow, so we don't bother showing/hiding... copyTo.setVisible(true); moveTo.setVisible(true); rename.setVisible(true); - copyTo.setEnabled(canCopy); - moveTo.setEnabled(canCopy && canDelete); - rename.setEnabled(canRename); - - menu.findItem(R.id.menu_share).setVisible(true); - menu.findItem(R.id.menu_delete).setVisible(canDelete); - menu.findItem(R.id.menu_open).setVisible(false); + copyTo.setEnabled(!selection.containsPartialFiles()); + moveTo.setEnabled(!selection.containsPartialFiles() && selection.canDelete()); + rename.setEnabled(!selection.containsPartialFiles() && selection.canRename()); Menus.disableHiddenItems(menu, copy, paste); } @@ -256,7 +256,7 @@ public abstract class FragmentTuner { } @Override - public boolean enableManagedMode() { + public boolean managedModeEnabled() { // When in downloads top level directory, we also show active downloads. // And while we don't allow folders in Downloads, we do allow Zip files in // downloads that themselves can be opened and viewed like directories. @@ -267,12 +267,21 @@ public abstract class FragmentTuner { } @Override - public boolean allowDragNDrop() { + public boolean dragAndDropEnabled() { return true; } } - private static boolean isDirectory(String mimeType) { - return Document.MIME_TYPE_DIR.equals(mimeType); + /** + * Access to meta data about the selection. + */ + interface SelectionDetails { + boolean containsDirectories(); + boolean containsPartialFiles(); + + // TODO: Update these to express characteristics instead of answering concrete questions, + // since the answer to those questions is (or can be) activity specific. + boolean canDelete(); + boolean canRename(); } } |