[Physical Keyboard][A11y Page] Add Bounce keys dialog

1.Add dialog for adjust bounce key to different values.
demo video: b/346949547#comment24.
2.Update summary text.
3.When receive update from content uri, update toggle state directly.
4.Rename Controller to more suitable name.

Bug: 346949547
Test: atest SettingsRoboTests
Flag: com.android.settings.keyboard.keyboard_and_touchpad_a11y_new_page_enabled
Change-Id: I94d8d1a77528b13f59b2d67dfa30e17dd177e385
This commit is contained in:
shaoweishen
2024-08-22 10:36:47 +00:00
parent 7c6b213c73
commit f0a01c51dd
9 changed files with 262 additions and 41 deletions

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/bounce_key_dialog_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textSize="20sp"
android:gravity="center_horizontal"
android:text="@string/bounce_keys_dialog_title"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
/>
<TextView
android:id="@+id/bounce_key_dialog_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="26dp"
android:layout_marginTop="8dp"
android:textSize="16sp"
android:gravity="center"
android:text="@string/bounce_keys_dialog_subtitle"
android:textColor="?android:attr/textColorSecondary"
/>
<RadioGroup
android:id="@+id/bounce_key_value_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp">
<RadioButton
android:id="@+id/bounce_key_value_200"
android:text="@string/bounce_keys_dialog_option_200"
android:paddingStart="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:background="@null"/>
<RadioButton
android:id="@+id/bounce_key_value_400"
android:text="@string/bounce_keys_dialog_option_400"
android:paddingStart="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="16dp"
android:background="@null"/>
<RadioButton
android:id="@+id/bounce_key_value_600"
android:text="@string/bounce_keys_dialog_option_600"
android:paddingStart="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="16dp"
android:background="@null"/>
</RadioGroup>
</LinearLayout>

View File

@@ -4520,11 +4520,21 @@
<!-- 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=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>
<string name="bounce_keys_summary">The keyboard ignores quickly repeated presses of the same key </string>
<!-- Title for the 'Bounce keys' threshold dialog. [CHAR LIMIT=35] -->
<string name="bounce_keys_dialog_title">Bounce key threshold</string>
<!-- Subtitle for the 'Bounce keys' threshold dialog. [CHAR LIMIT=300] -->
<string name="bounce_keys_dialog_subtitle">Choose the duration of time your keyboard ignores repeated key presses</string>
<!-- Option title for the 'Bounce keys' threshold dialog for 200 millisecond. [CHAR LIMIT=35] -->
<string name="bounce_keys_dialog_option_200">0.2s</string>
<!-- Option title for the 'Bounce keys' threshold dialog for 400 millisecond. [CHAR LIMIT=35] -->
<string name="bounce_keys_dialog_option_400">0.4s</string>
<!-- Option title for the 'Bounce keys' threshold dialog for 600 millisecond. [CHAR LIMIT=35] -->
<string name="bounce_keys_dialog_option_600">0.6s</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=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>
<string name="slow_keys_summary">Adjusts the time it takes for a key press to activate</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=300] -->
@@ -4532,7 +4542,7 @@
<!-- Title for a toggle switch for 'mouse' keys, an accessibility setting that allows the mouse pointer to be controlled using certain keys on a physical keyboard if keyboard is connected. [CHAR LIMIT=35] -->
<string name="mouse_keys">Mouse keys</string>
<!-- Summary text for the accessibility setting 'Mouse keys' preference sub-screen. [CHAR LIMIT=100] -->
<string name="mouse_keys_summary">Use the physical keyboard to control the mouse.</string>
<string name="mouse_keys_summary">Use your keyboard to control the pointer</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] -->

View File

@@ -32,9 +32,10 @@
android:defaultValue="false"
settings:controller="com.android.settings.inputmethod.KeyboardAccessibilityStickyKeysController"/>
<SwitchPreferenceCompat
<com.android.settingslib.PrimarySwitchPreference
android:key="accessibility_bounce_keys"
android:title="@string/bounce_keys"
android:summary="@string/bounce_keys_summary"
android:defaultValue="false"
settings:controller="com.android.settings.inputmethod.KeyboardAccessibilityBounceKeysController"/>
@@ -42,6 +43,7 @@
android:key="accessibility_slow_keys"
android:title="@string/slow_keys"
android:defaultValue="false"
android:summary="@string/slow_keys_summary"
settings:controller="com.android.settings.inputmethod.KeyboardAccessibilitySlowKeysController" />
<SwitchPreferenceCompat

View File

