Merge "Disable share when dirs selected." into nyc-dev

This commit is contained in:
Steve McKay
2016-05-04 16:46:18 +00:00
committed by Android (Google) Code Review
3 changed files with 91 additions and 60 deletions

View File

@@ -17,6 +17,7 @@
package com.android.documentsui; package com.android.documentsui;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.provider.DocumentsContract.Document;
import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentInfo;
import com.android.internal.util.Predicate; import com.android.internal.util.Predicate;
@@ -99,4 +100,8 @@ public class MimePredicate implements Predicate<DocumentInfo> {
public static boolean isApkType(@Nullable String mimeType) { public static boolean isApkType(@Nullable String mimeType) {
return APK_TYPE.equals(mimeType); return APK_TYPE.equals(mimeType);
} }
public static boolean isDirectoryType(@Nullable String mimeType) {
return Document.MIME_TYPE_DIR.equals(mimeType);
}
} }

View File

@@ -62,7 +62,6 @@ import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TypedValue;
import android.view.ActionMode; import android.view.ActionMode;
import android.view.DragEvent; import android.view.DragEvent;
import android.view.GestureDetector; import android.view.GestureDetector;
@@ -89,6 +88,7 @@ import com.android.documentsui.Events.MotionInputEvent;
import com.android.documentsui.Menus; import com.android.documentsui.Menus;
import com.android.documentsui.MessageBar; import com.android.documentsui.MessageBar;
import com.android.documentsui.Metrics; import com.android.documentsui.Metrics;
import com.android.documentsui.MimePredicate;
import com.android.documentsui.R; import com.android.documentsui.R;
import com.android.documentsui.RecentsLoader; import com.android.documentsui.RecentsLoader;
import com.android.documentsui.RootsCache; 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;
import com.android.documentsui.services.FileOperationService.OpType; import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations; import com.android.documentsui.services.FileOperations;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -110,7 +111,6 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Set; 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, * 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. * and clearing selection when action mode is explicitly exited by the user.
*/ */
private final class SelectionModeListener private final class SelectionModeListener implements MultiSelectManager.Callback,
implements MultiSelectManager.Callback, ActionMode.Callback { ActionMode.Callback, FragmentTuner.SelectionDetails {
private Selection mSelected = new Selection(); 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 mNoDeleteCount = 0;
private int mNoRenameCount = -1; private int mNoRenameCount = 0;
private Menu mMenu; private Menu mMenu;
@Override @Override
@@ -506,14 +510,19 @@ public class DirectoryFragment extends Fragment
// TODO: Should this be happening in onSelectionChanged? Technically this callback is // TODO: Should this be happening in onSelectionChanged? Technically this callback is
// triggered on "silent" selection updates (i.e. we might be reacting to unfinalized // triggered on "silent" selection updates (i.e. we might be reacting to unfinalized
// selection changes here) // 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); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if ((docFlags & Document.FLAG_PARTIAL) != 0) { if ((docFlags & Document.FLAG_PARTIAL) != 0) {
mNoCopyCount += selected ? 1 : -1; mPartialCount += selected ? 1 : -1;
} }
if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) { if ((docFlags & Document.FLAG_SUPPORTS_DELETE) == 0) {
mNoDeleteCount += selected ? 1 : -1; mNoDeleteCount += selected ? 1 : -1;
} }
if ((docFlags & Document.FLAG_SUPPORTS_RENAME) != 0) { if ((docFlags & Document.FLAG_SUPPORTS_RENAME) == 0) {
mNoRenameCount += selected ? 1 : -1; mNoRenameCount += selected ? 1 : -1;
} }
} }
@@ -552,8 +561,11 @@ public class DirectoryFragment extends Fragment
// clear selection // clear selection
mSelectionManager.clearSelection(); mSelectionManager.clearSelection();
mSelected.clear(); mSelected.clear();
mDirectoryCount = 0;
mPartialCount = 0;
mNoDeleteCount = 0; mNoDeleteCount = 0;
mNoRenameCount = -1; mNoRenameCount = 0;
// Re-enable TalkBack for the toolbars, as they are no longer covered by action mode. // Re-enable TalkBack for the toolbars, as they are no longer covered by action mode.
final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar); final Toolbar toolbar = (Toolbar) getActivity().findViewById(R.id.toolbar);
@@ -601,24 +613,29 @@ public class DirectoryFragment extends Fragment
return true; return true;
} }
boolean canCopySelection() { @Override
return mNoCopyCount == 0; public boolean containsDirectories() {
return mDirectoryCount > 0;
} }
boolean canDeleteSelection() { @Override
public boolean containsPartialFiles() {
return mPartialCount > 0;
}
@Override
public boolean canDelete() {
return mNoDeleteCount == 0; return mNoDeleteCount == 0;
} }
boolean canRenameSelection() { @Override
public boolean canRename() {
return mNoRenameCount == 0 && mSelectionManager.getSelection().size() == 1; return mNoRenameCount == 0 && mSelectionManager.getSelection().size() == 1;
} }
private void updateActionMenu() { private void updateActionMenu() {
assert(mMenu != null); assert(mMenu != null);
mTuner.updateActionMenu(mMenu, this);
// Delegate update logic to our owning action, since specialized logic is desired.
mTuner.updateActionMenu(
mMenu, mType, canCopySelection(), canDeleteSelection(), canRenameSelection());
Menus.disableHiddenItems(mMenu); Menus.disableHiddenItems(mMenu);
} }
@@ -1174,7 +1191,7 @@ public class DirectoryFragment extends Fragment
view.setOnDragListener(mOnDragListener); view.setOnDragListener(mOnDragListener);
} }
if (mTuner.allowDragNDrop()) { if (mTuner.dragAndDropEnabled()) {
// Make all items draggable. // Make all items draggable.
view.setOnLongClickListener(onLongClickListener); view.setOnLongClickListener(onLongClickListener);
} }
@@ -1761,7 +1778,7 @@ public class DirectoryFragment extends Fragment
mRoot.authority, mRoot.rootId, mQuery) mRoot.authority, mRoot.rootId, mQuery)
: DocumentsContract.buildChildDocumentsUri( : DocumentsContract.buildChildDocumentsUri(
mDocument.authority, mDocument.documentId); mDocument.authority, mDocument.documentId);
if (mTuner.enableManagedMode()) { if (mTuner.managedModeEnabled()) {
contentsUri = DocumentsContract.setManageMode(contentsUri); contentsUri = DocumentsContract.setManageMode(contentsUri);
} }
return new DirectoryLoader( return new DirectoryLoader(

View File

@@ -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. // 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 // A folder is enabled such that it may be double clicked, even in settings
@@ -73,18 +70,23 @@ public abstract class FragmentTuner {
return true; 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. * When managed mode is enabled, active downloads will be visible in the UI.
* Presumably this should only be true when in the downloads directory. * 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 * 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. * Provides support for Platform specific specializations of DirectoryFragment.
@@ -105,7 +107,7 @@ public abstract class FragmentTuner {
return false; return false;
} }
if (isDirectory(docMimeType)) { if (MimePredicate.isDirectoryType(docMimeType)) {
return false; return false;
} }
@@ -121,9 +123,9 @@ public abstract class FragmentTuner {
} }
@Override @Override
public boolean isDocumentEnabled(String docMimeType, int docFlags) { public boolean isDocumentEnabled(String mimeType, int docFlags) {
// Directories are always enabled. // Directories are always enabled.
if (isDirectory(docMimeType)) { if (MimePredicate.isDirectoryType(mimeType)) {
return true; return true;
} }
@@ -141,13 +143,11 @@ public abstract class FragmentTuner {
} }
} }
return MimePredicate.mimeMatches(mState.acceptMimes, docMimeType); return MimePredicate.mimeMatches(mState.acceptMimes, mimeType);
} }
@Override @Override
public void updateActionMenu( public void updateActionMenu(Menu menu, SelectionDetails selection) {
Menu menu, @ResultType int dirType,
boolean canCopy, boolean canDelete, boolean canRename) {
MenuItem open = menu.findItem(R.id.menu_open); MenuItem open = menu.findItem(R.id.menu_open);
MenuItem share = menu.findItem(R.id.menu_share); 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 rename = menu.findItem(R.id.menu_rename);
MenuItem selectAll = menu.findItem(R.id.menu_select_all); MenuItem selectAll = menu.findItem(R.id.menu_select_all);
open.setVisible(mState.action == ACTION_GET_CONTENT || open.setVisible(mState.action == ACTION_GET_CONTENT
mState.action == ACTION_OPEN); || mState.action == ACTION_OPEN);
share.setVisible(false); share.setVisible(false);
delete.setVisible(false); delete.setVisible(false);
rename.setVisible(false); rename.setVisible(false);
@@ -191,16 +191,6 @@ public abstract class FragmentTuner {
} }
mModelPreviousLoaded = true; mModelPreviousLoaded = true;
} }
@Override
public boolean enableManagedMode() {
return false;
}
@Override
public boolean allowDragNDrop() {
return false;
}
} }
/** /**
@@ -217,29 +207,39 @@ public abstract class FragmentTuner {
} }
@Override @Override
public void updateActionMenu( public void updateActionMenu(Menu menu, SelectionDetails selection) {
Menu menu, @ResultType int dirType,
boolean canCopy, boolean canDelete, boolean canRename) {
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 copy = menu.findItem(R.id.menu_copy_to_clipboard);
MenuItem paste = menu.findItem(R.id.menu_paste_from_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 rename = menu.findItem(R.id.menu_rename);
MenuItem moveTo = menu.findItem(R.id.menu_move_to); MenuItem moveTo = menu.findItem(R.id.menu_move_to);
MenuItem copyTo = menu.findItem(R.id.menu_copy_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); copyTo.setVisible(true);
moveTo.setVisible(true); moveTo.setVisible(true);
rename.setVisible(true); rename.setVisible(true);
copyTo.setEnabled(canCopy); copyTo.setEnabled(!selection.containsPartialFiles());
moveTo.setEnabled(canCopy && canDelete); moveTo.setEnabled(!selection.containsPartialFiles() && selection.canDelete());
rename.setEnabled(canRename); rename.setEnabled(!selection.containsPartialFiles() && selection.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);
Menus.disableHiddenItems(menu, copy, paste); Menus.disableHiddenItems(menu, copy, paste);
} }
@@ -256,7 +256,7 @@ public abstract class FragmentTuner {
} }
@Override @Override
public boolean enableManagedMode() { public boolean managedModeEnabled() {
// When in downloads top level directory, we also show active downloads. // 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 // 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. // downloads that themselves can be opened and viewed like directories.
@@ -267,12 +267,21 @@ public abstract class FragmentTuner {
} }
@Override @Override
public boolean allowDragNDrop() { public boolean dragAndDropEnabled() {
return true; 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();
} }
} }