diff options
author | Ang Li <ihcinihsdk@google.com> | 2024-04-18 02:36:52 +0000 |
---|---|---|
committer | Ang Li <ihcinihsdk@google.com> | 2024-04-18 02:37:13 +0000 |
commit | 4774468762423e04ef3bb56a477205cc9c192307 (patch) | |
tree | 96e08ecd5ae77ac50b2bf021e7a1801c939267d6 /shadows | |
parent | b3b17db94476c25225589165fbf53ec938f5b6dc (diff) | |
parent | 1920a4cbbf574f775bd360b901b3e4102b7ad8b4 (diff) | |
download | robolectric-4774468762423e04ef3bb56a477205cc9c192307.tar.gz |
Merge branch 'upstream-google' into attempt_merge_upstream
Change-Id: Ic6cfe2bf398c32bb4ce8750dbec7f95bd2645f44
Diffstat (limited to 'shadows')
7 files changed, 254 insertions, 36 deletions
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java b/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java new file mode 100644 index 000000000..45ede54c0 --- /dev/null +++ b/shadows/framework/src/main/java/org/robolectric/shadows/EmergencyNumberBuilder.java @@ -0,0 +1,71 @@ +package org.robolectric.shadows; + +import static android.os.Build.VERSION_CODES.Q; + +import android.annotation.RequiresApi; +import android.telephony.emergency.EmergencyNumber; +import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting; +import android.telephony.emergency.EmergencyNumber.EmergencyNumberSources; +import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories; +import java.util.ArrayList; +import java.util.List; +import org.robolectric.util.ReflectionHelpers; +import org.robolectric.util.ReflectionHelpers.ClassParameter; + +/** Builder for {@link android.telephony.emergency.EmergencyNumber}. */ +@RequiresApi(Q) +public class EmergencyNumberBuilder { + + private final String number; + private final String countryIso; + private final String mnc; + private final List<String> emergencyUrns = new ArrayList<String>(); + private int emergencyServiceCategories = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED; + private int emergencyNumberSources = EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT; + private int emergencyCallRouting = EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN; + + private EmergencyNumberBuilder(String number, String countryIso, String mnc) { + this.number = number; + this.countryIso = countryIso; + this.mnc = mnc; + } + + public static EmergencyNumberBuilder newBuilder(String number, String countryIso, String mnc) { + return new EmergencyNumberBuilder(number, countryIso, mnc); + } + + public EmergencyNumberBuilder setEmergencyServiceCategories( + @EmergencyServiceCategories int emergencyServiceCategories) { + this.emergencyServiceCategories = emergencyServiceCategories; + return this; + } + + public EmergencyNumberBuilder addEmergencyUrn(String emergencyUrn) { + emergencyUrns.add(emergencyUrn); + return this; + } + + public EmergencyNumberBuilder setEmergencyNumberSources( + @EmergencyNumberSources int emergencyNumberSources) { + this.emergencyNumberSources = emergencyNumberSources; + return this; + } + + public EmergencyNumberBuilder setEmergencyCallRouting( + @EmergencyCallRouting int emergencyCallRouting) { + this.emergencyCallRouting = emergencyCallRouting; + return this; + } + + public EmergencyNumber build() { + return ReflectionHelpers.callConstructor( + EmergencyNumber.class, + ClassParameter.from(String.class, number), + ClassParameter.from(String.class, countryIso), + ClassParameter.from(String.class, mnc), + ClassParameter.from(int.class, emergencyServiceCategories), + ClassParameter.from(List.class, emergencyUrns), + ClassParameter.from(int.class, emergencyNumberSources), + ClassParameter.from(int.class, emergencyCallRouting)); + } +} diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java b/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java index fea0a9a97..63e9d7bd8 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/HardwareRenderingScreenshot.java @@ -16,7 +16,6 @@ import android.util.DisplayMetrics; import android.view.Surface; import android.view.View; import com.android.internal.R; -import java.nio.IntBuffer; import org.robolectric.annotation.GraphicsMode; import org.robolectric.util.ReflectionHelpers; @@ -26,7 +25,7 @@ import org.robolectric.util.ReflectionHelpers; */ public final class HardwareRenderingScreenshot { - static final String USE_HARDWARE_RENDERER_NATIVE_ENV = "robolectric.screenshot.hwrdr.native"; + static final String PIXEL_COPY_RENDER_MODE = "robolectric.pixelCopyRenderMode"; private HardwareRenderingScreenshot() {} @@ -37,7 +36,7 @@ public final class HardwareRenderingScreenshot { */ static boolean canTakeScreenshot() { return VERSION.SDK_INT >= VERSION_CODES.S - && Boolean.getBoolean(HardwareRenderingScreenshot.USE_HARDWARE_RENDERER_NATIVE_ENV) + && "hardware".equalsIgnoreCase(System.getProperty(PIXEL_COPY_RENDER_MODE, "")) && ShadowView.useRealGraphics(); } @@ -71,21 +70,8 @@ public final class HardwareRenderingScreenshot { renderer.setContentRoot(node); renderer.createRenderRequest().syncAndDraw(); - - int[] renderPixels = new int[width * height]; - Plane[] planes = nativeImage.getPlanes(); - IntBuffer srcBuff = planes[0].getBuffer().asIntBuffer(); - srcBuff.get(renderPixels); - - destBitmap.setPixels( - renderPixels, - /* offset= */ 0, - /* stride= */ width, - /* x= */ 0, - /* y= */ 0, - width, - height); + destBitmap.copyPixelsFromBuffer(planes[0].getBuffer()); surface.release(); } } 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/ShadowLocationManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java index d33e639c6..02d6b1776 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLocationManager.java @@ -1968,7 +1968,9 @@ public class ShadowLocationManager { ArrayList<Location> deliverableLocations = new ArrayList<>(locations.length); for (Location location : locations) { if (lastDeliveredLocation != null) { - if (location.getTime() - lastDeliveredLocation.getTime() + if (NANOSECONDS.toMillis( + location.getElapsedRealtimeNanos() + - lastDeliveredLocation.getElapsedRealtimeNanos()) < request.getMinUpdateIntervalMillis()) { Log.w(TAG, "location rejected for simulated delivery - too fast"); continue; 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"); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java index a070ce1cb..9039e885e 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPixelCopy.java @@ -25,6 +25,7 @@ import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowWindowManagerGlobal.WindowManagerGlobalReflector; +import org.robolectric.util.PerfStatsCollector; import org.robolectric.util.reflector.Accessor; import org.robolectric.util.reflector.Constructor; import org.robolectric.util.reflector.ForType; @@ -168,10 +169,18 @@ public class ShadowPixelCopy { Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); if (HardwareRenderingScreenshot.canTakeScreenshot()) { - HardwareRenderingScreenshot.takeScreenshot(view, bitmap); + PerfStatsCollector.getInstance() + .measure( + "ShadowPixelCopy-Hardware", + () -> HardwareRenderingScreenshot.takeScreenshot(view, bitmap)); } else { - Canvas screenshotCanvas = new Canvas(bitmap); - view.draw(screenshotCanvas); + PerfStatsCollector.getInstance() + .measure( + "ShadowPixelCopy-Software", + () -> { + Canvas screenshotCanvas = new Canvas(bitmap); + view.draw(screenshotCanvas); + }); } Rect dst = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java index 33f800f7d..682f53b6a 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewConfiguration.java @@ -46,7 +46,9 @@ public class ShadowViewConfiguration { private static final int DOUBLE_TAP_SLOP = 100; private static final int WINDOW_TOUCH_SLOP = 16; private static final int MAXIMUM_FLING_VELOCITY = 4000; - private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; + + // The previous hardcoded value for draw cache size. Some screenshot tests depend on this value. + private static final int MIN_MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; private int edgeSlop; private int fadingEdgeLength; @@ -57,10 +59,11 @@ public class ShadowViewConfiguration { private int pagingTouchSlop; private int doubleTapSlop; private int windowTouchSlop; + private int maximumDrawingCacheSize; private static boolean hasPermanentMenuKey = true; private void setup(Context context) { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); float density = metrics.density; edgeSlop = (int) (density * ViewConfiguration.getEdgeSlop() + 0.5f); @@ -72,6 +75,12 @@ public class ShadowViewConfiguration { pagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f); doubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); windowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); + // Some screenshot tests were misconfigured and try to draw very large views onto small + // screens using SW rendering. To avoid breaking these tests, we keep the drawing cache a bit + // larger when screens are configured to be arbitrarily small. + // TODO(hoisie): Investigate removing this Math.max logic. + maximumDrawingCacheSize = + Math.max(MIN_MAXIMUM_DRAWING_CACHE_SIZE, 4 * metrics.widthPixels * metrics.heightPixels); } @Implementation @@ -174,8 +183,8 @@ public class ShadowViewConfiguration { } @Implementation - protected static int getMaximumDrawingCacheSize() { - return MAXIMUM_DRAWING_CACHE_SIZE; + protected int getScaledMaximumDrawingCacheSize() { + return maximumDrawingCacheSize; } @Implementation |