summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/documentsui/BaseActivity.java41
-rw-r--r--src/com/android/documentsui/HorizontalBreadcrumb.java11
-rw-r--r--src/com/android/documentsui/MenuManager.java33
-rw-r--r--src/com/android/documentsui/NavigationViewManager.java24
-rw-r--r--src/com/android/documentsui/archives/ArchiveRegistry.java11
-rw-r--r--src/com/android/documentsui/archives/ArchivesProvider.java13
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java7
-rw-r--r--src/com/android/documentsui/dirlist/SelectionMetadata.java19
-rw-r--r--src/com/android/documentsui/files/MenuManager.java11
-rw-r--r--src/com/android/documentsui/sorting/TableHeaderController.java25
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