diff options
11 files changed, 119 insertions, 29 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index caf699280e08..72a68f85b6b7 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1797,18 +1797,20 @@ package android.hardware.input { public class InputSettings { method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") public static int getAccessibilityBounceKeysThreshold(@NonNull android.content.Context); - method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysDelay(@NonNull android.content.Context); - method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") public static int getAccessibilityRepeatKeysTimeout(@NonNull android.content.Context); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") public static int getAccessibilitySlowKeysThreshold(@NonNull android.content.Context); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") public static int getRepeatKeysDelay(@NonNull android.content.Context); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") public static int getRepeatKeysTimeout(@NonNull android.content.Context); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") public static boolean isAccessibilityMouseKeysEnabled(@NonNull android.content.Context); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") public static boolean isAccessibilityStickyKeysEnabled(@NonNull android.content.Context); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") public static boolean isRepeatKeysEnabled(@NonNull android.content.Context); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_bounce_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityBounceKeysThreshold(@NonNull android.content.Context, int); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_mouse_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityMouseKeysEnabled(@NonNull android.content.Context, boolean); - method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysDelay(@NonNull android.content.Context, int); - method @FlaggedApi("com.android.hardware.input.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityRepeatKeysTimeout(@NonNull android.content.Context, int); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_slow_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilitySlowKeysThreshold(@NonNull android.content.Context, int); method @FlaggedApi("com.android.hardware.input.keyboard_a11y_sticky_keys_flag") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setAccessibilityStickyKeysEnabled(@NonNull android.content.Context, boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, @FloatRange(from=0, to=1) float); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setRepeatKeysDelay(@NonNull android.content.Context, int); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setRepeatKeysEnabled(@NonNull android.content.Context, boolean); + method @FlaggedApi("com.android.input.flags.keyboard_repeat_keys") @RequiresPermission(android.Manifest.permission.WRITE_SETTINGS) public static void setRepeatKeysTimeout(@NonNull android.content.Context, int); field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0 } diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 8592dedbb2bb..177ee6f1540a 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -20,15 +20,15 @@ import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FL import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS; import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG; import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG; -import static com.android.hardware.input.Flags.FLAG_KEYBOARD_REPEAT_KEYS; import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag; import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag; import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; import static com.android.hardware.input.Flags.keyboardA11yMouseKeys; -import static com.android.hardware.input.Flags.keyboardRepeatKeys; import static com.android.hardware.input.Flags.touchpadTapDragging; import static com.android.hardware.input.Flags.touchpadVisualizer; import static com.android.input.flags.Flags.enableInputFilterRustImpl; +import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS; +import static com.android.input.flags.Flags.keyboardRepeatKeys; import android.Manifest; import android.annotation.FlaggedApi; @@ -800,7 +800,7 @@ public class InputSettings { * * <p> * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular - * key on the physical keyboard is held down. This accessibility feature allows the user + * key on the physical keyboard is held down. This feature allows the user * to configure the timeout before the key repeats begin as well as the delay * between successive key repeats. * </p> @@ -812,7 +812,31 @@ public class InputSettings { } /** - * Get Accessibility repeat keys timeout duration in milliseconds. + * Whether "Repeat keys" feature is enabled. + * Repeat keys is ON by default. + * The repeat keys timeout and delay would have the default values in the default ON case. + * + * <p> + * 'Repeat keys’ is a feature which allows users to generate key repeats when a particular + * key on the physical keyboard is held down. This feature allows the user + * to configure the timeout before the key repeats begin as well as the delay + * between successive key repeats. + * </p> + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) + public static boolean isRepeatKeysEnabled(@NonNull Context context) { + if (!isRepeatKeysFeatureFlagEnabled()) { + return true; + } + return Settings.Secure.getIntForUser(context.getContentResolver(), + Settings.Secure.KEY_REPEAT_ENABLED, 1, UserHandle.USER_CURRENT) != 0; + } + + /** + * Get repeat keys timeout duration in milliseconds. * The default key repeat timeout is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_TIMEOUT_MS}. * * @param context The application context @@ -823,7 +847,7 @@ public class InputSettings { * * <p> * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular - * key on the physical keyboard is held down. This accessibility feature allows the user + * key on the physical keyboard is held down. This feature allows the user * to configure the timeout before the key repeats begin as well as the delay * between successive key repeats. * </p> @@ -832,14 +856,17 @@ public class InputSettings { */ @TestApi @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) - public static int getAccessibilityRepeatKeysTimeout(@NonNull Context context) { + public static int getRepeatKeysTimeout(@NonNull Context context) { + if (!isRepeatKeysFeatureFlagEnabled()) { + return ViewConfiguration.getKeyRepeatTimeout(); + } return Settings.Secure.getIntForUser(context.getContentResolver(), Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(), UserHandle.USER_CURRENT); } /** - * Get Accessibility repeat keys delay rate in milliseconds. + * Get repeat keys delay rate in milliseconds. * The default key repeat delay is {@link ViewConfiguration#DEFAULT_KEY_REPEAT_DELAY_MS}. * * @param context The application context @@ -850,7 +877,7 @@ public class InputSettings { * * <p> * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular - * key on the physical keyboard is held down. This accessibility feature allows the user + * key on the physical keyboard is held down. This feature allows the user * to configure the timeout before the key repeats begin as well as the delay * between successive key repeats. * </p> @@ -859,14 +886,41 @@ public class InputSettings { */ @TestApi @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) - public static int getAccessibilityRepeatKeysDelay(@NonNull Context context) { + public static int getRepeatKeysDelay(@NonNull Context context) { + if (!isRepeatKeysFeatureFlagEnabled()) { + return ViewConfiguration.getKeyRepeatDelay(); + } return Settings.Secure.getIntForUser(context.getContentResolver(), Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(), UserHandle.USER_CURRENT); } /** - * Set Accessibility repeat keys timeout duration in milliseconds. + * Set repeat keys feature enabled/disabled. + * + * <p> + * 'Repeat keys’ is a feature which allows users to generate key repeats when a particular + * key on the physical keyboard is held down. This feature allows the user + * to configure the timeout before the key repeats begin as well as the delay + * between successive key repeats. + * </p> + * + * @hide + */ + @TestApi + @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) + @RequiresPermission(Manifest.permission.WRITE_SETTINGS) + public static void setRepeatKeysEnabled(@NonNull Context context, + boolean enabled) { + if (!isRepeatKeysFeatureFlagEnabled()) { + return; + } + Settings.Secure.putIntForUser(context.getContentResolver(), + Settings.Secure.KEY_REPEAT_ENABLED, enabled ? 1 : 0, UserHandle.USER_CURRENT); + } + + /** + * Set repeat keys timeout duration in milliseconds. * * @param timeoutTimeMillis time duration for which a key should be pressed after which the * pressed key will be repeated. The timeout must be between @@ -875,7 +929,7 @@ public class InputSettings { * * <p> * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular - * key on the physical keyboard is held down. This accessibility feature allows the user + * key on the physical keyboard is held down. This feature allows the user * to configure the timeout before the key repeats begin as well as the delay * between successive key repeats. * </p> @@ -885,8 +939,12 @@ public class InputSettings { @TestApi @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) @RequiresPermission(Manifest.permission.WRITE_SETTINGS) - public static void setAccessibilityRepeatKeysTimeout(@NonNull Context context, + public static void setRepeatKeysTimeout(@NonNull Context context, int timeoutTimeMillis) { + if (!isRepeatKeysFeatureFlagEnabled() + && !isRepeatKeysEnabled(context)) { + return; + } if (timeoutTimeMillis < MIN_KEY_REPEAT_TIMEOUT_MILLIS || timeoutTimeMillis > MAX_KEY_REPEAT_TIMEOUT_MILLIS) { throw new IllegalArgumentException( @@ -900,7 +958,7 @@ public class InputSettings { } /** - * Set Accessibility repeat key delay duration in milliseconds. + * Set repeat key delay duration in milliseconds. * * @param delayTimeMillis Time duration between successive key repeats when a key is * pressed down. The delay duration must be between @@ -908,7 +966,7 @@ public class InputSettings { * {@link #MAX_KEY_REPEAT_DELAY_MILLIS} * <p> * ‘Repeat keys’ is a feature which allows users to generate key repeats when a particular - * key on the physical keyboard is held down. This accessibility feature allows the user + * key on the physical keyboard is held down. This feature allows the user * to configure the timeout before the key repeats begin as well as the delay * between successive key repeats. * </p> @@ -918,8 +976,12 @@ public class InputSettings { @TestApi @FlaggedApi(FLAG_KEYBOARD_REPEAT_KEYS) @RequiresPermission(Manifest.permission.WRITE_SETTINGS) - public static void setAccessibilityRepeatKeysDelay(@NonNull Context context, + public static void setRepeatKeysDelay(@NonNull Context context, int delayTimeMillis) { + if (!isRepeatKeysFeatureFlagEnabled() + && !isRepeatKeysEnabled(context)) { + return; + } if (delayTimeMillis < MIN_KEY_REPEAT_DELAY_MILLIS || delayTimeMillis > MAX_KEY_REPEAT_DELAY_MILLIS) { throw new IllegalArgumentException( diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0ada9934482c..b8a8be159d12 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9158,15 +9158,27 @@ public final class Settings { public static final String MULTI_PRESS_TIMEOUT = "multi_press_timeout"; /** + * Whether to enable key repeats for Physical Keyboard. + * + * If set to false, continuous key presses on + * physical keyboard will not cause the pressed key to repeated. + * @hide + */ + @Readable + public static final String KEY_REPEAT_ENABLED = "key_repeat_enabled"; + + /** * The duration before a key repeat begins in milliseconds. * @hide */ + @Readable public static final String KEY_REPEAT_TIMEOUT_MS = "key_repeat_timeout"; /** * The duration between successive key repeats in milliseconds. * @hide */ + @Readable public static final String KEY_REPEAT_DELAY_MS = "key_repeat_delay"; /** diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index cb7c226bfc64..606e829c41fa 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -401,6 +401,7 @@ message SecureSettingsProto { optional SettingProto long_press_timeout = 35 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto key_press_timeout_ms = 96 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto key_press_delay_ms = 97 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto key_repeat_enabled = 102 [ (android.privacy).dest = DEST_AUTOMATIC ]; message ManagedProfile { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -735,5 +736,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 102; + // Next tag = 103; } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 40a8199a0690..d7109398b956 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -86,6 +86,7 @@ public class SecureSettings { Settings.Secure.DOUBLE_TAP_TO_WAKE, Settings.Secure.WAKE_GESTURE_ENABLED, Settings.Secure.LONG_PRESS_TIMEOUT, + Settings.Secure.KEY_REPEAT_ENABLED, Settings.Secure.KEY_REPEAT_TIMEOUT_MS, Settings.Secure.KEY_REPEAT_DELAY_MS, Settings.Secure.CAMERA_GESTURE_DISABLED, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 3b9c68389632..fa16a44f4592 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -133,6 +133,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.DOUBLE_TAP_TO_WAKE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.WAKE_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LONG_PRESS_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.KEY_REPEAT_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.KEY_REPEAT_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.KEY_REPEAT_DELAY_MS, NON_NEGATIVE_INTEGER_VALIDATOR); VALIDATORS.put(Secure.CAMERA_GESTURE_DISABLED, BOOLEAN_VALIDATOR); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 6bb56c9edcab..e885c14afddb 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -79,6 +79,7 @@ final class CoreSettingsObserver extends ContentObserver { sSecureSettingToTypeMap.put(Settings.Secure.MULTI_PRESS_TIMEOUT, int.class); sSecureSettingToTypeMap.put(Settings.Secure.KEY_REPEAT_TIMEOUT_MS, int.class); sSecureSettingToTypeMap.put(Settings.Secure.KEY_REPEAT_DELAY_MS, int.class); + sSecureSettingToTypeMap.put(Settings.Secure.KEY_REPEAT_ENABLED, int.class); sSecureSettingToTypeMap.put(Settings.Secure.STYLUS_POINTER_ICON_ENABLED, int.class); // add other secure settings here... diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 835fb72e524a..d70bd8b17ddf 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -94,6 +94,8 @@ class InputSettingsObserver extends ContentObserver { (reason) -> updateKeyRepeatInfo()), Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_DELAY_MS), (reason) -> updateKeyRepeatInfo()), + Map.entry(Settings.Secure.getUriFor(Settings.Secure.KEY_REPEAT_ENABLED), + (reason) -> updateKeyRepeatInfo()), Map.entry(Settings.System.getUriFor(Settings.System.SHOW_ROTARY_INPUT), (reason) -> updateShowRotaryInput()), Map.entry(Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS), @@ -230,6 +232,11 @@ class InputSettingsObserver extends ContentObserver { } private void updateKeyRepeatInfo() { + // Key repeat is enabled by default + final boolean keyRepeatEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_ENABLED, 1, + UserHandle.USER_CURRENT) != 0; + // Use ViewConfiguration getters only as fallbacks because they may return stale values. final int timeoutMs = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_TIMEOUT_MS, ViewConfiguration.getKeyRepeatTimeout(), @@ -237,7 +244,7 @@ class InputSettingsObserver extends ContentObserver { final int delayMs = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_DELAY_MS, ViewConfiguration.getKeyRepeatDelay(), UserHandle.USER_CURRENT); - mNative.setKeyRepeatConfiguration(timeoutMs, delayMs); + mNative.setKeyRepeatConfiguration(timeoutMs, delayMs, keyRepeatEnabled); } private void updateMaximumObscuringOpacityForTouch() { diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 1e7c97f9e551..5dd461dda061 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -212,7 +212,7 @@ interface NativeInputManagerService { void setMotionClassifierEnabled(boolean enabled); - void setKeyRepeatConfiguration(int timeoutMs, int delayMs); + void setKeyRepeatConfiguration(int timeoutMs, int delayMs, boolean keyRepeatEnabled); InputSensorInfo[] getSensorList(int deviceId); @@ -509,7 +509,8 @@ interface NativeInputManagerService { public native void setMotionClassifierEnabled(boolean enabled); @Override - public native void setKeyRepeatConfiguration(int timeoutMs, int delayMs); + public native void setKeyRepeatConfiguration(int timeoutMs, int delayMs, + boolean keyRepeatEnabled); @Override public native InputSensorInfo[] getSensorList(int deviceId); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 155e73c53819..d2493c513b5f 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -2725,12 +2725,13 @@ static void nativeSetMotionClassifierEnabled(JNIEnv* env, jobject nativeImplObj, } static void nativeSetKeyRepeatConfiguration(JNIEnv* env, jobject nativeImplObj, jint timeoutMs, - jint delayMs) { + jint delayMs, jboolean keyRepeatEnabled) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); im->getInputManager()->getDispatcher().setKeyRepeatConfiguration(std::chrono::milliseconds( timeoutMs), std::chrono::milliseconds( - delayMs)); + delayMs), + keyRepeatEnabled); } static jobject createInputSensorInfo(JNIEnv* env, jstring name, jstring vendor, jint version, @@ -3029,7 +3030,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"setDisplayEligibilityForPointerCapture", "(IZ)V", (void*)nativeSetDisplayEligibilityForPointerCapture}, {"setMotionClassifierEnabled", "(Z)V", (void*)nativeSetMotionClassifierEnabled}, - {"setKeyRepeatConfiguration", "(II)V", (void*)nativeSetKeyRepeatConfiguration}, + {"setKeyRepeatConfiguration", "(IIZ)V", (void*)nativeSetKeyRepeatConfiguration}, {"getSensorList", "(I)[Landroid/hardware/input/InputSensorInfo;", (void*)nativeGetSensorList}, {"getTouchpadHardwareProperties", diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 2a82d5f9bd7c..351ec4635977 100644 --- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -212,9 +212,10 @@ class InputManagerServiceTests { verify(native).setMotionClassifierEnabled(anyBoolean()) verify(native).setMaximumObscuringOpacityForTouch(anyFloat()) verify(native).setStylusPointerIconEnabled(anyBoolean()) - // Called twice at boot, since there are individual callbacks to update the - // key repeat timeout and the key repeat delay. - verify(native, times(2)).setKeyRepeatConfiguration(anyInt(), anyInt()) + // Called thrice at boot, since there are individual callbacks to update the + // key repeat timeout, the key repeat delay and whether key repeat enabled. + verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt(), + anyBoolean()) } @Test |