@@ -35,25 +35,25 @@ import com.android.settings.core.TogglePreferenceController;
import com.android.settings.keyboard.Flags;
/**
* Abstract class for toggle controllers of Keyboard accessibility related function.
* Abstract class for toggle controllers of Keyboard input setting related function.
*/
public abstract class KeyboardAccessibilityController extends TogglePreferenceController implements
public abstract class InputSettingPreferenceController extends TogglePreferenceController implements
LifecycleObserver {
private final ContentResolver mContentResolver;
private final ContentObserver mContentObserver = new ContentObserver(new Handler(true)) {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (getSettingUri().equals(uri)) {
updateKeyboardAccessibilitySettings();
onInputSettingUpdated();
}
}
};
protected abstract void updateKeyboardAccessibilitySettings();
protected abstract void onInputSettingUpdated();
protected abstract Uri getSettingUri();
public KeyboardAccessibilityController(@NonNull Context context,
public InputSettingPreferenceController(@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
mContentResolver = context.getContentResolver();
@@ -94,7 +94,7 @@ public abstract class KeyboardAccessibilityController extends TogglePreferenceCo
false,
mContentObserver,
UserHandle.myUserId());
updateKeyboardAccessibilitySettings();
onInputSettingUpdated();
}
private void unregisterSettingsObserver() {

View File

@@ -20,20 +20,38 @@ import android.content.Context;
import android.hardware.input.InputSettings;
import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
import android.widget.RadioGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.PrimarySwitchPreference;
public class KeyboardAccessibilityBounceKeysController extends
KeyboardAccessibilityController implements
InputSettingPreferenceController implements
LifecycleObserver {
public static final int BOUNCE_KEYS_THRESHOLD = 500;
private AlertDialog mAlertDialog;
@Nullable
private PrimarySwitchPreference mPrimaryPreference;
public KeyboardAccessibilityBounceKeysController(@NonNull Context context,
@NonNull String key) {
super(context, key);
constructDialog(context);
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mPrimaryPreference = screen.findPreference(getPreferenceKey());
}
@Override
@@ -43,6 +61,17 @@ public class KeyboardAccessibilityBounceKeysController extends
: UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return false;
}
if (mAlertDialog != null) {
mAlertDialog.show();
}
return true;
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilityBounceKeysEnabled(mContext);
@@ -55,16 +84,12 @@ public class KeyboardAccessibilityBounceKeysController extends
return true;
}
@NonNull
@Override
public CharSequence getSummary() {
return mContext.getString(R.string.bounce_keys_summary, BOUNCE_KEYS_THRESHOLD);
}
@Override
protected void updateKeyboardAccessibilitySettings() {
setChecked(
InputSettings.isAccessibilityBounceKeysEnabled(mContext));
protected void onInputSettingUpdated() {
if (mPrimaryPreference != null) {
mPrimaryPreference.setChecked(
InputSettings.isAccessibilityBounceKeysEnabled(mContext));
}
}
@Override
@@ -72,4 +97,31 @@ public class KeyboardAccessibilityBounceKeysController extends
return Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS);
}
private void constructDialog(Context context) {
mAlertDialog = new AlertDialog.Builder(context)
.setView(R.layout.dialog_a11y_bounce_key)
.setPositiveButton(android.R.string.ok,
(dialog, which) -> {
RadioGroup radioGroup =
mAlertDialog.findViewById(R.id.bounce_key_value_group);
int checkedRadioButtonId = radioGroup.getCheckedRadioButtonId();
int threshold = checkedRadioButtonId == R.id.bounce_key_value_600 ? 600
: checkedRadioButtonId == R.id.bounce_key_value_400 ? 400
: checkedRadioButtonId == R.id.bounce_key_value_200
? 200 : 0;
InputSettings.setAccessibilityBounceKeysThreshold(context, threshold);
})
.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
.create();
mAlertDialog.setOnShowListener(dialog -> {
RadioGroup radioGroup = mAlertDialog.findViewById(R.id.bounce_key_value_group);
int bounceKeysThreshold = InputSettings.getAccessibilityBounceKeysThreshold(context);
switch (bounceKeysThreshold) {
case 600 -> radioGroup.check(R.id.bounce_key_value_600);
case 400 -> radioGroup.check(R.id.bounce_key_value_400);
default -> radioGroup.check(R.id.bounce_key_value_200);
}
});
}
}

View File

