summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ben Lin <linben@google.com> 2017-04-19 15:19:49 -0700
committer Ben Lin <linben@google.com> 2017-04-19 17:27:29 -0700
commitc1a32aea59f47b06f3157a7f87ef5b9bf45f7627 (patch)
treefdb01e4bbd51ffbe2ade3bd886d0125983bab617
parent7a4fa239abefd05d03d2e62c651d7b08f682d8b4 (diff)
Disable Move/Move to/Cut to clipboard for read-only files.
Bug: 37357780 Change-Id: I26f4e604c080b79f54f5ee9f9a07535d577e4a0a
-rw-r--r--src/com/android/documentsui/DragAndDropManager.java12
-rw-r--r--src/com/android/documentsui/base/DocumentFilters.java10
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java5
-rw-r--r--src/com/android/documentsui/files/ActionHandler.java6
-rw-r--r--src/com/android/documentsui/ui/DialogController.java8
-rw-r--r--tests/common/com/android/documentsui/testing/TestEnv.java4
-rw-r--r--tests/common/com/android/documentsui/ui/TestDialogController.java9
-rw-r--r--tests/unit/com/android/documentsui/DragAndDropManagerTests.java21
-rw-r--r--tests/unit/com/android/documentsui/files/ActionHandlerTest.java10
9 files changed, 82 insertions, 3 deletions
diff --git a/src/com/android/documentsui/DragAndDropManager.java b/src/com/android/documentsui/DragAndDropManager.java
index e783df99a..c6aa3e2d5 100644
--- a/src/com/android/documentsui/DragAndDropManager.java
+++ b/src/com/android/documentsui/DragAndDropManager.java
@@ -167,6 +167,10 @@ public interface DragAndDropManager {
// type of file operations.
private boolean mIsCtrlPressed;
+ // Boolean flag for current drag and drop operation. Returns true if the files can only
+ // be copied (ie. Read-Only files)
+ private boolean mMustBeCopied;
+
// Drag events info. These are used to derive state and update drag shadow when user changes
// Ctrl key state.
private View mView;
@@ -231,6 +235,9 @@ public interface DragAndDropManager {
List<Uri> uris = new ArrayList<>(srcs.size());
for (DocumentInfo doc : srcs) {
uris.add(doc.derivedUri);
+ if (!doc.isRemoveSupported() && !doc.isDeleteSupported()) {
+ mMustBeCopied = true;
+ }
}
mClipData = mClipper.getClipDataForDocuments(
uris, FileOperationService.OPERATION_UNKNOWN, parent);
@@ -447,9 +454,14 @@ public interface DragAndDropManager {
mClipData = null;
mDestDoc = null;
mDestRoot = null;
+ mMustBeCopied = false;
}
private @OpType int calculateOpType(ClipData clipData, RootInfo destRoot) {
+ if (mMustBeCopied) {
+ return FileOperationService.OPERATION_COPY;
+ }
+
final String srcRootUri = clipData.getDescription().getExtras().getString(SRC_ROOT_KEY);
final String destRootUri = destRoot.getUri().toString();
diff --git a/src/com/android/documentsui/base/DocumentFilters.java b/src/com/android/documentsui/base/DocumentFilters.java
index de8085b04..a095b558e 100644
--- a/src/com/android/documentsui/base/DocumentFilters.java
+++ b/src/com/android/documentsui/base/DocumentFilters.java
@@ -34,6 +34,7 @@ public final class DocumentFilters {
public static final Predicate<Cursor> ANY = (Cursor c) -> { return true; };
public static final Predicate<Cursor> VIRTUAL = DocumentFilters::isVirtual;
+ public static final Predicate<Cursor> NOT_MOVABLE = DocumentFilters::isNotMovable;
private static final Predicate<Cursor> O_SHARABLE = DocumentFilters::isSharableInO;
private static final Predicate<Cursor> PREO_SHARABLE = DocumentFilters::isSharablePreO;
@@ -70,4 +71,13 @@ public final class DocumentFilters {
int flags = getCursorInt(c, Document.COLUMN_FLAGS);
return (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}
+
+ /**
+ * Filter that passes (returns true) for files that can not be moved.
+ */
+ private static final boolean isNotMovable(Cursor c) {
+ int flags = getCursorInt(c, Document.COLUMN_FLAGS);
+ return (flags & Document.FLAG_SUPPORTS_REMOVE) == 0
+ && (flags & Document.FLAG_SUPPORTS_DELETE) == 0;
+ }
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index cf4b6a2d7..aef9aca4a 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -75,6 +75,7 @@ import com.android.documentsui.Metrics;
import com.android.documentsui.Model;
import com.android.documentsui.R;
import com.android.documentsui.ThumbnailCache;
+import com.android.documentsui.base.DocumentFilters;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.EventHandler;
@@ -657,6 +658,10 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
return true;
case R.id.menu_move_to:
+ if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
+ mInjector.dialogs.showOperationUnsupported();
+ return true;
+ }
// Exit selection mode first, so we avoid deselecting deleted documents.
mActionModeController.finishActionMode();
transferDocuments(selection, null, FileOperationService.OPERATION_MOVE);
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index 635a19984..e77de2927 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -236,6 +236,12 @@ public class ActionHandler<T extends Activity & Addons> extends AbstractActionHa
if (selection.isEmpty()) {
return;
}
+
+ if (mModel.hasDocuments(selection, DocumentFilters.NOT_MOVABLE)) {
+ mDialogs.showOperationUnsupported();
+ return;
+ }
+
mSelectionMgr.clearSelection();
mClipper.clipDocumentsForCut(mModel::getItemUri, selection, mState.stack.peek());
diff --git a/src/com/android/documentsui/ui/DialogController.java b/src/com/android/documentsui/ui/DialogController.java
index 4c2d88684..0de98683f 100644
--- a/src/com/android/documentsui/ui/DialogController.java
+++ b/src/com/android/documentsui/ui/DialogController.java
@@ -50,6 +50,7 @@ public interface DialogController {
void showProgressDialog(String jobId, FileOperation operation);
void showNoApplicationFound();
+ void showOperationUnsupported();
void showViewInArchivesUnsupported();
void showDocumentsClipped(int size);
@@ -110,7 +111,7 @@ public interface DialogController {
public void showFileOperationStatus(@Status int status, @OpType int opType,
int docCount) {
if (status == FileOperations.Callback.STATUS_REJECTED) {
- Snackbars.showOperationRejected(mActivity);
+ showOperationUnsupported();
return;
}
if (status == FileOperations.Callback.STATUS_FAILED) {
@@ -182,6 +183,11 @@ public interface DialogController {
}
@Override
+ public void showOperationUnsupported() {
+ Snackbars.showOperationRejected(mActivity);
+ }
+
+ @Override
public void showViewInArchivesUnsupported() {
Snackbars.makeSnackbar(mActivity, R.string.toast_view_in_archives_unsupported,
Snackbar.LENGTH_SHORT).show();
diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java
index 92bf2ab78..902c19fd2 100644
--- a/tests/common/com/android/documentsui/testing/TestEnv.java
+++ b/tests/common/com/android/documentsui/testing/TestEnv.java
@@ -55,6 +55,7 @@ public class TestEnv {
public static DocumentInfo FILE_ARCHIVE;
public static DocumentInfo FILE_IN_ARCHIVE;
public static DocumentInfo FILE_VIRTUAL;
+ public static DocumentInfo FILE_READ_ONLY;
public final TestScheduledExecutorService mExecutor;
public final State state = new State();
@@ -138,8 +139,9 @@ public class TestEnv {
"UbuntuFlappyBird.iso",
Document.FLAG_SUPPORTS_DELETE
| Document.FLAG_PARTIAL);
+ FILE_READ_ONLY = model.createFile("topsecretsystemfile.bin", 0);
FILE_ARCHIVE = model.createFile("whatsinthere.zip");
- FILE_IN_ARCHIVE = archiveModel.createFile("whatsinthere.png");
+ FILE_IN_ARCHIVE = archiveModel.createFile("whatsinthere.png", 0);
FILE_VIRTUAL = model.createDocument(
"virtualdoc.vnd",
"application/vnd.google-apps.document",
diff --git a/tests/common/com/android/documentsui/ui/TestDialogController.java b/tests/common/com/android/documentsui/ui/TestDialogController.java
index 350755be8..b67ce46dd 100644
--- a/tests/common/com/android/documentsui/ui/TestDialogController.java
+++ b/tests/common/com/android/documentsui/ui/TestDialogController.java
@@ -33,6 +33,7 @@ public class TestDialogController implements DialogController {
private boolean mNoApplicationFound;
private boolean mDocumentsClipped;
private boolean mViewInArchivesUnsupported;
+ private boolean mShowOperationUnsupported;
private DocumentInfo mOverwriteTarget;
public TestDialogController() {
@@ -61,6 +62,11 @@ public class TestDialogController implements DialogController {
}
@Override
+ public void showOperationUnsupported() {
+ mShowOperationUnsupported = true;
+ }
+
+ @Override
public void showViewInArchivesUnsupported() {
mViewInArchivesUnsupported = true;
}
@@ -87,6 +93,9 @@ public class TestDialogController implements DialogController {
Assert.assertFalse(mNoApplicationFound);
}
+ public void assertShowOperationUnsupported() {
+ Assert.assertTrue(mShowOperationUnsupported);
+ }
public void assertViewInArchivesShownUnsupported() {
Assert.assertTrue(mViewInArchivesUnsupported);
}
diff --git a/tests/unit/com/android/documentsui/DragAndDropManagerTests.java b/tests/unit/com/android/documentsui/DragAndDropManagerTests.java
index 2e9c2cb04..fafbccb4f 100644
--- a/tests/unit/com/android/documentsui/DragAndDropManagerTests.java
+++ b/tests/unit/com/android/documentsui/DragAndDropManagerTests.java
@@ -37,7 +37,6 @@ import com.android.documentsui.DragAndDropManager.RuntimeDragAndDropManager;
import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.services.FileOperationService;
-import com.android.documentsui.services.FileOperationService.OpType;
import com.android.documentsui.services.FileOperations;
import com.android.documentsui.testing.ClipDatas;
import com.android.documentsui.testing.KeyEvents;
@@ -723,6 +722,26 @@ public class DragAndDropManagerTests {
mClipper.opType.assertLastArgument(FileOperationService.OPERATION_MOVE);
}
+ @Test
+ public void testDrop_Copies_SameRoot_ReadOnlyFile_DropOnDocument() {
+ mManager.startDrag(
+ mStartDragView,
+ TestEnv.FOLDER_0,
+ Arrays.asList(TestEnv.FILE_READ_ONLY),
+ TestProvidersAccess.DOWNLOADS,
+ Arrays.asList(TestEnv.FOLDER_0.derivedUri, TestEnv.FILE_READ_ONLY.derivedUri),
+ mIconHelper);
+
+ mManager.updateState(mUpdateShadowView, TestProvidersAccess.DOWNLOADS, TestEnv.FOLDER_2);
+
+ final DocumentStack stack = new DocumentStack(
+ TestProvidersAccess.DOWNLOADS, TestEnv.FOLDER_1, TestEnv.FOLDER_2);
+ assertTrue(mManager.drop(mClipData, mManager, stack, mCallback));
+
+ mClipper.copy.assertLastArgument(Pair.create(stack, mClipData));
+ mClipper.opType.assertLastArgument(FileOperationService.OPERATION_COPY);
+ }
+
private void assertStateUpdated(@State int expected) {
mShadowBuilder.state.assertLastArgument(expected);
mShadowUpdateListener.assertCalled();
diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
index c183cfe5f..ef9945825 100644
--- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
@@ -154,6 +154,16 @@ public class ActionHandlerTest {
mActionModeAddons.finishOnConfirmed.assertRejected();
}
+ @Test
+ public void testCutSelectedDocuments_ContainsNonMovableItem() {
+ mEnv.selectDocument(TestEnv.FILE_READ_ONLY);
+
+ mHandler.cutToClipboard();
+ mDialogs.assertDocumentsClippedNotShown();
+ mDialogs.assertShowOperationUnsupported();
+ mActivity.startService.assertNotCalled();
+ }
+
// Recents root means when deleting the srcParent will be null.
@Test
public void testDeleteSelectedDocuments_RecentsRoot() {