aboutsummaryrefslogtreecommitdiff
path: root/shadows
diff options
context:
space:
mode:
authorJulia Sullivan <juliansull@google.com>2024-04-17 11:01:25 -0700
committerCopybara-Service <copybara-worker@google.com>2024-04-17 11:02:07 -0700
commite54bde4a2055e3cfcb63b5f4f1e9e6db5d1f8928 (patch)
treeed0eed24db171c3720bad530a1536e2eb11cfd75 /shadows
parent9bb0d6d5a0627a2bd99cdd1fbda497c7d7aaf98e (diff)
downloadrobolectric-e54bde4a2055e3cfcb63b5f4f1e9e6db5d1f8928.tar.gz
Update ShadowMotionEvent and AndroidTestEnvironment to support Android V
In ShadowMotionEvent, nativeGetXOffset was replaced by nativeGetRawXOffset, and nativeGetYOffset nativeGetRawYOffset. The logic for MotionEvent.split was moved into native code, so it needs to be shadowed starting in V. Also, the signature of AppCompatCallbacks.install was updated to include an extra long parameter. PiperOrigin-RevId: 625741342
Diffstat (limited to 'shadows')
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java133
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java30
2 files changed, 152 insertions, 11 deletions
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java b/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
index d136be83d..cbda24865 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/NativeInput.java
@@ -3,6 +3,7 @@ package org.robolectric.shadows;
import static com.google.common.base.Preconditions.checkState;
import static org.robolectric.shadows.NativeAndroidInput.AINPUT_EVENT_TYPE_MOTION;
import static org.robolectric.shadows.NativeAndroidInput.AINPUT_SOURCE_CLASS_POINTER;
+import static org.robolectric.shadows.NativeAndroidInput.AKEY_EVENT_FLAG_CANCELED;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_CANCEL;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_DOWN;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_ACTION_MASK;
@@ -28,6 +29,7 @@ import android.view.MotionEvent.PointerProperties;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
import org.robolectric.res.android.Ref;
/**
@@ -64,11 +66,11 @@ public class NativeInput {
*/
static class AInputEvent {}
- /*
+ /**
* Pointer coordinate data.
*
- * Deviates from original platform implementation to store axises in simple SparseArray as opposed
- * to complicated bitset + array arrangement.
+ * <p>Deviates from original platform implementation to store axises in simple SparseArray as
+ * opposed to complicated bitset + array arrangement.
*/
static class PointerCoords {
@@ -272,9 +274,7 @@ public class NativeInput {
// nsecs_t mEventTime;
}
- /*
- * Motion events.
- */
+ /** Motion events. */
static class MotionEvent extends InputEvent {
// constants copied from android bionic/libc/include/math.h
@@ -284,6 +284,18 @@ public class NativeInput {
@SuppressWarnings("FloatingPointLiteralPrecision")
private static final double M_PI_2 = 1.57079632679489661923f; /* pi/2 */
+ public static final int ACTION_MASK = 0xff;
+ public static final int ACTION_DOWN = 0;
+ public static final int ACTION_UP = 1;
+ public static final int ACTION_MOVE = 2;
+ public static final int ACTION_CANCEL = 3;
+ public static final int ACTION_POINTER_DOWN = 5;
+ public static final int ACTION_POINTER_UP = 6;
+ private static final int HISTORY_CURRENT = -0x80000000;
+ public static final int FLAG_CANCELED = 0x20;
+ public static final int ACTION_POINTER_INDEX_MASK = 0xff00;
+ public static final int ACTION_POINTER_INDEX_SHIFT = 8;
+
private int mAction;
private int mActionButton;
private int mFlags;
@@ -540,6 +552,115 @@ public class NativeInput {
return getHistoricalAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, pointerIndex, historicalIndex);
}
+ private android.view.MotionEvent.PointerCoords[] getNativePointerCoords() {
+ android.view.MotionEvent.PointerCoords[] nativePointerCoords =
+ new android.view.MotionEvent.PointerCoords[mSamplePointerCoords.size()];
+ for (int i = 0; i < mSamplePointerCoords.size(); i++) {
+ android.view.MotionEvent.PointerCoords newPc = new android.view.MotionEvent.PointerCoords();
+ PointerCoords pc = mSamplePointerCoords.get(i);
+ newPc.x = pc.getX();
+ newPc.y = pc.getY();
+ newPc.setAxisValue(AMOTION_EVENT_AXIS_X, pc.getX());
+ newPc.setAxisValue(AMOTION_EVENT_AXIS_Y, pc.getY());
+ nativePointerCoords[i] = newPc;
+ }
+ return nativePointerCoords;
+ }
+
+ private int resolveActionForSplitMotionEvent(
+ int action,
+ int flags,
+ PointerProperties[] pointerProperties,
+ PointerProperties[] splitPointerProperties) {
+ int maskedAction = getActionMasked();
+ if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN
+ && maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) {
+ // The action is unaffected by splitting this motion event.
+ return action;
+ }
+
+ int actionIndex = getActionIndex();
+
+ int affectedPointerId = pointerProperties[actionIndex].id;
+ Optional<Integer> splitActionIndex = Optional.empty();
+ for (int i = 0; i < splitPointerProperties.length; i++) {
+ if (affectedPointerId == splitPointerProperties[i].id) {
+ splitActionIndex = Optional.of(i);
+ break;
+ }
+ }
+ if (!splitActionIndex.isPresent()) {
+ // The affected pointer is not part of the split motion event.
+ return AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ if (splitPointerProperties.length > 1) {
+ return maskedAction | (splitActionIndex.get() << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ return ((flags & AKEY_EVENT_FLAG_CANCELED) != 0)
+ ? AMOTION_EVENT_ACTION_CANCEL
+ : AMOTION_EVENT_ACTION_UP;
+ }
+ return AMOTION_EVENT_ACTION_DOWN;
+ }
+
+ public android.view.MotionEvent nativeSplit(int idBits) {
+ final int pointerCount = getPointerCount();
+ List<PointerProperties> pointerProperties = new ArrayList<>(mPointerProperties);
+ final PointerProperties[] pp = pointerProperties.toArray(new PointerProperties[pointerCount]);
+ final android.view.MotionEvent.PointerCoords[] pc = getNativePointerCoords();
+
+ List<PointerProperties> splitPointerProperties = new ArrayList<>();
+ List<android.view.MotionEvent.PointerCoords> splitPointerCoords = new ArrayList<>();
+
+ // Split the matching ids out for the new MotionEvent.
+ for (int i = 0; i < pointerCount; i++) {
+ final int idBit = 1 << pp[i].id;
+ if ((idBit & idBits) != 0) {
+ splitPointerProperties.add(pp[i]);
+ }
+ }
+ for (int i = 0; i < pc.length; i++) {
+ final int idBit = 1 << pp[i % pointerCount].id;
+ if ((idBit & idBits) != 0) {
+ splitPointerCoords.add(pc[i]);
+ }
+ }
+
+ // Convert them to arrays
+ PointerProperties[] splitPointerPropertiesArray =
+ new PointerProperties[splitPointerProperties.size()];
+ splitPointerProperties.toArray(splitPointerPropertiesArray);
+
+ android.view.MotionEvent.PointerCoords[] splitPointerCoordsArray =
+ new android.view.MotionEvent.PointerCoords[splitPointerCoords.size()];
+ splitPointerCoords.toArray(splitPointerCoordsArray);
+
+ int splitAction =
+ resolveActionForSplitMotionEvent(
+ getAction(), getFlags(), pp, splitPointerPropertiesArray);
+
+ android.view.MotionEvent newEvent =
+ android.view.MotionEvent.obtain(
+ getDownTime(),
+ getEventTime(),
+ splitAction,
+ splitPointerProperties.size(),
+ splitPointerPropertiesArray,
+ splitPointerCoordsArray,
+ getMetaState(),
+ getButtonState(),
+ getXPrecision(),
+ getYPrecision(),
+ getDeviceId(),
+ getEdgeFlags(),
+ getSource(),
+ getFlags());
+ return newEvent;
+ }
+
public int findPointerIndex(int pointerId) {
int pointerCount = mPointerProperties.size();
for (int i = 0; i < pointerCount; i++) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
index dc5345ace..371cb6d9e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMotionEvent.java
@@ -4,6 +4,7 @@ import static android.os.Build.VERSION_CODES.KITKAT_WATCH;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.robolectric.shadows.NativeAndroidInput.AMOTION_EVENT_AXIS_ORIENTATION;
@@ -33,6 +34,7 @@ import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.res.android.NativeObjRegistry;
import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.versioning.AndroidVersions.V;
/**
* Shadow of MotionEvent.
@@ -51,7 +53,7 @@ import org.robolectric.util.ReflectionHelpers;
* the MotionEvent.obtain methods or via MotionEventBuilder.
*/
@SuppressWarnings({"UnusedDeclaration"})
-@Implements(MotionEvent.class)
+@Implements(value = MotionEvent.class)
public class ShadowMotionEvent extends ShadowInputEvent {
private static NativeObjRegistry<NativeInput.MotionEvent> nativeMotionEventRegistry =
@@ -814,7 +816,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return nativeGetXOffset((long) nativePtr);
}
- @Implementation(minSdk = LOLLIPOP)
+ @Implementation(minSdk = LOLLIPOP, maxSdk = UPSIDE_DOWN_CAKE)
@HiddenApi
@InDevelopment
protected static float nativeGetXOffset(long nativePtr) {
@@ -828,7 +830,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return nativeGetYOffset((long) nativePtr);
}
- @Implementation(minSdk = LOLLIPOP)
+ @Implementation(minSdk = LOLLIPOP, maxSdk = UPSIDE_DOWN_CAKE)
@HiddenApi
@InDevelopment
protected static float nativeGetYOffset(long nativePtr) {
@@ -836,6 +838,24 @@ public class ShadowMotionEvent extends ShadowInputEvent {
return event.getYOffset();
}
+ @Implementation(minSdk = V.SDK_INT)
+ protected final MotionEvent split(int idBits) {
+ NativeInput.MotionEvent event = getNativeMotionEvent();
+ return event.nativeSplit(idBits);
+ }
+
+ @Implementation(minSdk = V.SDK_INT)
+ @HiddenApi
+ protected static float nativeGetRawXOffset(long nativePtr) {
+ return getNativeMotionEvent(nativePtr).getXOffset();
+ }
+
+ @Implementation(minSdk = V.SDK_INT)
+ @HiddenApi
+ protected static float nativeGetRawYOffset(long nativePtr) {
+ return getNativeMotionEvent(nativePtr).getYOffset();
+ }
+
@Implementation(maxSdk = KITKAT_WATCH)
@HiddenApi
protected static float nativeGetXPrecision(int nativePtr) {
@@ -940,7 +960,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
event.scale(scale);
}
- private static NativeInput.MotionEvent getNativeMotionEvent(long nativePtr) {
+ protected static NativeInput.MotionEvent getNativeMotionEvent(long nativePtr) {
// check that MotionEvent was initialized properly. This can occur if MotionEvent was mocked
checkState(
nativePtr > 0,
@@ -961,7 +981,7 @@ public class ShadowMotionEvent extends ShadowInputEvent {
event.transform(m);
}
- private NativeInput.MotionEvent getNativeMotionEvent() {
+ protected NativeInput.MotionEvent getNativeMotionEvent() {
long nativePtr;
if (RuntimeEnvironment.getApiLevel() <= KITKAT_WATCH) {
Integer nativePtrInt = ReflectionHelpers.getField(realMotionEvent, "mNativePtr");