@@ -23,14 +23,25 @@ import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
public class KeyboardAccessibilityMouseKeysController extends
KeyboardAccessibilityController implements
InputSettingPreferenceController implements
LifecycleObserver {
private TwoStatePreference mTwoStatePreference;
public KeyboardAccessibilityMouseKeysController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mTwoStatePreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilityMouseKeysEnabled(mContext);
@@ -51,9 +62,11 @@ public class KeyboardAccessibilityMouseKeysController extends
}
@Override
protected void updateKeyboardAccessibilitySettings() {
setChecked(
InputSettings.isAccessibilityMouseKeysEnabled(mContext));
protected void onInputSettingUpdated() {
if (mTwoStatePreference != null) {
mTwoStatePreference.setChecked(
InputSettings.isAccessibilityMouseKeysEnabled(mContext));
}
}
@Override

View File

@@ -23,18 +23,26 @@ import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;
import com.android.settings.R;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
public class KeyboardAccessibilitySlowKeysController extends
KeyboardAccessibilityController implements
InputSettingPreferenceController implements
LifecycleObserver {
public static final int SLOW_KEYS_THRESHOLD = 500;
private TwoStatePreference mTwoStatePreference;
public KeyboardAccessibilitySlowKeysController(@NonNull Context context, @NonNull String key) {
super(context, key);
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mTwoStatePreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilitySlowKeysEnabled(mContext);
@@ -54,16 +62,12 @@ public class KeyboardAccessibilitySlowKeysController extends
: UNSUPPORTED_ON_DEVICE;
}
@NonNull
@Override
public CharSequence getSummary() {
return mContext.getString(R.string.slow_keys_summary, SLOW_KEYS_THRESHOLD);
}
@Override
protected void updateKeyboardAccessibilitySettings() {
setChecked(
InputSettings.isAccessibilitySlowKeysEnabled(mContext));
protected void onInputSettingUpdated() {
if (mTwoStatePreference != null) {
mTwoStatePreference.setChecked(
InputSettings.isAccessibilitySlowKeysEnabled(mContext));
}
}
@Override

View File

@@ -23,15 +23,26 @@ import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
public class KeyboardAccessibilityStickyKeysController extends
KeyboardAccessibilityController implements
InputSettingPreferenceController implements
LifecycleObserver {
private TwoStatePreference mTwoStatePreference;
public KeyboardAccessibilityStickyKeysController(@NonNull Context context,
@NonNull String key) {
super(context, key);
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mTwoStatePreference = screen.findPreference(getPreferenceKey());
}
@Override
public boolean isChecked() {
return InputSettings.isAccessibilityStickyKeysEnabled(mContext);
@@ -52,9 +63,11 @@ public class KeyboardAccessibilityStickyKeysController extends
}
@Override
protected void updateKeyboardAccessibilitySettings() {
setChecked(
InputSettings.isAccessibilityStickyKeysEnabled(mContext));
protected void onInputSettingUpdated() {
if (mTwoStatePreference != null) {
mTwoStatePreference.setChecked(
InputSettings.isAccessibilityStickyKeysEnabled(mContext));
}
}
@Override

View File

@@ -18,39 +18,59 @@ package com.android.settings.inputmethod;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.input.InputSettings;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.widget.RadioGroup;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.keyboard.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
com.android.settings.testutils.shadow.ShadowFragment.class,
ShadowAlertDialogCompat.class,
})
public class KeyboardAccessibilityBounceKeysControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final String PREFERENCE_KEY = "accessibility_bounce_keys";
@Mock
private Preference mPreference;
private Context mContext;
private KeyboardAccessibilityBounceKeysController mKeyboardAccessibilityBounceKeysController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
mKeyboardAccessibilityBounceKeysController = new KeyboardAccessibilityBounceKeysController(
mContext,
"accessibility_bounce_keys");
PREFERENCE_KEY);
when(mPreference.getKey()).thenReturn(PREFERENCE_KEY);
}
@Test
@@ -82,4 +102,28 @@ public class KeyboardAccessibilityBounceKeysControllerTest {
assertThat(isEnabled).isFalse();
}
@Test
public void handlePreferenceTreeClick_dialogShows() {
mKeyboardAccessibilityBounceKeysController.handlePreferenceTreeClick(mPreference);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(alertDialog.isShowing()).isTrue();
}
@Test
public void handlePreferenceTreeClick_performClickOn200_updatesBounceKeysThreshold() {
mKeyboardAccessibilityBounceKeysController.handlePreferenceTreeClick(mPreference);
AlertDialog alertDialog = ShadowAlertDialogCompat.getLatestAlertDialog();
RadioGroup radioGroup = alertDialog.findViewById(R.id.bounce_key_value_group);
radioGroup.check(R.id.bounce_key_value_200);
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
ShadowLooper.idleMainLooper();
assertThat(alertDialog.isShowing()).isFalse();
int threshold = InputSettings.getAccessibilityBounceKeysThreshold(mContext);
assertThat(threshold).isEqualTo(200);
}
}