Merge changes from topic "accessibility_floating_menu_ui" into sc-dev
* changes:
Remove half circle option in size selector
Update the vector drawable for accessibility button preview preference
Remove the accessibility gesture tutorial dialog in system navigation
Update the dialog content for accessibility floating menu
Add the preference controller to control accessibility button fade preference
Add the preference controller to control accessibility button preview preference
Add the preference controller to control accessibility button opacity preference
Add the preference controller to control accessibility button size preference
Add the preference controller to control accessibility button location preference
Setup basic layout and resources for the accessibility button settings page
diff --git a/res/drawable/accessibility_button_navigation.xml b/res/drawable/accessibility_button_navigation.xml
new file mode 100644
index 0000000..82e3c70
--- /dev/null
+++ b/res/drawable/accessibility_button_navigation.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <group>
+ <clip-path
+ android:pathData="M0,0h125v153h-125z"/>
+ <path
+ android:pathData="M7.4,-62.9L117.6,-62.9A6.3,6.3 0,0 1,123.9 -56.6L123.9,145.6A6.3,6.3 0,0 1,117.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#DADCE0"
+ android:strokeColor="#BDC1C6"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,-62.9L116.6,-62.9A6.3,6.3 0,0 1,122.9 -56.6L122.9,145.6A6.3,6.3 0,0 1,116.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"/>
+ <path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"
+ android:fillColor="#F8F9FA"/>
+ <group>
+ <clip-path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"/>
+ </group>
+ </group>
+ <path
+ android:pathData="M7,126H118V140.6C118,143.582 115.582,146 112.6,146H12.4C9.418,146 7,143.582 7,140.6V126Z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.87"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M63.5,138.688C64.713,138.688 65.697,137.708 65.697,136.5C65.697,135.292 64.713,134.312 63.5,134.312C62.286,134.312 61.303,135.292 61.303,136.5C61.303,137.708 62.286,138.688 63.5,138.688Z"
+ android:fillColor="#00000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#9AA0A6"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M33.694,133.953C33.827,133.876 33.994,133.972 33.994,134.126V138.874C33.994,139.028 33.827,139.125 33.694,139.047L29.604,136.673C29.471,136.596 29.471,136.404 29.604,136.327L33.694,133.953Z"
+ android:fillColor="#00000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#9AA0A6"/>
+ <path
+ android:pathData="M96.111,131.2C96.111,131.86 95.611,132.4 95,132.4C94.389,132.4 93.889,131.86 93.889,131.2C93.889,130.54 94.389,130 95,130C95.611,130 96.111,130.54 96.111,131.2ZM95,133C96.572,133 98.272,132.82 99.722,132.4L100,133.6C98.967,133.9 97.778,134.098 96.667,134.2V142H95.556V138.4H94.444V142H93.333V134.2C92.222,134.098 91.033,133.9 90,133.6L90.278,132.4C91.728,132.82 93.428,133 95,133Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M94.5,135.5m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0"
+ android:strokeWidth="4"
+ android:fillColor="#00000000"
+ android:strokeColor="#4285F4"/>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_button_preview_base.xml b/res/drawable/accessibility_button_preview_base.xml
new file mode 100644
index 0000000..9e3ec59
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_base.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <group>
+ <clip-path
+ android:pathData="M0,0h125v153h-125z"/>
+ <path
+ android:pathData="M7.4,-62.9L117.6,-62.9A6.3,6.3 0,0 1,123.9 -56.6L123.9,145.6A6.3,6.3 0,0 1,117.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#DADCE0"
+ android:strokeColor="#BDC1C6"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,-62.9L116.6,-62.9A6.3,6.3 0,0 1,122.9 -56.6L122.9,145.6A6.3,6.3 0,0 1,116.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"/>
+ <path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"
+ android:fillColor="#F8F9FA"/>
+ <group>
+ <clip-path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"/>
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_button_preview_large_floating_menu.xml b/res/drawable/accessibility_button_preview_large_floating_menu.xml
new file mode 100644
index 0000000..e003dc7
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_large_floating_menu.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <path
+ android:pathData="M0,0h125v153h-125z"
+ android:fillColor="#00000000"/>
+ <group>
+ <clip-path
+ android:pathData="M89,95h29v34h-29z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M105,97.5L131,97.5A14.5,14.5 0,0 1,145.5 112L145.5,112A14.5,14.5 0,0 1,131 126.5L105,126.5A14.5,14.5 0,0 1,90.5 112L90.5,112A14.5,14.5 0,0 1,105 97.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M105.4,112m-11.2,0a11.2,11.2 0,1 1,22.4 0a11.2,11.2 0,1 1,-22.4 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M106.467,107.733C106.467,108.32 105.987,108.8 105.4,108.8C104.814,108.8 104.334,108.32 104.334,107.733C104.334,107.147 104.814,106.667 105.4,106.667C105.987,106.667 106.467,107.147 106.467,107.733ZM105.4,109.333C106.91,109.333 108.542,109.173 109.934,108.8L110.2,109.867C109.208,110.133 108.067,110.309 107,110.4V117.333H105.934V114.133H104.867V117.333H103.8V110.4C102.734,110.309 101.592,110.133 100.6,109.867L100.867,108.8C102.259,109.173 103.891,109.333 105.4,109.333Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/accessibility_button_preview_small_floating_menu.xml b/res/drawable/accessibility_button_preview_small_floating_menu.xml
new file mode 100644
index 0000000..3ff8e4b
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_small_floating_menu.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <path
+ android:pathData="M0,0h125v153h-125z"
+ android:fillColor="#00000000"/>
+ <group>
+ <clip-path
+ android:pathData="M89,106h29v22h-29z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M111,107.5L137,107.5A9.5,9.5 0,0 1,146.5 117L146.5,117A9.5,9.5 0,0 1,137 126.5L111,126.5A9.5,9.5 0,0 1,101.5 117L101.5,117A9.5,9.5 0,0 1,111 107.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M111.168,116.968m-7.168,0a7.168,7.168 0,1 1,14.336 0a7.168,7.168 0,1 1,-14.336 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M111.851,114.237C111.851,114.612 111.543,114.92 111.168,114.92C110.792,114.92 110.485,114.612 110.485,114.237C110.485,113.861 110.792,113.554 111.168,113.554C111.543,113.554 111.851,113.861 111.851,114.237ZM111.168,115.261C112.134,115.261 113.178,115.158 114.069,114.92L114.24,115.602C113.605,115.773 112.875,115.886 112.192,115.944V120.381H111.509V118.333H110.827V120.381H110.144V115.944C109.461,115.886 108.731,115.773 108.096,115.602L108.267,114.92C109.157,115.158 110.202,115.261 111.168,115.261Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/accessibility_shortcut_type_software_floating.xml b/res/drawable/accessibility_shortcut_type_software_floating.xml
new file mode 100644
index 0000000..9582015
--- /dev/null
+++ b/res/drawable/accessibility_shortcut_type_software_floating.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="180dp"
+ android:height="180dp"
+ android:viewportWidth="180"
+ android:viewportHeight="180">
+ <path
+ android:pathData="M90,90m-89,0a89,89 0,1 1,178 0a89,89 0,1 1,-178 0"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#ECEEEF"/>
+ <group>
+ <clip-path
+ android:pathData="M90,90m-87,0a87,87 0,1 1,174 0a87,87 0,1 1,-174 0"/>
+ <path
+ android:pathData="M35.4,-70.9L144.6,-70.9A6.3,6.3 0,0 1,150.9 -64.6L150.9,137.6A6.3,6.3 0,0 1,144.6 143.9L35.4,143.9A6.3,6.3 0,0 1,29.1 137.6L29.1,-64.6A6.3,6.3 0,0 1,35.4 -70.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#F2F3F4"
+ android:strokeColor="#DADCE0"/>
+ <group>
+ <clip-path
+ android:pathData="M35.4,-70.9L144.6,-70.9A6.3,6.3 0,0 1,150.9 -64.6L150.9,137.6A6.3,6.3 0,0 1,144.6 143.9L35.4,143.9A6.3,6.3 0,0 1,29.1 137.6L29.1,-64.6A6.3,6.3 0,0 1,35.4 -70.9z"/>
+ <path
+ android:pathData="M40.4,-69L140.6,-69A5.4,5.4 0,0 1,146 -63.6L146,132.6A5.4,5.4 0,0 1,140.6 138L40.4,138A5.4,5.4 0,0 1,35 132.6L35,-63.6A5.4,5.4 0,0 1,40.4 -69z"
+ android:fillColor="#ffffff"/>
+ <group>
+ <clip-path
+ android:pathData="M40.4,-69L140.6,-69A5.4,5.4 0,0 1,146 -63.6L146,132.6A5.4,5.4 0,0 1,140.6 138L40.4,138A5.4,5.4 0,0 1,35 132.6L35,-63.6A5.4,5.4 0,0 1,40.4 -69z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M132,90.5L158,90.5A14.5,14.5 0,0 1,172.5 105L172.5,105A14.5,14.5 0,0 1,158 119.5L132,119.5A14.5,14.5 0,0 1,117.5 105L117.5,105A14.5,14.5 0,0 1,132 90.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M132.4,105m-11.2,0a11.2,11.2 0,1 1,22.4 0a11.2,11.2 0,1 1,-22.4 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M133.467,100.733C133.467,101.32 132.987,101.8 132.4,101.8C131.813,101.8 131.333,101.32 131.333,100.733C131.333,100.147 131.813,99.666 132.4,99.666C132.987,99.666 133.467,100.147 133.467,100.733ZM132.4,102.333C133.909,102.333 135.541,102.173 136.933,101.8L137.2,102.867C136.208,103.133 135.067,103.309 134,103.4V110.333H132.933V107.133H131.867V110.333H130.8V103.4C129.733,103.309 128.592,103.133 127.6,102.867L127.867,101.8C129.259,102.173 130.891,102.333 132.4,102.333Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M121.719,120.653C121.719,121.29 121.198,121.81 120.562,121.81C119.927,121.81 119.406,121.29 119.406,120.653C119.406,120.017 119.927,119.497 120.562,119.497C121.198,119.497 121.719,120.017 121.719,120.653ZM120.562,122.533C122.38,122.533 124.346,122.316 126.023,121.81L126.344,123.255C125.149,123.617 123.774,123.855 122.49,123.978V133.374H121.205V129.038H119.92V133.374H118.635V123.978C117.351,123.855 115.976,123.617 114.781,123.255L115.102,121.81C116.779,122.316 118.745,122.533 120.562,122.533Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <group>
+ <clip-path
+ android:pathData="M121.719,120.653C121.719,121.29 121.198,121.81 120.562,121.81C119.927,121.81 119.406,121.29 119.406,120.653C119.406,120.017 119.927,119.497 120.562,119.497C121.198,119.497 121.719,120.017 121.719,120.653ZM120.562,122.533C122.38,122.533 124.346,122.316 126.023,121.81L126.344,123.255C125.149,123.617 123.774,123.855 122.49,123.978V133.374H121.205V129.038H119.92V133.374H118.635V123.978C117.351,123.855 115.976,123.617 114.781,123.255L115.102,121.81C116.779,122.316 118.745,122.533 120.562,122.533Z"
+ android:fillType="evenOdd"/>
+ </group>
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/res/layout/accessibility_button_preview.xml b/res/layout/accessibility_button_preview.xml
new file mode 100644
index 0000000..07cb0ff
--- /dev/null
+++ b/res/layout/accessibility_button_preview.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:importantForAccessibility="noHideDescendants">
+
+ <ImageView
+ android:id="@+id/preview_image"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/accessibility_button_preview_height"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:focusable="false"
+ android:clickable="false"
+ android:adjustViewBounds="true"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 183dd1e..2057c50 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -966,6 +966,35 @@
<item>-1</item>
</integer-array>
+ <!-- Titles for the accessibility button location. [CHAR LIMIT=35] -->
+ <string-array name="accessibility_button_location_selector_titles">
+ <item>Floating over other apps</item>
+ <item>Navigation bar</item>
+ </string-array>
+
+ <!-- Values for the accessibility button location. -->
+ <!-- Should Keep in sync with Settings.Secure.ACCESSIBILITY_BUTTON_MODE_* -->
+ <string-array name="accessibility_button_location_selector_values" translatable="false">
+ <!-- Floating over other apps -->
+ <item>1</item>
+ <!-- Navigation bar -->
+ <item>0</item>
+ </string-array>
+
+ <!-- Titles for the accessibility button size. [CHAR LIMIT=35] -->
+ <string-array name="accessibility_button_size_selector_titles">
+ <item>Small</item>
+ <item>Large</item>
+ </string-array>
+
+ <!-- Values for the accessibility button size. -->
+ <string-array name="accessibility_button_size_selector_values" translatable="false" >
+ <!-- Small -->
+ <item>0</item>
+ <!-- Large -->
+ <item>1</item>
+ </string-array>
+
<!-- Match this with the constants in VpnProfile. --> <skip />
<!-- Short names for each VPN type, not really translatable. [CHAR LIMIT=20] -->
<string-array name="vpn_types" translatable="false">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4fef726..dbf21fc 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,6 +57,8 @@
<dimen name="color_mode_preview_height">320dp</dimen>
+ <dimen name="accessibility_button_preview_height">200dp</dimen>
+
<dimen name="ring_progress_bar_thickness">4dp</dimen>
<!-- Weight of the left pane in a multi-pane preference layout. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 89665de..75b7958 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -5106,6 +5106,8 @@
<string name="accessibility_tutorial_dialog_title_gesture_settings">Use new accessibility gesture</string>
<!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the 3-button nav bar. [CHAR LIMIT=NONE] -->
<string name="accessibility_tutorial_dialog_message_button">To use this feature, tap the accessibility button <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> on the bottom of your screen.\n\nTo switch between features, touch & hold the accessibility button.</string>
+ <!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the accessibility floating button. [CHAR LIMIT=100] -->
+ <string name="accessibility_tutorial_dialog_message_floating_button">To use this feature, tap the accessibility button on your screen.</string>
<!-- Instruction for the accessibility tutorial dialog in accessibility service with volume keys. [CHAR LIMIT=100] -->
<string name="accessibility_tutorial_dialog_message_volume">To use this feature, press & hold both volume keys.</string>
<!-- Instruction for the accessibility tutorial dialog in accessibility service with triple tap. [CHAR LIMIT=100] -->
@@ -5136,6 +5138,8 @@
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture">Swipe up from the bottom of the screen with 2 fingers.\n\nTo switch between features, swipe up with 2 fingers and hold.</string>
<!-- Summary for software shortcut in gesture mode in accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture_talkback">Swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.</string>
+ <!-- Summary for software shortcut in accessibility edit shortcut dialog when user had enabled the accessibility floating button mode (Floating over other apps). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_dialog_summary_software_floating"><annotation id="link">Customize accessibility button</annotation></string>
<!-- Title for hardware shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_hardware">Hold volume keys</string>
<!-- Part of list to compose user's accessibility shortcut list. [CHAR LIMIT=NONE] -->
@@ -5164,6 +5168,26 @@
<string name="accessibility_shortcut_service_on_lock_screen_title">Shortcut from lock screen</string>
<!-- Description of accessibility shortcut. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_description">Allow feature shortcut to turn on from the lock screen. Hold both volume keys for a few seconds.</string>
+ <!-- Title for the accessibility button page. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_title">Accessibility button</string>
+ <!-- Summary text for the accessibility button preference. [CHAR LIMIT=50] -->
+ <string name="accessibility_button_summary">Quickly access accessibility features</string>
+ <!-- Description for the accessibility button page. Explain how this page works. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_button_description">Quickly access accessibility features from any screen. \n\nTo get started, go to accessibility settings and select a feature. Tap on the shortcut and select the accessibility button.</string>
+ <!-- Title for the location of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_location_title">Location</string>
+ <!-- Title for the size of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_size_title">Size</string>
+ <!-- Title for the fade of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_fade_title">Fade when not in use</string>
+ <!-- Summary for the fade of the accessibility button. [CHAR LIMIT=80] -->
+ <string name="accessibility_button_fade_summary">Fades after a few seconds so it\u2019s easier to see your screen</string>
+ <!-- Title for the transparency of the accessibility button. Will become fade when not interact with the accessibility button. [CHAR LIMIT=40] -->
+ <string name="accessibility_button_opacity_title">Transparency when not in use</string>
+ <!-- Label on the left side of transparency adjustment slider [CHAR LIMIT=30] -->
+ <string name="accessibility_button_low_label">Transparent</string>
+ <!-- Label on the right side of transparency adjustment slider [CHAR LIMIT=30] -->
+ <string name="accessibility_button_high_label">Non-transparent</string>
<!-- Title for the accessibility preference to high contrast text. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_high_text_contrast_preference_title">High contrast text</string>
<!-- Title for the accessibility preference to auto update screen magnification. [CHAR LIMIT=35] -->
diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml
new file mode 100644
index 0000000..5e81616
--- /dev/null
+++ b/res/xml/accessibility_button_settings.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/accessibility_button_title">
+
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="caption_preview"
+ android:title="@string/summary_placeholder"
+ android:layout="@layout/accessibility_button_preview"
+ android:selectable="false"
+ settings:searchable="false"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.AccessibilityButtonPreviewPreferenceController"/>
+
+ <ListPreference
+ android:entries="@array/accessibility_button_location_selector_titles"
+ android:entryValues="@array/accessibility_button_location_selector_values"
+ android:key="accessibility_button_location"
+ android:title="@string/accessibility_button_location_title"
+ android:summary="%s"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.AccessibilityButtonLocationPreferenceController"/>
+
+ <ListPreference
+ android:entries="@array/accessibility_button_size_selector_titles"
+ android:entryValues="@array/accessibility_button_size_selector_values"
+ android:key="accessibility_button_size"
+ android:title="@string/accessibility_button_size_title"
+ android:summary="%s"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuSizePreferenceController"/>
+
+ <SwitchPreference
+ android:key="accessibility_button_fade"
+ android:title="@string/accessibility_button_fade_title"
+ android:summary="@string/accessibility_button_fade_summary"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuFadePreferenceController"/>
+
+ <com.android.settings.widget.SeekBarPreference
+ android:key="accessibility_button_opacity"
+ android:title="@string/accessibility_button_opacity_title"
+ android:selectable="true"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuOpacityPreferenceController"/>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="accessibility_button_footer"
+ android:title="@string/accessibility_button_description"
+ android:selectable="false"
+ settings:searchable="false"
+ android:persistent="false"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/accessibility_shortcuts_settings.xml b/res/xml/accessibility_shortcuts_settings.xml
index 35314e7..465f96d 100644
--- a/res/xml/accessibility_shortcuts_settings.xml
+++ b/res/xml/accessibility_shortcuts_settings.xml
@@ -21,6 +21,13 @@
android:persistent="false"
android:title="@string/accessibility_shortcuts_settings_title">
+ <Preference
+ android:fragment="com.android.settings.accessibility.AccessibilityButtonFragment"
+ android:key="accessibility_button_preference"
+ android:persistent="false"
+ android:title="@string/accessibility_button_title"
+ android:summary="@string/accessibility_button_summary"/>
+
<SwitchPreference
android:key="accessibility_shortcut_preference"
android:persistent="false"
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonFragment.java b/src/com/android/settings/accessibility/AccessibilityButtonFragment.java
new file mode 100644
index 0000000..c3e683a
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonFragment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Settings fragment containing accessibility button properties. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class AccessibilityButtonFragment extends DashboardFragment {
+
+ private static final String TAG = "AccessibilityButtonFragment";
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.accessibility_button_settings;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.ACCESSIBILITY_BUTTON_SETTINGS;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.accessibility_button_settings);
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
new file mode 100644
index 0000000..e9ed19a
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import com.google.common.primitives.Ints;
+
+/** Preference controller that controls the preferred location in accessibility button page. */
+public class AccessibilityButtonLocationPreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
+ private int mDefaultLocation;
+
+ public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isGestureNavigateEnabled(mContext)
+ ? DISABLED_DEPENDENT_SETTING : AVAILABLE;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(getCurrentAccessibilityButtonMode());
+ }
+
+ private String getCurrentAccessibilityButtonMode() {
+ final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
+ return String.valueOf(mode);
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultLocation = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
new file mode 100644
index 0000000..69a7a46
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.LayoutPreference;
+
+/** Preference controller that controls the preview effect in accessibility button page. */
+public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ private static final int SMALL_SIZE = 0;
+ private static final float DEFAULT_OPACITY = 0.55f;
+ private static final int DEFAULT_SIZE = 0;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+ private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
+
+ @VisibleForTesting
+ ImageView mPreview;
+
+ public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updatePreviewPreference();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final LayoutPreference preference = screen.findPreference(getPreferenceKey());
+ mPreview = preference.findViewById(R.id.preview_image);
+
+ updatePreviewPreference();
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updatePreviewPreference() {
+ if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) {
+ final int size = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE);
+ final int opacity = (int) (Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100);
+ final int floatingMenuIconId = (size == SMALL_SIZE)
+ ? R.drawable.accessibility_button_preview_small_floating_menu
+ : R.drawable.accessibility_button_preview_large_floating_menu;
+
+ mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity));
+ // Only change opacity(alpha) would not invoke redraw view, need to invalidate manually.
+ mPreview.invalidate();
+ } else {
+ mPreview.setImageDrawable(
+ mContext.getDrawable(R.drawable.accessibility_button_navigation));
+ }
+ }
+
+ private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
+ if (mFloatingMenuPreviewDrawable == null) {
+ mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
+ mContext, resId, opacity);
+ } else {
+ mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
+ }
+
+ return mFloatingMenuPreviewDrawable;
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index f349a12..6b31988 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import android.app.Dialog;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
@@ -24,6 +25,7 @@
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,6 +42,8 @@
import androidx.core.content.ContextCompat;
import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.utils.AnnotationSpan;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -253,6 +257,8 @@
summary.setVisibility(View.GONE);
} else {
summary.setText(summaryText);
+ summary.setMovementMethod(LinkMovementMethod.getInstance());
+ summary.setFocusable(false);
}
final ImageView image = view.findViewById(R.id.image);
image.setImageResource(imageResId);
@@ -260,10 +266,13 @@
private static void initSoftwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
+ final CharSequence title = context.getText(
+ R.string.accessibility_shortcut_edit_dialog_title_software);
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
- setupShortcutWidget(dialogView, retrieveTitle(context),
- retrieveSummary(context, lineHeight), retrieveImageResId(context));
+
+ setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
+ retrieveImageResId(context));
}
private static void initHardwareShortcut(Context context, View view) {
@@ -297,35 +306,28 @@
});
}
- private static CharSequence retrieveTitle(Context context) {
- int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- return context.getText(resId);
- }
-
private static CharSequence retrieveSummary(Context context, int lineHeight) {
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
- return context.getText(resId);
- }
- return getSummaryStringWithIcon(context, lineHeight);
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight);
}
private static int retrieveImageResId(Context context) {
- // TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
- return resId;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
+ }
+
+ private static CharSequence getSummaryStringWithLink(Context context) {
+ final View.OnClickListener linkListener = v -> new SubSettingLauncher(context)
+ .setDestination(AccessibilityButtonFragment.class.getName())
+ .setSourceMetricsCategory(
+ SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS)
+ .launch();
+ final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
+ AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
+
+ return AnnotationSpan.linkify(context.getText(
+ R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo);
}
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 482822e..5ea5462 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -333,7 +333,8 @@
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
- final CharSequence title = getSoftwareTitle(context);
+ final CharSequence title = context.getText(
+ R.string.accessibility_tutorial_dialog_title_button);
final ImageView image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
@@ -390,44 +391,19 @@
return tutorialPages;
}
- private static CharSequence getSoftwareTitle(Context context) {
- final boolean isGestureNavigationEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final int resId = isGestureNavigationEnabled
- ? R.string.accessibility_tutorial_dialog_title_gesture
- : R.string.accessibility_tutorial_dialog_title_button;
-
- return context.getText(resId);
- }
-
private static ImageView createSoftwareImage(Context context) {
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
+ final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
return createImageView(context, resId);
}
private static CharSequence getSoftwareInstruction(Context context) {
- final boolean isGestureNavigateEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
- int resId = R.string.accessibility_tutorial_dialog_message_button;
- if (isGestureNavigateEnabled) {
- resId = isTouchExploreEnabled
- ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
- : R.string.accessibility_tutorial_dialog_message_gesture;
- }
-
- CharSequence text = context.getText(resId);
- if (resId == R.string.accessibility_tutorial_dialog_message_button) {
- text = getSoftwareInstructionWithIcon(context, text);
- }
-
- return text;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button)
+ : getSoftwareInstructionWithIcon(context,
+ context.getText(R.string.accessibility_tutorial_dialog_message_button));
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index f547209..5c316a4 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -143,6 +144,13 @@
== NAV_BAR_MODE_GESTURAL;
}
+ /** Determines if a accessibility floating menu is being used. */
+ public static boolean isFloatingMenuEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+ }
+
/** Determines if a touch explore is being used. */
public static boolean isTouchExploreEnabled(Context context) {
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
diff --git a/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
new file mode 100644
index 0000000..dd419d0
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the fade switch button in accessibility button page. */
+public class FloatingMenuFadePreferenceController extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SwitchPreference mPreference;
+
+ public FloatingMenuFadePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isEnabled = (boolean) newValue;
+ putFloatingMenuFadeValue(isEnabled);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final SwitchPreference switchPreference = (SwitchPreference) preference;
+
+ switchPreference.setChecked(getFloatingMenuFadeValue() == ON);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private int getFloatingMenuFadeValue() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON);
+ }
+
+ private void putFloatingMenuFadeValue(boolean isEnabled) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ isEnabled ? ON : OFF);
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
new file mode 100644
index 0000000..bfce114
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+
+import java.util.Objects;
+
+/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */
+public class FloatingMenuLayerDrawable extends LayerDrawable {
+
+ private FloatingMenuLayerDrawableState mState;
+
+ /**
+ * Creates a new layer drawable with the list of specified layers.
+ *
+ * @param layers a list of drawables to use as layers in this new drawable,
+ * must be non-null
+ */
+ private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
+ super(layers);
+ }
+
+ /**
+ * Create the {@link LayerDrawable} that contains device icon as background and floating menu
+ * icon with given {@code opacity} value as foreground.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ * @return the drawable that combines the device icon and the floating menu icon
+ */
+ public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
+ int opacity) {
+ final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
+ final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
+ new Drawable[]{bg, null});
+
+ basicDrawable.updateLayerDrawable(context, resId, opacity);
+ return basicDrawable;
+ }
+
+ /**
+ * Update the drawable with given {@code resId} drawable and {@code opacity}(alpha)
+ * value at index 1 layer.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ */
+ public void updateLayerDrawable(Context context, int resId, int opacity) {
+ final Drawable icon = context.getDrawable(resId);
+ icon.setAlpha(opacity);
+ this.setDrawable(/* index= */ 1, icon);
+ this.setConstantState(context, resId, opacity);
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ /** Stores the constant state and data to the given drawable. */
+ private void setConstantState(Context context, int resId, int opacity) {
+ mState = new FloatingMenuLayerDrawableState(context, resId, opacity);
+ }
+
+ /** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
+ @VisibleForTesting
+ static class FloatingMenuLayerDrawableState extends ConstantState {
+
+ private final Context mContext;
+ private final int mResId;
+ private final int mOpacity;
+
+ FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
+ mContext = context;
+ mResId = resId;
+ mOpacity = opacity;
+ }
+
+ @NonNull
+ @Override
+ public Drawable newDrawable() {
+ return createLayerDrawable(mContext, mResId, mOpacity);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
+ return mResId == that.mResId
+ && mOpacity == that.mOpacity
+ && Objects.equals(mContext, that.mContext);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mContext, mResId, mOpacity);
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
new file mode 100644
index 0000000..fea6fb6
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.SliderPreferenceController;
+import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the opacity seekbar in accessibility button page. */
+public class FloatingMenuOpacityPreferenceController extends SliderPreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ @VisibleForTesting
+ static final float DEFAULT_OPACITY = 0.55f;
+ private static final int FADE_ENABLED = 1;
+ private static final float MIN_PROGRESS = 10f;
+ private static final float MAX_PROGRESS = 100f;
+ @VisibleForTesting
+ static final float PRECISION = 100f;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SeekBarPreference mPreference;
+
+ public FloatingMenuOpacityPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setContinuousUpdates(true);
+ mPreference.setMax(getMax());
+ mPreference.setMin(getMin());
+ mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
+
+ updateState(mPreference);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public int getSliderPosition() {
+ return convertOpacityFloatToInt(getOpacity());
+ }
+
+ @Override
+ public boolean setSliderPosition(int position) {
+ final float value = convertOpacityIntToFloat(position);
+
+ return Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, value);
+ }
+
+ @Override
+ public int getMax() {
+ return (int) MAX_PROGRESS;
+ }
+
+ @Override
+ public int getMin() {
+ return (int) MIN_PROGRESS;
+ }
+
+ private void updateAvailabilityStatus() {
+ final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED)
+ == FADE_ENABLED;
+
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled);
+ }
+
+ private int convertOpacityFloatToInt(float value) {
+ return Math.round(value * PRECISION);
+ }
+
+ private float convertOpacityIntToFloat(int value) {
+ return (float) value / PRECISION;
+ }
+
+ private float getOpacity() {
+ float value = Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY);
+ final float minValue = MIN_PROGRESS / PRECISION;
+ final float maxValue = MAX_PROGRESS / PRECISION;
+
+ return (value < minValue || value > maxValue) ? DEFAULT_OPACITY : value;
+ }
+}
+
diff --git a/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
new file mode 100644
index 0000000..2f0f833
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import com.google.common.primitives.Ints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Preference controller that controls the preferred size in accessibility button page. */
+public class FloatingMenuSizePreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ ListPreference mPreference;
+
+ private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
+ private int mDefaultSize;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ Size.SMALL,
+ Size.LARGE,
+ })
+ @VisibleForTesting
+ @interface Size {
+ int SMALL = 0;
+ int LARGE = 1;
+ }
+
+ public FloatingMenuSizePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ putAccessibilityFloatingMenuSize(value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize)));
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultSize = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+
+ @Size
+ private int getAccessibilityFloatingMenuSize(@Size int defaultValue) {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue);
+ }
+
+ private void putAccessibilityFloatingMenuSize(@Size int value) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value);
+ }
+}
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index ec22a28..cf9c08b 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -608,19 +608,15 @@
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List<CharSequence> list = new ArrayList<>();
- if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List<CharSequence> list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index dde5be1..738d284 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -226,27 +226,23 @@
return context.getText(R.string.switch_off_text);
}
- final int shortcutType = PreferredShortcuts.retrieveUserShortcutType(context,
+ final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List<CharSequence> list = new ArrayList<>();
- if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List<CharSequence> list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
- if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
list.add(tripleTapTitle);
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index 14fe6a6..388d87a 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -30,15 +30,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.SettingsTutorialDialogWrapperActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -188,12 +185,7 @@
protected boolean setDefaultKey(String key) {
setCurrentSystemNavigationMode(mOverlayManager, key);
setIllustrationVideo(mVideoPreference, key);
- if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
- isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
- Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
+
return true;
}
@@ -267,18 +259,6 @@
}
}
- private boolean isAnyServiceSupportAccessibilityButton() {
- final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
- final List<String> targets = ams.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- return !targets.isEmpty();
- }
-
- private boolean isNavBarMagnificationEnabled() {
- return Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
- }
-
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) {
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
new file mode 100644
index 0000000..473b566
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link AccessibilityButtonFragment}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonFragmentTest {
+
+ private Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void getNonIndexableKeys_existInXmlLayout() {
+ final List<String> niks = AccessibilityButtonFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List<String> keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.accessibility_button_settings);
+
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
new file mode 100644
index 0000000..a67038a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonLocationPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonLocationPreferenceControllerTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy
+ private final Resources mResources = mContext.getResources();
+ private final ContentResolver mContentResolver = mContext.getContentResolver();
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private AccessibilityButtonLocationPreferenceController mController;
+
+
+ @Before
+ public void setUp() {
+ mController = new AccessibilityButtonLocationPreferenceController(mContext,
+ "test_key");
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureEnabled_returnDisabledDependentSetting() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureDisabled_returnAvailable() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void updateState_a11yBtnModeNavigationBar_navigationBarValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.updateState(mListPreference);
+
+ final String navigationBarValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ assertThat(mListPreference.getValue()).isEqualTo(navigationBarValue);
+ }
+
+ @Test
+ public void onPreferenceChange_a11yBtnModeFloatingMenu_floatingMenuValue() {
+ final String floatingMenuValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ mController.onPreferenceChange(mListPreference, floatingMenuValue);
+
+ assertThat(mListPreference.getValue()).isEqualTo(floatingMenuValue);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
new file mode 100644
index 0000000..eb88175
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonPreviewPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonPreviewPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private AccessibilityButtonPreviewPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new AccessibilityButtonPreviewPreferenceController(mContext, "test_key");
+ mController.mPreview = new ImageView(mContext);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeNavigationBar_getNavigationBarDrawable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable navigationBarDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_navigation);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ navigationBarDrawable.getConstantState());
+ }
+
+ @Test
+ public void onChange_updatePreviewPreferenceWithConfig_expectedPreviewDrawable() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, /* small size */ 0);
+ Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.1f);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable smallFloatingMenuWithTenOpacityDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext,
+ R.drawable.accessibility_button_preview_small_floating_menu, 10);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ smallFloatingMenuWithTenOpacityDrawable.getConstantState());
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
new file mode 100644
index 0000000..5cf87ee
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuFadePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuFadePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final SwitchPreference mSwitchPreference = new SwitchPreference(mContext);
+ private FloatingMenuFadePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuFadePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_keyFloatingMenuFadeDisabled_fadeIsDisabled() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChange_floatingMenuFadeEnabled_keyFloatingMenuFadeIsOn() {
+ mController.onPreferenceChange(mSwitchPreference, Boolean.TRUE);
+
+ final int actualValue = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+ assertThat(actualValue).isEqualTo(ON);
+ }
+
+ @Test
+ public void onChange_floatingMenuFadeChangeToDisabled_preferenceDisabled() {
+ mController.mPreference = mSwitchPreference;
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
new file mode 100644
index 0000000..ec449d2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuLayerDrawable}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuLayerDrawableTest {
+
+ private static final int TEST_RES_ID =
+ com.android.internal.R.drawable.ic_accessibility_magnification;
+ private static final int TEST_RES_ID_2 =
+ com.android.internal.R.drawable.ic_accessibility_color_inversion;
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void createLayerDrawable_configCorrect() {
+ final Drawable expected1stDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_preview_base);
+ final Drawable expected2ndDrawable = mContext.getDrawable(TEST_RES_ID);
+
+ final FloatingMenuLayerDrawable actualDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID,
+ /* opacity= */ 27);
+
+ final Drawable actual1stDrawable = actualDrawable.getDrawable(0);
+ final Drawable actual2ndDrawable = actualDrawable.getDrawable(1);
+ // These are VectorDrawables, so it can use getConstantState() to compare.
+ assertThat(actual1stDrawable.getConstantState()).isEqualTo(
+ expected1stDrawable.getConstantState());
+ assertThat(actual2ndDrawable.getConstantState()).isEqualTo(
+ expected2ndDrawable.getConstantState());
+ }
+
+ @Test
+ public void updateLayerDrawable_expectedFloatingMenuLayerDrawableState() {
+ final FloatingMenuLayerDrawable originalDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */
+ 72);
+
+ originalDrawable.updateLayerDrawable(mContext, TEST_RES_ID_2, /* opacity= */ 27);
+
+ assertThat(originalDrawable.getConstantState()).isEqualTo(
+ new FloatingMenuLayerDrawable.FloatingMenuLayerDrawableState(mContext,
+ TEST_RES_ID_2, /* opacity= */ 27));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
new file mode 100644
index 0000000..1638f90
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.DEFAULT_OPACITY;
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.PRECISION;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.widget.SeekBarPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuOpacityPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuOpacityPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private FloatingMenuOpacityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuOpacityPreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = new SeekBarPreference(mContext);
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void getSliderPosition_putNormalOpacityValue_expectedValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.35f);
+
+ assertThat(mController.getSliderPosition()).isEqualTo(35);
+ }
+
+ @Test
+ public void getSliderPosition_putOutOfBoundOpacityValue_defaultValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.01f);
+
+ final int defaultValue = Math.round(DEFAULT_OPACITY * PRECISION);
+ assertThat(mController.getSliderPosition()).isEqualTo(defaultValue);
+ }
+
+ @Test
+ public void setSliderPosition_expectedValue() {
+ mController.setSliderPosition(27);
+
+ final float value = Settings.Secure.getFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, -1);
+ assertThat(value).isEqualTo(0.27f);
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
new file mode 100644
index 0000000..4d7d98d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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.settings.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuSizePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuSizePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private FloatingMenuSizePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuSizePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_floatingMenuLargeSizeAndFullCircle_largeSizeValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+ FloatingMenuSizePreferenceController.Size.LARGE);
+
+ mController.updateState(mListPreference);
+
+ final String largeSize = String.valueOf(FloatingMenuSizePreferenceController.Size.LARGE);
+ assertThat(mListPreference.getValue()).isEqualTo(largeSize);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = mListPreference;
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}