Loading core/java/android/hardware/input/IInputManager.aidl +11 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.hardware.input.IInputDeviceBatteryListener; import android.hardware.input.IInputDeviceBatteryState; import android.hardware.input.IKeyboardBacklightListener; import android.hardware.input.IKeyboardBacklightState; import android.hardware.input.IStickyModifierStateListener; import android.hardware.input.ITabletModeChangedListener; import android.hardware.input.TouchCalibration; import android.os.CombinedVibration; Loading Loading @@ -241,4 +242,14 @@ interface IInputManager { void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener); HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId); @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)") void registerStickyModifierStateListener(IStickyModifierStateListener listener); @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)") void unregisterStickyModifierStateListener(IStickyModifierStateListener listener); } core/java/android/hardware/input/IStickyModifierStateListener.aidl 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * 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. */ package android.hardware.input; /** @hide */ oneway interface IStickyModifierStateListener { /** * Called when the sticky modifier state is changed when A11y Sticky keys feature is enabled */ void onStickyModifierStateChanged(int modifierState, int lockedModifierState); } core/java/android/hardware/input/InputManager.java +55 −0 Original line number Diff line number Diff line Loading @@ -1296,6 +1296,42 @@ public final class InputManager { mGlobal.unregisterKeyboardBacklightListener(listener); } /** * Registers a Sticky modifier state change listener to be notified about {@link * StickyModifierState} changes. * * @param executor an executor on which the callback will be called * @param listener the {@link StickyModifierStateListener} * @throws IllegalArgumentException if {@code listener} has already been registered previously. * @throws NullPointerException if {@code listener} or {@code executor} is null. * @hide * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void registerStickyModifierStateListener(@NonNull Executor executor, @NonNull StickyModifierStateListener listener) throws IllegalArgumentException { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.registerStickyModifierStateListener(executor, listener); } /** * Unregisters a previously added Sticky modifier state change listener. * * @param listener the {@link StickyModifierStateListener} * @hide * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void unregisterStickyModifierStateListener( @NonNull StickyModifierStateListener listener) { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.unregisterStickyModifierStateListener(listener); } /** * A callback used to be notified about battery state changes for an input device. The * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the Loading Loading @@ -1378,4 +1414,23 @@ public final class InputManager { void onKeyboardBacklightChanged( int deviceId, @NonNull KeyboardBacklightState state, boolean isTriggeredByKeyPress); } /** * A callback used to be notified about sticky modifier state changes when A11y Sticky keys * feature is enabled. * * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) * @hide */ public interface StickyModifierStateListener { /** * Called when the sticky modifier state changes. * This method will be called once after the listener is successfully registered to provide * the initial modifier state. * * @param state the new sticky modifier state, never null. */ void onStickyModifierStateChanged(@NonNull StickyModifierState state); } } core/java/android/hardware/input/InputManagerGlobal.java +162 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.hardware.input.InputManager.InputDeviceBatteryListener; import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.input.InputManager.KeyboardBacklightListener; import android.hardware.input.InputManager.OnTabletModeChangedListener; import android.hardware.input.InputManager.StickyModifierStateListener; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; Loading @@ -52,6 +53,7 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -100,6 +102,14 @@ public final class InputManagerGlobal { @GuardedBy("mKeyboardBacklightListenerLock") @Nullable private IKeyboardBacklightListener mKeyboardBacklightListener; private final Object mStickyModifierStateListenerLock = new Object(); @GuardedBy("mStickyModifierStateListenerLock") @Nullable private ArrayList<StickyModifierStateListenerDelegate> mStickyModifierStateListeners; @GuardedBy("mStickyModifierStateListenerLock") @Nullable private IStickyModifierStateListener mStickyModifierStateListener; // InputDeviceSensorManager gets notified synchronously from the binder thread when input // devices change, so it must be synchronized with the input device listeners. @GuardedBy("mInputDeviceListeners") Loading Loading @@ -905,6 +915,158 @@ public final class InputManagerGlobal { } } private static final class StickyModifierStateListenerDelegate { final InputManager.StickyModifierStateListener mListener; final Executor mExecutor; StickyModifierStateListenerDelegate(StickyModifierStateListener listener, Executor executor) { mListener = listener; mExecutor = executor; } void notifyStickyModifierStateChange(int modifierState, int lockedModifierState) { mExecutor.execute(() -> mListener.onStickyModifierStateChanged( new LocalStickyModifierState(modifierState, lockedModifierState))); } } private class LocalStickyModifierStateListener extends IStickyModifierStateListener.Stub { @Override public void onStickyModifierStateChanged(int modifierState, int lockedModifierState) { synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListeners == null) return; final int numListeners = mStickyModifierStateListeners.size(); for (int i = 0; i < numListeners; i++) { mStickyModifierStateListeners.get(i) .notifyStickyModifierStateChange(modifierState, lockedModifierState); } } } } // Implementation of the android.hardware.input.StickyModifierState interface used to report // the sticky modifier state via the StickyModifierStateListener interfaces. private static final class LocalStickyModifierState extends StickyModifierState { private final int mModifierState; private final int mLockedModifierState; LocalStickyModifierState(int modifierState, int lockedModifierState) { mModifierState = modifierState; mLockedModifierState = lockedModifierState; } @Override public boolean isShiftModifierOn() { return (mModifierState & KeyEvent.META_SHIFT_ON) != 0; } @Override public boolean isShiftModifierLocked() { return (mLockedModifierState & KeyEvent.META_SHIFT_ON) != 0; } @Override public boolean isCtrlModifierOn() { return (mModifierState & KeyEvent.META_CTRL_ON) != 0; } @Override public boolean isCtrlModifierLocked() { return (mLockedModifierState & KeyEvent.META_CTRL_ON) != 0; } @Override public boolean isMetaModifierOn() { return (mModifierState & KeyEvent.META_META_ON) != 0; } @Override public boolean isMetaModifierLocked() { return (mLockedModifierState & KeyEvent.META_META_ON) != 0; } @Override public boolean isAltModifierOn() { return (mModifierState & KeyEvent.META_ALT_LEFT_ON) != 0; } @Override public boolean isAltModifierLocked() { return (mLockedModifierState & KeyEvent.META_ALT_LEFT_ON) != 0; } @Override public boolean isAltGrModifierOn() { return (mModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0; } @Override public boolean isAltGrModifierLocked() { return (mLockedModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0; } } /** * @see InputManager#registerStickyModifierStateListener(Executor, StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) void registerStickyModifierStateListener(@NonNull Executor executor, @NonNull StickyModifierStateListener listener) throws IllegalArgumentException { Objects.requireNonNull(executor, "executor should not be null"); Objects.requireNonNull(listener, "listener should not be null"); synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListener == null) { mStickyModifierStateListeners = new ArrayList<>(); mStickyModifierStateListener = new LocalStickyModifierStateListener(); try { mIm.registerStickyModifierStateListener(mStickyModifierStateListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final int numListeners = mStickyModifierStateListeners.size(); for (int i = 0; i < numListeners; i++) { if (mStickyModifierStateListeners.get(i).mListener == listener) { throw new IllegalArgumentException("Listener has already been registered!"); } } StickyModifierStateListenerDelegate delegate = new StickyModifierStateListenerDelegate(listener, executor); mStickyModifierStateListeners.add(delegate); } } /** * @see InputManager#unregisterStickyModifierStateListener(StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) void unregisterStickyModifierStateListener( @NonNull StickyModifierStateListener listener) { Objects.requireNonNull(listener, "listener should not be null"); synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListeners == null) { return; } mStickyModifierStateListeners.removeIf((delegate) -> delegate.mListener == listener); if (mStickyModifierStateListeners.isEmpty()) { try { mIm.unregisterStickyModifierStateListener(mStickyModifierStateListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mStickyModifierStateListeners = null; mStickyModifierStateListener = null; } } } /** * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier) */ Loading core/java/android/hardware/input/StickyModifierState.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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. */ package android.hardware.input; /** * The StickyModifierState class is a representation of a modifier state when A11y Sticky keys * feature is enabled * * @hide */ public abstract class StickyModifierState { /** * Represents whether current sticky modifier state includes 'Shift' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Shift' modifier in * its metaState. * * @return whether Shift modifier key is on. */ public abstract boolean isShiftModifierOn(); /** * Represents whether current sticky modifier state includes 'Shift' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Shift' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Shift' key again to clear the locked state. * * @return whether Shift modifier key is locked. */ public abstract boolean isShiftModifierLocked(); /** * Represents whether current sticky modifier state includes 'Ctrl' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Ctrl' modifier in * its metaState. * * @return whether Ctrl modifier key is on. */ public abstract boolean isCtrlModifierOn(); /** * Represents whether current sticky modifier state includes 'Ctrl' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Ctrl' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Ctrl' key again to clear the locked state. * * @return whether Ctrl modifier key is locked. */ public abstract boolean isCtrlModifierLocked(); /** * Represents whether current sticky modifier state includes 'Meta' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Meta' modifier in * its metaState. * * @return whether Meta modifier key is on. */ public abstract boolean isMetaModifierOn(); /** * Represents whether current sticky modifier state includes 'Meta' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Meta' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Meta' key again to clear the locked state. * * @return whether Meta modifier key is locked. */ public abstract boolean isMetaModifierLocked(); /** * Represents whether current sticky modifier state includes 'Alt' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Alt' modifier in * its metaState. * * @return whether Alt modifier key is on. */ public abstract boolean isAltModifierOn(); /** * Represents whether current sticky modifier state includes 'Alt' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Alt' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Alt' key again to clear the locked state. * * @return whether Alt modifier key is locked. */ public abstract boolean isAltModifierLocked(); /** * Represents whether current sticky modifier state includes 'AltGr' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'AltGr' modifier in * its metaState. * * @return whether AltGr modifier key is on. */ public abstract boolean isAltGrModifierOn(); /** * Represents whether current sticky modifier state includes 'AltGr' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'AltGr' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'AltGr' key again to clear the locked state. * * @return whether AltGr modifier key is locked. */ public abstract boolean isAltGrModifierLocked(); } Loading
core/java/android/hardware/input/IInputManager.aidl +11 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.hardware.input.IInputDeviceBatteryListener; import android.hardware.input.IInputDeviceBatteryState; import android.hardware.input.IKeyboardBacklightListener; import android.hardware.input.IKeyboardBacklightState; import android.hardware.input.IStickyModifierStateListener; import android.hardware.input.ITabletModeChangedListener; import android.hardware.input.TouchCalibration; import android.os.CombinedVibration; Loading Loading @@ -241,4 +242,14 @@ interface IInputManager { void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener); HostUsiVersion getHostUsiVersionFromDisplayConfig(int displayId); @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)") void registerStickyModifierStateListener(IStickyModifierStateListener listener); @EnforcePermission("MONITOR_STICKY_MODIFIER_STATE") @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)") void unregisterStickyModifierStateListener(IStickyModifierStateListener listener); }
core/java/android/hardware/input/IStickyModifierStateListener.aidl 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * 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. */ package android.hardware.input; /** @hide */ oneway interface IStickyModifierStateListener { /** * Called when the sticky modifier state is changed when A11y Sticky keys feature is enabled */ void onStickyModifierStateChanged(int modifierState, int lockedModifierState); }
core/java/android/hardware/input/InputManager.java +55 −0 Original line number Diff line number Diff line Loading @@ -1296,6 +1296,42 @@ public final class InputManager { mGlobal.unregisterKeyboardBacklightListener(listener); } /** * Registers a Sticky modifier state change listener to be notified about {@link * StickyModifierState} changes. * * @param executor an executor on which the callback will be called * @param listener the {@link StickyModifierStateListener} * @throws IllegalArgumentException if {@code listener} has already been registered previously. * @throws NullPointerException if {@code listener} or {@code executor} is null. * @hide * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void registerStickyModifierStateListener(@NonNull Executor executor, @NonNull StickyModifierStateListener listener) throws IllegalArgumentException { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.registerStickyModifierStateListener(executor, listener); } /** * Unregisters a previously added Sticky modifier state change listener. * * @param listener the {@link StickyModifierStateListener} * @hide * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) public void unregisterStickyModifierStateListener( @NonNull StickyModifierStateListener listener) { if (!InputSettings.isAccessibilityStickyKeysFeatureEnabled()) { return; } mGlobal.unregisterStickyModifierStateListener(listener); } /** * A callback used to be notified about battery state changes for an input device. The * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the Loading Loading @@ -1378,4 +1414,23 @@ public final class InputManager { void onKeyboardBacklightChanged( int deviceId, @NonNull KeyboardBacklightState state, boolean isTriggeredByKeyPress); } /** * A callback used to be notified about sticky modifier state changes when A11y Sticky keys * feature is enabled. * * @see #registerStickyModifierStateListener(Executor, StickyModifierStateListener) * @see #unregisterStickyModifierStateListener(StickyModifierStateListener) * @hide */ public interface StickyModifierStateListener { /** * Called when the sticky modifier state changes. * This method will be called once after the listener is successfully registered to provide * the initial modifier state. * * @param state the new sticky modifier state, never null. */ void onStickyModifierStateChanged(@NonNull StickyModifierState state); } }
core/java/android/hardware/input/InputManagerGlobal.java +162 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.hardware.input.InputManager.InputDeviceBatteryListener; import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.input.InputManager.KeyboardBacklightListener; import android.hardware.input.InputManager.OnTabletModeChangedListener; import android.hardware.input.InputManager.StickyModifierStateListener; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; Loading @@ -52,6 +53,7 @@ import android.view.InputDevice; import android.view.InputEvent; import android.view.InputMonitor; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -100,6 +102,14 @@ public final class InputManagerGlobal { @GuardedBy("mKeyboardBacklightListenerLock") @Nullable private IKeyboardBacklightListener mKeyboardBacklightListener; private final Object mStickyModifierStateListenerLock = new Object(); @GuardedBy("mStickyModifierStateListenerLock") @Nullable private ArrayList<StickyModifierStateListenerDelegate> mStickyModifierStateListeners; @GuardedBy("mStickyModifierStateListenerLock") @Nullable private IStickyModifierStateListener mStickyModifierStateListener; // InputDeviceSensorManager gets notified synchronously from the binder thread when input // devices change, so it must be synchronized with the input device listeners. @GuardedBy("mInputDeviceListeners") Loading Loading @@ -905,6 +915,158 @@ public final class InputManagerGlobal { } } private static final class StickyModifierStateListenerDelegate { final InputManager.StickyModifierStateListener mListener; final Executor mExecutor; StickyModifierStateListenerDelegate(StickyModifierStateListener listener, Executor executor) { mListener = listener; mExecutor = executor; } void notifyStickyModifierStateChange(int modifierState, int lockedModifierState) { mExecutor.execute(() -> mListener.onStickyModifierStateChanged( new LocalStickyModifierState(modifierState, lockedModifierState))); } } private class LocalStickyModifierStateListener extends IStickyModifierStateListener.Stub { @Override public void onStickyModifierStateChanged(int modifierState, int lockedModifierState) { synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListeners == null) return; final int numListeners = mStickyModifierStateListeners.size(); for (int i = 0; i < numListeners; i++) { mStickyModifierStateListeners.get(i) .notifyStickyModifierStateChange(modifierState, lockedModifierState); } } } } // Implementation of the android.hardware.input.StickyModifierState interface used to report // the sticky modifier state via the StickyModifierStateListener interfaces. private static final class LocalStickyModifierState extends StickyModifierState { private final int mModifierState; private final int mLockedModifierState; LocalStickyModifierState(int modifierState, int lockedModifierState) { mModifierState = modifierState; mLockedModifierState = lockedModifierState; } @Override public boolean isShiftModifierOn() { return (mModifierState & KeyEvent.META_SHIFT_ON) != 0; } @Override public boolean isShiftModifierLocked() { return (mLockedModifierState & KeyEvent.META_SHIFT_ON) != 0; } @Override public boolean isCtrlModifierOn() { return (mModifierState & KeyEvent.META_CTRL_ON) != 0; } @Override public boolean isCtrlModifierLocked() { return (mLockedModifierState & KeyEvent.META_CTRL_ON) != 0; } @Override public boolean isMetaModifierOn() { return (mModifierState & KeyEvent.META_META_ON) != 0; } @Override public boolean isMetaModifierLocked() { return (mLockedModifierState & KeyEvent.META_META_ON) != 0; } @Override public boolean isAltModifierOn() { return (mModifierState & KeyEvent.META_ALT_LEFT_ON) != 0; } @Override public boolean isAltModifierLocked() { return (mLockedModifierState & KeyEvent.META_ALT_LEFT_ON) != 0; } @Override public boolean isAltGrModifierOn() { return (mModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0; } @Override public boolean isAltGrModifierLocked() { return (mLockedModifierState & KeyEvent.META_ALT_RIGHT_ON) != 0; } } /** * @see InputManager#registerStickyModifierStateListener(Executor, StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) void registerStickyModifierStateListener(@NonNull Executor executor, @NonNull StickyModifierStateListener listener) throws IllegalArgumentException { Objects.requireNonNull(executor, "executor should not be null"); Objects.requireNonNull(listener, "listener should not be null"); synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListener == null) { mStickyModifierStateListeners = new ArrayList<>(); mStickyModifierStateListener = new LocalStickyModifierStateListener(); try { mIm.registerStickyModifierStateListener(mStickyModifierStateListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } final int numListeners = mStickyModifierStateListeners.size(); for (int i = 0; i < numListeners; i++) { if (mStickyModifierStateListeners.get(i).mListener == listener) { throw new IllegalArgumentException("Listener has already been registered!"); } } StickyModifierStateListenerDelegate delegate = new StickyModifierStateListenerDelegate(listener, executor); mStickyModifierStateListeners.add(delegate); } } /** * @see InputManager#unregisterStickyModifierStateListener(StickyModifierStateListener) */ @RequiresPermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE) void unregisterStickyModifierStateListener( @NonNull StickyModifierStateListener listener) { Objects.requireNonNull(listener, "listener should not be null"); synchronized (mStickyModifierStateListenerLock) { if (mStickyModifierStateListeners == null) { return; } mStickyModifierStateListeners.removeIf((delegate) -> delegate.mListener == listener); if (mStickyModifierStateListeners.isEmpty()) { try { mIm.unregisterStickyModifierStateListener(mStickyModifierStateListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } mStickyModifierStateListeners = null; mStickyModifierStateListener = null; } } } /** * @see InputManager#getKeyboardLayoutsForInputDevice(InputDeviceIdentifier) */ Loading
core/java/android/hardware/input/StickyModifierState.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * 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. */ package android.hardware.input; /** * The StickyModifierState class is a representation of a modifier state when A11y Sticky keys * feature is enabled * * @hide */ public abstract class StickyModifierState { /** * Represents whether current sticky modifier state includes 'Shift' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Shift' modifier in * its metaState. * * @return whether Shift modifier key is on. */ public abstract boolean isShiftModifierOn(); /** * Represents whether current sticky modifier state includes 'Shift' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Shift' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Shift' key again to clear the locked state. * * @return whether Shift modifier key is locked. */ public abstract boolean isShiftModifierLocked(); /** * Represents whether current sticky modifier state includes 'Ctrl' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Ctrl' modifier in * its metaState. * * @return whether Ctrl modifier key is on. */ public abstract boolean isCtrlModifierOn(); /** * Represents whether current sticky modifier state includes 'Ctrl' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Ctrl' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Ctrl' key again to clear the locked state. * * @return whether Ctrl modifier key is locked. */ public abstract boolean isCtrlModifierLocked(); /** * Represents whether current sticky modifier state includes 'Meta' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Meta' modifier in * its metaState. * * @return whether Meta modifier key is on. */ public abstract boolean isMetaModifierOn(); /** * Represents whether current sticky modifier state includes 'Meta' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Meta' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Meta' key again to clear the locked state. * * @return whether Meta modifier key is locked. */ public abstract boolean isMetaModifierLocked(); /** * Represents whether current sticky modifier state includes 'Alt' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'Alt' modifier in * its metaState. * * @return whether Alt modifier key is on. */ public abstract boolean isAltModifierOn(); /** * Represents whether current sticky modifier state includes 'Alt' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'Alt' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'Alt' key again to clear the locked state. * * @return whether Alt modifier key is locked. */ public abstract boolean isAltModifierLocked(); /** * Represents whether current sticky modifier state includes 'AltGr' modifier. * <p> If {@code true} the next {@link android.view.KeyEvent} will contain 'AltGr' modifier in * its metaState. * * @return whether AltGr modifier key is on. */ public abstract boolean isAltGrModifierOn(); /** * Represents whether current sticky modifier state includes 'AltGr' modifier, and it is * locked. * <p> If {@code true} any subsequent {@link android.view.KeyEvent} will contain 'AltGr' * modifier in its metaState and this state will remain sticky (will not be cleared), until * user presses 'AltGr' key again to clear the locked state. * * @return whether AltGr modifier key is locked. */ public abstract boolean isAltGrModifierLocked(); }