diff options
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, |