1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.input; 18 19 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG; 20 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG; 21 import static com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG; 22 import static com.android.hardware.input.Flags.keyboardA11yBounceKeysFlag; 23 import static com.android.hardware.input.Flags.keyboardA11ySlowKeysFlag; 24 import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag; 25 import static com.android.hardware.input.Flags.touchpadTapDragging; 26 import static com.android.input.flags.Flags.enableInputFilterRustImpl; 27 28 import android.Manifest; 29 import android.annotation.FlaggedApi; 30 import android.annotation.FloatRange; 31 import android.annotation.NonNull; 32 import android.annotation.RequiresPermission; 33 import android.annotation.SuppressLint; 34 import android.annotation.TestApi; 35 import android.app.AppGlobals; 36 import android.content.Context; 37 import android.os.UserHandle; 38 import android.provider.Settings; 39 import android.sysprop.InputProperties; 40 41 /** 42 * InputSettings encapsulates reading and writing settings related to input 43 * 44 * @hide 45 */ 46 @TestApi 47 public class InputSettings { 48 /** 49 * Pointer Speed: The minimum (slowest) pointer speed (-7). 50 * @hide 51 */ 52 public static final int MIN_POINTER_SPEED = -7; 53 54 /** 55 * Pointer Speed: The maximum (fastest) pointer speed (7). 56 * @hide 57 */ 58 public static final int MAX_POINTER_SPEED = 7; 59 60 /** 61 * Pointer Speed: The default pointer speed (0). 62 */ 63 @SuppressLint("UnflaggedApi") // TestApi without associated feature. 64 public static final int DEFAULT_POINTER_SPEED = 0; 65 66 /** 67 * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1). 68 * @hide 69 */ 70 public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f; 71 72 /** 73 * The maximum allowed Accessibility bounce keys threshold. 74 * @hide 75 */ 76 public static final int MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS = 5000; 77 78 /** 79 * The maximum allowed Accessibility slow keys threshold. 80 * @hide 81 */ 82 public static final int MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS = 5000; 83 84 /** 85 * Default value for {@link Settings.Secure#STYLUS_POINTER_ICON_ENABLED}. 86 * @hide 87 */ 88 public static final int DEFAULT_STYLUS_POINTER_ICON_ENABLED = 1; 89 InputSettings()90 private InputSettings() { 91 } 92 93 /** 94 * Gets the mouse pointer speed. 95 * <p> 96 * Only returns the permanent mouse pointer speed. Ignores any temporary pointer 97 * speed set by {@link InputManager#tryPointerSpeed}. 98 * </p> 99 * 100 * @param context The application context. 101 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 102 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 103 * 104 * @hide 105 */ 106 @SuppressLint("NonUserGetterCalled") getPointerSpeed(Context context)107 public static int getPointerSpeed(Context context) { 108 return Settings.System.getInt(context.getContentResolver(), 109 Settings.System.POINTER_SPEED, DEFAULT_POINTER_SPEED); 110 } 111 112 /** 113 * Sets the mouse pointer speed. 114 * <p> 115 * Requires {@link android.Manifest.permission#WRITE_SETTINGS}. 116 * </p> 117 * 118 * @param context The application context. 119 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 120 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 121 * 122 * @hide 123 */ 124 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setPointerSpeed(Context context, int speed)125 public static void setPointerSpeed(Context context, int speed) { 126 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 127 throw new IllegalArgumentException("speed out of range"); 128 } 129 130 Settings.System.putInt(context.getContentResolver(), 131 Settings.System.POINTER_SPEED, speed); 132 } 133 134 /** 135 * Returns the maximum allowed obscuring opacity per UID to propagate touches. 136 * 137 * <p>For certain window types (e.g. {@link LayoutParams#TYPE_APPLICATION_OVERLAY}), 138 * the decision of honoring {@link LayoutParams#FLAG_NOT_TOUCHABLE} or not depends on 139 * the combined obscuring opacity of the windows above the touch-consuming window, per 140 * UID. Check documentation of {@link LayoutParams#FLAG_NOT_TOUCHABLE} for more details. 141 * 142 * <p>The value returned is between 0 (inclusive) and 1 (inclusive). 143 * 144 * @see LayoutParams#FLAG_NOT_TOUCHABLE 145 * 146 * @hide 147 */ 148 @FloatRange(from = 0, to = 1) getMaximumObscuringOpacityForTouch(Context context)149 public static float getMaximumObscuringOpacityForTouch(Context context) { 150 return Settings.Global.getFloat(context.getContentResolver(), 151 Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, 152 DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH); 153 } 154 155 /** 156 * Sets the maximum allowed obscuring opacity by UID to propagate touches. 157 * 158 * <p>For certain window types (e.g. SAWs), the decision of honoring {@link LayoutParams 159 * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows 160 * above the touch-consuming window. 161 * 162 * <p>For a certain UID: 163 * <ul> 164 * <li>If it's the same as the UID of the touch-consuming window, allow it to propagate 165 * the touch. 166 * <li>Otherwise take all its windows of eligible window types above the touch-consuming 167 * window, compute their combined obscuring opacity considering that {@code 168 * opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is 169 * less than or equal to this setting and there are no other windows preventing the 170 * touch, allow the UID to propagate the touch. 171 * </ul> 172 * 173 * <p>This value should be between 0 (inclusive) and 1 (inclusive). 174 * 175 * @see #getMaximumObscuringOpacityForTouch(Context) 176 * 177 * @hide 178 */ 179 @TestApi 180 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) setMaximumObscuringOpacityForTouch( @onNull Context context, @FloatRange(from = 0, to = 1) float opacity)181 public static void setMaximumObscuringOpacityForTouch( 182 @NonNull Context context, 183 @FloatRange(from = 0, to = 1) float opacity) { 184 if (opacity < 0 || opacity > 1) { 185 throw new IllegalArgumentException( 186 "Maximum obscuring opacity for touch should be >= 0 and <= 1"); 187 } 188 Settings.Global.putFloat(context.getContentResolver(), 189 Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity); 190 } 191 192 /** 193 * Whether stylus has ever been used on device (false by default). 194 * @hide 195 */ isStylusEverUsed(@onNull Context context)196 public static boolean isStylusEverUsed(@NonNull Context context) { 197 return Settings.Global.getInt(context.getContentResolver(), 198 Settings.Global.STYLUS_EVER_USED, 0) == 1; 199 } 200 201 /** 202 * Set whether stylus has ever been used on device. 203 * Should only ever be set to true once after stylus first usage. 204 * @hide 205 */ 206 @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) setStylusEverUsed(@onNull Context context, boolean stylusEverUsed)207 public static void setStylusEverUsed(@NonNull Context context, boolean stylusEverUsed) { 208 Settings.Global.putInt(context.getContentResolver(), 209 Settings.Global.STYLUS_EVER_USED, stylusEverUsed ? 1 : 0); 210 } 211 212 213 /** 214 * Gets the touchpad pointer speed. 215 * 216 * The returned value only applies to gesture-compatible touchpads. 217 * 218 * @param context The application context. 219 * @return The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 220 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 221 * 222 * @hide 223 */ getTouchpadPointerSpeed(@onNull Context context)224 public static int getTouchpadPointerSpeed(@NonNull Context context) { 225 return Settings.System.getIntForUser(context.getContentResolver(), 226 Settings.System.TOUCHPAD_POINTER_SPEED, DEFAULT_POINTER_SPEED, 227 UserHandle.USER_CURRENT); 228 } 229 230 /** 231 * Sets the touchpad pointer speed, and saves it in the settings. 232 * 233 * The new speed will only apply to gesture-compatible touchpads. 234 * 235 * @param context The application context. 236 * @param speed The pointer speed as a value between {@link #MIN_POINTER_SPEED} and 237 * {@link #MAX_POINTER_SPEED}, or the default value {@link #DEFAULT_POINTER_SPEED}. 238 * 239 * @hide 240 */ 241 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setTouchpadPointerSpeed(@onNull Context context, int speed)242 public static void setTouchpadPointerSpeed(@NonNull Context context, int speed) { 243 if (speed < MIN_POINTER_SPEED || speed > MAX_POINTER_SPEED) { 244 throw new IllegalArgumentException("speed out of range"); 245 } 246 247 Settings.System.putIntForUser(context.getContentResolver(), 248 Settings.System.TOUCHPAD_POINTER_SPEED, speed, UserHandle.USER_CURRENT); 249 } 250 251 /** 252 * Returns true if moving two fingers upwards on the touchpad should 253 * scroll down, which is known as natural scrolling. 254 * 255 * The returned value only applies to gesture-compatible touchpads. 256 * 257 * @param context The application context. 258 * @return Whether the touchpad should use natural scrolling. 259 * 260 * @hide 261 */ useTouchpadNaturalScrolling(@onNull Context context)262 public static boolean useTouchpadNaturalScrolling(@NonNull Context context) { 263 return Settings.System.getIntForUser(context.getContentResolver(), 264 Settings.System.TOUCHPAD_NATURAL_SCROLLING, 1, UserHandle.USER_CURRENT) == 1; 265 } 266 267 /** 268 * Sets the natural scroll behavior for the touchpad. 269 * 270 * If natural scrolling is enabled, moving two fingers upwards on the 271 * touchpad will scroll down. 272 * 273 * @param context The application context. 274 * @param enabled Will enable natural scroll if true, disable it if false 275 * 276 * @hide 277 */ 278 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setTouchpadNaturalScrolling(@onNull Context context, boolean enabled)279 public static void setTouchpadNaturalScrolling(@NonNull Context context, boolean enabled) { 280 Settings.System.putIntForUser(context.getContentResolver(), 281 Settings.System.TOUCHPAD_NATURAL_SCROLLING, enabled ? 1 : 0, 282 UserHandle.USER_CURRENT); 283 } 284 285 /** 286 * Returns true if the touchpad should use tap to click. 287 * 288 * The returned value only applies to gesture-compatible touchpads. 289 * 290 * @param context The application context. 291 * @return Whether the touchpad should use tap to click. 292 * 293 * @hide 294 */ useTouchpadTapToClick(@onNull Context context)295 public static boolean useTouchpadTapToClick(@NonNull Context context) { 296 return Settings.System.getIntForUser(context.getContentResolver(), 297 Settings.System.TOUCHPAD_TAP_TO_CLICK, 1, UserHandle.USER_CURRENT) == 1; 298 } 299 300 /** 301 * Sets the tap to click behavior for the touchpad. 302 * 303 * The new behavior is only applied to gesture-compatible touchpads. 304 * 305 * @param context The application context. 306 * @param enabled Will enable tap to click if true, disable it if false 307 * 308 * @hide 309 */ 310 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setTouchpadTapToClick(@onNull Context context, boolean enabled)311 public static void setTouchpadTapToClick(@NonNull Context context, boolean enabled) { 312 Settings.System.putIntForUser(context.getContentResolver(), 313 Settings.System.TOUCHPAD_TAP_TO_CLICK, enabled ? 1 : 0, 314 UserHandle.USER_CURRENT); 315 } 316 317 /** 318 * Returns true if the feature flag for touchpad tap dragging is enabled. 319 * 320 * @hide 321 */ isTouchpadTapDraggingFeatureFlagEnabled()322 public static boolean isTouchpadTapDraggingFeatureFlagEnabled() { 323 return touchpadTapDragging(); 324 } 325 326 /** 327 * Returns true if the touchpad should allow tap dragging. 328 * 329 * The returned value only applies to gesture-compatible touchpads. 330 * 331 * @param context The application context. 332 * @return Whether the touchpad should allow tap dragging. 333 * 334 * @hide 335 */ useTouchpadTapDragging(@onNull Context context)336 public static boolean useTouchpadTapDragging(@NonNull Context context) { 337 if (!isTouchpadTapDraggingFeatureFlagEnabled()) { 338 return false; 339 } 340 return Settings.System.getIntForUser(context.getContentResolver(), 341 Settings.System.TOUCHPAD_TAP_DRAGGING, 0, UserHandle.USER_CURRENT) == 1; 342 } 343 344 /** 345 * Sets the tap dragging behavior for the touchpad. 346 * 347 * The new behavior is only applied to gesture-compatible touchpads. 348 * 349 * @param context The application context. 350 * @param enabled Will enable tap dragging if true, disable it if false 351 * 352 * @hide 353 */ 354 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setTouchpadTapDragging(@onNull Context context, boolean enabled)355 public static void setTouchpadTapDragging(@NonNull Context context, boolean enabled) { 356 if (!isTouchpadTapDraggingFeatureFlagEnabled()) { 357 return; 358 } 359 Settings.System.putIntForUser(context.getContentResolver(), 360 Settings.System.TOUCHPAD_TAP_DRAGGING, enabled ? 1 : 0, 361 UserHandle.USER_CURRENT); 362 } 363 364 /** 365 * Returns true if the touchpad should use the right click zone. 366 * 367 * The returned value only applies to gesture-compatible touchpads. 368 * 369 * @param context The application context. 370 * @return Whether the touchpad should use the right click zone. 371 * 372 * @hide 373 */ useTouchpadRightClickZone(@onNull Context context)374 public static boolean useTouchpadRightClickZone(@NonNull Context context) { 375 return Settings.System.getIntForUser(context.getContentResolver(), 376 Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, 0, UserHandle.USER_CURRENT) == 1; 377 } 378 379 /** 380 * Sets the right click zone behavior for the touchpad. 381 * 382 * The new behavior is only applied to gesture-compatible touchpads. 383 * 384 * @param context The application context. 385 * @param enabled Will enable the right click zone if true, disable it if false 386 * 387 * @hide 388 */ 389 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setTouchpadRightClickZone(@onNull Context context, boolean enabled)390 public static void setTouchpadRightClickZone(@NonNull Context context, boolean enabled) { 391 Settings.System.putIntForUser(context.getContentResolver(), 392 Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, enabled ? 1 : 0, 393 UserHandle.USER_CURRENT); 394 } 395 396 /** 397 * Whether a pointer icon will be shown over the location of a stylus pointer. 398 * 399 * @hide 400 */ isStylusPointerIconEnabled(@onNull Context context, boolean forceReloadSetting)401 public static boolean isStylusPointerIconEnabled(@NonNull Context context, 402 boolean forceReloadSetting) { 403 if (InputProperties.force_enable_stylus_pointer_icon().orElse(false)) { 404 // Sysprop override is set 405 return true; 406 } 407 if (!context.getResources().getBoolean( 408 com.android.internal.R.bool.config_enableStylusPointerIcon)) { 409 // Stylus pointer icons are disabled for the build 410 return false; 411 } 412 if (forceReloadSetting) { 413 return Settings.Secure.getIntForUser(context.getContentResolver(), 414 Settings.Secure.STYLUS_POINTER_ICON_ENABLED, 415 DEFAULT_STYLUS_POINTER_ICON_ENABLED, UserHandle.USER_CURRENT_OR_SELF) != 0; 416 } 417 return AppGlobals.getIntCoreSetting(Settings.Secure.STYLUS_POINTER_ICON_ENABLED, 418 DEFAULT_STYLUS_POINTER_ICON_ENABLED) != 0; 419 } 420 421 /** 422 * Whether a pointer icon will be shown over the location of a stylus pointer. 423 * 424 * @hide 425 * @see #isStylusPointerIconEnabled(Context, boolean) 426 */ isStylusPointerIconEnabled(@onNull Context context)427 public static boolean isStylusPointerIconEnabled(@NonNull Context context) { 428 return isStylusPointerIconEnabled(context, false /* forceReloadSetting */); 429 } 430 431 /** 432 * Whether Accessibility bounce keys feature is enabled. 433 * 434 * <p> 435 * Bounce keys’ is an accessibility feature to aid users who have physical disabilities, 436 * that allows the user to configure the device to ignore rapid, repeated keypresses of the 437 * same key. 438 * </p> 439 * 440 * @hide 441 */ isAccessibilityBounceKeysFeatureEnabled()442 public static boolean isAccessibilityBounceKeysFeatureEnabled() { 443 return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl(); 444 } 445 446 /** 447 * Whether Accessibility bounce keys is enabled. 448 * 449 * <p> 450 * ‘Bounce keys’ is an accessibility feature to aid users who have physical disabilities, 451 * that allows the user to configure the device to ignore rapid, repeated keypresses of the 452 * same key. 453 * </p> 454 * 455 * @hide 456 */ isAccessibilityBounceKeysEnabled(@onNull Context context)457 public static boolean isAccessibilityBounceKeysEnabled(@NonNull Context context) { 458 return getAccessibilityBounceKeysThreshold(context) != 0; 459 } 460 461 /** 462 * Get Accessibility bounce keys threshold duration in milliseconds. 463 * 464 * <p> 465 * ‘Bounce keys’ is an accessibility feature to aid users who have physical disabilities, 466 * that allows the user to configure the device to ignore rapid, repeated keypresses of the 467 * same key. 468 * </p> 469 * 470 * @hide 471 */ 472 @TestApi 473 @FlaggedApi(FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG) getAccessibilityBounceKeysThreshold(@onNull Context context)474 public static int getAccessibilityBounceKeysThreshold(@NonNull Context context) { 475 if (!isAccessibilityBounceKeysFeatureEnabled()) { 476 return 0; 477 } 478 return Settings.Secure.getIntForUser(context.getContentResolver(), 479 Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, 0, UserHandle.USER_CURRENT); 480 } 481 482 /** 483 * Set Accessibility bounce keys threshold duration in milliseconds. 484 * @param thresholdTimeMillis time duration for which a key down will be ignored after a 485 * previous key up for the same key on the same device between 0 and 486 * {@link MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS} 487 * 488 * <p> 489 * ‘Bounce keys’ is an accessibility feature to aid users who have physical disabilities, 490 * that allows the user to configure the device to ignore rapid, repeated keypresses of the 491 * same key. 492 * </p> 493 * 494 * @hide 495 */ 496 @TestApi 497 @FlaggedApi(FLAG_KEYBOARD_A11Y_BOUNCE_KEYS_FLAG) 498 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setAccessibilityBounceKeysThreshold(@onNull Context context, int thresholdTimeMillis)499 public static void setAccessibilityBounceKeysThreshold(@NonNull Context context, 500 int thresholdTimeMillis) { 501 if (!isAccessibilityBounceKeysFeatureEnabled()) { 502 return; 503 } 504 if (thresholdTimeMillis < 0 505 || thresholdTimeMillis > MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS) { 506 throw new IllegalArgumentException( 507 "Provided Bounce keys threshold should be in range [0, " 508 + MAX_ACCESSIBILITY_BOUNCE_KEYS_THRESHOLD_MILLIS + "]"); 509 } 510 Settings.Secure.putIntForUser(context.getContentResolver(), 511 Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS, thresholdTimeMillis, 512 UserHandle.USER_CURRENT); 513 } 514 515 /** 516 * Whether Accessibility slow keys feature flags is enabled. 517 * 518 * <p> 519 * 'Slow keys' is an accessibility feature to aid users who have physical disabilities, that 520 * allows the user to specify the duration for which one must press-and-hold a key before the 521 * system accepts the keypress. 522 * </p> 523 * 524 * @hide 525 */ isAccessibilitySlowKeysFeatureFlagEnabled()526 public static boolean isAccessibilitySlowKeysFeatureFlagEnabled() { 527 return keyboardA11ySlowKeysFlag() && enableInputFilterRustImpl(); 528 } 529 530 /** 531 * Whether Accessibility slow keys is enabled. 532 * 533 * <p> 534 * 'Slow keys' is an accessibility feature to aid users who have physical disabilities, that 535 * allows the user to specify the duration for which one must press-and-hold a key before the 536 * system accepts the keypress. 537 * </p> 538 * 539 * @hide 540 */ isAccessibilitySlowKeysEnabled(@onNull Context context)541 public static boolean isAccessibilitySlowKeysEnabled(@NonNull Context context) { 542 return getAccessibilitySlowKeysThreshold(context) != 0; 543 } 544 545 /** 546 * Get Accessibility slow keys threshold duration in milliseconds. 547 * 548 * <p> 549 * 'Slow keys' is an accessibility feature to aid users who have physical disabilities, that 550 * allows the user to specify the duration for which one must press-and-hold a key before the 551 * system accepts the keypress. 552 * </p> 553 * 554 * @hide 555 */ 556 @TestApi 557 @FlaggedApi(FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG) getAccessibilitySlowKeysThreshold(@onNull Context context)558 public static int getAccessibilitySlowKeysThreshold(@NonNull Context context) { 559 if (!isAccessibilitySlowKeysFeatureFlagEnabled()) { 560 return 0; 561 } 562 return Settings.Secure.getIntForUser(context.getContentResolver(), 563 Settings.Secure.ACCESSIBILITY_SLOW_KEYS, 0, UserHandle.USER_CURRENT); 564 } 565 566 /** 567 * Set Accessibility slow keys threshold duration in milliseconds. 568 * @param thresholdTimeMillis time duration for which a key should be pressed to be registered 569 * in the system. The threshold must be between 0 and 570 * {@link MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS} 571 * 572 * <p> 573 * 'Slow keys' is an accessibility feature to aid users who have physical disabilities, that 574 * allows the user to specify the duration for which one must press-and-hold a key before the 575 * system accepts the keypress. 576 * </p> 577 * 578 * @hide 579 */ 580 @TestApi 581 @FlaggedApi(FLAG_KEYBOARD_A11Y_SLOW_KEYS_FLAG) 582 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setAccessibilitySlowKeysThreshold(@onNull Context context, int thresholdTimeMillis)583 public static void setAccessibilitySlowKeysThreshold(@NonNull Context context, 584 int thresholdTimeMillis) { 585 if (!isAccessibilitySlowKeysFeatureFlagEnabled()) { 586 return; 587 } 588 if (thresholdTimeMillis < 0 589 || thresholdTimeMillis > MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS) { 590 throw new IllegalArgumentException( 591 "Provided Slow keys threshold should be in range [0, " 592 + MAX_ACCESSIBILITY_SLOW_KEYS_THRESHOLD_MILLIS + "]"); 593 } 594 Settings.Secure.putIntForUser(context.getContentResolver(), 595 Settings.Secure.ACCESSIBILITY_SLOW_KEYS, thresholdTimeMillis, 596 UserHandle.USER_CURRENT); 597 } 598 599 /** 600 * Whether Accessibility sticky keys feature is enabled. 601 * 602 * <p> 603 * 'Sticky keys' is an accessibility feature that assists users who have physical 604 * disabilities or help users reduce repetitive strain injury. It serializes keystrokes 605 * instead of pressing multiple keys at a time, allowing the user to press and release a 606 * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain 607 * active until any other key is pressed. 608 * </p> 609 * 610 * @hide 611 */ isAccessibilityStickyKeysFeatureEnabled()612 public static boolean isAccessibilityStickyKeysFeatureEnabled() { 613 return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl(); 614 } 615 616 /** 617 * Whether Accessibility sticky keys is enabled. 618 * 619 * <p> 620 * 'Sticky keys' is an accessibility feature that assists users who have physical 621 * disabilities or help users reduce repetitive strain injury. It serializes keystrokes 622 * instead of pressing multiple keys at a time, allowing the user to press and release a 623 * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain 624 * active until any other key is pressed. 625 * </p> 626 * 627 * @hide 628 */ 629 @TestApi 630 @FlaggedApi(FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG) isAccessibilityStickyKeysEnabled(@onNull Context context)631 public static boolean isAccessibilityStickyKeysEnabled(@NonNull Context context) { 632 if (!isAccessibilityStickyKeysFeatureEnabled()) { 633 return false; 634 } 635 return Settings.Secure.getIntForUser(context.getContentResolver(), 636 Settings.Secure.ACCESSIBILITY_STICKY_KEYS, 0, UserHandle.USER_CURRENT) != 0; 637 } 638 639 /** 640 * Set Accessibility sticky keys feature enabled/disabled. 641 * 642 * <p> 643 * 'Sticky keys' is an accessibility feature that assists users who have physical 644 * disabilities or help users reduce repetitive strain injury. It serializes keystrokes 645 * instead of pressing multiple keys at a time, allowing the user to press and release a 646 * modifier key, such as Shift, Ctrl, Alt, or any other modifier key, and have it remain 647 * active until any other key is pressed. 648 * </p> 649 * 650 * @hide 651 */ 652 @TestApi 653 @FlaggedApi(FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG) 654 @RequiresPermission(Manifest.permission.WRITE_SETTINGS) setAccessibilityStickyKeysEnabled(@onNull Context context, boolean enabled)655 public static void setAccessibilityStickyKeysEnabled(@NonNull Context context, 656 boolean enabled) { 657 if (!isAccessibilityStickyKeysFeatureEnabled()) { 658 return; 659 } 660 Settings.Secure.putIntForUser(context.getContentResolver(), 661 Settings.Secure.ACCESSIBILITY_STICKY_KEYS, enabled ? 1 : 0, 662 UserHandle.USER_CURRENT); 663 } 664 665 } 666