[A11y Setting] Add keyboard keys preferences under A11y page

Bug: 325851068
Test: make RunSettingsRoboTests
Change-Id: Ic7eb1ec9e260a95d040bee784cbff479a4f087d0
This commit is contained in:
shaoweishen
2024-02-20 13:01:19 +00:00
parent b3646a4957
commit a5f5ff4add
14 changed files with 811 additions and 19 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -4328,16 +4328,16 @@
<string name="show_ime_summary">Keep it on screen while physical keyboard is active</string> <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] --> <!-- Title for the 'Bounce keys' preference switch. [CHAR LIMIT=35] -->
<string name="bounce_keys">Bounce keys</string> <string name="bounce_keys">Bounce keys</string>
<!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=100] --> <!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=300] -->
<string name="bounce_keys_summary">Enable Bounce keys for physical keyboard accessibility</string> <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] --> <!-- Title for the 'Slow keys' preference switch. [CHAR LIMIT=35] -->
<string name="slow_keys">Slow keys</string> <string name="slow_keys">Slow keys</string>
<!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=100] --> <!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=300] -->
<string name="slow_keys_summary">Enable Slow keys for physical keyboard accessibility</string> <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] --> <!-- Title for the 'Sticky keys' preference switch. [CHAR LIMIT=35] -->
<string name="sticky_keys">Sticky keys</string> <string name="sticky_keys">Sticky keys</string>
<!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=100] --> <!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=300] -->
<string name="sticky_keys_summary">Enable Sticky keys for physical keyboard accessibility</string> <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] --> <!-- Title for the button to trigger the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=35] -->
<string name="keyboard_shortcuts_helper">Keyboard shortcuts</string> <string name="keyboard_shortcuts_helper">Keyboard shortcuts</string>
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] --> <!-- 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> <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] --> <!-- Title for the accessibility preference category of screen reader services and settings. [CHAR LIMIT=50] -->
<string name="screen_reader_category_title">Screen reader</string> <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] --> <!-- Title for the accessibility preference category of caption services and settings. [CHAR LIMIT=50] -->
<string name="captions_category_title">Captions</string> <string name="captions_category_title">Captions</string>
<!-- Title for the accessibility preference category of audio services and settings. [CHAR LIMIT=50] --> <!-- Title for the accessibility preference category of audio services and settings. [CHAR LIMIT=50] -->

View File

@@ -105,6 +105,37 @@
</PreferenceCategory> </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 <PreferenceCategory
android:key="captions_category" android:key="captions_category"
android:persistent="false" android:persistent="false"

View File

@@ -43,22 +43,20 @@
android:key="keyboard_a11y_category" android:key="keyboard_a11y_category"
android:title="@string/keyboard_a11y_category"> android:title="@string/keyboard_a11y_category">
<SwitchPreference <SwitchPreferenceCompat
android:key="accessibility_sticky_keys" android:key="accessibility_sticky_keys"
android:title="@string/sticky_keys" android:title="@string/sticky_keys"
android:summary="@string/sticky_keys_summary" android:summary="@string/sticky_keys_summary"
android:defaultValue="false" /> android:defaultValue="false" />
<SwitchPreference <SwitchPreferenceCompat
android:key="accessibility_slow_keys"
android:title="@string/slow_keys"
android:summary="@string/slow_keys_summary"
android:defaultValue="false" />
<SwitchPreference
android:key="accessibility_bounce_keys" android:key="accessibility_bounce_keys"
android:title="@string/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" /> android:defaultValue="false" />
</PreferenceCategory> </PreferenceCategory>

View File

