aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xrobolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java17
-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
3 files changed, 166 insertions, 14 deletions
diff --git a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
index bf626e4a9..e21545d9f 100755
--- a/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
+++ b/robolectric/src/main/java/org/robolectric/android/internal/AndroidTestEnvironment.java
@@ -100,8 +100,10 @@ import org.robolectric.shadows.ShadowView;
import org.robolectric.util.Logger;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.Scheduler;
import org.robolectric.util.TempDirectory;
+import org.robolectric.versioning.AndroidVersions;
import org.robolectric.versioning.AndroidVersions.V;
@SuppressLint("NewApi")
@@ -399,9 +401,18 @@ public class AndroidTestEnvironment implements TestEnvironment {
populateAssetPaths(appResources.getAssets(), appManifest);
}
- // circument the 'No Compatibility callbacks set!' log. See #8509
- if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.R) {
- AppCompatCallbacks.install(new long[0]);
+ // Circumvent the 'No Compatibility callbacks set!' log. See #8509
+ if (apiLevel >= AndroidVersions.V.SDK_INT) {
+ // Adds loggableChanges parameter.
+ ReflectionHelpers.callStaticMethod(
+ AppCompatCallbacks.class,
+ "install",
+ ClassParameter.from(long[].class, new long[0]),
+ ClassParameter.from(long[].class, new long[0]));
+ } else if (apiLevel >= AndroidVersions.R.SDK_INT) {
+ // Invoke the previous version.
+ ReflectionHelpers.callStaticMethod(
+ AppCompatCallbacks.class, "install", ClassParameter.from(long[].class, new long[0]));
}
PerfStatsCollector.getInstance()
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");