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