summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flags.aconfig5
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/color/list_item_ripple_color.xml4
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/drawable/grid_nameplate_background.xml83
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/drawable/list_item_background.xml57
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout-w600dp/fragment_save_cancel_button.xml26
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/drawer_layout.xml2
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/fixed_layout.xml3
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_pick.xml67
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save.xml64
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save_cancel_button.xml29
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/layout/nav_rail_layout.xml12
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/values-w600dp/dimens.xml2
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml5
-rw-r--r--res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml4
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/com/android/documentsui/AbstractActionHandler.java13
-rw-r--r--src/com/android/documentsui/MenuManager.java33
-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/base/DocumentInfo.java5
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java13
-rw-r--r--src/com/android/documentsui/dirlist/SelectionMetadata.java23
-rw-r--r--src/com/android/documentsui/picker/PickFragment.java20
-rw-r--r--src/com/android/documentsui/picker/SaveFragment.java41
-rw-r--r--src/com/android/documentsui/util/FlagUtils.kt4
-rw-r--r--tests/common/com/android/documentsui/testing/TestSelectionDetails.java8
-rw-r--r--tests/unit/com/android/documentsui/files/MenuManagerTest.java43
-rw-r--r--tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt4
-rw-r--r--tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt6
-rw-r--r--tests/unit/com/android/documentsui/picker/MenuManagerTest.java24
30 files changed, 492 insertions, 134 deletions
diff --git a/flags.aconfig b/flags.aconfig
index 21fc9567c..1e31323a5 100644
--- a/flags.aconfig
+++ b/flags.aconfig
@@ -10,10 +10,11 @@ flag {
}
flag {
- name: "use_search_v2_rw"
+ name: "use_search_v2_read_only"
namespace: "documentsui"
- description: "Read/write flag that enables the next generation search functionality."
+ description: "Enables the next generation search functionality."
bug: "383412640"
+ is_fixed_read_only: true
}
flag {
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/color/list_item_ripple_color.xml b/res/flag(com.android.documentsui.flags.use_material3)/color/list_item_ripple_color.xml
index 6c2d0714e..85e5b46ce 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/color/list_item_ripple_color.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/color/list_item_ripple_color.xml
@@ -17,6 +17,10 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="@android:color/transparent" />
+ <!-- By default <ripple> introduces a gray-ish layer for the focused state which we don't
+ want, hence explicitly setting focused ripple color to transparent to get rid of that.
+ -->
+ <item android:state_focused="true" android:color="@android:color/transparent" />
<item android:state_selected="true" android:alpha="@dimen/ripple_overlay_alpha"
android:color="?attr/colorOnPrimaryContainer" />
<item android:alpha="@dimen/ripple_overlay_alpha"
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/drawable/grid_nameplate_background.xml b/res/flag(com.android.documentsui.flags.use_material3)/drawable/grid_nameplate_background.xml
index d88b70d71..502efaede 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/drawable/grid_nameplate_background.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/drawable/grid_nameplate_background.xml
@@ -16,6 +16,41 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- selected -->
+ <item
+ android:state_focused="true"
+ android:state_hovered="true"
+ android:state_selected="true">
+ <layer-list>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape>
+ <corners android:radius="@dimen/grid_item_nameplate_inner_radius" />
+ <solid android:color="?attr/colorPrimaryContainer" />
+ </shape>
+ </item>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape android:tint="?attr/colorOnPrimaryContainer">
+ <corners android:radius="@dimen/grid_item_nameplate_inner_radius" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/grid_item_nameplate_radius" />
+ <stroke
+ android:width="@dimen/focus_ring_width"
+ android:color="?attr/colorSecondary" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
<item android:state_selected="true" android:state_focused="true">
<layer-list>
<item
@@ -38,6 +73,24 @@
</item>
</layer-list>
</item>
+ <item
+ android:state_hovered="true"
+ android:state_selected="true">
+ <layer-list>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/grid_item_nameplate_radius" />
+ <solid android:color="?attr/colorPrimaryContainer" />
+ </shape>
+ </item>
+ <item>
+ <shape android:tint="?attr/colorOnPrimaryContainer">
+ <corners android:radius="@dimen/grid_item_nameplate_radius" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
<item android:state_selected="true">
<shape>
<corners android:radius="@dimen/grid_item_nameplate_radius" />
@@ -46,6 +99,30 @@
</item>
<!-- unselected -->
+ <item
+ android:state_focused="true"
+ android:state_hovered="true">
+ <layer-list>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape android:tint="?attr/colorOnSurface">
+ <corners android:radius="@dimen/grid_item_nameplate_inner_radius" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/grid_item_nameplate_radius" />
+ <stroke
+ android:width="@dimen/focus_ring_width"
+ android:color="?attr/colorSecondary" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
<item android:state_focused="true">
<shape>
<corners android:radius="@dimen/grid_item_nameplate_radius" />
@@ -54,4 +131,10 @@
android:color="?attr/colorSecondary" />
</shape>
</item>
+ <item android:state_hovered="true">
+ <shape android:tint="?attr/colorOnSurface">
+ <corners android:radius="@dimen/grid_item_nameplate_radius" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
</selector> \ No newline at end of file
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/drawable/list_item_background.xml b/res/flag(com.android.documentsui.flags.use_material3)/drawable/list_item_background.xml
index 31bbec1f2..79c2eb1b7 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/drawable/list_item_background.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/drawable/list_item_background.xml
@@ -26,6 +26,41 @@
<item>
<selector>
<!-- Selected -->
+ <item
+ android:state_selected="true"
+ android:state_focused="true"
+ android:state_hovered="true">
+ <layer-list>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape>
+ <corners android:radius="@dimen/list_item_height" />
+ <solid android:color="@color/list_item_selected_background_color" />
+ </shape>
+ </item>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape android:tint="?attr/colorOnPrimaryContainer">
+ <corners android:radius="@dimen/list_item_height" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/list_item_height" />
+ <stroke
+ android:width="@dimen/focus_ring_width"
+ android:color="?attr/colorSecondary" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
<item android:state_selected="true" android:state_drag_hovered="true">
<layer-list>
<item>
@@ -104,6 +139,28 @@
</item>
<!-- Unselected -->
+ <item android:state_focused="true" android:state_hovered="true">
+ <layer-list>
+ <item
+ android:bottom="@dimen/focus_ring_gap"
+ android:left="@dimen/focus_ring_gap"
+ android:right="@dimen/focus_ring_gap"
+ android:top="@dimen/focus_ring_gap">
+ <shape android:tint="?attr/colorOnSurface">
+ <corners android:radius="@dimen/list_item_height" />
+ <solid android:color="@color/overlay_hover_color_percentage" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/list_item_height" />
+ <stroke
+ android:width="@dimen/focus_ring_width"
+ android:color="?attr/colorSecondary" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
<item android:state_drag_hovered="true">
<shape android:tint="?attr/colorOnSurface">
<corners android:radius="@dimen/list_item_height" />
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout-w600dp/fragment_save_cancel_button.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout-w600dp/fragment_save_cancel_button.xml
new file mode 100644
index 000000000..e879b81ab
--- /dev/null
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout-w600dp/fragment_save_cancel_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.google.android.material.button.MaterialButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@android:id/button2"
+ style="@style/MaterialTonalButton"
+ app:cornerRadius="@dimen/button_corner_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:layout_marginEnd="4dp"
+ android:text="@android:string/cancel"/>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/drawer_layout.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/drawer_layout.xml
index 8e23aad19..fc4a22bc6 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/layout/drawer_layout.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/drawer_layout.xml
@@ -89,7 +89,7 @@
android:id="@+id/container_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorBackgroundFloating" />
+ android:background="?attr/colorSurfaceContainer" />
</LinearLayout>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/fixed_layout.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/fixed_layout.xml
index feaa34e6d..5496f0d84 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/layout/fixed_layout.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/fixed_layout.xml
@@ -32,7 +32,6 @@
android:paddingTop="@dimen/layout_padding_top"
android:paddingBottom="@dimen/layout_padding_bottom"
android:paddingEnd="@dimen/layout_padding_end">
-
<!-- Navigation: left hand side. -->
<FrameLayout
android:id="@+id/container_roots"
@@ -124,7 +123,7 @@
android:id="@+id/container_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorBackgroundFloating"
+ android:background="?attr/colorSurfaceContainer"
android:elevation="8dp" />
</LinearLayout>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_pick.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_pick.xml
index 742861a3e..a126492d0 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_pick.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_pick.xml
@@ -13,48 +13,59 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical|end"
- android:paddingStart="@dimen/bottom_bar_padding"
- android:paddingEnd="@dimen/bottom_bar_padding">
-
- <com.google.android.material.button.MaterialButton
- android:id="@android:id/button2"
- style="?attr/materialButtonOutlinedStyle"
- app:cornerRadius="@dimen/button_corner_radius"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:text="@android:string/cancel" />
+ android:paddingTop="@dimen/picker_saver_container_padding_top"
+ android:paddingBottom="@dimen/picker_saver_container_padding_bottom">
- <FrameLayout
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:background="?attr/colorSurfaceContainer"
+ android:gravity="center_vertical|end"
+ android:paddingStart="@dimen/bottom_bar_padding"
+ android:paddingEnd="@dimen/bottom_bar_padding"
+ android:paddingTop="@dimen/picker_saver_padding_top"
+ android:paddingBottom="@dimen/picker_saver_padding_bottom">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@android:id/button2"
+ style="@style/MaterialTonalButton"
+ app:cornerRadius="@dimen/button_corner_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/picker_saver_button_gap"
+ android:layout_marginEnd="@dimen/picker_saver_button_gap"
+ android:text="@android:string/cancel" />
<com.google.android.material.button.MaterialButton
android:id="@android:id/button1"
- style="?attr/materialButtonStyle"
+ style="@style/MaterialButton"
app:cornerRadius="@dimen/button_corner_radius"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="4dp"
android:backgroundTint="@color/fragment_pick_button_background_color"
android:textColor="@color/fragment_pick_button_text_color"
android:layout_marginEnd="4dp" />
- <!-- Handles touch events when button1 is disabled. -->
- <FrameLayout
- android:id="@+id/pick_button_overlay"
- android:importantForAccessibility="no"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ </LinearLayout>
- </FrameLayout>
+ <!-- Handles touch events when button1 is disabled. -->
+ <View
+ android:id="@+id/pick_button_overlay"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:background="@android:color/transparent"
+ android:visibility="gone"
+ android:importantForAccessibility="no"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
-</LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save.xml
index 401eaec88..416e4d6d2 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save.xml
@@ -18,42 +18,40 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="@dimen/list_item_padding"
- android:paddingEnd="@dimen/bottom_bar_padding"
- android:orientation="horizontal"
- android:baselineAligned="false"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
+ android:paddingTop="@dimen/picker_saver_container_padding_top"
+ android:paddingBottom="@dimen/picker_saver_container_padding_bottom">
- <FrameLayout
- android:layout_width="@dimen/icon_size"
- android:layout_height="@dimen/icon_size"
- android:layout_marginEnd="16dp">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/root_icon_size"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:contentDescription="@null" />
-
- </FrameLayout>
-
- <EditText
- android:id="@android:id/title"
- android:layout_width="0dp"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:singleLine="true"
- android:selectAllOnFocus="true" />
+ android:paddingTop="@dimen/picker_saver_padding_top"
+ android:paddingBottom="@dimen/picker_saver_padding_bottom"
+ android:paddingEnd="@dimen/bottom_bar_padding"
+ android:orientation="horizontal"
+ android:baselineAligned="false"
+ android:gravity="center_vertical|end"
+ android:paddingStart="@dimen/list_item_padding">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/title_wrapper"
+ style="?attr/textInputFilledStyle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="@string/file_name_hint">
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:selectAllOnFocus="true" />
+ </com.google.android.material.textfield.TextInputLayout>
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
+ <include layout="@layout/fragment_save_cancel_button" />
<com.google.android.material.button.MaterialButton
android:id="@android:id/button1"
- style="@style/Widget.Material3.Button.UnelevatedButton"
+ style="@style/MaterialButton"
app:cornerRadius="@dimen/button_corner_radius"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -61,7 +59,7 @@
android:layout_marginEnd="4dp"
android:text="@string/menu_save"/>
- <ProgressBar
+ <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@android:id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -69,8 +67,8 @@
android:visibility="gone"
android:indeterminate="true"
android:padding="8dp"
- style="?android:attr/progressBarStyle" />
+ app:trackColor="?attr/colorSecondaryContainer" />
- </FrameLayout>
+ </LinearLayout>
</LinearLayout>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save_cancel_button.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save_cancel_button.xml
new file mode 100644
index 000000000..5179630c9
--- /dev/null
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/fragment_save_cancel_button.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- The "Cancel" button is default invisible. On form factors with FEATURE_PC set, it is
+ updated to be shown always. -->
+<com.google.android.material.button.MaterialButton
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@android:id/button2"
+ style="@style/Widget.Material3.Button.TonalButton"
+ app:cornerRadius="@dimen/button_corner_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:layout_marginEnd="4dp"
+ android:visibility="gone"
+ android:text="@android:string/cancel"/> \ No newline at end of file
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/layout/nav_rail_layout.xml b/res/flag(com.android.documentsui.flags.use_material3)/layout/nav_rail_layout.xml
index a014d8866..bd85b7dac 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/layout/nav_rail_layout.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/layout/nav_rail_layout.xml
@@ -35,8 +35,8 @@
android:orientation="horizontal"
android:baselineAligned="false"
android:paddingTop="@dimen/layout_padding_top"
- android:paddingBottom="@dimen/layout_padding_bottom"
android:paddingEnd="@dimen/layout_padding_end"
+ android:paddingBottom="@dimen/layout_padding_bottom"
android:background="?attr/colorSurfaceContainer">
<!-- Navigation rail: left hand side. -->
@@ -144,17 +144,17 @@
android:layout_marginTop="@dimen/main_container_section_gap"
android:background="@drawable/main_container_bottom_section_background">
- <com.android.documentsui.HorizontalBreadcrumb
- android:id="@+id/horizontal_breadcrumb"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <com.android.documentsui.HorizontalBreadcrumb
+ android:id="@+id/horizontal_breadcrumb"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
</LinearLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/container_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/colorBackgroundFloating"
+ android:background="?attr/colorSurfaceContainer"
android:elevation="8dp" />
</LinearLayout>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values-w600dp/dimens.xml b/res/flag(com.android.documentsui.flags.use_material3)/values-w600dp/dimens.xml
index 880d6c0b0..00c9a662a 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/values-w600dp/dimens.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/values-w600dp/dimens.xml
@@ -31,5 +31,7 @@
<dimen name="table_header_padding_start">28dp</dimen>
<!-- list_container_padding + list_item_padding_end -->
<dimen name="table_header_padding_end">20dp</dimen>
+
+ <dimen name="picker_saver_container_padding_bottom">0dp</dimen>
</resources>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml b/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml
index 0e532d39a..76e194c4f 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/values/dimens.xml
@@ -158,6 +158,11 @@
<dimen name="main_container_section_gap">2dp</dimen>
<dimen name="main_container_corner_radius_large">16dp</dimen>
<dimen name="main_container_corner_radius_small">4dp</dimen>
+ <dimen name="picker_saver_padding_top">@dimen/space_extra_small_1</dimen>
+ <dimen name="picker_saver_padding_bottom">@dimen/space_extra_small_1</dimen>
+ <dimen name="picker_saver_button_gap">@dimen/space_extra_small_4</dimen>
+ <dimen name="picker_saver_container_padding_top">@dimen/space_small_1</dimen>
+ <dimen name="picker_saver_container_padding_bottom">@dimen/space_small_1</dimen>
<dimen name="layout_padding_top">@dimen/space_small_1</dimen>
<dimen name="layout_padding_bottom">@dimen/space_small_1</dimen>
<dimen name="layout_padding_end">@dimen/space_small_1</dimen>
diff --git a/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml b/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml
index 693679cf9..5d0687a37 100644
--- a/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml
+++ b/res/flag(com.android.documentsui.flags.use_material3)/values/styles.xml
@@ -90,6 +90,10 @@
<item name="android:textAppearance">@style/MaterialButtonTextAppearance</item>
</style>
+ <style name="MaterialTonalButton" parent="@style/Widget.Material3.Button.TonalButton">
+ <item name="android:textAppearance">@style/MaterialButtonTextAppearance</item>
+ </style>
+
<style name="MaterialOutlinedButton" parent="@style/Widget.Material3.Button.OutlinedButton">
<item name="android:textAppearance">@style/MaterialButtonTextAppearance</item>
</style>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fc97d2c7d..f8ade4c47 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -611,4 +611,6 @@
<!-- Unicode Character “•” (U+2022). -->
<string name="bullet">\u2022</string>
+ <!-- Text used at the top of the text field when saving a document. [CHAR_LIMIT=100] -->
+ <string name="file_name_hint">File name</string>
</resources>
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index de193e235..89e9bc2d6 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -20,7 +20,8 @@ import static com.android.documentsui.base.DocumentInfo.getCursorInt;
import static com.android.documentsui.base.DocumentInfo.getCursorString;
import static com.android.documentsui.base.SharedMinimal.DEBUG;
import static com.android.documentsui.util.FlagUtils.isDesktopFileHandlingFlagEnabled;
-import static com.android.documentsui.util.FlagUtils.isUseSearchV2RwFlagEnabled;
+import static com.android.documentsui.util.FlagUtils.isUseSearchV2FlagEnabled;
+import static com.android.documentsui.util.FlagUtils.isZipNgFlagEnabled;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
@@ -458,17 +459,17 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA
private boolean viewDocument(DocumentInfo doc) {
if (doc.isPartial()) {
- Log.w(TAG, "Can't view partial file.");
+ Log.w(TAG, "Cannot view partial file");
return false;
}
- if (doc.isInArchive()) {
- Log.w(TAG, "Can't view files in archives.");
+ if (!isZipNgFlagEnabled() && doc.isInArchive()) {
+ Log.w(TAG, "Cannot view file in archive");
return false;
}
if (doc.isDirectory()) {
- Log.w(TAG, "Can't view directories.");
+ Log.w(TAG, "Cannot view directory");
return true;
}
@@ -916,7 +917,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA
mState.stack.changeRoot(mActivity.getCurrentRoot());
}
- if (isUseSearchV2RwFlagEnabled()) {
+ if (isUseSearchV2FlagEnabled()) {
return onCreateLoaderV2(id, args);
}
return onCreateLoaderV1(id, args);
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/base/DocumentInfo.java b/src/com/android/documentsui/base/DocumentInfo.java
index ad09c45cf..6306f14f1 100644
--- a/src/com/android/documentsui/base/DocumentInfo.java
+++ b/src/com/android/documentsui/base/DocumentInfo.java
@@ -17,6 +17,7 @@
package com.android.documentsui.base;
import static com.android.documentsui.base.SharedMinimal.DEBUG;
+import static com.android.documentsui.util.FlagUtils.isZipNgFlagEnabled;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
@@ -319,7 +320,8 @@ public class DocumentInfo implements Durable, Parcelable {
// Containers are documents which can be opened in DocumentsUI as folders.
public boolean isContainer() {
- return isDirectory() || (isArchive() && !isInArchive() && !isPartial());
+ return isDirectory() || (isArchive() && !isPartial() && (isZipNgFlagEnabled()
+ || !isInArchive()));
}
public boolean isVirtual() {
@@ -342,7 +344,6 @@ public class DocumentInfo implements Durable, Parcelable {
return userId.buildDocumentUriAsUser(authority, documentId);
}
-
/**
* Returns a tree document uri representing this {@link DocumentInfo}. The URI may contain user
* information. Use this when uri is needed externally.
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 6de42db59..2ea906a60 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -23,6 +23,7 @@ import static com.android.documentsui.base.State.MODE_GRID;
import static com.android.documentsui.base.State.MODE_LIST;
import static com.android.documentsui.util.FlagUtils.isDesktopFileHandlingFlagEnabled;
import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
+import static com.android.documentsui.util.FlagUtils.isZipNgFlagEnabled;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
@@ -946,11 +947,13 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
mSelectionMgr.copySelection(selection);
final int id = item.getItemId();
- if (isDesktopFileHandlingFlagEnabled() && id == R.id.dir_menu_open) {
- // On desktop, "open" is displayed in file management mode (i.e. `files.MenuManager`).
- // This menu item behaves the same as double click on the menu item which is handled by
- // onItemActivated but since onItemActivated requires a RecylcerView ItemDetails, we're
- // using viewDocument that takes a Selection.
+ if ((isDesktopFileHandlingFlagEnabled() && id == R.id.dir_menu_open)
+ || (isZipNgFlagEnabled() && id == R.id.dir_menu_browse)) {
+ // The "Open" menu item is displayed in desktop mode.
+ // The "Browse" menu item is displayed for supported archives in advanced ZIP mode.
+ // These menu items behave the same as a double click on the matching document which
+ // is handled by onItemActivated but since onItemActivated requires a RecyclerView
+ // ItemDetails, we're using viewDocument that takes a Selection.
viewDocument(selection);
return true;
} else if (id == R.id.action_menu_select || id == R.id.dir_menu_open) {
diff --git a/src/com/android/documentsui/dirlist/SelectionMetadata.java b/src/com/android/documentsui/dirlist/SelectionMetadata.java
index 74b6061b3..0d0644502 100644
--- a/src/com/android/documentsui/dirlist/SelectionMetadata.java
+++ b/src/com/android/documentsui/dirlist/SelectionMetadata.java
@@ -18,6 +18,7 @@ package com.android.documentsui.dirlist;
import static com.android.documentsui.base.DocumentInfo.getCursorInt;
import static com.android.documentsui.base.DocumentInfo.getCursorString;
+import static com.android.documentsui.util.FlagUtils.isZipNgFlagEnabled;
import android.database.Cursor;
import android.provider.DocumentsContract.Document;
@@ -56,7 +57,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 +86,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 +107,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 +124,8 @@ public class SelectionMetadata extends SelectionObserver<String>
mWritableDirectoryCount = 0;
mNoDeleteCount = 0;
mNoRenameCount = 0;
+ mInArchiveCount = 0;
+ mArchiveCount = 0;
}
@Override
@@ -143,6 +154,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;
}
@@ -169,6 +185,7 @@ public class SelectionMetadata extends SelectionObserver<String>
@Override
public boolean canOpen() {
- return size() == 1 && mDirectoryCount == 0 && mInArchiveCount == 0 && mPartialCount == 0;
+ return mFileCount == 1 && mDirectoryCount == 0 && mPartialCount == 0 && (
+ isZipNgFlagEnabled() || mInArchiveCount == 0);
}
}
diff --git a/src/com/android/documentsui/picker/PickFragment.java b/src/com/android/documentsui/picker/PickFragment.java
index e9610a510..66f05fa0f 100644
--- a/src/com/android/documentsui/picker/PickFragment.java
+++ b/src/com/android/documentsui/picker/PickFragment.java
@@ -22,7 +22,9 @@ import static com.android.documentsui.services.FileOperationService.OPERATION_DE
import static com.android.documentsui.services.FileOperationService.OPERATION_EXTRACT;
import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
import static com.android.documentsui.services.FileOperationService.OPERATION_UNKNOWN;
+import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -179,13 +181,19 @@ public class PickFragment extends Fragment {
switch (mAction) {
case State.ACTION_OPEN_TREE:
mPick.setText(getString(R.string.open_tree_button));
- mPick.setWidth(Integer.MAX_VALUE);
- mCancel.setVisibility(View.GONE);
+ // On laptops we want the "Use this folder" button to appear with the "Cancel"
+ // button as a back gesture with a mouse is not easy.
+ if (!isUseMaterial3FlagEnabled()
+ || !getActivity().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_PC)) {
+ mPick.setWidth(Integer.MAX_VALUE);
+ mCancel.setVisibility(View.GONE);
+ mPickOverlay.setVisibility(
+ mPickTarget.isBlockedFromTree() && mRestrictScopeStorage
+ ? View.VISIBLE
+ : View.GONE);
+ }
mPick.setEnabled(!(mPickTarget.isBlockedFromTree() && mRestrictScopeStorage));
- mPickOverlay.setVisibility(
- mPickTarget.isBlockedFromTree() && mRestrictScopeStorage
- ? View.VISIBLE
- : View.GONE);
break;
case State.ACTION_PICK_COPY_DESTINATION:
int titleId;
diff --git a/src/com/android/documentsui/picker/SaveFragment.java b/src/com/android/documentsui/picker/SaveFragment.java
index f881768b9..8316688e7 100644
--- a/src/com/android/documentsui/picker/SaveFragment.java
+++ b/src/com/android/documentsui/picker/SaveFragment.java
@@ -16,7 +16,11 @@
package com.android.documentsui.picker;
+import static com.android.documentsui.util.FlagUtils.isUseMaterial3FlagEnabled;
+
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
@@ -42,6 +46,9 @@ import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.Shared;
+import com.google.android.material.button.MaterialButton;
+import com.google.android.material.textfield.TextInputLayout;
+
/**
* Display document title editor and save button.
*/
@@ -54,6 +61,7 @@ public class SaveFragment extends Fragment {
private DocumentInfo mReplaceTarget;
private EditText mDisplayName;
private TextView mSave;
+ private MaterialButton mCancel;
private ProgressBar mProgress;
private boolean mIgnoreNextEdit;
@@ -84,9 +92,16 @@ public class SaveFragment extends Fragment {
final View view = inflater.inflate(R.layout.fragment_save, container, false);
- final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
- icon.setImageDrawable(
- IconUtils.loadMimeIcon(context, getArguments().getString(EXTRA_MIME_TYPE)));
+ final Drawable icon =
+ IconUtils.loadMimeIcon(context, getArguments().getString(EXTRA_MIME_TYPE));
+ if (isUseMaterial3FlagEnabled()) {
+ final TextInputLayout titleWrapper =
+ (TextInputLayout) view.findViewById(R.id.title_wrapper);
+ titleWrapper.setStartIconDrawable(icon);
+ } else {
+ final ImageView iconHolder = view.findViewById(android.R.id.icon);
+ iconHolder.setImageDrawable(icon);
+ }
mDisplayName = (EditText) view.findViewById(android.R.id.title);
mDisplayName.addTextChangedListener(mDisplayNameWatcher);
@@ -122,6 +137,19 @@ public class SaveFragment extends Fragment {
mSave.setOnClickListener(mSaveListener);
mSave.setEnabled(false);
+ mCancel = (MaterialButton) view.findViewById(android.R.id.button2);
+ // For >600dp, this button is always available (via the values-600dp layout override).
+ // However on smaller layouts, the button is default GONE to save on space (the back gesture
+ // can cancel the saver) and when FEATURE_PC is set a cancel button is required due to the
+ // lack of a back gesture (mainly mouse support).
+ if (isUseMaterial3FlagEnabled()
+ && mCancel != null
+ && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
+ mCancel.setOnClickListener(mCancelListener);
+ mCancel.setVisibility(View.VISIBLE);
+ mCancel.setEnabled(true);
+ }
+
mProgress = (ProgressBar) view.findViewById(android.R.id.progress);
return view;
@@ -173,6 +201,13 @@ public class SaveFragment extends Fragment {
};
+ private View.OnClickListener mCancelListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mInjector.actions.finishPicking();
+ }
+ };
+
private void performSave() {
if (mReplaceTarget != null) {
mInjector.actions.saveDocument(getChildFragmentManager(), mReplaceTarget);
diff --git a/src/com/android/documentsui/util/FlagUtils.kt b/src/com/android/documentsui/util/FlagUtils.kt
index 22febc317..a041dde44 100644
--- a/src/com/android/documentsui/util/FlagUtils.kt
+++ b/src/com/android/documentsui/util/FlagUtils.kt
@@ -35,8 +35,8 @@ class FlagUtils {
}
@JvmStatic
- fun isUseSearchV2RwFlagEnabled(): Boolean {
- return Flags.useSearchV2Rw()
+ fun isUseSearchV2FlagEnabled(): Boolean {
+ return Flags.useSearchV2ReadOnly()
}
@JvmStatic
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/loaders/FolderLoaderTest.kt b/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt
index 44c410eff..0193ea77d 100644
--- a/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt
+++ b/tests/unit/com/android/documentsui/loaders/FolderLoaderTest.kt
@@ -22,7 +22,7 @@ import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.filters.SmallTest
import com.android.documentsui.ContentLock
import com.android.documentsui.base.DocumentInfo
-import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_RW
+import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_READ_ONLY
import com.android.documentsui.testing.TestFileTypeLookup
import com.android.documentsui.testing.TestProvidersAccess
import java.time.Duration
@@ -59,7 +59,7 @@ class FolderLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@Test
- @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW)
+ @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_READ_ONLY)
fun testLoadInBackground() {
val mockProvider = mEnv.mockProviders[TestProvidersAccess.DOWNLOADS.authority]
val docs = createDocuments(TOTAL_FILE_COUNT)
diff --git a/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt b/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt
index e480337ab..7265af34b 100644
--- a/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt
+++ b/tests/unit/com/android/documentsui/loaders/SearchLoaderTest.kt
@@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.documentsui.ContentLock
import com.android.documentsui.LockingContentObserver
import com.android.documentsui.base.DocumentInfo
-import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_RW
+import com.android.documentsui.flags.Flags.FLAG_USE_SEARCH_V2_READ_ONLY
import com.android.documentsui.testing.TestFileTypeLookup
import com.android.documentsui.testing.TestProvidersAccess
import java.time.Duration
@@ -80,7 +80,7 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes
}
@Test
- @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW)
+ @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_READ_ONLY)
fun testLoadInBackground() {
val mockProvider = mEnv.mockProviders[TestProvidersAccess.DOWNLOADS.authority]
val docs = createDocuments(TOTAL_FILE_COUNT)
@@ -119,7 +119,7 @@ class SearchLoaderTest(private val testParams: LoaderTestParams) : BaseLoaderTes
}
@Test
- @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_RW)
+ @RequiresFlagsEnabled(FLAG_USE_SEARCH_V2_READ_ONLY)
@Ignore("b/397095797")
fun testBlankQueryAndRecency() {
val userIds = listOf(TestProvidersAccess.DOWNLOADS.userId)
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,