@@ -23,12 +23,14 @@ import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.hardware.input.InputManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.view.InputDevice;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -42,6 +44,7 @@ import com.android.internal.content.PackageMonitor;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType; import com.android.settings.accessibility.AccessibilityUtil.AccessibilityServiceFragmentType;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.inputmethod.PhysicalKeyboardFragment;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
@@ -56,7 +59,8 @@ import java.util.Map;
/** Activity with the accessibility settings. */ /** Activity with the accessibility settings. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC) @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"; private static final String TAG = "AccessibilitySettings";
@@ -67,12 +71,14 @@ public class AccessibilitySettings extends DashboardFragment {
private static final String CATEGORY_SPEECH = "speech_category"; private static final String CATEGORY_SPEECH = "speech_category";
private static final String CATEGORY_DISPLAY = "display_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_DOWNLOADED_SERVICES = "user_installed_services_category";
private static final String CATEGORY_KEYBOARD_OPTIONS = "physical_keyboard_options_category";
@VisibleForTesting @VisibleForTesting
static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category"; static final String CATEGORY_INTERACTION_CONTROL = "interaction_control_category";
private static final String[] CATEGORIES = new String[]{ private static final String[] CATEGORIES = new String[]{
CATEGORY_SCREEN_READER, CATEGORY_CAPTIONS, CATEGORY_AUDIO, CATEGORY_DISPLAY, 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. // Extras passed to sub-fragments.
@@ -169,6 +175,9 @@ public class AccessibilitySettings extends DashboardFragment {
// Observe changes from accessibility selection menu // Observe changes from accessibility selection menu
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
shortcutFeatureKeys.add(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); 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 = new AccessibilitySettingsContentObserver(mHandler);
mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys, mSettingsContentObserver.registerKeysToObserverCallback(shortcutFeatureKeys,
key -> onContentChanged()); key -> onContentChanged());
@@ -197,6 +206,7 @@ public class AccessibilitySettings extends DashboardFragment {
initializeAllPreferences(); initializeAllPreferences();
updateAllPreferences(); updateAllPreferences();
registerContentMonitors(); registerContentMonitors();
registerInputDeviceListener();
} }
@Override @Override
@@ -224,6 +234,7 @@ public class AccessibilitySettings extends DashboardFragment {
@Override @Override
public void onDestroy() { public void onDestroy() {
unregisterContentMonitors(); unregisterContentMonitors();
unRegisterInputDeviceListener();
super.onDestroy(); super.onDestroy();
} }
@@ -313,9 +324,9 @@ public class AccessibilitySettings extends DashboardFragment {
@VisibleForTesting @VisibleForTesting
void updateAllPreferences() { void updateAllPreferences() {
updateSystemPreferences();
updateServicePreferences(); updateServicePreferences();
updatePreferencesState(); updatePreferencesState();
updateSystemPreferences();
} }
private void registerContentMonitors() { private void registerContentMonitors() {
@@ -326,6 +337,22 @@ public class AccessibilitySettings extends DashboardFragment {
mSettingsContentObserver.register(getContentResolver()); 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() { private void unregisterContentMonitors() {
mSettingsPackageMonitor.unregister(); mSettingsPackageMonitor.unregister();
mSettingsContentObserver.unregister(getContentResolver()); mSettingsContentObserver.unregister(getContentResolver());
@@ -405,6 +432,7 @@ public class AccessibilitySettings extends DashboardFragment {
// Hide category if it is empty. // Hide category if it is empty.
updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER); updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER);
updatePreferenceCategoryVisibility(CATEGORY_SPEECH); updatePreferenceCategoryVisibility(CATEGORY_SPEECH);
updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS);
} }
private List<RestrictedPreference> getInstalledAccessibilityList(Context context) { private List<RestrictedPreference> getInstalledAccessibilityList(Context context) {
@@ -499,7 +527,7 @@ public class AccessibilitySettings extends DashboardFragment {
* Updates preferences related to system configurations. * Updates preferences related to system configurations.
*/ */
protected void updateSystemPreferences() { protected void updateSystemPreferences() {
// Do nothing. updateKeyboardPreferencesVisibility();
} }
private void updatePreferencesState() { private void updatePreferencesState() {
@@ -509,6 +537,53 @@ public class AccessibilitySettings extends DashboardFragment {
findPreference(controller.getPreferenceKey()))); 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 = public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.accessibility_settings) { new BaseSearchIndexProvider(R.xml.accessibility_settings) {
@Override @Override
@@ -519,4 +594,15 @@ public class AccessibilitySettings extends DashboardFragment {
context); 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);
}
} }

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -61,6 +61,8 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; 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 @SearchIndexable
public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
implements InputManager.InputDeviceListener, implements InputManager.InputDeviceListener,
@@ -83,6 +85,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
Secure.ACCESSIBILITY_SLOW_KEYS); Secure.ACCESSIBILITY_SLOW_KEYS);
private static final Uri sAccessibilityStickyKeysUri = Secure.getUriFor( private static final Uri sAccessibilityStickyKeysUri = Secure.getUriFor(
Secure.ACCESSIBILITY_STICKY_KEYS); Secure.ACCESSIBILITY_STICKY_KEYS);
public static final int BOUNCE_KEYS_THRESHOLD = 500;
public static final int SLOW_KEYS_THRESHOLD = 500;
@NonNull @NonNull
private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>(); private final ArrayList<HardKeyboardDeviceInfo> mLastHardKeyboards = new ArrayList<>();
@@ -132,8 +136,12 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
mKeyboardA11yCategory = Objects.requireNonNull(findPreference(KEYBOARD_A11Y_CATEGORY)); mKeyboardA11yCategory = Objects.requireNonNull(findPreference(KEYBOARD_A11Y_CATEGORY));
mAccessibilityBounceKeys = Objects.requireNonNull( mAccessibilityBounceKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_BOUNCE_KEYS)); mKeyboardA11yCategory.findPreference(ACCESSIBILITY_BOUNCE_KEYS));
mAccessibilityBounceKeys.setSummary(
getContext().getString(R.string.bounce_keys_summary, BOUNCE_KEYS_THRESHOLD));
mAccessibilitySlowKeys = Objects.requireNonNull( mAccessibilitySlowKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_SLOW_KEYS)); mKeyboardA11yCategory.findPreference(ACCESSIBILITY_SLOW_KEYS));
mAccessibilitySlowKeys.setSummary(
getContext().getString(R.string.slow_keys_summary, SLOW_KEYS_THRESHOLD));
mAccessibilityStickyKeys = Objects.requireNonNull( mAccessibilityStickyKeys = Objects.requireNonNull(
mKeyboardA11yCategory.findPreference(ACCESSIBILITY_STICKY_KEYS)); mKeyboardA11yCategory.findPreference(ACCESSIBILITY_STICKY_KEYS));

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}