Merge "Initial porting of View and Res for AccessibilityMenu"
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 273 B |
Binary file not shown.
|
After Width: | Height: | Size: 275 B |
Binary file not shown.
|
After Width: | Height: | Size: 276 B |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="isAtLeastP">true</bool>
|
||||
|
||||
</resources>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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"/>
|
||||
<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"
|
||||
/>
|
||||
@@ -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 onAccessibilityEvent(AccessibilityEvent event) {
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user