Merge "Disable share when dirs selected." into nyc-dev
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user