diff options
7 files changed, 119 insertions, 32 deletions
diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java index 144a55245..888a45996 100644 --- a/src/com/android/documentsui/MenuManager.java +++ b/src/com/android/documentsui/MenuManager.java @@ -159,11 +159,11 @@ public abstract class MenuManager { } /** - * @see DirectoryFragment#onCreateContextMenu - * * Called when user tries to generate a context menu anchored to a file when the selection * doesn't contain any folder. * + * @see DirectoryFragment#onCreateContextMenu + * * @param selectionDetails * containsFiles may return false because this may be called when user right clicks on an * unselectable item in pickers @@ -193,11 +193,11 @@ public abstract class MenuManager { } /** - * @see DirectoryFragment#onCreateContextMenu - * * Called when user tries to generate a context menu anchored to a folder when the selection * doesn't contain any file. * + * @see DirectoryFragment#onCreateContextMenu + * * @param selectionDetails * containDirectories may return false because this may be called when user right clicks on * an unselectable item in pickers @@ -418,25 +418,42 @@ public abstract class MenuManager { } protected abstract void updateSelectAll(MenuItem selectAll); + protected abstract void updateSelectAll(MenuItem selectAll, SelectionDetails selectionDetails); + protected abstract void updateDeselectAll( MenuItem deselectAll, SelectionDetails selectionDetails); + protected abstract void updateCreateDir(MenuItem createDir); /** * Access to meta data about the selection. */ public interface SelectionDetails { + /** Gets the total number of items (files and directories) in the selection. */ + int size(); + + /** Returns whether the selection contains at least a directory. */ boolean containsDirectories(); + /** Returns whether the selection contains at least a file. */ boolean containsFiles(); - int size(); - + /** + * Returns whether the selection contains at least a file that has not been fully downloaded + * yet. + */ boolean containsPartialFiles(); + /** Returns whether the selection contains at least a file located in a mounted archive. */ boolean containsFilesInArchive(); + /** + * Returns whether the selection contains exactly one file which is also a supported archive + * type. + */ + boolean isArchive(); + // 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(); @@ -450,10 +467,6 @@ public abstract class MenuManager { boolean canOpen(); boolean canViewInOwner(); - - default boolean isArchive() { - return false; - } } public static class DirectoryDetails { diff --git a/src/com/android/documentsui/archives/ArchiveRegistry.java b/src/com/android/documentsui/archives/ArchiveRegistry.java index 91e0e20f5..3417e45ad 100644 --- a/src/com/android/documentsui/archives/ArchiveRegistry.java +++ b/src/com/android/documentsui/archives/ArchiveRegistry.java @@ -24,13 +24,12 @@ import static org.apache.commons.compress.compressors.CompressorStreamFactory.XZ import androidx.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - import org.apache.commons.compress.compressors.brotli.BrotliUtils; import org.apache.commons.compress.compressors.xz.XZUtils; +import java.util.HashMap; +import java.util.Map; + /** * To query how to generate ArchiveHandle, how to create CompressInputStream and how to create * ArchiveInputStream by using MIME type in ArchiveRegistry. @@ -136,8 +135,4 @@ final class ArchiveRegistry { static Integer getArchiveType(String mimeType) { return sHandleArchiveMap.get(mimeType); } - - static Set<String> getSupportList() { - return sHandleArchiveMap.keySet(); - } } diff --git a/src/com/android/documentsui/archives/ArchivesProvider.java b/src/com/android/documentsui/archives/ArchivesProvider.java index 3406cd708..dd221f416 100644 --- a/src/com/android/documentsui/archives/ArchivesProvider.java +++ b/src/com/android/documentsui/archives/ArchivesProvider.java @@ -44,7 +44,6 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.Set; /** * Provides basic implementation for creating, extracting and accessing @@ -62,7 +61,6 @@ public class ArchivesProvider extends DocumentsProvider { private static final String TAG = "ArchivesProvider"; private static final String METHOD_ACQUIRE_ARCHIVE = "acquireArchive"; private static final String METHOD_RELEASE_ARCHIVE = "releaseArchive"; - private static final Set<String> ZIP_MIME_TYPES = ArchiveRegistry.getSupportList(); @GuardedBy("mArchives") private final Map<Key, Loader> mArchives = new HashMap<>(); @@ -235,16 +233,9 @@ public class ArchivesProvider extends DocumentsProvider { return loader.get().openDocumentThumbnail(documentId, sizeHint, signal); } - /** - * Returns true if the passed mime type is supported by the helper. - */ + /** Returns whether the given mime type is a supported archive type. */ public static boolean isSupportedArchiveType(String mimeType) { - for (final String zipMimeType : ZIP_MIME_TYPES) { - if (zipMimeType.equals(mimeType)) { - return true; - } - } - return false; + return ArchiveRegistry.getArchiveType(mimeType) != null; } /** diff --git a/src/com/android/documentsui/dirlist/SelectionMetadata.java b/src/com/android/documentsui/dirlist/SelectionMetadata.java index 74b6061b3..0559a3b80 100644 --- a/src/com/android/documentsui/dirlist/SelectionMetadata.java +++ b/src/com/android/documentsui/dirlist/SelectionMetadata.java @@ -56,7 +56,13 @@ public class SelectionMetadata extends SelectionObserver<String> private int mWritableDirectoryCount = 0; private int mNoDeleteCount = 0; private int mNoRenameCount = 0; + + /** Number of files that are located in mounted archives. */ private int mInArchiveCount = 0; + + /** Number of archives. */ + private int mArchiveCount = 0; + private boolean mSupportsSettings = false; public SelectionMetadata(Function<String, Cursor> docFinder) { @@ -79,6 +85,9 @@ public class SelectionMetadata extends SelectionObserver<String> mDirectoryCount += delta; } else { mFileCount += delta; + if (ArchivesProvider.isSupportedArchiveType(mimeType)) { + mArchiveCount += delta; + } } final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); @@ -97,9 +106,8 @@ public class SelectionMetadata extends SelectionObserver<String> if ((docFlags & Document.FLAG_PARTIAL) != 0) { mPartialCount += delta; } - mSupportsSettings = (docFlags & Document.FLAG_SUPPORTS_SETTINGS) != 0 && - (mFileCount + mDirectoryCount) == 1; + mSupportsSettings = (docFlags & Document.FLAG_SUPPORTS_SETTINGS) != 0 && size() == 1; final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY); if (ArchivesProvider.AUTHORITY.equals(authority)) { @@ -115,6 +123,8 @@ public class SelectionMetadata extends SelectionObserver<String> mWritableDirectoryCount = 0; mNoDeleteCount = 0; mNoRenameCount = 0; + mInArchiveCount = 0; + mArchiveCount = 0; } @Override @@ -143,6 +153,11 @@ public class SelectionMetadata extends SelectionObserver<String> } @Override + public boolean isArchive() { + return mDirectoryCount == 0 && mFileCount == 1 && mArchiveCount == 1; + } + + @Override public boolean canDelete() { return size() > 0 && mNoDeleteCount == 0; } diff --git a/tests/common/com/android/documentsui/testing/TestSelectionDetails.java b/tests/common/com/android/documentsui/testing/TestSelectionDetails.java index e798174f8..4411209d1 100644 --- a/tests/common/com/android/documentsui/testing/TestSelectionDetails.java +++ b/tests/common/com/android/documentsui/testing/TestSelectionDetails.java @@ -30,6 +30,7 @@ public class TestSelectionDetails implements SelectionDetails { public boolean containsFilesInArchive; public boolean containDirectories; public boolean containFiles; + public boolean isArchive; public boolean canPasteInto; public boolean canExtract; public boolean canOpen; @@ -56,6 +57,11 @@ public class TestSelectionDetails implements SelectionDetails { } @Override + public boolean isArchive() { + return isArchive; + } + + @Override public boolean canRename() { return canRename; } @@ -89,4 +95,4 @@ public class TestSelectionDetails implements SelectionDetails { public int size() { return size; } - } +} diff --git a/tests/unit/com/android/documentsui/files/MenuManagerTest.java b/tests/unit/com/android/documentsui/files/MenuManagerTest.java index 2239dd4fb..8bc5ad707 100644 --- a/tests/unit/com/android/documentsui/files/MenuManagerTest.java +++ b/tests/unit/com/android/documentsui/files/MenuManagerTest.java @@ -22,6 +22,7 @@ import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.annotation.SuppressLint; import android.net.Uri; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; @@ -541,6 +542,7 @@ public final class MenuManagerTest { assertEquals(R.menu.mixed_context_menu, inflater.lastInflatedMenuId); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea() { mgr.updateContextMenuForContainer(testMenu, selectionDetails); @@ -553,6 +555,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CanDeselectAll() { selectionDetails.size = 1; @@ -564,6 +567,7 @@ public final class MenuManagerTest { mDirDeselectAll.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_NoItemToPaste() { dirDetails.hasItemsToPaste = false; @@ -576,6 +580,7 @@ public final class MenuManagerTest { dirCreateDir.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CantCreateDoc() { dirDetails.hasItemsToPaste = true; @@ -588,6 +593,7 @@ public final class MenuManagerTest { dirCreateDir.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CanPaste() { dirDetails.hasItemsToPaste = true; @@ -600,6 +606,7 @@ public final class MenuManagerTest { dirCreateDir.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CanCreateDirectory() { dirDetails.canCreateDirectory = true; @@ -611,6 +618,7 @@ public final class MenuManagerTest { dirCreateDir.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnFile() { selectionDetails.size = 1; @@ -626,6 +634,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test @RequiresFlagsDisabled({Flags.FLAG_DESKTOP_FILE_HANDLING_RO}) public void testContextMenu_OnFile_CanOpen() { @@ -635,6 +644,7 @@ public final class MenuManagerTest { dirOpenWith.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test @RequiresFlagsEnabled({Flags.FLAG_DESKTOP_FILE_HANDLING_RO}) public void testContextMenu_OnFile_CanOpenDesktop() { @@ -644,6 +654,7 @@ public final class MenuManagerTest { dirOpenWith.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnFile_NoOpen() { selectionDetails.canOpen = false; @@ -652,6 +663,7 @@ public final class MenuManagerTest { dirOpenWith.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMultipleFiles() { selectionDetails.size = 3; @@ -660,6 +672,7 @@ public final class MenuManagerTest { mDirCompress.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnWritableDirectory() { selectionDetails.size = 1; @@ -673,8 +686,11 @@ public final class MenuManagerTest { dirPasteIntoFolder.assertEnabledAndVisible(); dirRename.assertDisabledAndInvisible(); dirDelete.assertDisabledAndInvisible(); + mDirExtractHere.assertDisabledAndInvisible(); + mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnNonWritableDirectory() { selectionDetails.size = 1; @@ -687,8 +703,11 @@ public final class MenuManagerTest { dirPasteIntoFolder.assertDisabledAndInvisible(); dirRename.assertDisabledAndInvisible(); dirDelete.assertDisabledAndInvisible(); + mDirExtractHere.assertDisabledAndInvisible(); + mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_CanInspectContainer() { features.inspector = true; @@ -697,6 +716,7 @@ public final class MenuManagerTest { dirInspect.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnWritableDirectory_NothingToPaste() { selectionDetails.canPasteInto = true; @@ -706,6 +726,7 @@ public final class MenuManagerTest { dirPasteIntoFolder.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMultipleDirectories() { selectionDetails.size = 3; @@ -714,6 +735,7 @@ public final class MenuManagerTest { mDirCompress.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs() { selectionDetails.containDirectories = true; @@ -725,8 +747,11 @@ public final class MenuManagerTest { dirCopyToClipboard.assertEnabledAndVisible(); mDirCompress.assertDisabledAndInvisible(); dirDelete.assertEnabledAndVisible(); + mDirExtractHere.assertDisabledAndInvisible(); + mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs_hasPartialFile() { selectionDetails.containDirectories = true; @@ -741,6 +766,7 @@ public final class MenuManagerTest { dirDelete.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs_hasUndeletableFile() { selectionDetails.containDirectories = true; @@ -754,6 +780,7 @@ public final class MenuManagerTest { dirDelete.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_CanInspectSingleSelection() { selectionDetails.size = 1; @@ -761,6 +788,22 @@ public final class MenuManagerTest { dirInspect.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") + @Test + public void testContextMenu_OnArchive() { + selectionDetails.size = 1; + selectionDetails.containFiles = true; + selectionDetails.isArchive = true; + mgr.updateContextMenuForFiles(testMenu, selectionDetails); + if (isZipNgFlagEnabled()) { + mDirExtractHere.assertEnabledAndVisible(); + mDirBrowse.assertEnabledAndVisible(); + } else { + mDirExtractHere.assertDisabledAndInvisible(); + mDirBrowse.assertDisabledAndInvisible(); + } + } + @Test public void testRootContextMenu() { testRootInfo.flags = Root.FLAG_SUPPORTS_CREATE; diff --git a/tests/unit/com/android/documentsui/picker/MenuManagerTest.java b/tests/unit/com/android/documentsui/picker/MenuManagerTest.java index 485073128..27ffcbdaa 100644 --- a/tests/unit/com/android/documentsui/picker/MenuManagerTest.java +++ b/tests/unit/com/android/documentsui/picker/MenuManagerTest.java @@ -23,6 +23,7 @@ import static com.android.documentsui.base.State.ACTION_OPEN; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.annotation.SuppressLint; import android.database.MatrixCursor; import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Root; @@ -343,6 +344,7 @@ public final class MenuManagerTest { optionSelectAll.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea() { dirDetails.hasItemsToPaste = false; @@ -359,6 +361,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_NoItemToPaste() { dirDetails.hasItemsToPaste = false; @@ -373,6 +376,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CantCreateDoc() { dirDetails.hasItemsToPaste = true; @@ -387,6 +391,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_canPaste() { dirDetails.hasItemsToPaste = true; @@ -401,6 +406,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CanCreateDirectory() { dirDetails.canCreateDirectory = true; @@ -414,6 +420,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_EmptyArea_CanDeselectAll() { selectionDetails.size = 1; @@ -425,6 +432,7 @@ public final class MenuManagerTest { mDirDeselectAll.assertEnabledAndVisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnFile() { mgr.updateContextMenuForFiles(testMenu, selectionDetails); @@ -441,6 +449,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnDirectory() { selectionDetails.canPasteInto = true; @@ -458,6 +467,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs() { selectionDetails.containDirectories = true; @@ -473,6 +483,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs_hasPartialFile() { selectionDetails.containDirectories = true; @@ -489,6 +500,7 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") @Test public void testContextMenu_OnMixedDocs_hasUndeletableFile() { selectionDetails.containDirectories = true; @@ -504,6 +516,17 @@ public final class MenuManagerTest { mDirBrowse.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") + @Test + public void testContextMenu_OnArchive() { + selectionDetails.size = 1; + selectionDetails.containFiles = true; + selectionDetails.isArchive = true; + mgr.updateContextMenuForFiles(testMenu, selectionDetails); + mDirExtractHere.assertDisabledAndInvisible(); + mDirBrowse.assertDisabledAndInvisible(); + } + @Test public void testRootContextMenu() { mgr.updateRootContextMenu(testMenu, testRootInfo, testDocInfo); @@ -547,6 +570,7 @@ public final class MenuManagerTest { rootEjectRoot.assertDisabledAndInvisible(); } + @SuppressLint("VisibleForTests") private Model getTestModel(boolean onlyDirectory) { String[] COLUMNS = new String[]{ RootCursorWrapper.COLUMN_AUTHORITY, |