diff --git a/res/drawable/ic_bounce_keys.xml b/res/drawable/ic_bounce_keys.xml new file mode 100644 index 00000000000..424a5fc4a95 --- /dev/null +++ b/res/drawable/ic_bounce_keys.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/res/drawable/ic_slow_keys.xml b/res/drawable/ic_slow_keys.xml new file mode 100644 index 00000000000..b28d0ae1012 --- /dev/null +++ b/res/drawable/ic_slow_keys.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/res/drawable/ic_sticky_keys.xml b/res/drawable/ic_sticky_keys.xml new file mode 100644 index 00000000000..c07da15942c --- /dev/null +++ b/res/drawable/ic_sticky_keys.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 76c7106fae3..a4761d3ec8b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4328,16 +4328,16 @@ Keep it on screen while physical keyboard is active Bounce keys - - Enable Bounce keys for physical keyboard accessibility + + The keyboard ignores quickly repeated presses of the same key within %1$d ms Slow keys - - Enable Slow keys for physical keyboard accessibility + + Adjusts the time it takes for a key press to activate to %1$d ms Sticky keys - - Enable Sticky keys for physical keyboard accessibility + + Press one key at a time for shortcuts instead of holding keys down together Keyboard shortcuts @@ -4601,6 +4601,8 @@ Change font size Screen reader + + Physical keyboard options Captions diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml index 0f4065b5369..ad8bfc396ba 100644 --- a/res/xml/accessibility_settings.xml +++ b/res/xml/accessibility_settings.xml @@ -105,6 +105,37 @@ + + + + + + + + + - - - - + + diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 16414f8846e..fe89bf203b4 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -23,12 +23,14 @@ import android.content.ComponentName; 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.internal.content.PackageMonitor; 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 @@ import java.util.Map; /** 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 @@ public class AccessibilitySettings extends DashboardFragment { 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 @@ public class AccessibilitySettings extends DashboardFragment { // 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 @@ public class AccessibilitySettings extends DashboardFragment { initializeAllPreferences(); updateAllPreferences(); registerContentMonitors(); + registerInputDeviceListener(); } @Override @@ -224,6 +234,7 @@ public class AccessibilitySettings extends DashboardFragment { @Override public void onDestroy() { unregisterContentMonitors(); + unRegisterInputDeviceListener(); super.onDestroy(); } @@ -313,9 +324,9 @@ public class AccessibilitySettings extends DashboardFragment { @VisibleForTesting void updateAllPreferences() { - updateSystemPreferences(); updateServicePreferences(); updatePreferencesState(); + updateSystemPreferences(); } private void registerContentMonitors() { @@ -326,6 +337,22 @@ public class AccessibilitySettings extends DashboardFragment { 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 @@ public class AccessibilitySettings extends DashboardFragment { // Hide category if it is empty. updatePreferenceCategoryVisibility(CATEGORY_SCREEN_READER); updatePreferenceCategoryVisibility(CATEGORY_SPEECH); + updatePreferenceCategoryVisibility(CATEGORY_KEYBOARD_OPTIONS); } private List getInstalledAccessibilityList(Context context) { @@ -499,7 +527,7 @@ public class AccessibilitySettings extends DashboardFragment { * Updates preferences related to system configurations. */ protected void updateSystemPreferences() { - // Do nothing. + updateKeyboardPreferencesVisibility(); } private void updatePreferencesState() { @@ -509,6 +537,53 @@ public class AccessibilitySettings extends DashboardFragment { 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 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 @@ public class AccessibilitySettings extends DashboardFragment { 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 00000000000..6d988ac13be --- /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 00000000000..8bd231680ee --- /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 00000000000..ee5559df084 --- /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 b06edb2957b..f2ac5508d80 100644 --- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java +++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java @@ -61,6 +61,8 @@ import java.util.Arrays; 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 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment 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 mLastHardKeyboards = new ArrayList<>(); @@ -132,8 +136,12 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment 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 00000000000..96beb43dd1c --- /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 00000000000..321b69fa834 --- /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 00000000000..31d46b76eab --- /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); + } +}