diff options
| author | 2023-01-12 03:25:48 +0000 | |
|---|---|---|
| committer | 2023-01-12 03:25:48 +0000 | |
| commit | adc98d06d607238b010d51f0e243c68435550bc1 (patch) | |
| tree | 72b6d70118fdf229ee940386dd43e8748c7f0909 | |
| parent | 8b0fad3c9a4bfb6e82c734afca06c7df26fddf0b (diff) | |
| parent | adf5e308c32086b61fa5fb1b1c7ac4cbe891f9ee (diff) | |
Merge "Initial porting of View and Res for AccessibilityMenu"
37 files changed, 1684 insertions, 3 deletions
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp index a494f5e086ae..0b1a3e272d01 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp @@ -20,6 +20,17 @@ package { android_app { name: "AccessibilityMenu", + + static_libs: [ + "androidx.coordinatorlayout_coordinatorlayout", + "androidx.core_core", + "androidx.viewpager_viewpager", + ], + + uses_libs: [ + "org.apache.http.legacy", + ], + srcs: [ "src/**/*.java", ], diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml new file mode 100644 index 000000000000..c89e4c318805 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_enabled="false" + android:color="@color/footer_icon_disabled_color" /> <!-- disabled --> + <item android:color="@color/footer_icon_enabled_color" /> <!-- default --> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png Binary files differnew file mode 100644 index 000000000000..6149ee4b2365 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml new file mode 100644 index 000000000000..5ff245d6a11e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item> + <ripple + android:color="@color/ripple_material_color"> + <item android:id="@android:id/mask"> + <color android:color="@color/overlay_bg_color"/> + </item> + </ripple> + </item> + +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml new file mode 100644 index 000000000000..5ff245d6a11e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + + <item> + <ripple + android:color="@color/ripple_material_color"> + <item android:id="@android:id/mask"> + <color android:color="@color/overlay_bg_color"/> + </item> + </ripple> + </item> + +</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml new file mode 100644 index 000000000000..a2eaf95dcef0 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2023 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. + --> + +<vector android:height="108dp" android:viewportHeight="24" + android:viewportWidth="24" android:width="108dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="M7.875,10.625C7.1188,10.625 6.5,11.2437 6.5,12C6.5,12.7562 7.1188,13.375 7.875,13.375C8.6313,13.375 9.25,12.7562 9.25,12C9.25,11.2437 8.6313,10.625 7.875,10.625ZM16.125,10.625C15.3687,10.625 14.75,11.2437 14.75,12C14.75,12.7562 15.3687,13.375 16.125,13.375C16.8813,13.375 17.5,12.7562 17.5,12C17.5,11.2437 16.8813,10.625 16.125,10.625ZM10.625,12C10.625,11.2437 11.2438,10.625 12,10.625C12.7563,10.625 13.375,11.2437 13.375,12C13.375,12.7562 12.7563,13.375 12,13.375C11.2438,13.375 10.625,12.7562 10.625,12Z"/> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml new file mode 100644 index 000000000000..7e1262c2b4e7 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml @@ -0,0 +1,5 @@ +<vector android:height="32dp" android:tint="#FFFFFF" + android:viewportHeight="24.0" android:viewportWidth="24.0" + android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#FF000000" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml new file mode 100644 index 000000000000..f6af270095c3 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/footer_arrow_length" + android:height="@dimen/footer_arrow_length" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@color/footer_icon_color" + android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml new file mode 100644 index 000000000000..2f7b632d6adc --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/footer_arrow_length" + android:height="@dimen/footer_arrow_length" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="@color/footer_icon_color" + android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml new file mode 100644 index 000000000000..79e0e08d8899 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml @@ -0,0 +1,33 @@ +<vector android:height="48dp" android:viewportHeight="192.0" + android:viewportWidth="192.0" android:width="48dp" + xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#34a853" android:pathData="M37.14,173.74l-28.53,-90a14.53,14.53 0,0 1,5 -15.63L87.15,11a14.21,14.21 0,0 1,17.61 0.12l73.81,58.94a14.53,14.53 0,0 1,4.8 15.57l-28.48,88.18A14.32,14.32 0,0 1,141.22 184H50.84A14.33,14.33 0,0 1,37.14 173.74Z"/> + <path android:pathData="M137.61,94.07l-17,17 -17,-17 -17,17L70.3,94.72l-17,17L125.66,184h15.56a14.32,14.32 0,0 0,13.67 -10.19l15.25,-47.21Z"> + <aapt:attr name="android:fillColor"> + <gradient android:endX="27152.64" + android:endY="32745.600000000002" + android:startX="20910.72" + android:startY="21934.079999999998" android:type="linear"> + <item android:color="#33263238" android:offset="0.0"/> + <item android:color="#11205432" android:offset="0.47"/> + <item android:color="#051E6130" android:offset="1.0"/> + </gradient> + </aapt:attr> + </path> + <path android:fillAlpha="0.2" android:fillColor="#263238" android:pathData="M50.14,100.11a12,12 0,1 1,11.39 15.77,11.72 11.72,0 0,1 -5,-1.1l-3.41,-3.4ZM129.4,91.88a12,12 0,1 1,-12 12A12,12 0,0 1,129.4 91.88ZM95.4,91.88a12,12 0,1 1,-12 12A12,12 0,0 1,95.42 91.88Z"/> + <path android:fillColor="#fff" + android:pathData="M61.53,90.88a12,12 0,1 1,-12 12A12,12 0,0 1,61.53 90.88ZM129.41,90.88a12,12 0,1 1,-12 12A12,12 0,0 1,129.41 90.88ZM95.41,90.88a12,12 0,1 1,-12 12A12,12 0,0 1,95.42 90.88Z" + android:strokeAlpha="0" android:strokeColor="#000" + android:strokeLineCap="round" android:strokeLineJoin="round" android:strokeWidth="0.5"/> + <path android:fillAlpha="0.2" android:fillColor="#263238" android:pathData="M184,80.91a14.33,14.33 0,0 1,-0.63 4.7l-28.48,88.18A14.33,14.33 0,0 1,141.21 184H50.84a14.33,14.33 0,0 1,-13.7 -10.26l-28.53,-90A14.49,14.49 0,0 1,8 79.11a14.3,14.3 0,0 0,0.61 3.64l28.53,90A14.33,14.33 0,0 0,50.84 183h90.37a14.33,14.33 0,0 0,13.67 -10.19l28.48,-88.18A14.79,14.79 0,0 0,184 80.91Z"/> + <path android:fillAlpha="0.2" android:fillColor="#fff" android:pathData="M184,81.89A14.46,14.46 0,0 0,178.57 71L104.76,12.1A14.21,14.21 0,0 0,87.15 12L13.58,69.12A14.5,14.5 0,0 0,8 80.09a14.5,14.5 0,0 1,5.57 -12L87.15,11a14.21,14.21 0,0 1,17.61 0.12L178.57,70A14.48,14.48 0,0 1,184 81.89Z"/> + <path android:pathData="M37.14,173.74l-28.53,-90a14.53,14.53 0,0 1,5 -15.63L87.15,11a14.21,14.21 0,0 1,17.61 0.12l73.81,58.94a14.53,14.53 0,0 1,4.8 15.57l-28.48,88.18A14.32,14.32 0,0 1,141.22 184H50.84A14.33,14.33 0,0 1,37.14 173.74Z"/> + <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/colorAccessibilityMenuIcon" /> + <foreground> + <inset + android:drawable="@drawable/ic_a11y_menu_round" + android:inset="21.88%" /> + </foreground> + </adaptive-icon> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml new file mode 100644 index 000000000000..ebeebf81eedc --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml @@ -0,0 +1,8 @@ +<vector android:height="32dp" + android:viewportHeight="192.0" android:viewportWidth="192.0" + android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="#34A853" android:pathData="M172,60m-12,0a12,12 0,1 1,24 0a12,12 0,1 1,-24 0"/> + <path android:fillColor="#EA4335" android:pathData="M136,88m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/> + <path android:fillColor="#FBBC05" android:pathData="M136,148m-28,0a28,28 0,1 1,56 0a28,28 0,1 1,-56 0"/> + <path android:fillColor="#4285F4" android:pathData="M56,64m-48,0a48,48 0,1 1,96 0a48,48 0,1 1,-96 0"/> +</vector> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png Binary files differnew file mode 100644 index 000000000000..b0d169642461 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png Binary files differnew file mode 100644 index 000000000000..b777ffee62fd --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png Binary files differnew file mode 100644 index 000000000000..998bd9037462 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/view_background.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/view_background.xml new file mode 100644 index 000000000000..c1f76f370c52 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/view_background.xml @@ -0,0 +1,5 @@ +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="rectangle"> + <solid android:color="@color/overlay_bg_color" /> +</shape> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml new file mode 100644 index 000000000000..658c03bd388f --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/footerlayout" + android:layout_width="match_parent" + android:layout_height="@dimen/grid_item_btn_view_height" + android:layout_alignParentBottom="true" + android:layout_gravity="bottom" + android:layoutDirection="ltr" + android:orientation="vertical" + android:visibility="gone"> + + <View + android:id="@+id/top_listDivider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="horizontal"> + + <ImageButton + android:id="@+id/menu_prev_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@drawable/footer_button_background_left" + android:contentDescription="@string/previous_button_content_description" + android:scaleType="centerInside" + android:src="@drawable/ic_arrow_back_24dp" + android:tint="@color/footer_icon_tint_color"/> + + <View + android:layout_width="1dp" + android:layout_height="match_parent" + android:background="?android:attr/listDivider"/> + + <ImageButton + android:id="@+id/menu_next_button" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:background="@drawable/footer_button_background_right" + android:contentDescription="@string/next_button_content_description" + android:scaleType="centerInside" + android:src="@drawable/ic_arrow_forward_24dp" + android:tint="@color/footer_icon_tint_color"/> + + </LinearLayout> + + <View + android:id="@+id/bottom_listDivider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?android:attr/listDivider"/> + +</LinearLayout> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml new file mode 100644 index 000000000000..39e5a8c6876b --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingTop="@dimen/grid_item_padding" + android:paddingBottom="@dimen/grid_item_padding" + android:gravity="center"> + + <ImageButton + android:id="@+id/shortcutIconBtn" + android:layout_width="@dimen/image_button_width" + android:layout_height="@dimen/image_button_height" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:scaleType="fitCenter"></ImageButton> + +<TextView + android:id="@+id/shortcutLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/grid_item_text_view_margin_top" + android:layout_below="@+id/shortcutIconBtn" + android:layout_centerHorizontal="true" + android:ellipsize="end" + android:gravity="center_horizontal" + android:importantForAccessibility="no" + android:lines="2" + android:textSize="@dimen/label_text_size" + android:textAlignment="center" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.Button"/> + +</RelativeLayout> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml new file mode 100644 index 000000000000..c198443415dd --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<GridView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/gridview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:horizontalSpacing="@dimen/a11ymenu_grid_layout_margin" + android:listSelector="@android:color/transparent" + android:numColumns="3" + android:overScrollMode="never" + android:stretchMode="columnWidth"> +</GridView> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml new file mode 100644 index 000000000000..28a633e5d17a --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/row_width" + android:layout_height="match_parent" + android:id="@+id/coordinatorLayout" + android:background="@drawable/view_background" + > + <LinearLayout + android:layout_width="@dimen/row_width" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <androidx.viewpager.widget.ViewPager + android:id="@+id/view_pager" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="@dimen/table_margin_top" + android:paddingBottom="@dimen/a11ymenu_layout_margin" + android:paddingLeft="@dimen/a11ymenu_layout_margin" + android:paddingRight="@dimen/a11ymenu_layout_margin" + android:layout_gravity="center" + android:gravity="center" + /> + + <include layout="@layout/footerlayout_switch_page"/> + </LinearLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml new file mode 100644 index 000000000000..69f09343b1d1 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <dimen name="table_margin_top">0dp</dimen> + <dimen name="row_width">388dp</dimen> + <dimen name="image_button_height">45dp</dimen> + <dimen name="image_button_width">45dp</dimen> + <dimen name="image_button_marginBottom">1dp</dimen> + + <!-- dimens for gridview layout. --> + <dimen name="grid_item_padding">4dp</dimen> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml new file mode 100644 index 000000000000..33c0cca5131e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <color name="power_color">#dadce0</color> + <color name="quick_settings_color">#78d9ec</color> + <color name="a11y_settings_color">#d9affe</color> + <color name="recent_apps_color">#f0a5dd</color> + <color name="lockscreen_color">#85e4a0</color> + <color name="volume_color">#7ae3d4</color> + <color name="notifications_color">#f496ac</color> + <color name="screenshot_color">#adcbff</color> + <color name="assistant_color">#F1F3F4</color> + <color name="brightness_color">#fdd663</color> + + <color name="ripple_material_color">#10FFFFFF</color> + + <color name="overlay_bg_color">#313235</color> + <color name="footer_icon_color">#E8EAED</color> + <color name="footer_icon_enabled_color">#E8EAED</color> + <color name="footer_icon_disabled_color">#5F6368</color> + <color name="colorControlNormal">#202124</color> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml new file mode 100644 index 000000000000..81b3152375ff --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> + +<resources> + <!--Adds the theme to support SnackBar component and user configurable theme. --> + <style name="ServiceTheme" parent="android:Theme.DeviceDefault.DayNight"> + <item name="android:colorControlNormal">@color/colorControlNormal</item> + </style> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml new file mode 100644 index 000000000000..2f9d6b5c8a19 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <bool name="isAtLeastP">true</bool> + +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml new file mode 100644 index 000000000000..36d1fc1263c4 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources> + <color name="power_color">#757575</color> + <color name="quick_settings_color">#2196F3</color> + <color name="a11y_settings_color">#5806C9</color> + <color name="recent_apps_color">#AD2EC6</color> + <color name="lockscreen_color">#0F9D58</color> + <color name="volume_color">#01A2A0</color> + <color name="notifications_color">#F15B8D</color> + <color name="screenshot_color">#26459C</color> + <color name="assistant_color">#F1F3F4</color> + <color name="brightness_color">#E59810</color> + <color name="colorAccent">#1a73e8</color> + + <color name="ripple_material_color">#1f000000</color> + + <color name="overlay_bg_color">@android:color/white</color> + <color name="footer_icon_color">@android:color/black</color> + <color name="footer_icon_enabled_color">@android:color/black</color> + <color name="footer_icon_disabled_color">#ddd</color> + <color name="colorControlNormal">@android:color/white</color> + + <color name="colorAccessibilityMenuIcon">#3AA757</color> +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml new file mode 100644 index 000000000000..7ed18977cd54 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- the curve radius for the background of the complete layout --> + <dimen name="table_margin_top">22dp</dimen> + <dimen name="row_width">@dimen/custom_match_parent</dimen> + <dimen name="image_button_height">60dp</dimen> + <dimen name="image_button_width">60dp</dimen> + <dimen name="image_button_marginBottom">2dp</dimen> + <dimen name="a11ymenu_layout_margin">4dp</dimen> + <dimen name="custom_match_parent">-1px</dimen> + + <!-- dimens for gridview layout. --> + <dimen name="grid_item_text_view_margin_top">2dp</dimen> + <dimen name="grid_item_padding">10dp</dimen> + <dimen name="grid_item_btn_view_height">48dp</dimen> + <dimen name="a11ymenu_grid_layout_margin">8dp</dimen> + + <!-- dimens for a11y menu footer layout. --> + <dimen name="footer_arrow_length">24dp</dimen> + + <!-- text size for shortcut label when large button settings in on. --> + <dimen name="large_label_text_size">18sp</dimen> + <dimen name="label_text_size">14sp</dimen> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml new file mode 100644 index 000000000000..0c25ec4353a5 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- user customized shortcuts preference --> + <string name="pref_user_shortcuts">accessibility_menu_user_shortcuts</string> + <!-- key for user customized shortcuts --> + <string name="pref_user_shortcuts_key">pref_user_shortcuts_key</string> + <!-- value for empty shortcut --> + <string name="pref_user_shortcuts_value_empty">[]</string> + <!-- empty string for shortcut label --> + <string name="empty_content"></string> + + <string name="pref_large_buttons">pref_large_buttons</string> + + <!-- key for Help&feedback settings [CHAR_LIMIT=NONE] --> + <string name="pref_help">pref_help</string> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml new file mode 100644 index 000000000000..30fd0173ff3f --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- String defining the service name --> + <string name="accessibility_menu_service_name">Accessibility Menu</string> + <!-- Accessibility Menu detail intro. [CHAR_LIMIT=NONE] --> + <string name="accessibility_menu_intro"> + The Accessibility Menu provides a large on-screen menu to control your device. You can lock your device, control volume and brightness, take screenshots, and more. + </string> + <!-- String defining the label for the assistant button --> + <string name="assistant_label">Assistant</string> + <!-- String defining utterance for the assistant button for screen readers --> + <string name="assistant_utterance">Google Assistant</string> + <!-- String defining the label for the accessibility settings button --> + <string name="a11y_settings_label">Accessibility Settings</string> + <!-- String defining the label for the volume button --> + <string name="volume_label">Volume</string> + <!-- String defining utterance for the volume button for screen readers --> + <string name="volume_utterance">Volume controls</string> + <!-- String defining the label for the power button --> + <string name="power_label">Power</string> + <!-- String defining utterance for the power button for screen readers --> + <string name="power_utterance">Power options</string> + <!-- String defining the label for the recent apps button --> + <string name="recent_apps_label">Recent apps</string> + <!-- String defining the label for the lockscreen button --> + <string name="lockscreen_label">Lock screen</string> + <!-- String defining the label for the quick settings button --> + <string name="quick_settings_label">Quick Settings</string> + <!-- String defining the label for the notifications button --> + <string name="notifications_label">Notifications</string> + <!-- String defining the label for the screenshot button --> + <string name="screenshot_label">Screenshot</string> + <!-- String defining the utterance for the screenshot button for screen readers --> + <string name="screenshot_utterance">Take screenshot</string> + <!-- String defining the label for the volume up/down button --> + <string name="volume_up_label">Volume up</string> + <string name="volume_down_label">Volume down</string> + <!-- String defining the label for the brightness up/down button --> + <string name="brightness_up_label">Brightness up</string> + <string name="brightness_down_label">Brightness down</string> + <!-- String defining the content description for the footer previous/next button --> + <string name="previous_button_content_description">Go to previous screen</string> + <string name="next_button_content_description">Go to next screen</string> + + <string name="accessibility_menu_description"> + The Accessibility Menu provides a large on-screen menu to control your device. You can lock your device, control volume and brightness, take screenshots, and more. + </string> + <!-- Short summary of app that appears as subtext on the service preference in Settings --> + <string name="accessibility_menu_summary">Control device via large menu</string> + + <!-- TODO(b/113371047): string need to be reviewed --> + <!-- String defining the settings name --> + <string name="accessibility_menu_settings_name">Accessibility Menu Settings</string> + + <!-- String defining the title of Large button setting --> + <string name="accessibility_menu_large_buttons_title">Large buttons</string> + <!-- String defining the summary of Large button setting --> + <string name="accessibility_menu_large_buttons_summary">Increase size of Accessibility Menu Buttons</string> + <!-- String defining the title of the preference to show help and feedback menu [CHAR LIMIT=40] --> + <string name="pref_help_and_feedback_title">Help & feedback</string> + <!-- String defining the title of the preference to show help menu [CHAR LIMIT=40] --> + <string name="pref_help_title">Help</string> + + <!-- The percentage of the brightness, and double "%" is required to represent the symbol "%" --> + <string name="brightness_percentage_label">Brightness <xliff:g id="percentage">%1$s</xliff:g> %%</string> + <!-- The percentage of the music volume, and double "%" is required to represent the symbol "%" --> + <string name="music_volume_percentage_label">Music volume <xliff:g id="percentage">%1$s</xliff:g> %%</string> + + <!-- The label of a settings item that displays legal information about the licenses used in this app. [CHAR LIMIT=NONE] --> + <string name="pref_item_licenses">Open Source Licenses</string> + +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml new file mode 100644 index 000000000000..a2cf26730960 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 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. +--> + +<resources> + <!--The theme is for preference CollapsingToolbarBaseActivity settings--> + <style name="AccessibilityMenuSettings" parent="android:Theme.DeviceDefault.Light" /> + + <!--Adds the theme to support SnackBar component and user configurable theme. --> + <style name="ServiceTheme" parent="android:Theme.DeviceDefault.Light"> + <item name="android:colorControlNormal">@color/colorControlNormal</item> + </style> + + <!--The basic theme for service and test case only--> + <style name="A11yMenuBaseTheme" parent="android:Theme.DeviceDefault.Light"> + <item name="android:windowActionBar">false</item> + </style> +</resources> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml index 96882d335d4b..3dbbb1a658c9 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml @@ -13,4 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> -<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"/>
\ No newline at end of file +<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" + android:accessibilityFeedbackType="feedbackGeneric" + android:accessibilityFlags="flagRequestAccessibilityButton|flagRequestFilterKeyEvents" + android:canRequestFilterKeyEvents="true" + android:summary="@string/accessibility_menu_summary" + android:intro="@string/accessibility_menu_intro" + android:animatedImageDrawable="@drawable/a11ymenu_intro" + android:isAccessibilityTool="true" +/>
\ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 8b759004f657..5c4fdcc0e5d8 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -17,16 +17,39 @@ package com.android.systemui.accessibility.accessibilitymenu; import android.accessibilityservice.AccessibilityService; +import android.view.MotionEvent; +import android.view.View; import android.view.accessibility.AccessibilityEvent; +import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuOverlayLayout; + /** @hide */ -public class AccessibilityMenuService extends AccessibilityService { +public class AccessibilityMenuService extends AccessibilityService implements View.OnTouchListener { + private static final String TAG = "A11yMenuService"; + + private A11yMenuOverlayLayout mA11yMenuLayout; + + @Override + public void onCreate() { + super.onCreate(); + } @Override - public void onAccessibilityEvent(AccessibilityEvent event) { + protected void onServiceConnected() { + mA11yMenuLayout = new A11yMenuOverlayLayout(this); + super.onServiceConnected(); + mA11yMenuLayout.toggleVisibility(); } @Override + public void onAccessibilityEvent(AccessibilityEvent event) {} + + @Override public void onInterrupt() { } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java new file mode 100644 index 000000000000..fa42e61899fd --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022 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. + */ +package com.android.systemui.accessibility.accessibilitymenu.model; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** Provides a data structure for a11y menu shortcuts. */ +public class A11yMenuShortcut { + + public enum ShortcutId { + UNSPECIFIED_ID_VALUE, + ID_ASSISTANT_VALUE, + ID_A11YSETTING_VALUE, + ID_POWER_VALUE, + ID_VOLUME_DOWN_VALUE, + ID_VOLUME_UP_VALUE, + ID_RECENT_VALUE, + ID_BRIGHTNESS_DOWN_VALUE, + ID_BRIGHTNESS_UP_VALUE, + ID_LOCKSCREEN_VALUE, + ID_QUICKSETTING_VALUE, + ID_NOTIFICATION_VALUE, + ID_SCREENSHOT_VALUE + } + + private static final String TAG = "A11yMenuShortcut"; + + /** Shortcut id used to identify. */ + private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal(); + + // Resource IDs of shortcut button and label. + public int imageSrc; + public int imageColor; + public int imgContentDescription; + public int labelText; + + public A11yMenuShortcut(int id) { + setId(id); + } + + /** + * Sets Id to shortcut, checks the value first and updates shortcut resources. It will set id to + * + * @param id id set to shortcut + */ + public void setId(int id) { + mShortcutId = id; + + // TODO(jonesriley) load the proper resources based on id + imageSrc = R.drawable.ic_logo_assistant_32dp; + imageColor = android.R.color.darker_gray; + imgContentDescription = R.string.empty_content; + labelText = R.string.empty_content; + } + + public int getId() { + return mShortcutId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof A11yMenuShortcut)) { + return false; + } + + A11yMenuShortcut targetObject = (A11yMenuShortcut) o; + + return mShortcutId == targetObject.mShortcutId; + } + + @Override + public int hashCode() { + return mShortcutId; + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java new file mode 100644 index 000000000000..28ba4b54107f --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2022 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. + */ +package com.android.systemui.accessibility.accessibilitymenu.utils; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.RippleDrawable; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** Creates background drawable for a11y menu shortcut. */ +public class ShortcutDrawableUtils { + + /** + * To make the circular background of shortcut icons have higher resolution. The higher value of + * LENGTH is, the higher resolution of the circular background are. + */ + private static final int LENGTH = 480; + + private static final int RADIUS = LENGTH / 2; + private static final int COORDINATE = LENGTH / 2; + private static final int RIPPLE_COLOR_ID = R.color.ripple_material_color; + + private final Context mContext; + private final ColorStateList mRippleColorStateList; + + // Placeholder of drawable to prevent NullPointerException + private final ColorDrawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT); + + public ShortcutDrawableUtils(Context context) { + this.mContext = context; + + int rippleColor = context.getColor(RIPPLE_COLOR_ID); + mRippleColorStateList = ColorStateList.valueOf(rippleColor); + } + + /** + * Creates a circular drawable in specific color for shortcut. + * + * @param colorResId color resource ID + * @return drawable circular drawable + */ + public Drawable createCircularDrawable(int colorResId) { + Bitmap output = Bitmap.createBitmap(LENGTH, LENGTH, Config.ARGB_8888); + Canvas canvas = new Canvas(output); + int color = mContext.getColor(colorResId); + Paint paint = new Paint(); + paint.setColor(color); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStyle(Style.FILL); + canvas.drawCircle(COORDINATE, COORDINATE, RADIUS, paint); + + BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), output); + return drawable; + } + + /** + * Creates an adaptive icon drawable in specific color for shortcut. + * + * @param colorResId color resource ID + * @return drawable for adaptive icon + */ + public Drawable createAdaptiveIconDrawable(int colorResId) { + Drawable circleLayer = createCircularDrawable(colorResId); + RippleDrawable rippleLayer = new RippleDrawable(mRippleColorStateList, null, null); + + AdaptiveIconDrawable adaptiveIconDrawable = + new AdaptiveIconDrawable(circleLayer, mTransparentDrawable); + + Drawable[] layers = {adaptiveIconDrawable, rippleLayer}; + LayerDrawable drawable = new LayerDrawable(layers); + return drawable; + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java new file mode 100644 index 000000000000..e3401a9a7915 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.systemui.accessibility.accessibilitymenu.view; + +import android.graphics.Rect; +import android.view.LayoutInflater; +import android.view.TouchDelegate; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; +import com.android.systemui.accessibility.accessibilitymenu.utils.ShortcutDrawableUtils; + +import java.util.List; + +/** GridView Adapter for a11y menu overlay. */ +public class A11yMenuAdapter extends BaseAdapter { + + // The large scale of shortcut icon and label. + private static final float LARGE_BUTTON_SCALE = 1.5f; + private final int mLargeTextSize; + + private final AccessibilityMenuService mService; + private final LayoutInflater mInflater; + private final List<A11yMenuShortcut> mShortcutDataList; + private final ShortcutDrawableUtils mShortcutDrawableUtils; + + public A11yMenuAdapter( + AccessibilityMenuService service, List<A11yMenuShortcut> shortcutDataList) { + this.mService = service; + this.mShortcutDataList = shortcutDataList; + mInflater = LayoutInflater.from(service); + + mShortcutDrawableUtils = new ShortcutDrawableUtils(service); + + mLargeTextSize = + service.getResources().getDimensionPixelOffset(R.dimen.large_label_text_size); + } + + @Override + public int getCount() { + return mShortcutDataList.size(); + } + + @Override + public Object getItem(int position) { + return mShortcutDataList.get(position); + } + + @Override + public long getItemId(int position) { + return mShortcutDataList.get(position).getId(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + convertView = mInflater.inflate(R.layout.grid_item, null); + + A11yMenuShortcut shortcutItem = (A11yMenuShortcut) getItem(position); + // Sets shortcut icon and label resource. + configureShortcutView(convertView, shortcutItem); + + expandIconTouchArea(convertView); + setActionForMenuShortcut(convertView); + return convertView; + } + + /** + * Expand shortcut icon touch area to the border of grid item. + * The height is from the top of icon to the bottom of label. + * The width is from the left border of grid item to the right border of grid item. + */ + private void expandIconTouchArea(View convertView) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + TextView shortcutLabel = convertView.findViewById(R.id.shortcutLabel); + + shortcutIconButton.post( + () -> { + Rect iconHitRect = new Rect(); + shortcutIconButton.getHitRect(iconHitRect); + Rect labelHitRect = new Rect(); + shortcutLabel.getHitRect(labelHitRect); + + final int widthAdjustment = iconHitRect.left; + iconHitRect.left = 0; + iconHitRect.right += widthAdjustment; + iconHitRect.top = 0; + iconHitRect.bottom = labelHitRect.bottom; + ((View) shortcutIconButton.getParent()) + .setTouchDelegate(new TouchDelegate(iconHitRect, shortcutIconButton)); + }); + } + + private void setActionForMenuShortcut(View convertView) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + + shortcutIconButton.setOnClickListener( + (View v) -> { + // Handles shortcut click event by AccessibilityMenuService. + // service.handleClick(v); + }); + } + + private void configureShortcutView(View convertView, A11yMenuShortcut shortcutItem) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + TextView shortcutLabel = convertView.findViewById(R.id.shortcutLabel); + + // TODO: Enlarge shortcut icon & label when large button setting is on. + + if (shortcutItem.getId() == A11yMenuShortcut.ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()) { + // Sets empty shortcut icon and label when the shortcut is ADD_ITEM. + shortcutIconButton.setImageResource(android.R.color.transparent); + shortcutIconButton.setBackground(null); + } else { + // Sets shortcut ID as tagId, to handle menu item click in AccessibilityMenuService. + shortcutIconButton.setTag(shortcutItem.getId()); + shortcutIconButton.setContentDescription( + mService.getString(shortcutItem.imgContentDescription)); + shortcutLabel.setText(shortcutItem.labelText); + shortcutIconButton.setImageResource(shortcutItem.imageSrc); + + shortcutIconButton.setBackground( + mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor)); + } + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java new file mode 100644 index 000000000000..20c63df885d2 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.systemui.accessibility.accessibilitymenu.view; + +import android.graphics.Rect; +import android.view.TouchDelegate; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.widget.ImageButton; + +import androidx.annotation.Nullable; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** + * This class is for Accessibility menu footer layout. Handles switching between a11y menu pages. + */ +public class A11yMenuFooter { + + /** Provides an interface for footer of a11yMenu. */ + public interface A11yMenuFooterCallBack { + + /** Calls back when user clicks the left button. */ + void onLeftButtonClicked(); + + /** Calls back when user clicks the right button. */ + void onRightButtonClicked(); + } + + private final FooterButtonClickListener mFooterButtonClickListener; + + private ImageButton mPreviousPageBtn; + private ImageButton mNextPageBtn; + private View mTopListDivider; + private View mBottomListDivider; + private final A11yMenuFooterCallBack mCallBack; + + public A11yMenuFooter(ViewGroup menuLayout, A11yMenuFooterCallBack callBack) { + this.mCallBack = callBack; + mFooterButtonClickListener = new FooterButtonClickListener(); + configureFooterLayout(menuLayout); + } + + public @Nullable ImageButton getPreviousPageBtn() { + return mPreviousPageBtn; + } + + public @Nullable ImageButton getNextPageBtn() { + return mNextPageBtn; + } + + private void configureFooterLayout(ViewGroup menuLayout) { + ViewGroup footerContainer = menuLayout.findViewById(R.id.footerlayout); + footerContainer.setVisibility(View.VISIBLE); + + mPreviousPageBtn = menuLayout.findViewById(R.id.menu_prev_button); + mNextPageBtn = menuLayout.findViewById(R.id.menu_next_button); + mTopListDivider = menuLayout.findViewById(R.id.top_listDivider); + mBottomListDivider = menuLayout.findViewById(R.id.bottom_listDivider); + + // Registers listeners for footer buttons. + setListener(mPreviousPageBtn); + setListener(mNextPageBtn); + + menuLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener( + new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + menuLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + expandBtnTouchArea(mPreviousPageBtn, menuLayout); + expandBtnTouchArea(mNextPageBtn, (View) mNextPageBtn.getParent()); + } + }); + } + + private void expandBtnTouchArea(ImageButton btn, View btnParent) { + Rect btnRect = new Rect(); + btn.getHitRect(btnRect); + btnRect.top -= getHitRectHeight(mTopListDivider); + btnRect.bottom += getHitRectHeight(mBottomListDivider); + btnParent.setTouchDelegate(new TouchDelegate(btnRect, btn)); + } + + private static int getHitRectHeight(View listDivider) { + Rect hitRect = new Rect(); + listDivider.getHitRect(hitRect); + return hitRect.height(); + } + + private void setListener(@Nullable View view) { + if (view != null) { + view.setOnClickListener(mFooterButtonClickListener); + } + } + + /** Handles click event for footer buttons. */ + private class FooterButtonClickListener implements OnClickListener { + @Override + public void onClick(View view) { + if (view.getId() == R.id.menu_prev_button) { + mCallBack.onLeftButtonClicked(); + } else if (view.getId() == R.id.menu_next_button) { + mCallBack.onRightButtonClicked(); + } + } + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java new file mode 100644 index 000000000000..740bc8a412af --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.systemui.accessibility.accessibilitymenu.view; + +import static java.lang.Math.max; + +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.Display; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.FrameLayout; +import android.widget.Toast; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides functionality for Accessibility menu layout in a11y menu overlay. There are functions to + * configure or update Accessibility menu layout when orientation and display size changed, and + * functions to toggle menu visibility when button clicked or screen off. + */ +public class A11yMenuOverlayLayout { + + /** Predefined default shortcuts when large button setting is off. */ + private static final int[] SHORTCUT_LIST_DEFAULT = { + A11yMenuShortcut.ShortcutId.ID_ASSISTANT_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_A11YSETTING_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_POWER_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_VOLUME_UP_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_RECENT_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_LOCKSCREEN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_QUICKSETTING_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_NOTIFICATION_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_SCREENSHOT_VALUE.ordinal() + }; + + private final AccessibilityMenuService mService; + private final WindowManager mWindowManager; + private ViewGroup mLayout; + private WindowManager.LayoutParams mLayoutParameter; + private A11yMenuViewPager mA11yMenuViewPager; + + public A11yMenuOverlayLayout(AccessibilityMenuService service) { + mService = service; + mWindowManager = mService.getSystemService(WindowManager.class); + configureLayout(); + } + + /** Creates Accessibility menu layout and configure layout parameters. */ + public View configureLayout() { + return configureLayout(A11yMenuViewPager.DEFAULT_PAGE_INDEX); + } + + // TODO(b/78292783): Find a better way to inflate layout in the test. + /** + * Creates Accessibility menu layout, configure layout parameters and apply index to ViewPager. + * + * @param pageIndex the index of the ViewPager to show. + */ + public View configureLayout(int pageIndex) { + + int lastVisibilityState = View.GONE; + if (mLayout != null) { + lastVisibilityState = mLayout.getVisibility(); + mWindowManager.removeView(mLayout); + mLayout = null; + } + + if (mLayoutParameter == null) { + initLayoutParams(); + } + + mLayout = new FrameLayout(mService); + updateLayoutPosition(); + inflateLayoutAndSetOnTouchListener(mLayout); + mA11yMenuViewPager = new A11yMenuViewPager(mService); + mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex); + mWindowManager.addView(mLayout, mLayoutParameter); + mLayout.setVisibility(lastVisibilityState); + + return mLayout; + } + + /** Updates view layout with new layout parameters only. */ + public void updateViewLayout() { + if (mLayout == null || mLayoutParameter == null) { + return; + } + updateLayoutPosition(); + mWindowManager.updateViewLayout(mLayout, mLayoutParameter); + } + + private void initLayoutParams() { + mLayoutParameter = new WindowManager.LayoutParams(); + mLayoutParameter.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; + mLayoutParameter.format = PixelFormat.TRANSLUCENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; + mLayoutParameter.setTitle(mService.getString(R.string.accessibility_menu_service_name)); + } + + private void inflateLayoutAndSetOnTouchListener(ViewGroup view) { + LayoutInflater inflater = LayoutInflater.from(mService); + inflater.inflate(R.layout.paged_menu, view); + view.setOnTouchListener(mService); + } + + /** + * Loads shortcut data from default shortcut ID array. + * + * @return A list of default shortcuts + */ + private List<A11yMenuShortcut> createShortcutList() { + List<A11yMenuShortcut> shortcutList = new ArrayList<>(); + for (int shortcutId : SHORTCUT_LIST_DEFAULT) { + shortcutList.add(new A11yMenuShortcut(shortcutId)); + } + return shortcutList; + } + + /** Updates a11y menu layout position by configuring layout params. */ + private void updateLayoutPosition() { + Display display = mLayout.getDisplay(); + final int orientation = mService.getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + switch (display.getRotation()) { + case Surface.ROTATION_90: + case Surface.ROTATION_180: + mLayoutParameter.gravity = + Gravity.END | Gravity.BOTTOM + | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; + mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT; + mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; + mLayout.setBackgroundResource(R.drawable.shadow_90deg); + break; + case Surface.ROTATION_0: + case Surface.ROTATION_270: + mLayoutParameter.gravity = + Gravity.START | Gravity.BOTTOM + | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; + mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT; + mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; + mLayout.setBackgroundResource(R.drawable.shadow_270deg); + break; + default: + break; + } + } else { + mLayoutParameter.gravity = Gravity.BOTTOM; + mLayoutParameter.width = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.height = WindowManager.LayoutParams.WRAP_CONTENT; + mLayout.setBackgroundResource(R.drawable.shadow_0deg); + } + + // Adjusts the y position of a11y menu layout to make the layout not to overlap bottom + // navigation bar window. + updateLayoutByWindowInsetsIfNeeded(); + mLayout.setOnApplyWindowInsetsListener( + (view, insets) -> { + if (updateLayoutByWindowInsetsIfNeeded()) { + mWindowManager.updateViewLayout(mLayout, mLayoutParameter); + } + return view.onApplyWindowInsets(insets); + }); + } + + /** + * Returns {@code true} if the a11y menu layout params + * should be updated by {@link WindowManager} immediately due to window insets change. + * This method adjusts the layout position and size to + * make a11y menu not to overlap navigation bar window. + */ + private boolean updateLayoutByWindowInsetsIfNeeded() { + boolean shouldUpdateLayout = false; + WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); + Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( + WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + int xOffset = max(windowInsets.left, windowInsets.right); + int yOffset = windowInsets.bottom; + Rect windowBound = windowMetrics.getBounds(); + if (mLayoutParameter.x != xOffset || mLayoutParameter.y != yOffset) { + mLayoutParameter.x = xOffset; + mLayoutParameter.y = yOffset; + shouldUpdateLayout = true; + } + // for gestural navigation mode and the landscape mode, + // the layout height should be decreased by system bar + // and display cutout inset to fit the new + // frame size that doesn't overlap the navigation bar window. + int orientation = mService.getResources().getConfiguration().orientation; + if (mLayout.getHeight() != mLayoutParameter.height + && orientation == Configuration.ORIENTATION_LANDSCAPE) { + mLayoutParameter.height = windowBound.height() - yOffset; + shouldUpdateLayout = true; + } + return shouldUpdateLayout; + } + + /** + * Gets the current page index when device configuration changed. {@link + * AccessibilityMenuService#onConfigurationChanged(Configuration)} + * + * @return the current index of the ViewPager. + */ + public int getPageIndex() { + if (mA11yMenuViewPager != null) { + return mA11yMenuViewPager.mViewPager.getCurrentItem(); + } + return A11yMenuViewPager.DEFAULT_PAGE_INDEX; + } + + /** + * Hides a11y menu layout. And return if layout visibility has been changed. + * + * @return {@code true} layout visibility is toggled off; {@code false} is unchanged + */ + public boolean hideMenu() { + if (mLayout.getVisibility() == View.VISIBLE) { + mLayout.setVisibility(View.GONE); + return true; + } + return false; + } + + /** Toggles a11y menu layout visibility. */ + public void toggleVisibility() { + mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE); + } + + /** Shows hint text on Toast. */ + public void showToast(String text) { + final View viewPos = mLayout.findViewById(R.id.coordinatorLayout); + Toast.makeText(viewPos.getContext(), text, Toast.LENGTH_SHORT).show(); + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java new file mode 100644 index 000000000000..c510b876e847 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.systemui.accessibility.accessibilitymenu.view; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.GridView; + +import androidx.viewpager.widget.ViewPager; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; +import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuFooter.A11yMenuFooterCallBack; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class handles UI for viewPager and footer. + * It displays grid pages containing all shortcuts in viewPager, + * and handles the click events from footer to switch between pages. + */ +public class A11yMenuViewPager { + + /** The default index of the ViewPager. */ + public static final int DEFAULT_PAGE_INDEX = 0; + + /** + * The class holds the static parameters for grid view when large button settings is on/off. + */ + public static final class GridViewParams { + /** Total shortcuts count in the grid view when large button settings is off. */ + public static final int GRID_ITEM_COUNT = 9; + + /** The number of columns in the grid view when large button settings is off. */ + public static final int GRID_COLUMN_COUNT = 3; + + /** Total shortcuts count in the grid view when large button settings is on. */ + public static final int LARGE_GRID_ITEM_COUNT = 4; + + /** The number of columns in the grid view when large button settings is on. */ + public static final int LARGE_GRID_COLUMN_COUNT = 2; + + /** Temporary measure to test both item types. */ + private static final boolean USE_LARGE_ITEMS = true; + + /** + * Returns the number of items in the grid view. + * + * @param context The parent context + * @return Grid item count + */ + public static int getGridItemCount(Context context) { + return USE_LARGE_ITEMS + ? LARGE_GRID_ITEM_COUNT + : GRID_ITEM_COUNT; + } + + /** + * Returns the number of columns in the grid view. + * + * @param context The parent context + * @return Grid column count + */ + public static int getGridColumnCount(Context context) { + return USE_LARGE_ITEMS + ? LARGE_GRID_COLUMN_COUNT + : GRID_COLUMN_COUNT; + } + + /** + * Returns the number of rows in the grid view. + * + * @param context The parent context + * @return Grid row count + */ + public static int getGridRowCount(Context context) { + return USE_LARGE_ITEMS + ? (LARGE_GRID_ITEM_COUNT / LARGE_GRID_COLUMN_COUNT) + : (GRID_ITEM_COUNT / GRID_COLUMN_COUNT); + } + + /** + * Separates a provided list of accessibility shortcuts into multiple sub-lists. + * Does not modify the original list. + * + * @param pageItemCount The maximum size of an individual sub-list. + * @param shortcutList The list of shortcuts to be separated into sub-lists. + * @return A list of shortcut sub-lists. + */ + public static List<List<A11yMenuShortcut>> generateShortcutSubLists( + int pageItemCount, List<A11yMenuShortcut> shortcutList) { + int start = 0; + int end; + int shortcutListSize = shortcutList.size(); + List<List<A11yMenuShortcut>> subLists = new ArrayList<>(); + while (start < shortcutListSize) { + end = Math.min(start + pageItemCount, shortcutListSize); + subLists.add(shortcutList.subList(start, end)); + start = end; + } + return subLists; + } + + private GridViewParams() {} + } + + private final AccessibilityMenuService mService; + + /** + * The pager widget, which handles animation and allows swiping horizontally to access previous + * and next gridView pages. + */ + protected ViewPager mViewPager; + + private ViewPagerAdapter<GridView> mViewPagerAdapter; + private final List<GridView> mGridPageList = new ArrayList<>(); + + /** The footer, which provides buttons to switch between pages */ + protected A11yMenuFooter mA11yMenuFooter; + + /** The shortcut list intended to show in grid pages of viewPager */ + private List<A11yMenuShortcut> mA11yMenuShortcutList; + + /** The container layout for a11y menu. */ + private ViewGroup mA11yMenuLayout; + + public A11yMenuViewPager(AccessibilityMenuService service) { + this.mService = service; + } + + /** + * Configures UI for view pager and footer. + * + * @param a11yMenuLayout the container layout for a11y menu + * @param shortcutDataList the data list need to show in view pager + * @param pageIndex the index of ViewPager to show + */ + public void configureViewPagerAndFooter( + ViewGroup a11yMenuLayout, List<A11yMenuShortcut> shortcutDataList, int pageIndex) { + this.mA11yMenuLayout = a11yMenuLayout; + mA11yMenuShortcutList = shortcutDataList; + initViewPager(); + initChildPage(); + mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks); + updateFooterState(); + registerOnGlobalLayoutListener(); + goToPage(pageIndex); + } + + /** Initializes viewPager and its adapter. */ + private void initViewPager() { + mViewPager = mA11yMenuLayout.findViewById(R.id.view_pager); + mViewPagerAdapter = new ViewPagerAdapter<>(); + mViewPager.setAdapter(mViewPagerAdapter); + mViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER); + mViewPager.addOnPageChangeListener( + new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrollStateChanged(int state) {} + + @Override + public void onPageScrolled( + int position, float positionOffset, int positionOffsetPixels) {} + + @Override + public void onPageSelected(int position) { + updateFooterState(); + } + }); + } + + /** Creates child pages of viewPager by the length of shortcuts and initializes them. */ + private void initChildPage() { + if (mA11yMenuShortcutList == null || mA11yMenuShortcutList.isEmpty()) { + return; + } + + if (!mGridPageList.isEmpty()) { + mGridPageList.clear(); + } + + // Generate pages by calculating # of items per grid. + for (List<A11yMenuShortcut> page : GridViewParams.generateShortcutSubLists( + GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList) + ) { + addGridPage(page); + } + + mViewPagerAdapter.set(mGridPageList); + } + + private void addGridPage(List<A11yMenuShortcut> shortcutDataListInPage) { + LayoutInflater inflater = LayoutInflater.from(mService); + View view = inflater.inflate(R.layout.grid_view, null); + GridView gridView = view.findViewById(R.id.gridview); + A11yMenuAdapter adapter = new A11yMenuAdapter(mService, shortcutDataListInPage); + gridView.setNumColumns(GridViewParams.getGridColumnCount(mService)); + gridView.setAdapter(adapter); + mGridPageList.add(gridView); + } + + /** Updates footer's state by index of current page in view pager. */ + private void updateFooterState() { + int currentPage = mViewPager.getCurrentItem(); + int lastPage = mViewPager.getAdapter().getCount() - 1; + mA11yMenuFooter.getPreviousPageBtn().setEnabled(currentPage > 0); + mA11yMenuFooter.getNextPageBtn().setEnabled(currentPage < lastPage); + } + + private void goToPage(int pageIndex) { + if (mViewPager == null) { + return; + } + if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getCount())) { + mViewPager.setCurrentItem(pageIndex); + } + } + + /** Registers OnGlobalLayoutListener to adjust menu UI by running callback at first time. */ + private void registerOnGlobalLayoutListener() { + mA11yMenuLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener( + new OnGlobalLayoutListener() { + + boolean mIsFirstTime = true; + + @Override + public void onGlobalLayout() { + if (!mIsFirstTime) { + return; + } + + if (mGridPageList.isEmpty()) { + return; + } + + GridView firstGridView = mGridPageList.get(0); + if (firstGridView == null + || firstGridView.getChildAt(0) == null) { + return; + } + + mIsFirstTime = false; + + int gridItemHeight = firstGridView.getChildAt(0) + .getMeasuredHeight(); + adjustMenuUISize(gridItemHeight); + } + }); + } + + /** + * Adjusts menu UI to fit both landscape and portrait mode. + * + * <ol> + * <li>Adjust view pager's height. + * <li>Adjust vertical interval between grid items. + * <li>Adjust padding in view pager. + * </ol> + */ + private void adjustMenuUISize(int gridItemHeight) { + final int rowsInGridView = GridViewParams.getGridRowCount(mService); + final int defaultMargin = + (int) mService.getResources().getDimension(R.dimen.a11ymenu_layout_margin); + final int topMargin = (int) mService.getResources().getDimension(R.dimen.table_margin_top); + final int displayMode = mService.getResources().getConfiguration().orientation; + int viewPagerHeight = mViewPager.getMeasuredHeight(); + + if (displayMode == Configuration.ORIENTATION_PORTRAIT) { + // In portrait mode, we only need to adjust view pager's height to match its + // child's height. + viewPagerHeight = gridItemHeight * rowsInGridView + defaultMargin + topMargin; + } else if (displayMode == Configuration.ORIENTATION_LANDSCAPE) { + // In landscape mode, we need to adjust view pager's height to match screen height + // and adjust its child too, + // because a11y menu layout height is limited by the screen height. + DisplayMetrics displayMetrics = mService.getResources().getDisplayMetrics(); + float densityScale = (float) displayMetrics.densityDpi + / DisplayMetrics.DENSITY_DEVICE_STABLE; + View footerLayout = mA11yMenuLayout.findViewById(R.id.footerlayout); + // Keeps footer window height unchanged no matter the density is changed. + footerLayout.getLayoutParams().height = + (int) (footerLayout.getLayoutParams().height / densityScale); + // Adjust the view pager height for system bar and display cutout insets. + WindowManager windowManager = mService.getSystemService(WindowManager.class); + WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics(); + Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility( + WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + viewPagerHeight = + windowMetric.getBounds().height() + - footerLayout.getLayoutParams().height + - windowInsets.bottom; + // Sets vertical interval between grid items. + int interval = + (viewPagerHeight - topMargin - defaultMargin + - (rowsInGridView * gridItemHeight)) + / (rowsInGridView + 1); + for (GridView gridView : mGridPageList) { + gridView.setVerticalSpacing(interval); + } + + // Sets padding to view pager. + final int finalMarginTop = interval + topMargin; + mViewPager.setPadding(defaultMargin, finalMarginTop, defaultMargin, defaultMargin); + } + final ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams(); + layoutParams.height = viewPagerHeight; + mViewPager.setLayoutParams(layoutParams); + } + + /** Callback object to handle click events from A11yMenuFooter */ + protected A11yMenuFooterCallBack mFooterCallbacks = + new A11yMenuFooterCallBack() { + @Override + public void onLeftButtonClicked() { + // Moves to previous page. + int targetPage = mViewPager.getCurrentItem() - 1; + goToPage(targetPage); + updateFooterState(); + } + + @Override + public void onRightButtonClicked() { + // Moves to next page. + int targetPage = mViewPager.getCurrentItem() + 1; + goToPage(targetPage); + updateFooterState(); + } + }; +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java new file mode 100644 index 000000000000..5670d72842f4 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 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. + */ + +package com.android.systemui.accessibility.accessibilitymenu.view; + +import android.view.View; +import android.view.ViewGroup; + +import androidx.viewpager.widget.PagerAdapter; + +import java.util.List; + +/** The pager adapter, which provides the pages to the view pager widget. */ +class ViewPagerAdapter<T extends View> extends PagerAdapter { + + /** The widget list in each page of view pager. */ + private List<T> mWidgetList; + + ViewPagerAdapter() {} + + public void set(List<T> tList) { + mWidgetList = tList; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + if (mWidgetList == null) { + return 0; + } + return mWidgetList.size(); + } + + @Override + public int getItemPosition(Object object) { + return POSITION_NONE; + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + if (mWidgetList == null) { + return null; + } + container.addView(mWidgetList.get(position)); + return mWidgetList.get(position); + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } +} |