1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION.SDK_INT; 4 import static android.os.Build.VERSION_CODES.R; 5 import static android.os.Build.VERSION_CODES.TIRAMISU; 6 import static java.util.concurrent.TimeUnit.MILLISECONDS; 7 import static org.robolectric.util.reflector.Reflector.reflector; 8 9 import android.hardware.input.InputManager; 10 import android.util.SparseArray; 11 import android.view.InputDevice; 12 import android.view.InputEvent; 13 import android.view.KeyEvent; 14 import android.view.MotionEvent; 15 import android.view.VerifiedKeyEvent; 16 import android.view.VerifiedMotionEvent; 17 import org.robolectric.annotation.ClassName; 18 import org.robolectric.annotation.Implementation; 19 import org.robolectric.annotation.Implements; 20 import org.robolectric.annotation.RealObject; 21 import org.robolectric.annotation.Resetter; 22 import org.robolectric.util.ReflectionHelpers; 23 import org.robolectric.util.reflector.Accessor; 24 import org.robolectric.util.reflector.ForType; 25 import org.robolectric.versioning.AndroidVersions.U; 26 27 /** Shadow for {@link InputManager} */ 28 @Implements(value = InputManager.class) 29 public class ShadowInputManager { 30 31 @RealObject InputManager realInputManager; 32 33 @Implementation injectInputEvent(InputEvent event, int mode)34 protected boolean injectInputEvent(InputEvent event, int mode) { 35 // ignore 36 return true; 37 } 38 39 @Implementation deviceHasKeys(int id, int[] keyCodes)40 protected boolean[] deviceHasKeys(int id, int[] keyCodes) { 41 return new boolean[keyCodes.length]; 42 } 43 44 /** Used in {@link InputDevice#getDeviceIds()} */ 45 @Implementation getInputDeviceIds()46 protected int[] getInputDeviceIds() { 47 if (!ReflectionHelpers.hasField(InputManager.class, "mInputDevices")) { 48 return new int[0]; 49 } 50 51 SparseArray<InputDevice> inputDevices = getInputDevices(); 52 if (inputDevices == null) { 53 return new int[0]; 54 } 55 56 int[] ids = new int[inputDevices.size()]; 57 for (int i = 0; i < inputDevices.size(); i++) { 58 ids[i] = inputDevices.get(i).getId(); 59 } 60 61 return ids; 62 } 63 64 @Implementation(maxSdk = TIRAMISU) populateInputDevicesLocked()65 protected void populateInputDevicesLocked() throws ClassNotFoundException { 66 if (ReflectionHelpers.getField(realInputManager, "mInputDevicesChangedListener") == null) { 67 ReflectionHelpers.setField( 68 realInputManager, 69 "mInputDevicesChangedListener", 70 ReflectionHelpers.callConstructor( 71 Class.forName("android.hardware.input.InputManager$InputDevicesChangedListener"))); 72 } 73 74 if (getInputDevices() == null) { 75 final int[] ids = realInputManager.getInputDeviceIds(); 76 77 SparseArray<InputDevice> inputDevices = new SparseArray<>(); 78 for (int i = 0; i < ids.length; i++) { 79 inputDevices.put(ids[i], null); 80 } 81 setInputDevices(inputDevices); 82 } 83 } 84 getInputDevices()85 private SparseArray<InputDevice> getInputDevices() { 86 return reflector(InputManagerReflector.class, realInputManager).getInputDevices(); 87 } 88 setInputDevices(SparseArray<InputDevice> devices)89 private void setInputDevices(SparseArray<InputDevice> devices) { 90 reflector(InputManagerReflector.class, realInputManager).setInputDevices(devices); 91 } 92 93 /** 94 * Provides a local java implementation, since the real implementation is in system server + 95 * native code. 96 */ 97 @Implementation(minSdk = R) verifyInputEvent( InputEvent inputEvent)98 protected @ClassName("android.view.VerifiedInputEvent") Object verifyInputEvent( 99 InputEvent inputEvent) { 100 if (inputEvent instanceof MotionEvent) { 101 MotionEvent motionEvent = (MotionEvent) inputEvent; 102 return new VerifiedMotionEvent( 103 motionEvent.getDeviceId(), 104 MILLISECONDS.toNanos(motionEvent.getEventTime()), 105 motionEvent.getSource(), 106 motionEvent.getDisplayId(), 107 motionEvent.getRawX(), 108 motionEvent.getRawY(), 109 motionEvent.getActionMasked(), 110 MILLISECONDS.toNanos(motionEvent.getDownTime()), 111 motionEvent.getFlags(), 112 motionEvent.getMetaState(), 113 motionEvent.getButtonState()); 114 } else if (inputEvent instanceof KeyEvent) { 115 KeyEvent keyEvent = (KeyEvent) inputEvent; 116 return new VerifiedKeyEvent( 117 keyEvent.getDeviceId(), 118 MILLISECONDS.toNanos(keyEvent.getEventTime()), 119 keyEvent.getSource(), 120 keyEvent.getDisplayId(), 121 keyEvent.getAction(), 122 MILLISECONDS.toNanos(keyEvent.getDownTime()), 123 keyEvent.getFlags(), 124 keyEvent.getKeyCode(), 125 keyEvent.getScanCode(), 126 keyEvent.getMetaState(), 127 keyEvent.getRepeatCount()); 128 } else { 129 throw new IllegalArgumentException("unknown input event: " + inputEvent.getClass().getName()); 130 } 131 } 132 133 @Resetter reset()134 public static void reset() { 135 if (SDK_INT < U.SDK_INT) { 136 ReflectionHelpers.setStaticField(InputManager.class, "sInstance", null); 137 } 138 } 139 140 @ForType(InputManager.class) 141 interface InputManagerReflector { 142 @Accessor("mInputDevices") getInputDevices()143 SparseArray<InputDevice> getInputDevices(); 144 145 @Accessor("mInputDevices") setInputDevices(SparseArray<InputDevice> devices)146 void setInputDevices(SparseArray<InputDevice> devices); 147 } 148 } 149