diff options
Diffstat (limited to 'src')
10 files changed, 135 insertions, 60 deletions
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java index 0b5d96da9..790feeac4 100644 --- a/src/com/android/documentsui/BaseActivity.java +++ b/src/com/android/documentsui/BaseActivity.java @@ -569,23 +569,32 @@ public abstract class BaseActivity View root = findViewById(R.id.coordinator_layout); root.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - root.setOnApplyWindowInsetsListener((v, insets) -> { - root.setPadding(insets.getSystemWindowInsetLeft(), - insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), 0); - - // When use_material3 flag is ON, no additional bottom gap in full screen mode. - if (!isUseMaterial3FlagEnabled()) { - View saveContainer = findViewById(R.id.container_save); - saveContainer.setPadding( - 0, 0, 0, insets.getSystemWindowInsetBottom()); - - View rootsContainer = findViewById(R.id.container_roots); - rootsContainer.setPadding( - 0, 0, 0, insets.getSystemWindowInsetBottom()); - } + root.setOnApplyWindowInsetsListener( + (v, insets) -> { + root.setPadding( + insets.getSystemWindowInsetLeft(), + insets.getSystemWindowInsetTop(), + insets.getSystemWindowInsetRight(), + 0); + + // When use_material3 flag is ON and FEATURE_FREEFORM_WINDOW_MANAGEMENT is + // enabled, then there should not be any additional bottom gap in full screen + // mode. Otherwise need to take into account the system window insets such as + // the bottom swipe up navigation gesture. + if (!isUseMaterial3FlagEnabled() + || !getApplicationContext() + .getPackageManager() + .hasSystemFeature( + PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT)) { + View saveContainer = findViewById(R.id.container_save); + saveContainer.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom()); + + View rootsContainer = findViewById(R.id.container_roots); + rootsContainer.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom()); + } - return insets.consumeSystemWindowInsets(); - }); + return insets.consumeSystemWindowInsets(); + }); getWindow().setNavigationBarDividerColor(Color.TRANSPARENT); if (Build.VERSION.SDK_INT >= 29) { diff --git a/src/com/android/documentsui/HorizontalBreadcrumb.java b/src/com/android/documentsui/HorizontalBreadcrumb.java index 94f0e13f9..cb25479b3 100644 --- a/src/com/android/documentsui/HorizontalBreadcrumb.java +++ b/src/com/android/documentsui/HorizontalBreadcrumb.java @@ -25,6 +25,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -45,6 +46,9 @@ public final class HorizontalBreadcrumb extends RecyclerView implements Breadcru private LinearLayoutManager mLayoutManager; private BreadcrumbAdapter mAdapter; private IntConsumer mClickListener; + // Represents the top divider (border) of the breadcrumb on the compact size screen. + // It will be null on other screen sizes, or when the use_material3 flag is OFF. + private @Nullable View mTopDividerView; public HorizontalBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); @@ -61,12 +65,14 @@ public final class HorizontalBreadcrumb extends RecyclerView implements Breadcru @Override public void setup(Environment env, com.android.documentsui.base.State state, - IntConsumer listener) { + IntConsumer listener, + @Nullable View topDivider) { mClickListener = listener; mLayoutManager = new HorizontalBreadcrumbLinearLayoutManager( getContext(), LinearLayoutManager.HORIZONTAL, false); mAdapter = new BreadcrumbAdapter(state, env, this::onKey); + mTopDividerView = topDivider; // Since we are using GestureDetector to detect click events, a11y services don't know which // views are clickable because we aren't using View.OnClickListener. Thus, we need to use a // custom accessibility delegate to route click events correctly. @@ -109,6 +115,9 @@ public final class HorizontalBreadcrumb extends RecyclerView implements Breadcru setVisibility(GONE); setAdapter(null); } + if (mTopDividerView != null) { + mTopDividerView.setVisibility(visibility ? VISIBLE : GONE); + } mAdapter.updateLastItemSize(); } diff --git a/src/com/android/documentsui/MenuManager.java b/src/com/android/documentsui/MenuManager.java index eb4f98aab..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 @@ -389,11 +389,11 @@ public abstract class MenuManager { Menus.setEnabledAndVisible(extractTo, false); } - protected void updateExtractHere(@NonNull MenuItem it, SelectionDetails selection) { + protected void updateExtractHere(@NonNull MenuItem it, @NonNull SelectionDetails selection) { Menus.setEnabledAndVisible(it, false); } - protected void updateBrowse(@NonNull MenuItem it, SelectionDetails selection) { + protected void updateBrowse(@NonNull MenuItem it, @NonNull SelectionDetails selection) { Menus.setEnabledAndVisible(it, false); } @@ -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(); diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java index 86b5e517f..12afbd69b 100644 --- a/src/com/android/documentsui/NavigationViewManager.java +++ b/src/com/android/documentsui/NavigationViewManager.java @@ -27,10 +27,10 @@ import android.graphics.drawable.Drawable; import android.util.Log; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.Window; import android.view.WindowManager; -import android.widget.FrameLayout; import androidx.annotation.ColorRes; import androidx.annotation.Nullable; @@ -144,7 +144,13 @@ public class NavigationViewManager extends SelectionTracker.SelectionObserver<St mState = state; mEnv = env; mBreadcrumb = breadcrumb; - mBreadcrumb.setup(env, state, this::onNavigationItemSelected); + mBreadcrumb.setup( + env, + state, + this::onNavigationItemSelected, + isUseMaterial3FlagEnabled() + ? activity.findViewById(R.id.breadcrumb_top_divider) + : null); mConfigStore = configStore; mInjector = injector; mProfileTabs = @@ -297,7 +303,10 @@ public class NavigationViewManager extends SelectionTracker.SelectionObserver<St } public void update() { - updateScrollFlag(); + // If use_material3 flag is ON, we don't want any scroll behavior, thus skipping this logic. + if (!isUseMaterial3FlagEnabled()) { + updateScrollFlag(); + } updateToolbar(); mProfileTabs.updateView(); @@ -467,8 +476,11 @@ public class NavigationViewManager extends SelectionTracker.SelectionObserver<St } if (!mIsActionModeActivated) { - FrameLayout.LayoutParams headerLayoutParams = - (FrameLayout.LayoutParams) mHeader.getLayoutParams(); + // This could be either FrameLayout.LayoutParams (when use_material3 flag is OFF) or + // LinearLayout.LayoutParams (when use_material3 flag is ON), so use the common parent + // class instead to make it work for both scenarios. + ViewGroup.MarginLayoutParams headerLayoutParams = + (ViewGroup.MarginLayoutParams) mHeader.getLayoutParams(); headerLayoutParams.setMargins(0, /* top= */ headerTopOffset, 0, 0); mHeader.setLayoutParams(headerLayoutParams); } @@ -498,7 +510,7 @@ public class NavigationViewManager extends SelectionTracker.SelectionObserver<St } interface Breadcrumb { - void setup(Environment env, State state, IntConsumer listener); + void setup(Environment env, State state, IntConsumer listener, @Nullable View topDivider); void show(boolean visibility); 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/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java index 855a8273d..6de42db59 100644 --- a/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -827,6 +827,13 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On } private int getSaveLayoutHeight() { + // When use_material3 flag is on, the bottom section not only includes the container_save, + // but also includes the breadcrumb and the divider, so we need to use the total height + // for their parent container. + if (isUseMaterial3FlagEnabled()) { + View bottomSection = getActivity().findViewById(R.id.bottom_container); + return bottomSection == null ? 0 : bottomSection.getHeight(); + } View containerSave = getActivity().findViewById(R.id.container_save); return containerSave == null ? 0 : containerSave.getHeight(); } 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/src/com/android/documentsui/files/MenuManager.java b/src/com/android/documentsui/files/MenuManager.java index 9b3564eeb..7dc6b57d6 100644 --- a/src/com/android/documentsui/files/MenuManager.java +++ b/src/com/android/documentsui/files/MenuManager.java @@ -29,6 +29,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.recyclerview.selection.SelectionTracker; @@ -212,6 +213,16 @@ public final class MenuManager extends com.android.documentsui.MenuManager { } @Override + protected void updateExtractHere(@NonNull MenuItem it, @NonNull SelectionDetails selection) { + Menus.setEnabledAndVisible(it, selection.isArchive()); + } + + @Override + protected void updateBrowse(@NonNull MenuItem it, @NonNull SelectionDetails selection) { + Menus.setEnabledAndVisible(it, selection.isArchive()); + } + + @Override protected void updatePasteInto(MenuItem pasteInto, SelectionDetails selectionDetails) { Menus.setEnabledAndVisible(pasteInto, selectionDetails.canPasteInto() && mDirDetails.hasItemsToPaste()); diff --git a/src/com/android/documentsui/sorting/TableHeaderController.java b/src/com/android/documentsui/sorting/TableHeaderController.java index cb72ac916..fda7b2713 100644 --- a/src/com/android/documentsui/sorting/TableHeaderController.java +++ b/src/com/android/documentsui/sorting/TableHeaderController.java @@ -28,10 +28,11 @@ import javax.annotation.Nullable; /** View controller for table header that associates header cells in table header and columns. */ public final class TableHeaderController implements SortController.WidgetController { private final HeaderCell mTitleCell; - private final HeaderCell mSummaryCell; - private final HeaderCell mSizeCell; - private final HeaderCell mFileTypeCell; - private final HeaderCell mDateCell; + // The 4 cells below will be null in compact/medium screen sizes when use_material3 flag is ON. + private final @Nullable HeaderCell mSummaryCell; + private final @Nullable HeaderCell mSizeCell; + private final @Nullable HeaderCell mFileTypeCell; + private final @Nullable HeaderCell mDateCell; private final SortModel mModel; // We assign this here porque each method reference creates a new object // instance (which is wasteful). @@ -66,10 +67,18 @@ public final class TableHeaderController implements SortController.WidgetControl private void onModelUpdate(SortModel model, int updateTypeUnspecified) { bindCell(mTitleCell, SortModel.SORT_DIMENSION_ID_TITLE); - bindCell(mSummaryCell, SortModel.SORT_DIMENSION_ID_SUMMARY); - bindCell(mSizeCell, SortModel.SORT_DIMENSION_ID_SIZE); - bindCell(mFileTypeCell, SortModel.SORT_DIMENSION_ID_FILE_TYPE); - bindCell(mDateCell, SortModel.SORT_DIMENSION_ID_DATE); + if (mSummaryCell != null) { + bindCell(mSummaryCell, SortModel.SORT_DIMENSION_ID_SUMMARY); + } + if (mSizeCell != null) { + bindCell(mSizeCell, SortModel.SORT_DIMENSION_ID_SIZE); + } + if (mFileTypeCell != null) { + bindCell(mFileTypeCell, SortModel.SORT_DIMENSION_ID_FILE_TYPE); + } + if (mDateCell != null) { + bindCell(mDateCell, SortModel.SORT_DIMENSION_ID_DATE); + } } @Override |