[A11y Setting] Add keyboard keys preferences under A11y page
Bug: 325851068
Test: make RunSettingsRoboTests
Change-Id: Ic7eb1ec9e260a95d040bee784cbff479a4f087d0
diff --git a/res/drawable/ic_bounce_keys.xml b/res/drawable/ic_bounce_keys.xml
new file mode 100644
index 0000000..424a5fc
--- /dev/null
+++ b/res/drawable/ic_bounce_keys.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2024 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="@dimen/accessibility_icon_size"
+ android:height="@dimen/accessibility_icon_size"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+ <path
+ android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
+ android:fillColor="#5F6368"/>
+ <path
+ android:pathData="M15.865,9.507C17.448,9.47 19.038,10.047 20.245,11.255C22.585,13.602 22.585,17.397 20.237,19.753L19.18,18.695C20.935,16.94 20.935,14.083 19.18,12.328C18.28,11.42 17.08,11 15.895,11.03L16.683,11.818L15.625,12.875L13,10.25L15.625,7.625L16.69,8.682L15.865,9.507ZM15.318,19.183L16.375,18.125L19,20.75L16.368,23.368L15.31,22.31L16.135,21.485C14.552,21.522 12.962,20.945 11.755,19.737C9.415,17.397 9.415,13.595 11.755,11.255L12.82,12.305C11.065,14.06 11.065,16.917 12.82,18.673C13.72,19.58 14.92,20 16.105,19.97L15.318,19.183Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_slow_keys.xml b/res/drawable/ic_slow_keys.xml
new file mode 100644
index 0000000..b28d0ae
--- /dev/null
+++ b/res/drawable/ic_slow_keys.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2024 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="@dimen/accessibility_icon_size"
+ android:height="@dimen/accessibility_icon_size"
+ android:viewportWidth="32"
+ android:viewportHeight="32">
+ <path
+ android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
+ android:fillColor="#5F6368"/>
+ <path
+ android:pathData="M15.535,9.53C15.685,9.515 15.842,9.5 16,9.5C19.728,9.5 22.75,12.523 22.75,16.25C22.75,19.978 19.728,23 16,23C12.273,23 9.25,19.978 9.25,16.25H10.75C10.75,19.145 13.105,21.5 16,21.5C18.895,21.5 21.25,19.145 21.25,16.25C21.25,13.355 18.895,11 16,11C15.894,11 15.797,11.012 15.698,11.025C15.664,11.029 15.63,11.034 15.595,11.038L16.907,12.35L15.85,13.408L12.693,10.25L15.85,7.1L16.907,8.158L15.535,9.53ZM16.675,17.203C16.675,17.398 16.608,17.563 16.48,17.69C16.353,17.818 16.188,17.878 15.993,17.878C15.797,17.878 15.64,17.818 15.512,17.698C15.385,17.585 15.295,17.42 15.25,17.218L14.545,17.495C14.597,17.743 14.703,17.945 14.852,18.11C15.002,18.283 15.175,18.403 15.377,18.485C15.573,18.568 15.783,18.605 16,18.605C16.278,18.605 16.532,18.545 16.75,18.425C16.975,18.305 17.14,18.14 17.267,17.93C17.395,17.72 17.455,17.473 17.455,17.203C17.455,16.933 17.395,16.693 17.275,16.483C17.155,16.273 16.99,16.108 16.795,15.995C16.6,15.883 16.382,15.823 16.157,15.823C15.88,15.823 15.648,15.905 15.445,16.07L15.4,16.055L15.55,15.095H17.215V14.405H14.898L14.665,16.46L15.43,16.805C15.498,16.723 15.58,16.655 15.677,16.603C15.775,16.55 15.887,16.528 16.007,16.528C16.202,16.528 16.36,16.595 16.487,16.723C16.615,16.85 16.675,17.008 16.675,17.203Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_sticky_keys.xml b/res/drawable/ic_sticky_keys.xml
new file mode 100644
index 0000000..c07da15
--- /dev/null
+++ b/res/drawable/ic_sticky_keys.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2024 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="@dimen/accessibility_icon_size"
+ android:height="@dimen/accessibility_icon_size"
+ android:viewportHeight="32"
+ android:viewportWidth="32">
+ <path
+ android:fillColor="#5F6368"
+ android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" />
+ <path
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"
+ android:pathData="M22,10.25H10C9.175,10.25 8.507,10.925 8.507,11.75L8.5,19.25C8.5,20.075 9.175,20.75 10,20.75H22C22.825,20.75 23.5,20.075 23.5,19.25V11.75C23.5,10.925 22.825,10.25 22,10.25ZM22,11.75V19.25H10V11.75H22ZM16.75,12.5H15.25V14H16.75V12.5ZM15.25,14.75H16.75V16.25H15.25V14.75ZM14.5,12.5H13V14H14.5V12.5ZM13,14.75H14.5V16.25H13V14.75ZM12.25,14.75H10.75V16.25H12.25V14.75ZM10.75,12.5H12.25V14H10.75V12.5ZM19,17H13V18.5H19V17ZM17.5,14.75H19V16.25H17.5V14.75ZM19,12.5H17.5V14H19V12.5ZM19.75,14.75H21.25V16.25H19.75V14.75ZM21.25,12.5H19.75V14H21.25V12.5Z" />
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 76c7106..a4761d3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4328,16 +4328,16 @@
<string name="show_ime_summary">Keep it on screen while physical keyboard is active</string>
<!-- Title for the 'Bounce keys' preference switch. [CHAR LIMIT=35] -->
<string name="bounce_keys">Bounce keys</string>
- <!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=100] -->
- <string name="bounce_keys_summary">Enable Bounce keys for physical keyboard accessibility</string>
+ <!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=300] -->
+ <string name="bounce_keys_summary">The keyboard ignores quickly repeated presses of the same key within <xliff:g id="bounce_keys_threshold" example="500">%1$d</xliff:g> ms</string>
<!-- Title for the 'Slow keys' preference switch. [CHAR LIMIT=35] -->
<string name="slow_keys">Slow keys</string>
- <!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=100] -->
- <string name="slow_keys_summary">Enable Slow keys for physical keyboard accessibility</string>
+ <!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=300] -->
+ <string name="slow_keys_summary">Adjusts the time it takes for a key press to activate to <xliff:g id="slow_keys_threshold" example="500">%1$d</xliff:g> ms</string>
<!-- Title for the 'Sticky keys' preference switch. [CHAR LIMIT=35] -->
<string name="sticky_keys">Sticky keys</string>
- <!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=100] -->
- <string name="sticky_keys_summary">Enable Sticky keys for physical keyboard accessibility</string>
+ <!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=300] -->
+ <string name="sticky_keys_summary">Press one key at a time for shortcuts instead of holding keys down together</string>
<!-- Title for the button to trigger the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=35] -->
<string name="keyboard_shortcuts_helper">Keyboard shortcuts</string>
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
@@ -4601,6 +4601,8 @@
<string name="vision_settings_suggestion_title">Change font size</string>
<!-- Title for the accessibility preference category of screen reader services and settings. [CHAR LIMIT=50] -->
<string name="screen_reader_category_title">Screen reader</string>
+ <!-- Title for the accessibility preference category of physical keyboard options. [CHAR LIMIT=50] -->
+ <string name="keyboard_category_title">Physical keyboard options</string>
<!-- Title for the accessibility preference category of caption services and settings. [CHAR LIMIT=50] -->
<string name="captions_category_title">Captions</string>
<!-- Title for the accessibility preference category of audio services and settings. [CHAR LIMIT=50] -->
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index 0f4065b..ad8bfc3 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -106,6 +106,37 @@
</PreferenceCategory>
<PreferenceCategory
+ android:key="physical_keyboard_options_category"
+ android:persistent="false"
+ android:title="@string/keyboard_category_title">
+
+ <SwitchPreferenceCompat
+ android:icon="@drawable/ic_sticky_keys"
+ android:key="toggle_keyboard_sticky_keys"
+ android:persistent="false"
+ android:summary="@string/sticky_keys_summary"
+ android:title="@string/sticky_keys"
+ settings:controller="com.android.settings.accessibility.KeyboardStickyKeyPreferenceController"
+ settings:searchable="true" />
+
+ <SwitchPreferenceCompat
+ android:icon="@drawable/ic_bounce_keys"
+ android:key="toggle_keyboard_bounce_keys"
+ android:persistent="false"
+ android:title="@string/bounce_keys"
+ settings:controller="com.android.settings.accessibility.KeyboardBounceKeyPreferenceController"
+ settings:searchable="true" />
+
+ <SwitchPreferenceCompat
+ android:icon="@drawable/ic_slow_keys"
+ android:key="toggle_keyboard_slow_keys"
+ android:persistent="false"
+ android:title="@string/slow_keys"
+ settings:controller="com.android.settings.accessibility.KeyboardSlowKeyPreferenceController"
+ settings:searchable="true" />
+ </PreferenceCategory>
+
+ <PreferenceCategory
android:key="captions_category"
android:persistent="false"
android:title="@string/captions_category_title">
diff --git a/res/xml/physical_keyboard_settings.xml b/res/xml/physical_keyboard_settings.xml
index b95f23e..5b545bb 100644
--- a/res/xml/physical_keyboard_settings.xml
+++ b/res/xml/physical_keyboard_settings.xml
@@ -43,22 +43,20 @@
android:key="keyboard_a11y_category"
android:title="@string/keyboard_a11y_category">
- <SwitchPreference
+ <SwitchPreferenceCompat
android:key="accessibility_sticky_keys"
android:title="@string/sticky_keys"
android:summary="@string/sticky_keys_summary"
android:defaultValue="false" />
- <SwitchPreference
- android:key="accessibility_slow_keys"
- android:title="@string/slow_keys"
- android:summary="@string/slow_keys_summary"
- android:defaultValue="false" />
-
- <SwitchPreference
+ <SwitchPreferenceCompat
android:key="accessibility_bounce_keys"
android:title="@string/bounce_keys"
- android:summary="@string/bounce_keys_summary"
+ android:defaultValue="false" />
+
+ <SwitchPreferenceCompat
+ android:key="accessibility_slow_keys"
+ android:title="@string/slow_keys"
android:defaultValue="false" />
</PreferenceCategory>
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 16414f8..fe89bf2 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -23,12 +23,14 @@
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ServiceInfo;
+import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -42,6 +44,7 @@
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedPreference;
@@ -56,7 +59,8 @@
/** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class AccessibilitySettings extends DashboardFragment {
+public class AccessibilitySettings extends DashboardFragment implements
+ InputManager.InputDeviceListener {
private static final String TAG = "AccessibilitySettings";
@@ -67,12 +71,14 @@
private static final String CATEGORY_SPEECH = "speech_category";
private static final String CATEGORY_DISPLAY = "display_category";
private static final String CATEGORY_DOWNLOADED_SERVICES = "user_installed_services_category";
+ private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category";
@VisibleForTesting
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
private static final String[] CATEGORIES = new String[]{
CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY,
- CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL, CATEGORY_DOWNLOADED_SERVICES
+ CATEGORY_SPEECH, CATEGORY_INTERACTION_CONTROL,
+ CATEGORY_KEYBOARD_OPTIONS, CATEGORY_DOWNLOADED_SERVICES
};
// Extras passed to sub-fragments.
@@ -169,6 +175,9 @@
// Observe changes from accessibility selection menu
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_STICKY_KEYS);
+ shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SLOW_KEYS);
+ shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS);
mSettingsContentObserver = new AccessibilitySettingsContentObserver(mHandler);
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
key -> onContentChanged());
@@ -197,6 +206,7 @@
initializeAllPreferences();
updateAllPreferences();
registerContentMonitors();
+ registerInputDeviceListener();
}
@Override
@@ -224,6 +234,7 @@
@Override
public void onDestroy() {
unregisterContentMonitors();
+ unRegisterInputDeviceListener();
super.onDestroy();
}
@@ -313,9 +324,9 @@
@VisibleForTesting
void updateAllPreferences() {
- updateSystemPreferences();
updateServicePreferences();
updatePreferencesState();
+ updateSystemPreferences();
}
private void registerContentMonitors() {
@@ -326,6 +337,22 @@
mSettingsContentObserver.register(getContentResolver());
}
+ private void registerInputDeviceListener() {
+ InputManager mIm = getSystemService(InputManager.class);
+ if (mIm == null) {
+ return;
+ }
+ mIm.registerInputDeviceListener(this, null);
+ }
+
+ private void unRegisterInputDeviceListener() {
+ InputManager mIm = getSystemService(InputManager.class);
+ if (mIm == null) {
+ return;
+ }
+ mIm.unregisterInputDeviceListener(this);
+ }
+
private void unregisterContentMonitors() {
mSettingsPackageMonitor.unregister();
mSettingsContentObserver.unregister(getContentResolver());
@@ -405,6 +432,7 @@
// Hide category if it is empty.
updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER);
updatePreferenceCategoryVisibility(CATEGORY_SPEECH);
+ updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS);
}
private List<RestrictedPreference> getInstalledAccessibilityList(Context context) {
@@ -499,7 +527,7 @@
* Updates preferences related to system configurations.
*/
protected void updateSystemPreferences() {
- // Do nothing.
+ updateKeyboardPreferencesVisibility();
}
private void updatePreferencesState() {
@@ -509,6 +537,53 @@
findPreference(controller.getPreferenceKey())));
}
+ private void updateKeyboardPreferencesVisibility() {
+ if (!mCategoryToPrefCategoryMap.containsKey(CATEGORY_KEYBOARD_OPTIONS)) {
+ return;
+ }
+ boolean isVisible = isAnyHardKeyboardsExist()
+ && isAnyKeyboardPreferenceAvailable();
+ mCategoryToPrefCategoryMap.get(CATEGORY_KEYBOARD_OPTIONS).setVisible(
+ isVisible);
+ if (isVisible) {
+ //set summary here.
+ findPreference(KeyboardBounceKeyPreferenceController.PREF_KEY).setSummary(
+ getContext().getString(R.string.bounce_keys_summary,
+ PhysicalKeyboardFragment.BOUNCE_KEYS_THRESHOLD));
+ findPreference(KeyboardSlowKeyPreferenceController.PREF_KEY).setSummary(
+ getContext().getString(R.string.slow_keys_summary,
+ PhysicalKeyboardFragment.SLOW_KEYS_THRESHOLD));
+ }
+ }
+
+ private boolean isAnyHardKeyboardsExist() {
+ for (int deviceId : InputDevice.getDeviceIds()) {
+ final InputDevice device = InputDevice.getDevice(deviceId);
+ if (device != null && !device.isVirtual() && device.isFullKeyboard()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isAnyKeyboardPreferenceAvailable() {
+ for (List<AbstractPreferenceController> controllerList : getPreferenceControllers()) {
+ for (AbstractPreferenceController controller : controllerList) {
+ if (controller.getPreferenceKey().equals(
+ KeyboardBounceKeyPreferenceController.PREF_KEY)
+ || controller.getPreferenceKey().equals(
+ KeyboardSlowKeyPreferenceController.PREF_KEY)
+ || controller.getPreferenceKey().equals(
+ KeyboardStickyKeyPreferenceController.PREF_KEY)) {
+ if (controller.isAvailable()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_settings) {
@Override
@@ -519,4 +594,15 @@
context);
}
};
+
+ @Override
+ public void onInputDeviceAdded(int deviceId) {}
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {}
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ mHandler.postDelayed(mUpdateRunnable, DELAY_UPDATE_SERVICES_MILLIS);
+ }
}
diff --git a/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java
new file mode 100644
index 0000000..6d988ac
--- /dev/null
+++ b/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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.hardware.input.InputSettings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
+
+/**
+ * A toggle preference controller for keyboard bounce key.
+ */
+public class KeyboardBounceKeyPreferenceController extends TogglePreferenceController {
+
+ static final String PREF_KEY = "toggle_keyboard_bounce_keys";
+
+ public KeyboardBounceKeyPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return InputSettings.isAccessibilityBounceKeysFeatureEnabled()
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return InputSettings.isAccessibilityBounceKeysEnabled(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ InputSettings.setAccessibilityBounceKeysThreshold(mContext,
+ isChecked ? PhysicalKeyboardFragment.BOUNCE_KEYS_THRESHOLD
+ : 0);
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_accessibility;
+ }
+}
diff --git a/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java
new file mode 100644
index 0000000..8bd2316
--- /dev/null
+++ b/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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.hardware.input.InputSettings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.inputmethod.PhysicalKeyboardFragment;
+
+/**
+ * A toggle preference controller for keyboard slow key.
+ */
+public class KeyboardSlowKeyPreferenceController extends TogglePreferenceController {
+
+ static final String PREF_KEY = "toggle_keyboard_slow_keys";
+
+ public KeyboardSlowKeyPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return InputSettings.isAccessibilitySlowKeysFeatureFlagEnabled()
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return InputSettings.isAccessibilitySlowKeysEnabled(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ InputSettings.setAccessibilitySlowKeysThreshold(mContext,
+ isChecked ? PhysicalKeyboardFragment.SLOW_KEYS_THRESHOLD
+ : 0);
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_accessibility;
+ }
+}
diff --git a/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java b/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java
new file mode 100644
index 0000000..ee5559d
--- /dev/null
+++ b/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.hardware.input.InputSettings;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * A toggle preference controller for keyboard sticky key.
+ */
+public class KeyboardStickyKeyPreferenceController extends TogglePreferenceController {
+
+ static final String PREF_KEY = "toggle_keyboard_sticky_keys";
+
+ public KeyboardStickyKeyPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return InputSettings.isAccessibilityStickyKeysFeatureEnabled()
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return InputSettings.isAccessibilityStickyKeysEnabled(mContext);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ InputSettings.setAccessibilityStickyKeysEnabled(mContext, isChecked);
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_accessibility;
+ }
+}
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index b06edb2..f2ac550 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -61,6 +61,8 @@
import java.util.List;
import java.util.Objects;
+// TODO(b/327638540): Update implementation of preference here and reuse key preferences and
+// controllers between here and A11y Setting page.
@SearchIndexable
public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
implements InputManager.InputDeviceListener,
@@ -83,6 +85,8 @@
Secure.ACCESSIBILITY_SLOW_KEYS);
private static final Uri sAccessibilityStickyKeysUri = Secure.getUriFor(
Secure.ACCESSIBILITY_STICKY_KEYS);
+ public static final int BOUNCE_KEYS_THRESHOLD = 500;
+ public static final int SLOW_KEYS_THRESHOLD = 500;
@NonNull
private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
@@ -132,8 +136,12 @@
mKeyboardA11yCategory = Objects.requireNonNull(findPreference(KEYBOARD_A11Y_CATEGORY));
mAccessibilityBounceKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_BOUNCE_KEYS));
+ mAccessibilityBounceKeys.setSummary(
+ getContext().getString(R.string.bounce_keys_summary, BOUNCE_KEYS_THRESHOLD));
mAccessibilitySlowKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_SLOW_KEYS));
+ mAccessibilitySlowKeys.setSummary(
+ getContext().getString(R.string.slow_keys_summary, SLOW_KEYS_THRESHOLD));
mAccessibilityStickyKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_STICKY_KEYS));
diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java
new file mode 100644
index 0000000..96beb43
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardBounceKeyPreferenceControllerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@RunWith(RobolectricTestRunner.class)
+public class KeyboardBounceKeyPreferenceControllerTest {
+
+ private static final String KEY_ACCESSIBILITY_BOUNCE_KEYS =
+ Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS;
+ private static final int UNKNOWN = -1;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
+ private final KeyboardBounceKeyPreferenceController mController =
+ new KeyboardBounceKeyPreferenceController(mContext,
+ KeyboardBounceKeyPreferenceController.PREF_KEY);
+
+ @Before
+ public void setUp() {
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+ mSwitchPreference.setKey(KeyboardBounceKeyPreferenceController.PREF_KEY);
+ screen.addPreference(mSwitchPreference);
+ mController.displayPreference(screen);
+ }
+
+ @Test
+ public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_disableBounceKey_onResumeShouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_enableBounceKey_onResumeShouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_enableBounceKey_shouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(true);
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_disableBounceKey_shouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(false);
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_setFalse_shouldDisableBounceKey() {
+ mController.setChecked(false);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS, UNKNOWN)).isEqualTo(
+ OFF);
+ }
+
+ @Test
+ public void setChecked_setTrue_shouldEnableBounceKey() {
+ mController.setChecked(true);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS,
+ UNKNOWN)).isNotEqualTo(OFF);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java
new file mode 100644
index 0000000..321b69f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardSlowKeyPreferenceControllerTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@RunWith(RobolectricTestRunner.class)
+public class KeyboardSlowKeyPreferenceControllerTest {
+
+ private static final String KEY_ACCESSIBILITY_SLOW_KEYS =
+ Settings.Secure.ACCESSIBILITY_SLOW_KEYS;
+ private static final int UNKNOWN = -1;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
+ private final KeyboardSlowKeyPreferenceController mController =
+ new KeyboardSlowKeyPreferenceController(mContext,
+ KeyboardSlowKeyPreferenceController.PREF_KEY);
+
+ @Before
+ public void setUp() {
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+ mSwitchPreference.setKey(KeyboardSlowKeyPreferenceController.PREF_KEY);
+ screen.addPreference(mSwitchPreference);
+ mController.displayPreference(screen);
+ }
+
+ @Test
+ public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_disableSlowKey_onResumeShouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_enableSlowKey_onResumeShouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_enableSlowKey_shouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(true);
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_disableSlowKey_shouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(false);
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_setFalse_shouldDisableSlowKey() {
+ mController.setChecked(false);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, UNKNOWN)).isEqualTo(
+ OFF);
+ }
+
+ @Test
+ public void setChecked_setTrue_shouldEnableSlowKey() {
+ mController.setChecked(true);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, UNKNOWN)).isNotEqualTo(
+ OFF);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java
new file mode 100644
index 0000000..31d46b7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardStickyKeyPreferenceControllerTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2024 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.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.core.BasePreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+
+@RunWith(RobolectricTestRunner.class)
+public class KeyboardStickyKeyPreferenceControllerTest {
+
+ private static final String KEY_ACCESSIBILITY_STICKY_KEYS =
+ Settings.Secure.ACCESSIBILITY_STICKY_KEYS;
+ private static final int UNKNOWN = -1;
+
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
+ private final KeyboardStickyKeyPreferenceController mController =
+ new KeyboardStickyKeyPreferenceController(mContext,
+ KeyboardStickyKeyPreferenceController.PREF_KEY);
+
+ @Before
+ public void setUp() {
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
+ mSwitchPreference.setKey(KeyboardStickyKeyPreferenceController.PREF_KEY);
+ screen.addPreference(mSwitchPreference);
+ mController.displayPreference(screen);
+ }
+
+ @Test
+ public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void isChecked_disableStickyKey_onResumeShouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_enableStickyKey_onResumeShouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_enableStickyKey_shouldReturnTrue() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(true);
+ assertThat(mController.isChecked()).isTrue();
+ assertThat(mSwitchPreference.isChecked()).isTrue();
+ }
+
+ @Test
+ public void performClick_disableStickyKey_shouldReturnFalse() {
+ Settings.Secure.putInt(mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, ON);
+
+ mController.updateState(mSwitchPreference);
+
+ mSwitchPreference.performClick();
+
+ verify(mSwitchPreference).setChecked(false);
+ assertThat(mController.isChecked()).isFalse();
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_setFalse_shouldDisableStickyKey() {
+ mController.setChecked(false);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, UNKNOWN)).isEqualTo(OFF);
+ }
+
+ @Test
+ public void setChecked_setTrue_shouldEnableStickyKey() {
+ mController.setChecked(true);
+
+ assertThat(Settings.Secure.getInt(
+ mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, UNKNOWN)).isEqualTo(ON);
+ }
+}