aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRex Hoffman <rexhoffman@google.com>2022-12-17 01:20:08 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-12-17 01:20:08 +0000
commitdec7fbfd56318f64da1b81163754f2cdd7f457e2 (patch)
treeb3411ee1d396479139d774bf3c633a49701740e6
parent1125cabb041c59c353f985cc9af15756fed1017f (diff)
parent278071078a1e89942088142505cd9774b951c904 (diff)
downloadrobolectric-dec7fbfd56318f64da1b81163754f2cdd7f457e2.tar.gz
Merge branch 'upstream-google' into rng am: c5133d2145 am: 278071078a
Original change: https://googleplex-android-review.googlesource.com/c/platform/external/robolectric/+/20739536 Change-Id: I1934d1d249672b3f6a037a6f2b04b3febfc8e444 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--annotations/src/main/java/org/robolectric/annotation/GraphicsMode.java27
-rw-r--r--nativeruntime/build.gradle7
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedImageDrawableNatives.java69
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedVectorDrawableNatives.java70
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseCanvasNatives.java344
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseRecordingCanvasNatives.java343
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapFactoryNatives.java57
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapNatives.java134
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapShaderNatives.java19
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BlendModeColorFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/BlurMaskFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasNatives.java94
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasPropertyNatives.java32
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorFilterNatives.java32
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorMatrixColorFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorNatives.java16
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorSpaceRgbNatives.java33
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposePathEffectNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposeShaderNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/CornerPathEffectNatives.java13
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/DashPathEffectNatives.java13
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/DiscretePathEffectNatives.java13
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/EmbossMaskFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontBuilderNatives.java47
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyBuilderNatives.java37
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyNatives.java51
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFileUtilNatives.java35
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontNatives.java61
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/FontsFontFamilyNatives.java37
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererNatives.java173
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererObserverNatives.java29
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ImageDecoderNatives.java81
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/InterpolatorNatives.java25
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/LightingColorFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/LineBreakerNatives.java87
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/LinearGradientNatives.java35
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/MaskFilterNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/MatrixNatives.java123
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextBuilderNatives.java56
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextNatives.java42
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/NIOAccess.java100
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeAllocationRegistryNatives.java13
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeInterpolatorFactoryNatives.java34
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/NinePatchNatives.java39
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PaintNatives.java442
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PathDashPathEffectNatives.java15
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PathEffectNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PathMeasureNatives.java34
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PathNatives.java111
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PathParserNatives.java31
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PictureNatives.java32
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PorterDuffColorFilterNatives.java30
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/PropertyValuesHolderNatives.java41
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RadialGradientNatives.java33
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RecordingCanvasNatives.java70
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionIteratorNatives.java20
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionNatives.java66
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderEffectNatives.java39
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeAnimatorNatives.java56
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeNatives.java211
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/RuntimeShaderNatives.java23
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/ShaderNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/SumPathEffectNatives.java14
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/SurfaceNatives.java80
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/SweepGradientNatives.java20
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/TableMaskFilterNatives.java34
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/TypefaceNatives.java72
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/VectorDrawableNatives.java150
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/VirtualRefBasePtrNatives.java16
-rw-r--r--nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java29
-rw-r--r--nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java21
-rw-r--r--preinstrumented/src/main/java/org/robolectric/preinstrumented/JarInstrumentor.java5
-rw-r--r--robolectric/src/main/java/org/robolectric/plugins/GraphicsModeConfigurer.java65
-rw-r--r--robolectric/src/test/java/org/robolectric/plugins/GraphicsModeConfigurerTest.java21
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/GraphicsShadowPicker.java32
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmap.java10
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowCanvas.java14
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowMatrix.java10
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAllocationRegistry.java65
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedImageDrawable.java125
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedVectorDrawable.java120
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseCanvas.java877
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java597
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmap.java504
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapDrawable.java42
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapFactory.java153
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapShader.java69
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlendModeColorFilter.java29
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlurMaskFilter.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvas.java209
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvasProperty.java38
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColor.java34
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorFilter.java32
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorMatrixColorFilter.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorSpaceRgb.java39
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposePathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposeShader.java30
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCornerPathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDashPathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDiscretePathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDisplayListCanvas.java72
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeEmbossMaskFilter.java29
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFont.java281
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFamily.java96
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFileUtil.java45
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java86
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java396
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRendererObserver.java60
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeImageDecoder.java211
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeInterpolator.java55
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLightingColorFilter.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLineBreaker.java97
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLinearGradient.java60
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMaskFilter.java26
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMatrix.java264
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredParagraph.java73
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredText.java135
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNativeInterpolatorFactory.java85
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNinePatch.java50
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePaint.java847
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePath.java243
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathDashPathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathEffect.java26
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathMeasure.java76
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathParser.java76
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePicture.java72
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePorterDuffColorFilter.java39
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePropertyValuesHolder.java85
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRadialGradient.java83
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRecordingCanvas.java112
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegion.java184
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegionIterator.java39
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderEffect.java77
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNode.java468
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimator.java96
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimatorQ.java100
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeOP.java465
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRuntimeShader.java180
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeShader.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeStaticLayout.java330
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSumPathEffect.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSurface.java147
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSweepGradient.java44
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSystemFonts.java125
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTableMaskFilter.java44
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeThreadedRenderer.java183
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTypeface.java291
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVectorDrawable.java343
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVirtualRefBasePtr.java35
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowPath.java10
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowTypeface.java10
151 files changed, 14648 insertions, 34 deletions
diff --git a/annotations/src/main/java/org/robolectric/annotation/GraphicsMode.java b/annotations/src/main/java/org/robolectric/annotation/GraphicsMode.java
new file mode 100644
index 000000000..06f785a4c
--- /dev/null
+++ b/annotations/src/main/java/org/robolectric/annotation/GraphicsMode.java
@@ -0,0 +1,27 @@
+package org.robolectric.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * A {@link org.robolectric.pluginapi.config.Configurer} annotation for controlling which graphics
+ * shadow implementation is used for the {@link android.graphics} package.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD})
+public @interface GraphicsMode {
+
+ /** Specifies the different supported graphics modes. */
+ enum Mode {
+ /** Use legacy graphics shadows that are no-ops and fakes. */
+ LEGACY,
+ /** Use graphics shadows libraries backed by native Android graphics code. */
+ NATIVE,
+ }
+
+ Mode value();
+}
diff --git a/nativeruntime/build.gradle b/nativeruntime/build.gradle
index ecfe56915..495bf65f4 100644
--- a/nativeruntime/build.gradle
+++ b/nativeruntime/build.gradle
@@ -183,13 +183,16 @@ processResources {
dependencies {
api project(":utils")
-
- annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion"
+ api project(":utils:reflector")
api "com.google.guava:guava:$guavaJREVersion"
+ annotationProcessor "com.google.auto.service:auto-service:$autoServiceVersion"
compileOnly "com.google.auto.service:auto-service-annotations:$autoServiceVersion"
compileOnly AndroidSdk.MAX_SDK.coordinates
+ testCompileOnly AndroidSdk.MAX_SDK.coordinates
+ testRuntimeOnly AndroidSdk.MAX_SDK.coordinates
+ testImplementation project(":robolectric")
testImplementation "junit:junit:${junitVersion}"
testImplementation "com.google.truth:truth:${truthVersion}"
}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedImageDrawableNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedImageDrawableNatives.java
new file mode 100644
index 000000000..a5bcfb5d9
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedImageDrawableNatives.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.ImageDecoder;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedImageDrawable;
+
+/**
+ * Native methods for AnimatedImageDrawable JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+ */
+public final class AnimatedImageDrawableNatives {
+ public static native long nCreate(
+ long nativeImageDecoder,
+ ImageDecoder decoder,
+ int width,
+ int height,
+ long colorSpaceHandle,
+ boolean extended,
+ Rect cropRect);
+
+ public static native long nGetNativeFinalizer();
+
+ public static native long nDraw(long nativePtr, long canvasNativePtr);
+
+ public static native void nSetAlpha(long nativePtr, int alpha);
+
+ public static native int nGetAlpha(long nativePtr);
+
+ public static native void nSetColorFilter(long nativePtr, long nativeFilter);
+
+ public static native boolean nIsRunning(long nativePtr);
+
+ public static native boolean nStart(long nativePtr);
+
+ public static native boolean nStop(long nativePtr);
+
+ public static native int nGetRepeatCount(long nativePtr);
+
+ public static native void nSetRepeatCount(long nativePtr, int repeatCount);
+
+ public static native void nSetOnAnimationEndListener(
+ long nativePtr, AnimatedImageDrawable drawable);
+
+ public static native long nNativeByteSize(long nativePtr);
+
+ public static native void nSetMirrored(long nativePtr, boolean mirror);
+
+ public static native void nSetBounds(long nativePtr, Rect rect);
+
+ private AnimatedImageDrawableNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedVectorDrawableNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedVectorDrawableNatives.java
new file mode 100644
index 000000000..6d71e6ce4
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/AnimatedVectorDrawableNatives.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+
+/**
+ * Native methods for AnimatedVectorDrawable JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+ */
+public final class AnimatedVectorDrawableNatives {
+
+ public static native long nCreateAnimatorSet();
+
+ public static native void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr);
+
+ public static native void nAddAnimator(
+ long setPtr,
+ long propertyValuesHolder,
+ long nativeInterpolator,
+ long startDelay,
+ long duration,
+ int repeatCount,
+ int repeatMode);
+
+ public static native void nSetPropertyHolderData(long nativePtr, float[] data, int length);
+
+ public static native void nSetPropertyHolderData(long nativePtr, int[] data, int length);
+
+ public static native void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+
+ public static native void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id);
+
+ public static native long nCreateGroupPropertyHolder(
+ long nativePtr, int propertyId, float startValue, float endValue);
+
+ public static native long nCreatePathDataPropertyHolder(
+ long nativePtr, long startValuePtr, long endValuePtr);
+
+ public static native long nCreatePathColorPropertyHolder(
+ long nativePtr, int propertyId, int startValue, int endValue);
+
+ public static native long nCreatePathPropertyHolder(
+ long nativePtr, int propertyId, float startValue, float endValue);
+
+ public static native long nCreateRootAlphaPropertyHolder(
+ long nativePtr, float startValue, float endValue);
+
+ public static native void nEnd(long animatorSetPtr);
+
+ public static native void nReset(long animatorSetPtr);
+
+ private AnimatedVectorDrawableNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseCanvasNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseCanvasNatives.java
new file mode 100644
index 000000000..f8931eaf8
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseCanvasNatives.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.annotation.ColorLong;
+
+/**
+ * Native methods for BaseCanvas JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BaseCanvas.java
+ */
+public final class BaseCanvasNatives {
+
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float left,
+ float top,
+ long nativePaintOrZero,
+ int canvasDensity,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ int[] colors,
+ int offset,
+ int stride,
+ float x,
+ float y,
+ int width,
+ int height,
+ boolean hasAlpha,
+ long nativePaintOrZero);
+
+ public static native void nDrawColor(long nativeCanvas, int color, int mode);
+
+ public static native void nDrawColor(
+ long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode);
+
+ public static native void nDrawPaint(long nativeCanvas, long nativePaint);
+
+ public static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle);
+
+ public static native void nDrawPoints(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle);
+
+ public static native void nDrawLine(
+ long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint);
+
+ public static native void nDrawLines(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle);
+
+ public static native void nDrawRect(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint);
+
+ public static native void nDrawOval(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint);
+
+ public static native void nDrawCircle(
+ long nativeCanvas, float cx, float cy, float radius, long nativePaint);
+
+ public static native void nDrawArc(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweep,
+ boolean useCenter,
+ long nativePaint);
+
+ public static native void nDrawRoundRect(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ long nativePaint);
+
+ public static native void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float outerRx,
+ float outerRy,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float innerRx,
+ float innerRy,
+ long nativePaint);
+
+ public static native void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float[] outerRadii,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float[] innerRadii,
+ long nativePaint);
+
+ public static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
+
+ public static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
+
+ public static native void nDrawNinePatch(
+ long nativeCanvas,
+ long nativeBitmap,
+ long ninePatch,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmapMatrix(
+ long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint);
+
+ public static native void nDrawBitmapMesh(
+ long nativeCanvas,
+ long bitmapHandle,
+ int meshWidth,
+ int meshHeight,
+ float[] verts,
+ int vertOffset,
+ int[] colors,
+ int colorOffset,
+ long nativePaint);
+
+ public static native void nDrawVertices(
+ long nativeCanvas,
+ int mode,
+ int n,
+ float[] verts,
+ int vertOffset,
+ float[] texs,
+ int texOffset,
+ int[] colors,
+ int colorOffset,
+ short[] indices,
+ int indexOffset,
+ int indexCount,
+ long nativePaint);
+
+ public static native void nDrawGlyphs(
+ long nativeCanvas,
+ int[] glyphIds,
+ float[] positions,
+ int glyphIdStart,
+ int positionStart,
+ int glyphCount,
+ long nativeFont,
+ long nativePaint);
+
+ public static native void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint);
+
+ public static native void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint);
+
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativPrecomputedText);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextRunTypeface(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint);
+
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nPunchHole(
+ long renderer, float left, float top, float right, float bottom, float rx, float ry);
+
+ private BaseCanvasNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseRecordingCanvasNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseRecordingCanvasNatives.java
new file mode 100644
index 000000000..3bd3fa937
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BaseRecordingCanvasNatives.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.annotation.ColorLong;
+
+/**
+ * Native methods for BaseRecordingCanvas JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BaseRecordingCanvas.java
+ */
+public final class BaseRecordingCanvasNatives {
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float left,
+ float top,
+ long nativePaintOrZero,
+ int canvasDensity,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmap(
+ long nativeCanvas,
+ int[] colors,
+ int offset,
+ int stride,
+ float x,
+ float y,
+ int width,
+ int height,
+ boolean hasAlpha,
+ long nativePaintOrZero);
+
+ public static native void nDrawColor(long nativeCanvas, int color, int mode);
+
+ public static native void nDrawColor(
+ long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode);
+
+ public static native void nDrawPaint(long nativeCanvas, long nativePaint);
+
+ public static native void nDrawPoint(long canvasHandle, float x, float y, long paintHandle);
+
+ public static native void nDrawPoints(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle);
+
+ public static native void nDrawLine(
+ long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint);
+
+ public static native void nDrawLines(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle);
+
+ public static native void nDrawRect(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint);
+
+ public static native void nDrawOval(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint);
+
+ public static native void nDrawCircle(
+ long nativeCanvas, float cx, float cy, float radius, long nativePaint);
+
+ public static native void nDrawArc(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweep,
+ boolean useCenter,
+ long nativePaint);
+
+ public static native void nDrawRoundRect(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ long nativePaint);
+
+ public static native void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float outerRx,
+ float outerRy,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float innerRx,
+ float innerRy,
+ long nativePaint);
+
+ public static native void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float[] outerRadii,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float[] innerRadii,
+ long nativePaint);
+
+ public static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
+
+ public static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
+
+ public static native void nDrawNinePatch(
+ long nativeCanvas,
+ long nativeBitmap,
+ long ninePatch,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity);
+
+ public static native void nDrawBitmapMatrix(
+ long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint);
+
+ public static native void nDrawBitmapMesh(
+ long nativeCanvas,
+ long bitmapHandle,
+ int meshWidth,
+ int meshHeight,
+ float[] verts,
+ int vertOffset,
+ int[] colors,
+ int colorOffset,
+ long nativePaint);
+
+ public static native void nDrawVertices(
+ long nativeCanvas,
+ int mode,
+ int n,
+ float[] verts,
+ int vertOffset,
+ float[] texs,
+ int texOffset,
+ int[] colors,
+ int colorOffset,
+ short[] indices,
+ int indexOffset,
+ int indexCount,
+ long nativePaint);
+
+ public static native void nDrawGlyphs(
+ long nativeCanvas,
+ int[] glyphIds,
+ float[] positions,
+ int glyphIdStart,
+ int positionStart,
+ int glyphCount,
+ long nativeFont,
+ long nativePaint);
+
+ public static native void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint);
+
+ public static native void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint);
+
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativePrecomputedText);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextRunTypeface(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint);
+
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint,
+ long nativeTypeface);
+
+ // Variant for O..O_MR1 that includes a Typeface pointer.
+ public static native void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint,
+ long nativeTypeface);
+
+ public static native void nPunchHole(
+ long renderer, float left, float top, float right, float bottom, float rx, float ry);
+
+ private BaseRecordingCanvasNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapFactoryNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapFactoryNatives.java
new file mode 100644
index 000000000..8024f26df
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapFactoryNatives.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Rect;
+import java.io.FileDescriptor;
+import java.io.InputStream;
+
+/**
+ * Native methods for BitmapFactory JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BitmapFactory.java
+ */
+public final class BitmapFactoryNatives {
+ public static native Bitmap nativeDecodeStream(
+ InputStream is,
+ byte[] storage,
+ Rect padding,
+ Options opts,
+ long inBitmapHandle,
+ long colorSpaceHandle);
+
+ public static native Bitmap nativeDecodeFileDescriptor(
+ FileDescriptor fd, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle);
+
+ public static native Bitmap nativeDecodeAsset(
+ long nativeAsset, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle);
+
+ public static native Bitmap nativeDecodeByteArray(
+ byte[] data,
+ int offset,
+ int length,
+ Options opts,
+ long inBitmapHandle,
+ long colorSpaceHandle);
+
+ public static native boolean nativeIsSeekable(FileDescriptor fd);
+
+ private BitmapFactoryNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapNatives.java
new file mode 100644
index 000000000..b273771c4
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapNatives.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import java.io.OutputStream;
+import java.nio.Buffer;
+
+/**
+ * Native methods for Bitmap JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Bitmap.java
+ */
+public final class BitmapNatives {
+
+ public static native Bitmap nativeCreate(
+ int[] colors,
+ int offset,
+ int stride,
+ int width,
+ int height,
+ int nativeConfig,
+ boolean mutable,
+ long nativeColorSpace);
+
+ public static native Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig, boolean isMutable);
+
+ public static native Bitmap nativeCopyAshmem(long nativeSrcBitmap);
+
+ public static native Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig);
+
+ public static native long nativeGetNativeFinalizer();
+
+ public static native void nativeRecycle(long nativeBitmap);
+
+ public static native void nativeReconfigure(
+ long nativeBitmap, int width, int height, int config, boolean isPremultiplied);
+
+ public static native boolean nativeCompress(
+ long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage);
+
+ public static native void nativeErase(long nativeBitmap, int color);
+
+ public static native void nativeErase(long nativeBitmap, long colorSpacePtr, long color);
+
+ public static native int nativeRowBytes(long nativeBitmap);
+
+ public static native int nativeConfig(long nativeBitmap);
+
+ public static native int nativeGetPixel(long nativeBitmap, int x, int y);
+
+ public static native long nativeGetColor(long nativeBitmap, int x, int y);
+
+ public static native void nativeGetPixels(
+ long nativeBitmap, int[] pixels, int offset, int stride, int x, int y, int width, int height);
+
+ public static native void nativeSetPixel(long nativeBitmap, int x, int y, int color);
+
+ public static native void nativeSetPixels(
+ long nativeBitmap, int[] colors, int offset, int stride, int x, int y, int width, int height);
+
+ public static native void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst);
+
+ public static native void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src);
+
+ public static native int nativeGenerationId(long nativeBitmap);
+
+ public static native Bitmap nativeCreateFromParcel(Parcel p);
+ // returns true on success
+ public static native boolean nativeWriteToParcel(long nativeBitmap, int density, Parcel p);
+ // returns a new bitmap built from the native bitmap's alpha, and the paint
+ public static native Bitmap nativeExtractAlpha(
+ long nativeBitmap, long nativePaint, int[] offsetXY);
+
+ public static native boolean nativeHasAlpha(long nativeBitmap);
+
+ public static native boolean nativeIsPremultiplied(long nativeBitmap);
+
+ public static native void nativeSetPremultiplied(long nativeBitmap, boolean isPremul);
+
+ public static native void nativeSetHasAlpha(
+ long nativeBitmap, boolean hasAlpha, boolean requestPremul);
+
+ public static native boolean nativeHasMipMap(long nativeBitmap);
+
+ public static native void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap);
+
+ public static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
+
+ public static native void nativePrepareToDraw(long nativeBitmap);
+
+ public static native int nativeGetAllocationByteCount(long nativeBitmap);
+
+ public static native Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap);
+
+ public static native Bitmap nativeWrapHardwareBufferBitmap(
+ HardwareBuffer buffer, long nativeColorSpace);
+
+ public static native HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap);
+
+ public static native ColorSpace nativeComputeColorSpace(long nativePtr);
+
+ public static native void nativeSetColorSpace(long nativePtr, long nativeColorSpace);
+
+ public static native boolean nativeIsSRGB(long nativePtr);
+
+ public static native boolean nativeIsSRGBLinear(long nativePtr);
+
+ public static native void nativeSetImmutable(long nativePtr);
+
+ public static native boolean nativeIsImmutable(long nativePtr);
+
+ public static native boolean nativeIsBackedByAshmem(long nativePtr);
+
+ private BitmapNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapShaderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapShaderNatives.java
new file mode 100644
index 000000000..f0d1df102
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BitmapShaderNatives.java
@@ -0,0 +1,19 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for BitmapShader JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BitmapShader.java
+ */
+public final class BitmapShaderNatives {
+
+ public static native long nativeCreate(
+ long nativeMatrix,
+ long bitmapHandle,
+ int shaderTileModeX,
+ int shaderTileModeY,
+ boolean filter);
+
+ private BitmapShaderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlendModeColorFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlendModeColorFilterNatives.java
new file mode 100644
index 000000000..aa93a501b
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlendModeColorFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for BlendModeColorFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BlendModeColorFilter.java
+ */
+public final class BlendModeColorFilterNatives {
+
+ public static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
+
+ private BlendModeColorFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlurMaskFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlurMaskFilterNatives.java
new file mode 100644
index 000000000..72cdebc5b
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/BlurMaskFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for BlurMaskFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/BlurMaskFilter.java
+ */
+public final class BlurMaskFilterNatives {
+
+ public static native long nativeConstructor(float radius, int style);
+
+ private BlurMaskFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasNatives.java
new file mode 100644
index 000000000..398ce9645
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasNatives.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Rect;
+
+/**
+ * Native methods for Canvas JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Canvas.java
+ */
+public final class CanvasNatives {
+ public static native void nFreeCaches();
+
+ public static native void nFreeTextLayoutCaches();
+
+ public static native long nGetNativeFinalizer();
+
+ public static native void nSetCompatibilityVersion(int apiLevel);
+
+ public static native long nInitRaster(long bitmapHandle);
+
+ public static native void nSetBitmap(long canvasHandle, long bitmapHandle);
+
+ public static native boolean nGetClipBounds(long nativeCanvas, Rect bounds);
+
+ public static native boolean nIsOpaque(long canvasHandle);
+
+ public static native int nGetWidth(long canvasHandle);
+
+ public static native int nGetHeight(long canvasHandle);
+
+ public static native int nSave(long canvasHandle, int saveFlags);
+
+ public static native int nSaveLayer(
+ long nativeCanvas, float l, float t, float r, float b, long nativePaint);
+
+ public static native int nSaveLayerAlpha(
+ long nativeCanvas, float l, float t, float r, float b, int alpha);
+
+ public static native int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b);
+
+ public static native void nRestoreUnclippedLayer(
+ long nativeCanvas, int saveCount, long nativePaint);
+
+ public static native boolean nRestore(long canvasHandle);
+
+ public static native void nRestoreToCount(long canvasHandle, int saveCount);
+
+ public static native int nGetSaveCount(long canvasHandle);
+
+ public static native void nTranslate(long canvasHandle, float dx, float dy);
+
+ public static native void nScale(long canvasHandle, float sx, float sy);
+
+ public static native void nRotate(long canvasHandle, float degrees);
+
+ public static native void nSkew(long canvasHandle, float sx, float sy);
+
+ public static native void nConcat(long nativeCanvas, long nativeMatrix);
+
+ public static native void nSetMatrix(long nativeCanvas, long nativeMatrix);
+
+ public static native boolean nClipRect(
+ long nativeCanvas, float left, float top, float right, float bottom, int regionOp);
+
+ public static native boolean nClipPath(long nativeCanvas, long nativePath, int regionOp);
+
+ public static native void nSetDrawFilter(long nativeCanvas, long nativeFilter);
+
+ public static native void nGetMatrix(long nativeCanvas, long nativeMatrix);
+
+ public static native boolean nQuickReject(long nativeCanvas, long nativePath);
+
+ public static native boolean nQuickReject(
+ long nativeCanvas, float left, float top, float right, float bottom);
+
+ private CanvasNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasPropertyNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasPropertyNatives.java
new file mode 100644
index 000000000..8e229f6ea
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CanvasPropertyNatives.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for CanvasProperty JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/CanvasProperty.java
+ */
+public final class CanvasPropertyNatives {
+
+ public static native long nCreateFloat(float initialValue);
+
+ public static native long nCreatePaint(long initialValuePaintPtr);
+
+ private CanvasPropertyNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorFilterNatives.java
new file mode 100644
index 000000000..174fc8feb
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorFilterNatives.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for ColorFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ColorFilter.java
+ */
+public final class ColorFilterNatives {
+
+ public static native long nativeGetFinalizer();
+
+ public static native void nSafeUnref(long nativeFinalizer);
+
+ private ColorFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorMatrixColorFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorMatrixColorFilterNatives.java
new file mode 100644
index 000000000..7b0da18e8
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorMatrixColorFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for ColorMatrixColorFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ColorMatrixColorFilter.java
+ */
+public final class ColorMatrixColorFilterNatives {
+
+ public static native long nativeColorMatrixFilter(float[] array);
+
+ private ColorMatrixColorFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorNatives.java
new file mode 100644
index 000000000..1b0f0ddcf
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorNatives.java
@@ -0,0 +1,16 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for Color JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Color.java
+ */
+public final class ColorNatives {
+
+ public static native void nativeRGBToHSV(int red, int greed, int blue, float[] hsv);
+
+ public static native int nativeHSVToColor(int alpha, float[] hsv);
+
+ private ColorNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorSpaceRgbNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorSpaceRgbNatives.java
new file mode 100644
index 000000000..24f70a34c
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ColorSpaceRgbNatives.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for BitmapFactory JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ColorSpace.java
+ */
+public final class ColorSpaceRgbNatives {
+
+ public static native long nativeGetNativeFinalizer();
+
+ public static native long nativeCreate(
+ float a, float b, float c, float d, float e, float f, float g, float[] xyz);
+
+ private ColorSpaceRgbNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposePathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposePathEffectNatives.java
new file mode 100644
index 000000000..ed6133b98
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposePathEffectNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for ComposePathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ComposePathEffect.java
+ */
+public final class ComposePathEffectNatives {
+
+ public static native long nativeCreate(long nativeOuterpe, long nativeInnerpe);
+
+ private ComposePathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposeShaderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposeShaderNatives.java
new file mode 100644
index 000000000..9e0982dfa
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ComposeShaderNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for ComposeShader JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ComposeShader.java
+ */
+public class ComposeShaderNatives {
+ public static native long nativeCreate(
+ long nativeMatrix, long nativeShaderA, long nativeShaderB, int porterDuffMode);
+
+ private ComposeShaderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/CornerPathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CornerPathEffectNatives.java
new file mode 100644
index 000000000..44c572eee
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/CornerPathEffectNatives.java
@@ -0,0 +1,13 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for CornerPathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/CornerPathEffect.java
+ */
+public final class CornerPathEffectNatives {
+ public static native long nativeCreate(float radius);
+
+ private CornerPathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DashPathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DashPathEffectNatives.java
new file mode 100644
index 000000000..430a74fbf
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DashPathEffectNatives.java
@@ -0,0 +1,13 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for DashPathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/DashPathEffect.java
+ */
+public final class DashPathEffectNatives {
+ public static native long nativeCreate(float[] intervals, float phase);
+
+ private DashPathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DiscretePathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DiscretePathEffectNatives.java
new file mode 100644
index 000000000..f1bfa2f5e
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DiscretePathEffectNatives.java
@@ -0,0 +1,13 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for DiscretePathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/DiscretePathEffect.java
+ */
+public final class DiscretePathEffectNatives {
+ public static native long nativeCreate(float length, float deviation);
+
+ private DiscretePathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/EmbossMaskFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/EmbossMaskFilterNatives.java
new file mode 100644
index 000000000..27ef3da8f
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/EmbossMaskFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for EmbossMaskFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/EmbossMaskFilter.java
+ */
+public final class EmbossMaskFilterNatives {
+
+ public static native long nativeConstructor(
+ float[] direction, float ambient, float specular, float blurRadius);
+
+ private EmbossMaskFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontBuilderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontBuilderNatives.java
new file mode 100644
index 000000000..0a02b45e0
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontBuilderNatives.java
@@ -0,0 +1,47 @@
+package org.robolectric.nativeruntime;
+
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import java.nio.ByteBuffer;
+
+/**
+ * Native methods for android.graphics.fonts.Font$Builder JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/fonts/Font.java
+ */
+public final class FontBuilderNatives {
+ public static native long nInitBuilder();
+
+ public static native void nAddAxis(long builderPtr, int tag, float value);
+
+ public static native long nBuild(
+ long builderPtr,
+ ByteBuffer buffer,
+ String filePath,
+ String localeList,
+ int weight,
+ boolean italic,
+ int ttcIndex);
+
+ public static native long nGetReleaseNativeFont();
+
+ public static native long nClone(
+ long fontPtr, long builderPtr, int weight, boolean italic, int ttcIndex);
+
+ private FontBuilderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyBuilderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyBuilderNatives.java
new file mode 100644
index 000000000..4bc5e7f7f
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyBuilderNatives.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for android.graphics.fonts.FontFamily$Builder JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/fonts/FontFamily.java
+ */
+public final class FontFamilyBuilderNatives {
+
+ public static native long nInitBuilder();
+
+ public static native void nAddFont(long builderPtr, long fontPtr);
+
+ public static native long nBuild(
+ long builderPtr, String langTags, int variant, boolean isCustomFallback);
+
+ public static native long nGetReleaseNativeFamily();
+
+ private FontFamilyBuilderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyNatives.java
new file mode 100644
index 000000000..933baff04
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFamilyNatives.java
@@ -0,0 +1,51 @@
+package org.robolectric.nativeruntime;
+
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import java.nio.ByteBuffer;
+
+/**
+ * Native methods for the deprecated android.graphics.FontFamily JNI registration. Note this is
+ * different from {@link FontsFontFamilyNatives}.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/FontFamily.java
+ */
+public final class FontFamilyNatives {
+
+ public static native long nInitBuilder(String langs, int variant);
+
+ public static native void nAllowUnsupportedFont(long builderPtr);
+
+ public static native long nCreateFamily(long mBuilderPtr);
+
+ public static native long nGetBuilderReleaseFunc();
+
+ public static native long nGetFamilyReleaseFunc();
+ // By passing -1 to weight argument, the weight value is resolved by OS/2 table in the font.
+ // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
+ public static native boolean nAddFont(
+ long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic);
+
+ public static native boolean nAddFontWeightStyle(
+ long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic);
+
+ // The added axis values are only valid for the next nAddFont* method call.
+ public static native void nAddAxisValue(long builderPtr, int tag, float value);
+
+ private FontFamilyNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFileUtilNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFileUtilNatives.java
new file mode 100644
index 000000000..613ecb9b0
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontFileUtilNatives.java
@@ -0,0 +1,35 @@
+package org.robolectric.nativeruntime;
+
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import java.nio.ByteBuffer;
+
+/**
+ * Native methods for android.graphics.fonts.FontFileUtil JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/fonts/FontFileUtil.java
+ */
+public final class FontFileUtilNatives {
+ public static native long nGetFontRevision(ByteBuffer buffer, int index);
+
+ public static native String nGetFontPostScriptName(ByteBuffer buffer, int index);
+
+ public static native int nIsPostScriptType1Font(ByteBuffer buffer, int index);
+
+ private FontFileUtilNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontNatives.java
new file mode 100644
index 000000000..bb1994f81
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontNatives.java
@@ -0,0 +1,61 @@
+package org.robolectric.nativeruntime;
+
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import android.graphics.Paint;
+import android.graphics.RectF;
+import java.nio.ByteBuffer;
+
+/**
+ * Native methods for android.graphics.fonts.Font JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/fonts/Font.java
+ */
+public final class FontNatives {
+ public static native long nGetMinikinFontPtr(long font);
+
+ public static native long nCloneFont(long font);
+
+ public static native ByteBuffer nNewByteBuffer(long font);
+
+ public static native long nGetBufferAddress(long font);
+
+ public static native int nGetSourceId(long font);
+
+ public static native long nGetReleaseNativeFont();
+
+ public static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
+
+ public static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
+
+ public static native String nGetFontPath(long fontPtr);
+
+ public static native String nGetLocaleList(long familyPtr);
+
+ public static native int nGetPackedStyle(long fontPtr);
+
+ public static native int nGetIndex(long fontPtr);
+
+ public static native int nGetAxisCount(long fontPtr);
+
+ public static native long nGetAxisInfo(long fontPtr, int i);
+
+ public static native long[] nGetAvailableFontSet();
+
+ private FontNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontsFontFamilyNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontsFontFamilyNatives.java
new file mode 100644
index 000000000..20379d5ab
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/FontsFontFamilyNatives.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for android.graphics.fonts.FontFamily JNI registration. This is different from
+ * {@link FontFamilyNatives}.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/fonts/FontFamily.java
+ */
+public final class FontsFontFamilyNatives {
+
+ public static native int nGetFontSize(long family);
+
+ public static native long nGetFont(long family, int i);
+
+ public static native String nGetLangTags(long family);
+
+ public static native int nGetVariant(long family);
+
+ private FontsFontFamilyNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererNatives.java
new file mode 100644
index 000000000..8553ecc48
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererNatives.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer.ASurfaceTransactionCallback;
+import android.graphics.HardwareRenderer.FrameCompleteCallback;
+import android.graphics.HardwareRenderer.FrameDrawingCallback;
+import android.graphics.HardwareRenderer.PictureCapturedCallback;
+import android.graphics.HardwareRenderer.PrepareSurfaceControlForWebviewCallback;
+import android.view.Surface;
+import java.io.FileDescriptor;
+
+/**
+ * Native methods for {@link HardwareRenderer} JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/HardwareRenderer.java
+ */
+public final class HardwareRendererNatives {
+ public static native void disableVsync();
+
+ public static native void preload();
+
+ public static native boolean isWebViewOverlaysEnabled();
+
+ public static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
+
+ public static native void nRotateProcessStatsBuffer();
+
+ public static native void nSetProcessStatsBuffer(int fd);
+
+ public static native int nGetRenderThreadTid(long nativeProxy);
+
+ public static native long nCreateRootRenderNode();
+
+ public static native long nCreateProxy(boolean translucent, long rootRenderNode);
+
+ public static native void nDeleteProxy(long nativeProxy);
+
+ public static native boolean nLoadSystemProperties(long nativeProxy);
+
+ public static native void nSetName(long nativeProxy, String name);
+
+ public static native void nSetSurface(long nativeProxy, Surface window, boolean discardBuffer);
+
+ public static native void nSetSurfaceControl(long nativeProxy, long nativeSurfaceControl);
+
+ public static native boolean nPause(long nativeProxy);
+
+ public static native void nSetStopped(long nativeProxy, boolean stopped);
+
+ public static native void nSetLightGeometry(
+ long nativeProxy, float lightX, float lightY, float lightZ, float lightRadius);
+
+ public static native void nSetLightAlpha(
+ long nativeProxy, float ambientShadowAlpha, float spotShadowAlpha);
+
+ public static native void nSetOpaque(long nativeProxy, boolean opaque);
+
+ public static native void nSetColorMode(long nativeProxy, int colorMode);
+
+ public static native void nSetSdrWhitePoint(long nativeProxy, float whitePoint);
+
+ public static native void nSetIsHighEndGfx(boolean isHighEndGfx);
+
+ public static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
+
+ public static native void nDestroy(long nativeProxy, long rootRenderNode);
+
+ public static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
+
+ public static native void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator);
+
+ public static native long nCreateTextureLayer(long nativeProxy);
+
+ public static native void nBuildLayer(long nativeProxy, long node);
+
+ public static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmapHandle);
+
+ public static native void nPushLayerUpdate(long nativeProxy, long layer);
+
+ public static native void nCancelLayerUpdate(long nativeProxy, long layer);
+
+ public static native void nDetachSurfaceTexture(long nativeProxy, long layer);
+
+ public static native void nDestroyHardwareResources(long nativeProxy);
+
+ public static native void nTrimMemory(int level);
+
+ public static native void nOverrideProperty(String name, String value);
+
+ public static native void nFence(long nativeProxy);
+
+ public static native void nStopDrawing(long nativeProxy);
+
+ public static native void nNotifyFramePending(long nativeProxy);
+
+ public static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, int dumpFlags);
+
+ public static native void nAddRenderNode(
+ long nativeProxy, long rootRenderNode, boolean placeFront);
+
+ public static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
+
+ public static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
+
+ public static native void nSetContentDrawBounds(
+ long nativeProxy, int left, int top, int right, int bottom);
+
+ public static native void nSetPictureCaptureCallback(
+ long nativeProxy, PictureCapturedCallback callback);
+
+ public static native void nSetASurfaceTransactionCallback(
+ long nativeProxy, ASurfaceTransactionCallback callback);
+
+ public static native void nSetPrepareSurfaceControlForWebviewCallback(
+ long nativeProxy, PrepareSurfaceControlForWebviewCallback callback);
+
+ public static native void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback);
+
+ public static native void nSetFrameCompleteCallback(
+ long nativeProxy, FrameCompleteCallback callback);
+
+ public static native void nAddObserver(long nativeProxy, long nativeObserver);
+
+ public static native void nRemoveObserver(long nativeProxy, long nativeObserver);
+
+ public static native int nCopySurfaceInto(
+ Surface surface, int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle);
+
+ public static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
+
+ public static native void nSetHighContrastText(boolean enabled);
+
+ public static native void nHackySetRTAnimationsEnabled(boolean enabled);
+
+ public static native void nSetDebuggingEnabled(boolean enabled);
+
+ public static native void nSetIsolatedProcess(boolean enabled);
+
+ public static native void nSetContextPriority(int priority);
+
+ public static native void nAllocateBuffers(long nativeProxy);
+
+ public static native void nSetForceDark(long nativeProxy, boolean enabled);
+
+ public static native void nSetDisplayDensityDpi(int densityDpi);
+
+ public static native void nInitDisplayInfo(
+ int width,
+ int height,
+ float refreshRate,
+ int wideColorDataspace,
+ long appVsyncOffsetNanos,
+ long presentationDeadlineNanos);
+
+ private HardwareRendererNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererObserverNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererObserverNatives.java
new file mode 100644
index 000000000..4de5d34d3
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/HardwareRendererObserverNatives.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for {@link ImageDecoder} JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/HardwareRendererObserver.java
+ */
+public class HardwareRendererObserverNatives {
+ public static native int nGetNextBuffer(long nativePtr, long[] data);
+
+ public native long nCreateObserver(boolean waitForPresentTime);
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ImageDecoderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ImageDecoderNatives.java
new file mode 100644
index 000000000..36d492587
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ImageDecoderNatives.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.Rect;
+import android.util.Size;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * Native methods for {@link ImageDecoder} JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/ImageDecoder.java
+ */
+public final class ImageDecoderNatives {
+
+ public static native ImageDecoder nCreate(long asset, boolean preferAnimation, Source src)
+ throws IOException;
+
+ public static native ImageDecoder nCreate(
+ ByteBuffer buffer, int position, int limit, boolean preferAnimation, Source src)
+ throws IOException;
+
+ public static native ImageDecoder nCreate(
+ byte[] data, int offset, int length, boolean preferAnimation, Source src) throws IOException;
+
+ public static native ImageDecoder nCreate(
+ InputStream is, byte[] storage, boolean preferAnimation, Source src) throws IOException;
+ // The fd must be seekable.
+ public static native ImageDecoder nCreate(
+ FileDescriptor fd, long length, boolean preferAnimation, Source src) throws IOException;
+
+ public static native Bitmap nDecodeBitmap(
+ long nativePtr,
+ ImageDecoder decoder,
+ boolean doPostProcess,
+ int width,
+ int height,
+ Rect cropRect,
+ boolean mutable,
+ int allocator,
+ boolean unpremulRequired,
+ boolean conserveMemory,
+ boolean decodeAsAlphaMask,
+ long desiredColorSpace,
+ boolean extended)
+ throws IOException;
+
+ public static native Size nGetSampledSize(long nativePtr, int sampleSize);
+
+ public static native void nGetPadding(long nativePtr, Rect outRect);
+
+ public static native void nClose(long nativePtr);
+
+ public static native String nGetMimeType(long nativePtr);
+
+ public static native ColorSpace nGetColorSpace(long nativePtr);
+
+ private ImageDecoderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/InterpolatorNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/InterpolatorNatives.java
new file mode 100644
index 000000000..d923579b9
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/InterpolatorNatives.java
@@ -0,0 +1,25 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for Interpolator JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Interpolator.java
+ */
+public final class InterpolatorNatives {
+ public static native long nativeConstructor(int valueCount, int frameCount);
+
+ public static native void nativeDestructor(long nativeInstance);
+
+ public static native void nativeReset(long nativeInstance, int valueCount, int frameCount);
+
+ public static native void nativeSetKeyFrame(
+ long nativeInstance, int index, int msec, float[] values, float[] blend);
+
+ public static native void nativeSetRepeatMirror(
+ long nativeInstance, float repeatCount, boolean mirror);
+
+ public static native int nativeTimeToValues(long nativeInstance, int msec, float[] values);
+
+ private InterpolatorNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/LightingColorFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LightingColorFilterNatives.java
new file mode 100644
index 000000000..264a0c39a
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LightingColorFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for LightingColorFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/LightingColorFilter.java
+ */
+public final class LightingColorFilterNatives {
+
+ public static native long native_CreateLightingFilter(int mul, int add);
+
+ private LightingColorFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/LineBreakerNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LineBreakerNatives.java
new file mode 100644
index 000000000..dbf256e2d
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LineBreakerNatives.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+
+/**
+ * Native methods for LineBreaker JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/text/LineBreaker.java
+ */
+public final class LineBreakerNatives {
+ public static native long nInit(
+ int breakStrategy, int hyphenationFrequency, boolean isJustified, int[] indents);
+
+ public static native long nGetReleaseFunc();
+
+ public static native long nComputeLineBreaks(
+ long nativePtr,
+ char[] text,
+ long measuredTextPtr,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ float[] variableTabStops,
+ float defaultTabStop,
+ @IntRange(from = 0) int indentsOffset);
+
+ public static native int nComputeLineBreaksP(
+ /* non zero */ long nativePtr,
+
+ // Inputs
+ char[] text,
+ /* Non Zero */ long measuredTextPtr,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ float[] variableTabStops,
+ float defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+
+ // Outputs
+ /* LineBreaks */ Object recycle,
+ @IntRange(from = 0) int recycleLength,
+ int[] recycleBreaks,
+ float[] recycleWidths,
+ float[] recycleAscents,
+ float[] recycleDescents,
+ int[] recycleFlags,
+ float[] charWidths);
+
+ public static native int nGetLineCount(long ptr);
+
+ public static native int nGetLineBreakOffset(long ptr, int idx);
+
+ public static native float nGetLineWidth(long ptr, int idx);
+
+ public static native float nGetLineAscent(long ptr, int idx);
+
+ public static native float nGetLineDescent(long ptr, int idx);
+
+ public static native int nGetLineFlag(long ptr, int idx);
+
+ public static native long nGetReleaseResultFunc();
+
+ public static native void nFinishP(long nativePtr);
+
+ private LineBreakerNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/LinearGradientNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LinearGradientNatives.java
new file mode 100644
index 000000000..84b1bc8d3
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/LinearGradientNatives.java
@@ -0,0 +1,35 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for LinearGradient JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/LinearGradient.java
+ */
+public final class LinearGradientNatives {
+ public static native long nativeCreate(
+ long matrix,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ long[] colors,
+ float[] positions,
+ int tileMode,
+ long colorSpaceHandle);
+
+ public static native long nativeCreate1(
+ long matrix,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ int[] colors,
+ float[] positions,
+ int tileMode);
+
+ public static native long nativeCreate2(
+ long matrix, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode);
+
+ private LinearGradientNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/MaskFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MaskFilterNatives.java
new file mode 100644
index 000000000..683035166
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MaskFilterNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for MaskFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/MaskFilter.java
+ */
+public final class MaskFilterNatives {
+
+ public static native void nativeDestructor(long nativeFilter);
+
+ private MaskFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/MatrixNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MatrixNatives.java
new file mode 100644
index 000000000..d3b873af6
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MatrixNatives.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.RectF;
+
+/**
+ * Native methods for Matrix JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Matrix.java
+ */
+public class MatrixNatives {
+
+ public static native long nCreate(long nSrcOrZero);
+
+ public static native long nGetNativeFinalizer();
+
+ public static native boolean nSetRectToRect(long nObject, RectF src, RectF dst, int stf);
+
+ public static native boolean nSetPolyToPoly(
+ long nObject, float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
+
+ public static native void nMapPoints(
+ long nObject,
+ float[] dst,
+ int dstIndex,
+ float[] src,
+ int srcIndex,
+ int ptCount,
+ boolean isPts);
+
+ public static native boolean nMapRect(long nObject, RectF dst, RectF src);
+
+ public static native void nGetValues(long nObject, float[] values);
+
+ public static native void nSetValues(long nObject, float[] values);
+
+ // ------------------ Critical JNI ------------------------
+
+ public static native boolean nIsIdentity(long nObject);
+
+ public static native boolean nIsAffine(long nObject);
+
+ public static native boolean nRectStaysRect(long nObject);
+
+ public static native void nReset(long nObject);
+
+ public static native void nSet(long nObject, long nOther);
+
+ public static native void nSetTranslate(long nObject, float dx, float dy);
+
+ public static native void nSetScale(long nObject, float sx, float sy, float px, float py);
+
+ public static native void nSetScale(long nObject, float sx, float sy);
+
+ public static native void nSetRotate(long nObject, float degrees, float px, float py);
+
+ public static native void nSetRotate(long nObject, float degrees);
+
+ public static native void nSetSinCos(
+ long nObject, float sinValue, float cosValue, float px, float py);
+
+ public static native void nSetSinCos(long nObject, float sinValue, float cosValue);
+
+ public static native void nSetSkew(long nObject, float kx, float ky, float px, float py);
+
+ public static native void nSetSkew(long nObject, float kx, float ky);
+
+ public static native void nSetConcat(long nObject, long nA, long nB);
+
+ public static native void nPreTranslate(long nObject, float dx, float dy);
+
+ public static native void nPreScale(long nObject, float sx, float sy, float px, float py);
+
+ public static native void nPreScale(long nObject, float sx, float sy);
+
+ public static native void nPreRotate(long nObject, float degrees, float px, float py);
+
+ public static native void nPreRotate(long nObject, float degrees);
+
+ public static native void nPreSkew(long nObject, float kx, float ky, float px, float py);
+
+ public static native void nPreSkew(long nObject, float kx, float ky);
+
+ public static native void nPreConcat(long nObject, long nOtherMatrix);
+
+ public static native void nPostTranslate(long nObject, float dx, float dy);
+
+ public static native void nPostScale(long nObject, float sx, float sy, float px, float py);
+
+ public static native void nPostScale(long nObject, float sx, float sy);
+
+ public static native void nPostRotate(long nObject, float degrees, float px, float py);
+
+ public static native void nPostRotate(long nObject, float degrees);
+
+ public static native void nPostSkew(long nObject, float kx, float ky, float px, float py);
+
+ public static native void nPostSkew(long nObject, float kx, float ky);
+
+ public static native void nPostConcat(long nObject, long nOtherMatrix);
+
+ public static native boolean nInvert(long nObject, long nInverse);
+
+ public static native float nMapRadius(long nObject, float radius);
+
+ public static native boolean nEquals(long nA, long nB);
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextBuilderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextBuilderNatives.java
new file mode 100644
index 000000000..2ea75a242
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextBuilderNatives.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+
+/**
+ * Native methods for MeasuredText.Builder JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/text/MeasuredText.java
+ */
+public final class MeasuredTextBuilderNatives {
+
+ public static native /* Non Zero */ long nInitBuilder();
+
+ public static native void nAddStyleRun(
+ /* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+
+ public static native void nAddReplacementRun(
+ /* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ public static native long nBuildMeasuredText(
+ /* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
+ char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout);
+
+ public static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+
+ private MeasuredTextBuilderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextNatives.java
new file mode 100644
index 000000000..5f12a0d67
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/MeasuredTextNatives.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.annotation.IntRange;
+import android.graphics.Rect;
+
+/**
+ * Native methods for MeasuredText JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/text/MeasuredText.java
+ */
+public final class MeasuredTextNatives {
+
+ public static native float nGetWidth(
+ /* Non Zero */ long nativePtr, @IntRange(from = 0) int start, @IntRange(from = 0) int end);
+
+ public static native /* Non Zero */ long nGetReleaseFunc();
+
+ public static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ public static native void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect);
+
+ public static native float nGetCharWidthAt(long nativePtr, int offset);
+
+ private MeasuredTextNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/NIOAccess.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NIOAccess.java
new file mode 100644
index 000000000..cc20e2663
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NIOAccess.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.LongBuffer;
+import java.nio.ShortBuffer;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/**
+ * Analogue to libcore's <a
+ * href="https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:libcore/luni/src/main/java/java/nio/NIOAccess.java">NIOAccess</a>,
+ * which provides access to some internal methods and properties of {@link Buffer}. These methods
+ * are designed to work on the JVM and get called from native code such as libnativehelper.
+ */
+public final class NIOAccess {
+
+ private NIOAccess() {}
+
+ /**
+ * Returns the underlying native pointer to the data of the given Buffer starting at the Buffer's
+ * current position, or 0 if the Buffer is not backed by native heap storage.
+ */
+ public static long getBasePointer(Buffer b) {
+ long address = reflector(BufferReflector.class, b).getAddress();
+
+ if (address == 0L || !b.isDirect()) {
+ return 0L;
+ }
+ return address + ((long) b.position() << elementSizeShift(b));
+ }
+
+ /**
+ * Returns the underlying Java array containing the data of the given Buffer, or null if the
+ * Buffer is not backed by a Java array.
+ */
+ static Object getBaseArray(Buffer b) {
+ return b.hasArray() ? b.array() : null;
+ }
+
+ /**
+ * Returns the offset in bytes from the start of the underlying Java array object containing the
+ * data of the given Buffer to the actual start of the data. The start of the data takes into
+ * account the Buffer's current position. This method is only meaningful if getBaseArray() returns
+ * non-null.
+ */
+ static int getBaseArrayOffset(Buffer b) {
+ return b.hasArray() ? ((b.arrayOffset() + b.position()) << elementSizeShift(b)) : 0;
+ }
+
+ /**
+ * The Android version of java.nio.Buffer has an extra final field called _elementSizeShift that
+ * only depend on the implementation of the buffer. This method can be called instead when wanting
+ * to access the value of that field on the JVM.
+ */
+ public static int elementSizeShift(Buffer buffer) {
+ if (buffer instanceof ByteBuffer) {
+ return 0;
+ }
+ if (buffer instanceof ShortBuffer || buffer instanceof CharBuffer) {
+ return 1;
+ }
+ if (buffer instanceof IntBuffer || buffer instanceof FloatBuffer) {
+ return 2;
+ }
+ if (buffer instanceof LongBuffer || buffer instanceof DoubleBuffer) {
+ return 3;
+ }
+ return 0;
+ }
+
+ @ForType(Buffer.class)
+ interface BufferReflector {
+
+ @Accessor("address")
+ long getAddress();
+ }
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeAllocationRegistryNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeAllocationRegistryNatives.java
new file mode 100644
index 000000000..18a2a3595
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeAllocationRegistryNatives.java
@@ -0,0 +1,13 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for NativeAllocationRegistry JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:libcore/luni/src/main/java/libcore/util/NativeAllocationRegistry.java
+ */
+public final class NativeAllocationRegistryNatives {
+ public static native void applyFreeFunction(long freeFunction, long nativePtr);
+
+ private NativeAllocationRegistryNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeInterpolatorFactoryNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeInterpolatorFactoryNatives.java
new file mode 100644
index 000000000..728cb998f
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NativeInterpolatorFactoryNatives.java
@@ -0,0 +1,34 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for NativeInterpolatorFactory JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/NativeInterpolatorFactory.java
+ */
+public final class NativeInterpolatorFactoryNatives {
+
+ public static native long createAccelerateDecelerateInterpolator();
+
+ public static native long createAccelerateInterpolator(float factor);
+
+ public static native long createAnticipateInterpolator(float tension);
+
+ public static native long createAnticipateOvershootInterpolator(float tension);
+
+ public static native long createBounceInterpolator();
+
+ public static native long createCycleInterpolator(float cycles);
+
+ public static native long createDecelerateInterpolator(float factor);
+
+ public static native long createLinearInterpolator();
+
+ public static native long createOvershootInterpolator(float tension);
+
+ public static native long createPathInterpolator(float[] x, float[] y);
+
+ public static native long createLutInterpolator(float[] values);
+
+ private NativeInterpolatorFactoryNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/NinePatchNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NinePatchNatives.java
new file mode 100644
index 000000000..f76a29812
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/NinePatchNatives.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Rect;
+
+/**
+ * Native methods for NinePatch JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/NinePatch.java
+ */
+public final class NinePatchNatives {
+
+ public static native boolean isNinePatchChunk(byte[] chunk);
+
+ public static native long validateNinePatchChunk(byte[] chunk);
+
+ public static native void nativeFinalize(long chunk);
+
+ public static native long nativeGetTransparentRegion(
+ long bitmapHandle, long chunk, Rect location);
+
+ private NinePatchNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PaintNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PaintNatives.java
new file mode 100644
index 000000000..b803e1c4e
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PaintNatives.java
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+import android.annotation.ColorInt;
+import android.annotation.ColorLong;
+import android.graphics.Paint.FontMetrics;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Rect;
+
+/**
+ * Native methods for Paint JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Paint.java
+ */
+public final class PaintNatives {
+
+ public static native long nGetNativeFinalizer();
+
+ public static native long nInit();
+
+ public static native long nInitWithPaint(long paint);
+
+ public static native int nBreakText(
+ long nObject,
+ char[] text,
+ int index,
+ int count,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth);
+
+ public static native int nBreakText(
+ long nObject,
+ String text,
+ boolean measureForwards,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth);
+
+ public static native int nBreakText(
+ long nObject,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth);
+
+ public static native int nBreakText(
+ long nObject,
+ long typefacePtr,
+ String text,
+ boolean measureForwards,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth);
+
+ public static native int nGetColor(long paintPtr);
+
+ public static native int nGetAlpha(long paintPtr);
+
+ public static native float nGetTextAdvances(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ int contextIndex,
+ int contextCount,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex);
+
+ public static native float nGetTextAdvances(
+ long paintPtr,
+ long typefacePtr,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex);
+
+ public static native float nGetTextAdvances(
+ long paintPtr,
+ char[] text,
+ int index,
+ int count,
+ int contextIndex,
+ int contextCount,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex);
+
+ public static native float nGetTextAdvances(
+ long paintPtr,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex);
+
+ public native int nGetTextRunCursor(
+ long paintPtr,
+ char[] text,
+ int contextStart,
+ int contextLength,
+ int dir,
+ int offset,
+ int cursorOpt);
+
+ public native int nGetTextRunCursor(
+ long paintPtr,
+ String text,
+ int contextStart,
+ int contextEnd,
+ int dir,
+ int offset,
+ int cursorOpt);
+
+ public native int nGetTextRunCursor(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int contextStart,
+ int contextLength,
+ int dir,
+ int offset,
+ int cursorOpt);
+
+ public native int nGetTextRunCursor(
+ long paintPtr,
+ long typefacePtr,
+ String text,
+ int contextStart,
+ int contextEnd,
+ int dir,
+ int offset,
+ int cursorOpt);
+
+ public static native void nGetTextPath(
+ long paintPtr, int bidiFlags, char[] text, int index, int count, float x, float y, long path);
+
+ public static native void nGetTextPath(
+ long paintPtr, int bidiFlags, String text, int start, int end, float x, float y, long path);
+
+ public static native void nGetTextPath(
+ long paintPtr,
+ long typefacePtr,
+ int bidiFlags,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ long path);
+
+ public static native void nGetTextPath(
+ long paintPtr,
+ long typefacePtr,
+ int bidiFlags,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ long path);
+
+ public static native void nGetStringBounds(
+ long nativePaint, String text, int start, int end, int bidiFlags, Rect bounds);
+
+ public static native void nGetStringBounds(
+ long nativePaint,
+ long typefacePtr,
+ String text,
+ int start,
+ int end,
+ int bidiFlags,
+ Rect bounds);
+
+ public static native void nGetCharArrayBounds(
+ long nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds);
+
+ public static native void nGetCharArrayBounds(
+ long nativePaint,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ int bidiFlags,
+ Rect bounds);
+
+ public static native boolean nHasGlyph(long paintPtr, int bidiFlags, String string);
+
+ public static native boolean nHasGlyph(
+ long paintPtr, long typefacePtr, int bidiFlags, String string);
+
+ public static native float nGetRunAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset);
+
+ public static native float nGetRunAdvance(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset);
+
+ public static native int nGetOffsetForAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ float advance);
+
+ public static native int nGetOffsetForAdvance(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ float advance);
+
+ public static native int nSetTextLocales(long paintPtr, String locales);
+
+ public static native void nSetFontFeatureSettings(long paintPtr, String settings);
+
+ public static native float nGetFontMetrics(long paintPtr, FontMetrics metrics);
+
+ public static native float nGetFontMetrics(long paintPtr, long typefacePtr, FontMetrics metrics);
+
+ public static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
+
+ public static native int nGetFontMetricsInt(long paintPtr, long typefacePtr, FontMetricsInt fmi);
+
+ public static native void nReset(long paintPtr);
+
+ public static native void nSet(long paintPtrDest, long paintPtrSrc);
+
+ public static native int nGetStyle(long paintPtr);
+
+ public static native void nSetStyle(long paintPtr, int style);
+
+ public static native int nGetStrokeCap(long paintPtr);
+
+ public static native void nSetStrokeCap(long paintPtr, int cap);
+
+ public static native int nGetStrokeJoin(long paintPtr);
+
+ public static native void nSetStrokeJoin(long paintPtr, int join);
+
+ public static native boolean nGetFillPath(long paintPtr, long src, long dst);
+
+ public static native long nSetShader(long paintPtr, long shader);
+
+ public static native long nSetColorFilter(long paintPtr, long filter);
+
+ public static native void nSetXfermode(long paintPtr, int xfermode);
+
+ public static native long nSetPathEffect(long paintPtr, long effect);
+
+ public static native long nSetMaskFilter(long paintPtr, long maskfilter);
+
+ public static native void nSetTypeface(long paintPtr, long typeface);
+
+ public static native int nGetTextAlign(long paintPtr);
+
+ public static native void nSetTextAlign(long paintPtr, int align);
+
+ public static native void nSetTextLocalesByMinikinLocaleListId(
+ long paintPtr, int mMinikinLocaleListId);
+
+ public static native void nSetShadowLayer(
+ long paintPtr,
+ float radius,
+ float dx,
+ float dy,
+ long colorSpaceHandle,
+ @ColorLong long shadowColor);
+
+ public static native void nSetShadowLayer(
+ long paintPtr, float radius, float dx, float dy, @ColorInt int shadowColor);
+
+ public static native boolean nHasShadowLayer(long paintPtr);
+
+ public static native float nGetLetterSpacing(long paintPtr);
+
+ public static native void nSetLetterSpacing(long paintPtr, float letterSpacing);
+
+ public static native float nGetWordSpacing(long paintPtr);
+
+ public static native void nSetWordSpacing(long paintPtr, float wordSpacing);
+
+ public static native int nGetStartHyphenEdit(long paintPtr);
+
+ public static native int nGetEndHyphenEdit(long paintPtr);
+
+ public static native void nSetStartHyphenEdit(long paintPtr, int hyphen);
+
+ public static native void nSetEndHyphenEdit(long paintPtr, int hyphen);
+
+ public static native void nSetStrokeMiter(long paintPtr, float miter);
+
+ public static native float nGetStrokeMiter(long paintPtr);
+
+ public static native void nSetStrokeWidth(long paintPtr, float width);
+
+ public static native float nGetStrokeWidth(long paintPtr);
+
+ public static native void nSetAlpha(long paintPtr, int a);
+
+ public static native void nSetDither(long paintPtr, boolean dither);
+
+ public static native int nGetFlags(long paintPtr);
+
+ public static native void nSetFlags(long paintPtr, int flags);
+
+ public static native int nGetHinting(long paintPtr);
+
+ public static native void nSetHinting(long paintPtr, int mode);
+
+ public static native void nSetAntiAlias(long paintPtr, boolean aa);
+
+ public static native void nSetLinearText(long paintPtr, boolean linearText);
+
+ public static native void nSetSubpixelText(long paintPtr, boolean subpixelText);
+
+ public static native void nSetUnderlineText(long paintPtr, boolean underlineText);
+
+ public static native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
+
+ public static native void nSetFilterBitmap(long paintPtr, boolean filter);
+
+ public static native void nSetColor(long paintPtr, long colorSpaceHandle, @ColorLong long color);
+
+ public static native void nSetColor(long paintPtr, @ColorInt int color);
+
+ public static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
+
+ public static native boolean nIsElegantTextHeight(long paintPtr);
+
+ public static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
+
+ public static native float nGetTextSize(long paintPtr);
+
+ public static native float nGetTextScaleX(long paintPtr);
+
+ public static native void nSetTextScaleX(long paintPtr, float scaleX);
+
+ public static native float nGetTextSkewX(long paintPtr);
+
+ public static native void nSetTextSkewX(long paintPtr, float skewX);
+
+ public static native float nAscent(long paintPtr);
+
+ public static native float nAscent(long paintPtr, long typefacePtr);
+
+ public static native float nDescent(long paintPtr);
+
+ public static native float nDescent(long paintPtr, long typefacePtr);
+
+ public static native float nGetUnderlinePosition(long paintPtr);
+
+ public static native float nGetUnderlineThickness(long paintPtr);
+
+ public static native float nGetStrikeThruPosition(long paintPtr);
+
+ public static native float nGetStrikeThruThickness(long paintPtr);
+
+ public static native void nSetTextSize(long paintPtr, float textSize);
+
+ public static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
+
+ public static native void nGetFontMetricsIntForText(
+ long paintPtr,
+ char[] text,
+ int start,
+ int count,
+ int ctxStart,
+ int ctxCount,
+ boolean isRtl,
+ FontMetricsInt outMetrics);
+
+ public static native void nGetFontMetricsIntForText(
+ long paintPtr,
+ String text,
+ int start,
+ int count,
+ int ctxStart,
+ int ctxCount,
+ boolean isRtl,
+ FontMetricsInt outMetrics);
+
+ public static native float nGetRunCharacterAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset,
+ float[] advances,
+ int advancesIndex);
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathDashPathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathDashPathEffectNatives.java
new file mode 100644
index 000000000..5c508fb4e
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathDashPathEffectNatives.java
@@ -0,0 +1,15 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PathDashPathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PathDashPathEffect.java
+ */
+public final class PathDashPathEffectNatives {
+
+ public static native long nativeCreate(
+ long nativePath, float advance, float phase, int nativeStyle);
+
+ private PathDashPathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathEffectNatives.java
new file mode 100644
index 000000000..33215a4e6
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathEffectNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PathEffect.java
+ */
+public final class PathEffectNatives {
+
+ public static native void nativeDestructor(long nativePatheffect);
+
+ private PathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathMeasureNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathMeasureNatives.java
new file mode 100644
index 000000000..e1b8d8385
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathMeasureNatives.java
@@ -0,0 +1,34 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PathMeasure JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PathMeasure.java
+ */
+public final class PathMeasureNatives {
+
+ public static native long native_create(long nativePath, boolean forceClosed);
+
+ public static native void native_setPath(
+ long nativeInstance, long nativePath, boolean forceClosed);
+
+ public static native float native_getLength(long nativeInstance);
+
+ public static native boolean native_getPosTan(
+ long nativeInstance, float distance, float[] pos, float[] tan);
+
+ public static native boolean native_getMatrix(
+ long nativeInstance, float distance, long nativeMatrix, int flags);
+
+ public static native boolean native_getSegment(
+ long nativeInstance, float startD, float stopD, long nativePath, boolean startWithMoveTo);
+
+ public static native boolean native_isClosed(long nativeInstance);
+
+ public static native boolean native_nextContour(long nativeInstance);
+
+ public static native void native_destroy(long nativeInstance);
+
+ private PathMeasureNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathNatives.java
new file mode 100644
index 000000000..0870f6bf7
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathNatives.java
@@ -0,0 +1,111 @@
+package org.robolectric.nativeruntime;
+
+import android.graphics.RectF;
+
+/**
+ * Native methods for Path JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Path.java
+ */
+public final class PathNatives {
+
+ public static native long nInit();
+
+ public static native long nInit(long nPath);
+
+ public static native long nGetFinalizer();
+
+ public static native void nSet(long nativeDst, long nSrc);
+
+ public static native void nComputeBounds(long nPath, RectF bounds);
+
+ public static native void nIncReserve(long nPath, int extraPtCount);
+
+ public static native void nMoveTo(long nPath, float x, float y);
+
+ public static native void nRMoveTo(long nPath, float dx, float dy);
+
+ public static native void nLineTo(long nPath, float x, float y);
+
+ public static native void nRLineTo(long nPath, float dx, float dy);
+
+ public static native void nQuadTo(long nPath, float x1, float y1, float x2, float y2);
+
+ public static native void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2);
+
+ public static native void nCubicTo(
+ long nPath, float x1, float y1, float x2, float y2, float x3, float y3);
+
+ public static native void nRCubicTo(
+ long nPath, float x1, float y1, float x2, float y2, float x3, float y3);
+
+ public static native void nArcTo(
+ long nPath,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweepAngle,
+ boolean forceMoveTo);
+
+ public static native void nClose(long nPath);
+
+ public static native void nAddRect(
+ long nPath, float left, float top, float right, float bottom, int dir);
+
+ public static native void nAddOval(
+ long nPath, float left, float top, float right, float bottom, int dir);
+
+ public static native void nAddCircle(long nPath, float x, float y, float radius, int dir);
+
+ public static native void nAddArc(
+ long nPath,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweepAngle);
+
+ public static native void nAddRoundRect(
+ long nPath, float left, float top, float right, float bottom, float rx, float ry, int dir);
+
+ public static native void nAddRoundRect(
+ long nPath, float left, float top, float right, float bottom, float[] radii, int dir);
+
+ public static native void nAddPath(long nPath, long src, float dx, float dy);
+
+ public static native void nAddPath(long nPath, long src);
+
+ public static native void nAddPath(long nPath, long src, long matrix);
+
+ public static native void nOffset(long nPath, float dx, float dy);
+
+ public static native void nSetLastPoint(long nPath, float dx, float dy);
+
+ public static native void nTransform(long nPath, long matrix, long dstPath);
+
+ public static native void nTransform(long nPath, long matrix);
+
+ public static native boolean nOp(long path1, long path2, int op, long result);
+
+ public static native boolean nIsRect(long nPath, RectF rect);
+
+ public static native void nReset(long nPath);
+
+ public static native void nRewind(long nPath);
+
+ public static native boolean nIsEmpty(long nPath);
+
+ public static native boolean nIsConvex(long nPath);
+
+ public static native int nGetFillType(long nPath);
+
+ public static native void nSetFillType(long nPath, int ft);
+
+ public static native float[] nApproximate(long nPath, float error);
+
+ private PathNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathParserNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathParserNatives.java
new file mode 100644
index 000000000..fb1d5f2c8
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PathParserNatives.java
@@ -0,0 +1,31 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PathParser JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PathParser.java
+ */
+public final class PathParserNatives {
+
+ public static native void nParseStringForPath(long pathPtr, String pathString, int stringLength);
+
+ public static native long nCreatePathDataFromString(String pathString, int stringLength);
+
+ public static native void nCreatePathFromPathData(long outPathPtr, long pathData);
+
+ public static native long nCreateEmptyPathData();
+
+ public static native long nCreatePathData(long nativePtr);
+
+ public static native boolean nInterpolatePathData(
+ long outDataPtr, long fromDataPtr, long toDataPtr, float fraction);
+
+ public static native void nFinalize(long nativePtr);
+
+ public static native boolean nCanMorph(long fromDataPtr, long toDataPtr);
+
+ public static native void nSetPathData(long outDataPtr, long fromDataPtr);
+
+ private PathParserNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PictureNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PictureNatives.java
new file mode 100644
index 000000000..c2bdba6a2
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PictureNatives.java
@@ -0,0 +1,32 @@
+package org.robolectric.nativeruntime;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Native methods for Picture JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Picture.java
+ */
+public class PictureNatives {
+
+ public static native long nativeConstructor(long nativeSrcOr0);
+
+ public static native long nativeCreateFromStream(InputStream stream, byte[] storage);
+
+ public static native int nativeGetWidth(long nativePicture);
+
+ public static native int nativeGetHeight(long nativePicture);
+
+ public static native long nativeBeginRecording(long nativeCanvas, int w, int h);
+
+ public static native void nativeEndRecording(long nativeCanvas);
+
+ public static native void nativeDraw(long nativeCanvas, long nativePicture);
+
+ public static native boolean nativeWriteToStream(
+ long nativePicture, OutputStream stream, byte[] storage);
+
+ public static native void nativeDestructor(long nativePicture);
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PorterDuffColorFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PorterDuffColorFilterNatives.java
new file mode 100644
index 000000000..8071bfd51
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PorterDuffColorFilterNatives.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PorterDuffColorFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PorterDuffColorFilter.java
+ */
+public final class PorterDuffColorFilterNatives {
+
+ public static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
+
+ private PorterDuffColorFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/PropertyValuesHolderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PropertyValuesHolderNatives.java
new file mode 100644
index 000000000..7cc6e010a
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/PropertyValuesHolderNatives.java
@@ -0,0 +1,41 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for PropertyValuesHolder JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/PropertyValuesHolder.java
+ */
+public final class PropertyValuesHolderNatives {
+
+ public static native long nGetIntMethod(Class<?> targetClass, String methodName);
+
+ public static native long nGetFloatMethod(Class<?> targetClass, String methodName);
+
+ public static native long nGetMultipleIntMethod(
+ Class<?> targetClass, String methodName, int numParams);
+
+ public static native long nGetMultipleFloatMethod(
+ Class<?> targetClass, String methodName, int numParams);
+
+ public static native void nCallIntMethod(Object target, long methodID, int arg);
+
+ public static native void nCallFloatMethod(Object target, long methodID, float arg);
+
+ public static native void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2);
+
+ public static native void nCallFourIntMethod(
+ Object target, long methodID, int arg1, int arg2, int arg3, int arg4);
+
+ public static native void nCallMultipleIntMethod(Object target, long methodID, int[] args);
+
+ public static native void nCallTwoFloatMethod(
+ Object target, long methodID, float arg1, float arg2);
+
+ public static native void nCallFourFloatMethod(
+ Object target, long methodID, float arg1, float arg2, float arg3, float arg4);
+
+ public static native void nCallMultipleFloatMethod(Object target, long methodID, float[] args);
+
+ private PropertyValuesHolderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RadialGradientNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RadialGradientNatives.java
new file mode 100644
index 000000000..6c21a81b8
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RadialGradientNatives.java
@@ -0,0 +1,33 @@
+package org.robolectric.nativeruntime;
+
+import android.annotation.ColorLong;
+
+/**
+ * Native methods for RadialGradient JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RadialGradient.java
+ */
+public class RadialGradientNatives {
+
+ public static native long nativeCreate(
+ long matrix,
+ float startX,
+ float startY,
+ float startRadius,
+ float endX,
+ float endY,
+ float endRadius,
+ @ColorLong long[] colors,
+ float[] positions,
+ int tileMode,
+ long colorSpaceHandle);
+
+ public static native long nativeCreate1(
+ long matrix, float x, float y, float radius, int[] colors, float[] positions, int tileMode);
+
+ public static native long nativeCreate2(
+ long matrix, float x, float y, float radius, int color0, int color1, int tileMode);
+
+ RadialGradientNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RecordingCanvasNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RecordingCanvasNatives.java
new file mode 100644
index 000000000..da67153f5
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RecordingCanvasNatives.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for RecordingCanvas JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RecordingCanvas.java
+ */
+public final class RecordingCanvasNatives {
+
+ public static native long nCreateDisplayListCanvas(long node, int width, int height);
+
+ public static native void nResetDisplayListCanvas(long canvas, long node, int width, int height);
+
+ public static native int nGetMaximumTextureWidth();
+
+ public static native int nGetMaximumTextureHeight();
+
+ public static native void nEnableZ(long renderer, boolean enableZ);
+
+ public static native void nFinishRecording(long renderer, long renderNode);
+
+ public static native void nDrawRenderNode(long renderer, long renderNode);
+
+ public static native void nDrawTextureLayer(long renderer, long layer);
+
+ public static native void nDrawCircle(
+ long renderer, long propCx, long propCy, long propRadius, long propPaint);
+
+ public static native void nDrawRipple(
+ long renderer,
+ long propCx,
+ long propCy,
+ long propRadius,
+ long propPaint,
+ long propProgress,
+ long turbulencePhase,
+ int color,
+ long runtimeEffect);
+
+ public static native void nDrawRoundRect(
+ long renderer,
+ long propLeft,
+ long propTop,
+ long propRight,
+ long propBottom,
+ long propRx,
+ long propRy,
+ long propPaint);
+
+ public static native void nDrawWebViewFunctor(long canvas, int functor);
+
+ private RecordingCanvasNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionIteratorNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionIteratorNatives.java
new file mode 100644
index 000000000..ea2f17d44
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionIteratorNatives.java
@@ -0,0 +1,20 @@
+package org.robolectric.nativeruntime;
+
+import android.graphics.Rect;
+
+/**
+ * Native methods for RegionIterator JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RegionIterator.java
+ */
+public final class RegionIteratorNatives {
+
+ public static native long nativeConstructor(long nativeRegion);
+
+ public static native void nativeDestructor(long nativeIter);
+
+ public static native boolean nativeNext(long nativeIter, Rect r);
+
+ private RegionIteratorNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionNatives.java
new file mode 100644
index 000000000..c6d1bae52
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RegionNatives.java
@@ -0,0 +1,66 @@
+package org.robolectric.nativeruntime;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Parcel;
+
+/**
+ * Native methods for Region JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Region.java
+ */
+public final class RegionNatives {
+
+ // Must be this style to match AOSP branch
+ public long mNativeRegion;
+
+ public static native boolean nativeEquals(long nativeR1, long nativeR2);
+
+ public static native long nativeConstructor();
+
+ public static native void nativeDestructor(long nativeRegion);
+
+ public static native void nativeSetRegion(long nativeDst, long nativeSrc);
+
+ public static native boolean nativeSetRect(
+ long nativeDst, int left, int top, int right, int bottom);
+
+ public static native boolean nativeSetPath(long nativeDst, long nativePath, long nativeClip);
+
+ public static native boolean nativeGetBounds(long nativeRegion, Rect rect);
+
+ public static native boolean nativeGetBoundaryPath(long nativeRegion, long nativePath);
+
+ public static native boolean nativeOp(
+ long nativeDst, int left, int top, int right, int bottom, int op);
+
+ public static native boolean nativeOp(long nativeDst, Rect rect, long nativeRegion, int op);
+
+ public static native boolean nativeOp(
+ long nativeDst, long nativeRegion1, long nativeRegion2, int op);
+
+ public static native long nativeCreateFromParcel(Parcel p);
+
+ public static native boolean nativeWriteToParcel(long nativeRegion, Parcel p);
+
+ public static native String nativeToString(long nativeRegion);
+
+ public native boolean isEmpty();
+
+ public native boolean isRect();
+
+ public native boolean isComplex();
+
+ public native boolean contains(int x, int y);
+
+ public native boolean quickContains(int left, int top, int right, int bottom);
+
+ public native boolean quickReject(int left, int top, int right, int bottom);
+
+ public native boolean quickReject(Region rgn);
+
+ public native void translate(int dx, int dy, Region dst);
+
+ public native void scale(float scale, Region dst);
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderEffectNatives.java
new file mode 100644
index 000000000..dcf82d1b4
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderEffectNatives.java
@@ -0,0 +1,39 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for RenderEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RenderEffect.java
+ */
+public final class RenderEffectNatives {
+
+ public static native long nativeCreateOffsetEffect(
+ float offsetX, float offsetY, long nativeInput);
+
+ public static native long nativeCreateBlurEffect(
+ float radiusX, float radiusY, long nativeInput, int edgeTreatment);
+
+ public static native long nativeCreateBitmapEffect(
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom);
+
+ public static native long nativeCreateColorFilterEffect(long colorFilter, long nativeInput);
+
+ public static native long nativeCreateBlendModeEffect(long dst, long src, int blendmode);
+
+ public static native long nativeCreateChainEffect(long outer, long inner);
+
+ public static native long nativeCreateShaderEffect(long shader);
+
+ public static native long nativeGetFinalizer();
+
+ private RenderEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeAnimatorNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeAnimatorNatives.java
new file mode 100644
index 000000000..3d7de6e8d
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeAnimatorNatives.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for RenderNodeAnimator JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RenderNodeAnimator.java
+ */
+public final class RenderNodeAnimatorNatives {
+
+ public static native long nCreateAnimator(int property, float finalValue);
+
+ public static native long nCreateCanvasPropertyFloatAnimator(
+ long canvasProperty, float finalValue);
+
+ public static native long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue);
+
+ public static native long nCreateRevealAnimator(int x, int y, float startRadius, float endRadius);
+
+ public static native void nSetStartValue(long nativePtr, float startValue);
+
+ public static native void nSetDuration(long nativePtr, long duration);
+
+ public static native long nGetDuration(long nativePtr);
+
+ public static native void nSetStartDelay(long nativePtr, long startDelay);
+
+ public static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+
+ public static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+
+ public static native void nSetListener(long animPtr, Object listener);
+
+ public static native void nStart(long animPtr);
+
+ public static native void nEnd(long animPtr);
+
+ private RenderNodeAnimatorNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeNatives.java
new file mode 100644
index 000000000..adda69e61
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RenderNodeNatives.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.RenderNode.PositionUpdateListener;
+
+/**
+ * Native methods for RenderNode JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RenderNode.java
+ */
+public final class RenderNodeNatives {
+
+ public static native long nCreate(String name);
+
+ public static native long nGetNativeFinalizer();
+
+ public static native void nOutput(long renderNode);
+
+ public static native int nGetUsageSize(long renderNode);
+
+ public static native int nGetAllocatedSize(long renderNode);
+
+ public static native void nRequestPositionUpdates(
+ long renderNode, PositionUpdateListener callback);
+
+ public static native void nAddAnimator(long renderNode, long animatorPtr);
+
+ public static native void nEndAllAnimators(long renderNode);
+
+ public static native void nDiscardDisplayList(long renderNode);
+
+ public static native boolean nIsValid(long renderNode);
+
+ public static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
+
+ public static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
+
+ public static native boolean nHasIdentityMatrix(long renderNode);
+
+ public static native boolean nOffsetTopAndBottom(long renderNode, int offset);
+
+ public static native boolean nOffsetLeftAndRight(long renderNode, int offset);
+
+ public static native boolean nSetLeftTopRightBottom(
+ long renderNode, int left, int top, int right, int bottom);
+
+ public static native boolean nSetLeft(long renderNode, int left);
+
+ public static native boolean nSetTop(long renderNode, int top);
+
+ public static native boolean nSetRight(long renderNode, int right);
+
+ public static native boolean nSetBottom(long renderNode, int bottom);
+
+ public static native int nGetLeft(long renderNode);
+
+ public static native int nGetTop(long renderNode);
+
+ public static native int nGetRight(long renderNode);
+
+ public static native int nGetBottom(long renderNode);
+
+ public static native boolean nSetCameraDistance(long renderNode, float distance);
+
+ public static native boolean nSetPivotY(long renderNode, float pivotY);
+
+ public static native boolean nSetPivotX(long renderNode, float pivotX);
+
+ public static native boolean nResetPivot(long renderNode);
+
+ public static native boolean nSetLayerType(long renderNode, int layerType);
+
+ public static native int nGetLayerType(long renderNode);
+
+ public static native boolean nSetLayerPaint(long renderNode, long paint);
+
+ public static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
+
+ public static native boolean nGetClipToBounds(long renderNode);
+
+ public static native boolean nSetClipBounds(
+ long renderNode, int left, int top, int right, int bottom);
+
+ public static native boolean nSetClipBoundsEmpty(long renderNode);
+
+ public static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
+
+ public static native boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive);
+
+ public static native boolean nSetOutlineRoundRect(
+ long renderNode, int left, int top, int right, int bottom, float radius, float alpha);
+
+ public static native boolean nSetOutlinePath(long renderNode, long nativePath, float alpha);
+
+ public static native boolean nSetOutlineEmpty(long renderNode);
+
+ public static native boolean nSetOutlineNone(long renderNode);
+
+ public static native boolean nClearStretch(long renderNode);
+
+ public static native boolean nStretch(
+ long renderNode, float vecX, float vecY, float maxStretchX, float maxStretchY);
+
+ public static native boolean nHasShadow(long renderNode);
+
+ public static native boolean nSetSpotShadowColor(long renderNode, int color);
+
+ public static native boolean nSetAmbientShadowColor(long renderNode, int color);
+
+ public static native int nGetSpotShadowColor(long renderNode);
+
+ public static native int nGetAmbientShadowColor(long renderNode);
+
+ public static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
+
+ public static native boolean nSetRevealClip(
+ long renderNode, boolean shouldClip, float x, float y, float radius);
+
+ public static native boolean nSetAlpha(long renderNode, float alpha);
+
+ public static native boolean nSetRenderEffect(long renderNode, long renderEffect);
+
+ public static native boolean nSetHasOverlappingRendering(
+ long renderNode, boolean hasOverlappingRendering);
+
+ public static native void nSetUsageHint(long renderNode, int usageHint);
+
+ public static native boolean nSetElevation(long renderNode, float lift);
+
+ public static native boolean nSetTranslationX(long renderNode, float translationX);
+
+ public static native boolean nSetTranslationY(long renderNode, float translationY);
+
+ public static native boolean nSetTranslationZ(long renderNode, float translationZ);
+
+ public static native boolean nSetRotation(long renderNode, float rotation);
+
+ public static native boolean nSetRotationX(long renderNode, float rotationX);
+
+ public static native boolean nSetRotationY(long renderNode, float rotationY);
+
+ public static native boolean nSetScaleX(long renderNode, float scaleX);
+
+ public static native boolean nSetScaleY(long renderNode, float scaleY);
+
+ public static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
+
+ public static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
+
+ public static native boolean nHasOverlappingRendering(long renderNode);
+
+ public static native boolean nGetAnimationMatrix(long renderNode, long animationMatrix);
+
+ public static native boolean nGetClipToOutline(long renderNode);
+
+ public static native float nGetAlpha(long renderNode);
+
+ public static native float nGetCameraDistance(long renderNode);
+
+ public static native float nGetScaleX(long renderNode);
+
+ public static native float nGetScaleY(long renderNode);
+
+ public static native float nGetElevation(long renderNode);
+
+ public static native float nGetTranslationX(long renderNode);
+
+ public static native float nGetTranslationY(long renderNode);
+
+ public static native float nGetTranslationZ(long renderNode);
+
+ public static native float nGetRotation(long renderNode);
+
+ public static native float nGetRotationX(long renderNode);
+
+ public static native float nGetRotationY(long renderNode);
+
+ public static native boolean nIsPivotExplicitlySet(long renderNode);
+
+ public static native float nGetPivotX(long renderNode);
+
+ public static native float nGetPivotY(long renderNode);
+
+ public static native int nGetWidth(long renderNode);
+
+ public static native int nGetHeight(long renderNode);
+
+ public static native boolean nSetAllowForceDark(long renderNode, boolean allowForceDark);
+
+ public static native boolean nGetAllowForceDark(long renderNode);
+
+ public static native long nGetUniqueId(long renderNode);
+
+ private RenderNodeNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/RuntimeShaderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RuntimeShaderNatives.java
new file mode 100644
index 000000000..6d4e49f23
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/RuntimeShaderNatives.java
@@ -0,0 +1,23 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for RuntimeShader JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/RuntimeShader.java
+ */
+public class RuntimeShaderNatives {
+
+ public static native long nativeGetFinalizer();
+
+ public static native long nativeCreateBuilder(String sksl);
+
+ public static native long nativeCreateShader(long shaderBuilder, long matrix, boolean isOpaque);
+
+ public static native void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, float[] uniforms);
+
+ public static native void nativeUpdateShader(long shaderBuilder, String shaderName, long shader);
+
+ private RuntimeShaderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/ShaderNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ShaderNatives.java
new file mode 100644
index 000000000..b50fa5fc2
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/ShaderNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for Shader JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Shader.java
+ */
+public final class ShaderNatives {
+
+ public static native long nativeGetFinalizer();
+
+ private ShaderNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/SumPathEffectNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SumPathEffectNatives.java
new file mode 100644
index 000000000..d7edf0e56
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SumPathEffectNatives.java
@@ -0,0 +1,14 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for SumPathEffect JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/SumPathEffect.java
+ */
+public final class SumPathEffectNatives {
+
+ public static native long nativeCreate(long first, long second);
+
+ private SumPathEffectNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/SurfaceNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SurfaceNatives.java
new file mode 100644
index 000000000..882d811a5
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SurfaceNatives.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+
+/**
+ * Native methods for Surface JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/view/Surface.java
+ */
+public final class SurfaceNatives {
+
+ public static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture);
+
+ public static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
+
+ public static native long nativeGetFromSurfaceControl(
+ long surfaceObject, long surfaceControlNativeObject);
+
+ public static native long nativeGetFromBlastBufferQueue(
+ long surfaceObject, long blastBufferQueueNativeObject);
+
+ public static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty);
+
+ public static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
+
+ public static native void nativeRelease(long nativeObject);
+
+ public static native boolean nativeIsValid(long nativeObject);
+
+ public static native boolean nativeIsConsumerRunningBehind(long nativeObject);
+
+ public static native long nativeReadFromParcel(long nativeObject, Parcel source);
+
+ public static native void nativeWriteToParcel(long nativeObject, Parcel dest);
+
+ public static native void nativeAllocateBuffers(long nativeObject);
+
+ public static native int nativeGetWidth(long nativeObject);
+
+ public static native int nativeGetHeight(long nativeObject);
+
+ public static native long nativeGetNextFrameNumber(long nativeObject);
+
+ public static native int nativeSetScalingMode(long nativeObject, int scalingMode);
+
+ public static native int nativeForceScopedDisconnect(long nativeObject);
+
+ public static native int nativeAttachAndQueueBufferWithColorSpace(
+ long nativeObject, HardwareBuffer buffer, int colorSpaceId);
+
+ public static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled);
+
+ public static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
+
+ public static native int nativeSetFrameRate(
+ long nativeObject, float frameRate, int compatibility, int changeFrameRateStrategy);
+
+ private SurfaceNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/SweepGradientNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SweepGradientNatives.java
new file mode 100644
index 000000000..85d5a2d82
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/SweepGradientNatives.java
@@ -0,0 +1,20 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for SweepGradient JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/SweepGradient.java
+ */
+public class SweepGradientNatives {
+
+ public static native long nativeCreate(
+ long matrix, float x, float y, long[] colors, float[] positions, long colorSpaceHandle);
+
+ public static native long nativeCreate1(
+ long matrix, float x, float y, int[] colors, float[] positions);
+
+ public static native long nativeCreate2(long matrix, float x, float y, int color0, int color1);
+
+ private SweepGradientNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/TableMaskFilterNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/TableMaskFilterNatives.java
new file mode 100644
index 000000000..ca7f4f096
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/TableMaskFilterNatives.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2006 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 org.robolectric.nativeruntime;
+
+/**
+ * Native methods for TableMaskFilter JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/TableMaskFilter.java
+ */
+public final class TableMaskFilterNatives {
+
+ public static native long nativeNewTable(byte[] table);
+
+ public static native long nativeNewClip(int min, int max);
+
+ public static native long nativeNewGamma(float gamma);
+
+ private TableMaskFilterNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/TypefaceNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/TypefaceNatives.java
new file mode 100644
index 000000000..204d89a62
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/TypefaceNatives.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 org.robolectric.nativeruntime;
+
+import android.graphics.Typeface;
+import android.graphics.fonts.FontVariationAxis;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+/**
+ * Native methods for Typeface JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/Typeface.java
+ */
+public final class TypefaceNatives {
+
+ public static native long nativeCreateFromTypeface(long nativeInstance, int style);
+
+ public static native long nativeCreateFromTypefaceWithExactStyle(
+ long nativeInstance, int weight, boolean italic);
+
+ public static native long nativeCreateFromTypefaceWithVariation(
+ long nativeInstance, List<FontVariationAxis> axes);
+
+ public static native long nativeCreateWeightAlias(long nativeInstance, int weight);
+
+ public static native long nativeCreateFromArray(
+ long[] familyArray, long fallbackTypeface, int weight, int italic);
+
+ public static native int[] nativeGetSupportedAxes(long nativeInstance);
+
+ public static native void nativeSetDefault(long nativePtr);
+
+ public static native int nativeGetStyle(long nativePtr);
+
+ public static native int nativeGetWeight(long nativePtr);
+
+ public static native long nativeGetReleaseFunc();
+
+ public static native int nativeGetFamilySize(long naitvePtr);
+
+ public static native long nativeGetFamily(long nativePtr, int index);
+
+ public static native void nativeRegisterGenericFamily(String str, long nativePtr);
+
+ public static native int nativeWriteTypefaces(ByteBuffer buffer, long[] nativePtrs);
+
+ public static native long[] nativeReadTypefaces(ByteBuffer buffer);
+
+ public static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
+
+ public static native void nativeAddFontCollections(long nativePtr);
+
+ public static native void nativeWarmUpCache(String fileName);
+
+ private TypefaceNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/VectorDrawableNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/VectorDrawableNatives.java
new file mode 100644
index 000000000..39c054244
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/VectorDrawableNatives.java
@@ -0,0 +1,150 @@
+package org.robolectric.nativeruntime;
+
+import android.graphics.Rect;
+
+/**
+ * Native methods for VectorDrawable JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/VectorDrawable.java
+ */
+public final class VectorDrawableNatives {
+
+ public static native int nDraw(
+ long rendererPtr,
+ long canvasWrapperPtr,
+ long colorFilterPtr,
+ Rect bounds,
+ boolean needsMirroring,
+ boolean canReuseCache);
+
+ public static native boolean nGetFullPathProperties(long pathPtr, byte[] properties, int length);
+
+ public static native void nSetName(long nodePtr, String name);
+
+ public static native boolean nGetGroupProperties(long groupPtr, float[] properties, int length);
+
+ public static native void nSetPathString(long pathPtr, String pathString, int length);
+
+ public static native long nCreateTree(long rootGroupPtr);
+
+ public static native long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr);
+
+ public static native void nSetRendererViewportSize(
+ long rendererPtr, float viewportWidth, float viewportHeight);
+
+ public static native boolean nSetRootAlpha(long rendererPtr, float alpha);
+
+ public static native float nGetRootAlpha(long rendererPtr);
+
+ public static native void nSetAntiAlias(long rendererPtr, boolean aa);
+
+ public static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
+
+ public static native long nCreateFullPath();
+
+ public static native long nCreateFullPath(long nativeFullPathPtr);
+
+ public static native void nUpdateFullPathProperties(
+ long pathPtr,
+ float strokeWidth,
+ int strokeColor,
+ float strokeAlpha,
+ int fillColor,
+ float fillAlpha,
+ float trimPathStart,
+ float trimPathEnd,
+ float trimPathOffset,
+ float strokeMiterLimit,
+ int strokeLineCap,
+ int strokeLineJoin,
+ int fillType);
+
+ public static native void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr);
+
+ public static native void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr);
+
+ public static native long nCreateClipPath();
+
+ public static native long nCreateClipPath(long clipPathPtr);
+
+ public static native long nCreateGroup();
+
+ public static native long nCreateGroup(long groupPtr);
+
+ public static native void nUpdateGroupProperties(
+ long groupPtr,
+ float rotate,
+ float pivotX,
+ float pivotY,
+ float scaleX,
+ float scaleY,
+ float translateX,
+ float translateY);
+
+ public static native void nAddChild(long groupPtr, long nodePtr);
+
+ public static native float nGetRotation(long groupPtr);
+
+ public static native void nSetRotation(long groupPtr, float rotation);
+
+ public static native float nGetPivotX(long groupPtr);
+
+ public static native void nSetPivotX(long groupPtr, float pivotX);
+
+ public static native float nGetPivotY(long groupPtr);
+
+ public static native void nSetPivotY(long groupPtr, float pivotY);
+
+ public static native float nGetScaleX(long groupPtr);
+
+ public static native void nSetScaleX(long groupPtr, float scaleX);
+
+ public static native float nGetScaleY(long groupPtr);
+
+ public static native void nSetScaleY(long groupPtr, float scaleY);
+
+ public static native float nGetTranslateX(long groupPtr);
+
+ public static native void nSetTranslateX(long groupPtr, float translateX);
+
+ public static native float nGetTranslateY(long groupPtr);
+
+ public static native void nSetTranslateY(long groupPtr, float translateY);
+
+ public static native void nSetPathData(long pathPtr, long pathDataPtr);
+
+ public static native float nGetStrokeWidth(long pathPtr);
+
+ public static native void nSetStrokeWidth(long pathPtr, float width);
+
+ public static native int nGetStrokeColor(long pathPtr);
+
+ public static native void nSetStrokeColor(long pathPtr, int strokeColor);
+
+ public static native float nGetStrokeAlpha(long pathPtr);
+
+ public static native void nSetStrokeAlpha(long pathPtr, float alpha);
+
+ public static native int nGetFillColor(long pathPtr);
+
+ public static native void nSetFillColor(long pathPtr, int fillColor);
+
+ public static native float nGetFillAlpha(long pathPtr);
+
+ public static native void nSetFillAlpha(long pathPtr, float fillAlpha);
+
+ public static native float nGetTrimPathStart(long pathPtr);
+
+ public static native void nSetTrimPathStart(long pathPtr, float trimPathStart);
+
+ public static native float nGetTrimPathEnd(long pathPtr);
+
+ public static native void nSetTrimPathEnd(long pathPtr, float trimPathEnd);
+
+ public static native float nGetTrimPathOffset(long pathPtr);
+
+ public static native void nSetTrimPathOffset(long pathPtr, float trimPathOffset);
+
+ private VectorDrawableNatives() {}
+}
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/VirtualRefBasePtrNatives.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/VirtualRefBasePtrNatives.java
new file mode 100644
index 000000000..0c96f08e3
--- /dev/null
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/VirtualRefBasePtrNatives.java
@@ -0,0 +1,16 @@
+package org.robolectric.nativeruntime;
+
+/**
+ * Native methods for VirtualRefBasePtr JNI registration.
+ *
+ * <p>Native method signatures are derived from
+ * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/graphics/java/android/graphics/VirtualRefBasePtr.java
+ */
+public final class VirtualRefBasePtrNatives {
+
+ public static native void nIncStrong(long ptr);
+
+ public static native void nDecStrong(long ptr);
+
+ private VirtualRefBasePtrNatives() {}
+}
diff --git a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java
new file mode 100644
index 000000000..ec86818f1
--- /dev/null
+++ b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLazyLoadTest.java
@@ -0,0 +1,29 @@
+package org.robolectric.nativeruntime;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Application;
+import android.database.CursorWindow;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DefaultNativeRuntimeLazyLoadTest {
+
+ /**
+ * Checks to see that RNR is not loaded by default when an empty application is created. RNR load
+ * times are typically 0.5-1s, so it is desirable to have it lazy loaded when native code is
+ * called.
+ */
+ @SuppressWarnings("UnusedVariable")
+ @Test
+ public void lazyLoad() throws Exception {
+ Application application = RuntimeEnvironment.getApplication();
+ assertThat(DefaultNativeRuntimeLoader.isLoaded()).isFalse();
+ CursorWindow cursorWindow = new CursorWindow("hi");
+ cursorWindow.close();
+ assertThat(DefaultNativeRuntimeLoader.isLoaded()).isTrue();
+ }
+}
diff --git a/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java
new file mode 100644
index 000000000..cbb9cf1f5
--- /dev/null
+++ b/nativeruntime/src/test/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoaderTest.java
@@ -0,0 +1,21 @@
+package org.robolectric.nativeruntime;
+
+import android.database.CursorWindow;
+import android.database.sqlite.SQLiteDatabase;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public final class DefaultNativeRuntimeLoaderTest {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ @Test
+ public void concurrentLoad() throws Exception {
+ executor.execute(() -> SQLiteDatabase.create(null));
+ CursorWindow cursorWindow = new CursorWindow("sdfsdf");
+ cursorWindow.close();
+ }
+}
diff --git a/preinstrumented/src/main/java/org/robolectric/preinstrumented/JarInstrumentor.java b/preinstrumented/src/main/java/org/robolectric/preinstrumented/JarInstrumentor.java
index c23798eaa..a9c5aceab 100644
--- a/preinstrumented/src/main/java/org/robolectric/preinstrumented/JarInstrumentor.java
+++ b/preinstrumented/src/main/java/org/robolectric/preinstrumented/JarInstrumentor.java
@@ -149,6 +149,11 @@ public class JarInstrumentor {
ZipEntry buildProp = jarFile.getEntry("build.prop");
Properties buildProps = new Properties();
buildProps.load(jarFile.getInputStream(buildProp));
+ String codename = buildProps.getProperty("ro.build.version.codename");
+ // Check for a prerelease SDK.
+ if (!"REL".equals(codename)) {
+ return 10000;
+ }
return Integer.parseInt(buildProps.getProperty("ro.build.version.sdk"));
}
}
diff --git a/robolectric/src/main/java/org/robolectric/plugins/GraphicsModeConfigurer.java b/robolectric/src/main/java/org/robolectric/plugins/GraphicsModeConfigurer.java
new file mode 100644
index 000000000..8ff0f6196
--- /dev/null
+++ b/robolectric/src/main/java/org/robolectric/plugins/GraphicsModeConfigurer.java
@@ -0,0 +1,65 @@
+package org.robolectric.plugins;
+
+import com.google.auto.service.AutoService;
+import java.lang.reflect.Method;
+import java.util.Properties;
+import javax.annotation.Nonnull;
+import org.robolectric.annotation.GraphicsMode;
+import org.robolectric.annotation.GraphicsMode.Mode;
+import org.robolectric.pluginapi.config.Configurer;
+
+/** Provides configuration to Robolectric for its @{@link GraphicsMode} annotation. */
+@AutoService(Configurer.class)
+public class GraphicsModeConfigurer implements Configurer<GraphicsMode.Mode> {
+
+ private final Properties systemProperties;
+
+ public GraphicsModeConfigurer(Properties systemProperties) {
+ this.systemProperties = systemProperties;
+ }
+
+ @Override
+ public Class<GraphicsMode.Mode> getConfigClass() {
+ return GraphicsMode.Mode.class;
+ }
+
+ @Nonnull
+ @Override
+ public GraphicsMode.Mode defaultConfig() {
+ return GraphicsMode.Mode.valueOf(
+ systemProperties.getProperty("robolectric.graphicsMode", "LEGACY"));
+ }
+
+ @Override
+ public GraphicsMode.Mode getConfigFor(@Nonnull String packageName) {
+ try {
+ Package pkg = Class.forName(packageName + ".package-info").getPackage();
+ return valueFrom(pkg.getAnnotation(GraphicsMode.class));
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ return null;
+ }
+
+ @Override
+ public GraphicsMode.Mode getConfigFor(@Nonnull Class<?> testClass) {
+ return valueFrom(testClass.getAnnotation(GraphicsMode.class));
+ }
+
+ @Override
+ public GraphicsMode.Mode getConfigFor(@Nonnull Method method) {
+ return valueFrom(method.getAnnotation(GraphicsMode.class));
+ }
+
+ @Nonnull
+ @Override
+ public GraphicsMode.Mode merge(
+ @Nonnull GraphicsMode.Mode parentConfig, @Nonnull GraphicsMode.Mode childConfig) {
+ // just take the childConfig - since GraphicsMode only has a single 'value' attribute
+ return childConfig;
+ }
+
+ private Mode valueFrom(GraphicsMode graphicsMode) {
+ return graphicsMode == null ? null : graphicsMode.value();
+ }
+}
diff --git a/robolectric/src/test/java/org/robolectric/plugins/GraphicsModeConfigurerTest.java b/robolectric/src/test/java/org/robolectric/plugins/GraphicsModeConfigurerTest.java
new file mode 100644
index 000000000..dae47d316
--- /dev/null
+++ b/robolectric/src/test/java/org/robolectric/plugins/GraphicsModeConfigurerTest.java
@@ -0,0 +1,21 @@
+package org.robolectric.plugins;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.Properties;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.robolectric.annotation.GraphicsMode;
+import org.robolectric.annotation.GraphicsMode.Mode;
+
+/** Unit tests for methods annotated with @{@link GraphicsMode}. */
+@RunWith(JUnit4.class)
+public class GraphicsModeConfigurerTest {
+ @Test
+ public void defaultConfig() {
+ Properties systemProperties = new Properties();
+ GraphicsModeConfigurer configurer = new GraphicsModeConfigurer(systemProperties);
+ assertThat(configurer.defaultConfig()).isSameInstanceAs(Mode.LEGACY);
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/GraphicsShadowPicker.java b/shadows/framework/src/main/java/org/robolectric/shadows/GraphicsShadowPicker.java
new file mode 100644
index 000000000..8916375dc
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/GraphicsShadowPicker.java
@@ -0,0 +1,32 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.GraphicsMode;
+import org.robolectric.annotation.GraphicsMode.Mode;
+import org.robolectric.config.ConfigurationRegistry;
+import org.robolectric.shadow.api.ShadowPicker;
+
+/** A {@link ShadowPicker} that selects between shadows given the Graphics mode. */
+public class GraphicsShadowPicker<T> implements ShadowPicker<T> {
+
+ private final Class<? extends T> legacyShadowClass;
+ private final Class<? extends T> nativeShadowClass;
+
+ public GraphicsShadowPicker(
+ Class<? extends T> legacyShadowClass, Class<? extends T> nativeShadowClass) {
+ this.legacyShadowClass = legacyShadowClass;
+ this.nativeShadowClass = nativeShadowClass;
+ }
+
+ @Override
+ public Class<? extends T> pickShadowClass() {
+ if (RuntimeEnvironment.getApiLevel() >= O
+ && ConfigurationRegistry.get(GraphicsMode.Mode.class) == Mode.NATIVE) {
+ return nativeShadowClass;
+ } else {
+ return legacyShadowClass;
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmap.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmap.java
index d6f07c0b0..74133081c 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmap.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBitmap.java
@@ -5,7 +5,6 @@ import android.graphics.Matrix;
import java.io.InputStream;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadow.api.ShadowPicker;
import org.robolectric.shadows.ShadowBitmap.Picker;
/** Base class for {@link Bitmap} shadows. */
@@ -125,11 +124,10 @@ public abstract class ShadowBitmap {
public abstract void setDescription(String s);
- /** A {@link ShadowPicker} that always selects the legacy ShadowBitmap. */
- public static class Picker implements ShadowPicker<ShadowBitmap> {
- @Override
- public Class<? extends ShadowBitmap> pickShadowClass() {
- return ShadowLegacyBitmap.class;
+ /** Shadow picker for {@link Bitmap}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyBitmap.class, ShadowNativeBitmap.class);
}
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCanvas.java
index 8c962c451..f45737b2f 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCanvas.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowCanvas.java
@@ -6,11 +6,10 @@ import android.graphics.Path;
import android.graphics.RectF;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadow.api.ShadowPicker;
-import org.robolectric.shadows.ShadowCanvas.CanvasPicker;
+import org.robolectric.shadows.ShadowCanvas.Picker;
/** Base class for {@link Canvas} shadow classes. Mainly contains public shadow API signatures. */
-@Implements(value = Canvas.class, shadowPicker = CanvasPicker.class)
+@Implements(value = Canvas.class, shadowPicker = Picker.class)
public abstract class ShadowCanvas {
public static String visualize(Canvas canvas) {
@@ -202,11 +201,10 @@ public abstract class ShadowCanvas {
}
}
- /** A {@link ShadowPicker} that always selects the legacy ShadowCanvas */
- public static class CanvasPicker implements ShadowPicker<ShadowCanvas> {
- @Override
- public Class<? extends ShadowCanvas> pickShadowClass() {
- return ShadowLegacyCanvas.class;
+ /** Shadow picker for {@link Canvas}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyCanvas.class, ShadowNativeCanvas.class);
}
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMatrix.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMatrix.java
index b4898fb71..62b702107 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMatrix.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowMatrix.java
@@ -5,7 +5,6 @@ import android.graphics.Matrix;
import java.util.List;
import java.util.Map;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.ShadowPicker;
import org.robolectric.shadows.ShadowMatrix.Picker;
@SuppressWarnings({"UnusedDeclaration"})
@@ -43,11 +42,10 @@ public abstract class ShadowMatrix {
public abstract String getDescription();
- /** A {@link ShadowPicker} that always selects the legacy ShadowPath */
- public static class Picker implements ShadowPicker<ShadowMatrix> {
- @Override
- public Class<? extends ShadowMatrix> pickShadowClass() {
- return ShadowLegacyMatrix.class;
+ /** Shadow picker for {@link Matrix}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyMatrix.class, ShadowNativeMatrix.class);
}
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAllocationRegistry.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAllocationRegistry.java
new file mode 100644
index 000000000..ab3c6e3d5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAllocationRegistry.java
@@ -0,0 +1,65 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import libcore.util.NativeAllocationRegistry;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.NativeAllocationRegistryNatives;
+import org.robolectric.shadows.ShadowNativeAllocationRegistry.Picker;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.Direct;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link NativeAllocationRegistry} that is backed by native code */
+@Implements(
+ value = NativeAllocationRegistry.class,
+ minSdk = O,
+ isInAndroidSdk = false,
+ shadowPicker = Picker.class)
+public class ShadowNativeAllocationRegistry {
+
+ @RealObject protected NativeAllocationRegistry realNativeAllocationRegistry;
+
+ @Implementation
+ protected Runnable registerNativeAllocation(Object referent, long nativePtr) {
+ // Avoid registering native allocations for classes where native methods are no-ops (like
+ // Binder), or for classes that simulate native pointers (like binary resources) but don't
+ // actually use native libraries.
+ if (nativePtr != 0 && hasValidFreeFunction()) {
+ return reflector(NativeAllocationRegistryReflector.class, realNativeAllocationRegistry)
+ .registerNativeAllocation(referent, nativePtr);
+ } else {
+ return () -> {};
+ }
+ }
+
+ private boolean hasValidFreeFunction() {
+ return reflector(NativeAllocationRegistryReflector.class, realNativeAllocationRegistry)
+ .getFreeFunction()
+ != 0;
+ }
+
+ @Implementation
+ protected static void applyFreeFunction(long freeFunction, long nativePtr) {
+ NativeAllocationRegistryNatives.applyFreeFunction(freeFunction, nativePtr);
+ }
+
+ @ForType(NativeAllocationRegistry.class)
+ interface NativeAllocationRegistryReflector {
+ @Direct
+ Runnable registerNativeAllocation(Object referent, long nativePtr);
+
+ @Accessor("freeFunction")
+ long getFreeFunction();
+ }
+
+ /** Shadow picker for {@link NativeAllocationRegistry}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowNoopNativeAllocationRegistry.class, ShadowNativeAllocationRegistry.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedImageDrawable.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedImageDrawable.java
new file mode 100644
index 000000000..a73f0a61f
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedImageDrawable.java
@@ -0,0 +1,125 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.ImageDecoder;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedImageDrawable;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.AnimatedImageDrawableNatives;
+import org.robolectric.shadows.ShadowNativeAnimatedImageDrawable.Picker;
+
+/** Shadow for {@link AnimatedImageDrawable} that is backed by native code */
+@Implements(value = AnimatedImageDrawable.class, shadowPicker = Picker.class, minSdk = P)
+public class ShadowNativeAnimatedImageDrawable extends ShadowDrawable {
+ @Implementation(minSdk = Q)
+ protected static long nCreate(
+ long nativeImageDecoder,
+ ImageDecoder decoder,
+ int width,
+ int height,
+ long colorSpaceHandle,
+ boolean extended,
+ Rect cropRect)
+ throws IOException {
+ return AnimatedImageDrawableNatives.nCreate(
+ nativeImageDecoder, decoder, width, height, colorSpaceHandle, extended, cropRect);
+ }
+
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static long nCreate(
+ long nativeImageDecoder, ImageDecoder decoder, int width, int height, Rect cropRect)
+ throws IOException {
+ return nCreate(nativeImageDecoder, decoder, width, height, 0, false, cropRect);
+ }
+
+ @Implementation
+ protected static long nGetNativeFinalizer() {
+ return AnimatedImageDrawableNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation
+ protected static long nDraw(long nativePtr, long canvasNativePtr) {
+ return AnimatedImageDrawableNatives.nDraw(nativePtr, canvasNativePtr);
+ }
+
+ @Implementation
+ protected static void nSetAlpha(long nativePtr, int alpha) {
+ AnimatedImageDrawableNatives.nSetAlpha(nativePtr, alpha);
+ }
+
+ @Implementation
+ protected static int nGetAlpha(long nativePtr) {
+ return AnimatedImageDrawableNatives.nGetAlpha(nativePtr);
+ }
+
+ @Implementation
+ protected static void nSetColorFilter(long nativePtr, long nativeFilter) {
+ AnimatedImageDrawableNatives.nSetColorFilter(nativePtr, nativeFilter);
+ }
+
+ @Implementation
+ protected static boolean nIsRunning(long nativePtr) {
+ return AnimatedImageDrawableNatives.nIsRunning(nativePtr);
+ }
+
+ @Implementation
+ protected static boolean nStart(long nativePtr) {
+ return AnimatedImageDrawableNatives.nStart(nativePtr);
+ }
+
+ @Implementation
+ protected static boolean nStop(long nativePtr) {
+ return AnimatedImageDrawableNatives.nStop(nativePtr);
+ }
+
+ @Implementation
+ protected static int nGetRepeatCount(long nativePtr) {
+ return AnimatedImageDrawableNatives.nGetRepeatCount(nativePtr);
+ }
+
+ @Implementation
+ protected static void nSetRepeatCount(long nativePtr, int repeatCount) {
+ AnimatedImageDrawableNatives.nSetRepeatCount(nativePtr, repeatCount);
+ }
+
+ @Implementation(maxSdk = S_V2)
+ protected static void nSetOnAnimationEndListener(long nativePtr, AnimatedImageDrawable drawable) {
+ AnimatedImageDrawableNatives.nSetOnAnimationEndListener(nativePtr, drawable);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static void nSetOnAnimationEndListener(
+ long nativePtr, WeakReference<AnimatedImageDrawable> drawable) {
+ AnimatedImageDrawableNatives.nSetOnAnimationEndListener(nativePtr, drawable.get());
+ }
+
+ @Implementation
+ protected static long nNativeByteSize(long nativePtr) {
+ return AnimatedImageDrawableNatives.nNativeByteSize(nativePtr);
+ }
+
+ @Implementation
+ protected static void nSetMirrored(long nativePtr, boolean mirror) {
+ AnimatedImageDrawableNatives.nSetMirrored(nativePtr, mirror);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetBounds(long nativePtr, Rect rect) {
+ AnimatedImageDrawableNatives.nSetBounds(nativePtr, rect);
+ }
+
+ /** Shadow picker for {@link AnimatedImageDrawable}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeAnimatedImageDrawable.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedVectorDrawable.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedVectorDrawable.java
new file mode 100644
index 000000000..79b1eafaf
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeAnimatedVectorDrawable.java
@@ -0,0 +1,120 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.N;
+import static android.os.Build.VERSION_CODES.N_MR1;
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.AnimatedVectorDrawableNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeAnimatedVectorDrawable.Picker;
+
+/** Shadow for {@link AnimatedVectorDrawable} that is backed by native code */
+@Implements(value = AnimatedVectorDrawable.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeAnimatedVectorDrawable extends ShadowDrawable {
+
+ @Implementation(minSdk = N)
+ protected static long nCreateAnimatorSet() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return AnimatedVectorDrawableNatives.nCreateAnimatorSet();
+ }
+
+ @Implementation(minSdk = N_MR1)
+ protected static void nSetVectorDrawableTarget(long animatorPtr, long vectorDrawablePtr) {
+ AnimatedVectorDrawableNatives.nSetVectorDrawableTarget(animatorPtr, vectorDrawablePtr);
+ }
+
+ @Implementation(minSdk = N_MR1)
+ protected static void nAddAnimator(
+ long setPtr,
+ long propertyValuesHolder,
+ long nativeInterpolator,
+ long startDelay,
+ long duration,
+ int repeatCount,
+ int repeatMode) {
+ AnimatedVectorDrawableNatives.nAddAnimator(
+ setPtr,
+ propertyValuesHolder,
+ nativeInterpolator,
+ startDelay,
+ duration,
+ repeatCount,
+ repeatMode);
+ }
+
+ @Implementation(minSdk = N)
+ protected static void nSetPropertyHolderData(long nativePtr, float[] data, int length) {
+ AnimatedVectorDrawableNatives.nSetPropertyHolderData(nativePtr, data, length);
+ }
+
+ @Implementation(minSdk = N_MR1)
+ protected static void nSetPropertyHolderData(long nativePtr, int[] data, int length) {
+ AnimatedVectorDrawableNatives.nSetPropertyHolderData(nativePtr, data, length);
+ }
+
+ @Implementation(minSdk = N)
+ protected static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+ AnimatedVectorDrawableNatives.nStart(animatorSetPtr, set, id);
+ }
+
+ @Implementation(minSdk = N)
+ protected static void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+ AnimatedVectorDrawableNatives.nReverse(animatorSetPtr, set, id);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nCreateGroupPropertyHolder(
+ long nativePtr, int propertyId, float startValue, float endValue) {
+ return AnimatedVectorDrawableNatives.nCreateGroupPropertyHolder(
+ nativePtr, propertyId, startValue, endValue);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nCreatePathDataPropertyHolder(
+ long nativePtr, long startValuePtr, long endValuePtr) {
+ return AnimatedVectorDrawableNatives.nCreatePathDataPropertyHolder(
+ nativePtr, startValuePtr, endValuePtr);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nCreatePathColorPropertyHolder(
+ long nativePtr, int propertyId, int startValue, int endValue) {
+ return AnimatedVectorDrawableNatives.nCreatePathColorPropertyHolder(
+ nativePtr, propertyId, startValue, endValue);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nCreatePathPropertyHolder(
+ long nativePtr, int propertyId, float startValue, float endValue) {
+ return AnimatedVectorDrawableNatives.nCreatePathPropertyHolder(
+ nativePtr, propertyId, startValue, endValue);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nCreateRootAlphaPropertyHolder(
+ long nativePtr, float startValue, float endValue) {
+ return AnimatedVectorDrawableNatives.nCreateRootAlphaPropertyHolder(
+ nativePtr, startValue, endValue);
+ }
+
+ @Implementation(minSdk = N)
+ protected static void nEnd(long animatorSetPtr) {
+ AnimatedVectorDrawableNatives.nEnd(animatorSetPtr);
+ }
+
+ @Implementation(minSdk = N)
+ protected static void nReset(long animatorSetPtr) {
+ AnimatedVectorDrawableNatives.nReset(animatorSetPtr);
+ }
+
+ /** Shadow picker for {@link AnimatedVectorDrawable}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeAnimatedVectorDrawable.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseCanvas.java
new file mode 100644
index 000000000..42080522f
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseCanvas.java
@@ -0,0 +1,877 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.annotation.ColorLong;
+import android.graphics.BaseCanvas;
+import android.graphics.Bitmap;
+import android.graphics.Paint;
+import android.graphics.Path;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.BaseCanvasNatives;
+import org.robolectric.shadows.ShadowNativeBaseCanvas.Picker;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link BaseCanvas} that is backed by native code */
+@Implements(
+ value = BaseCanvas.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeBaseCanvas extends ShadowCanvas {
+
+ @RealObject BaseCanvas realBaseCanvas;
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float left,
+ float top,
+ long nativePaintOrZero,
+ int canvasDensity,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmapHandle,
+ left,
+ top,
+ nativePaintOrZero,
+ canvasDensity,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmapHandle,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ nativePaintOrZero,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ int[] colors,
+ int offset,
+ int stride,
+ float x,
+ float y,
+ int width,
+ int height,
+ boolean hasAlpha,
+ long nativePaintOrZero) {
+ BaseCanvasNatives.nDrawBitmap(
+ nativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha, nativePaintOrZero);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ Bitmap bitmap,
+ float left,
+ float top,
+ long nativePaintOrZero,
+ int canvasDensity,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmap.getNativeInstance(),
+ left,
+ top,
+ nativePaintOrZero,
+ canvasDensity,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ Bitmap bitmap,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmap.getNativeInstance(),
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ nativePaintOrZero,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawColor(long nativeCanvas, int color, int mode) {
+ BaseCanvasNatives.nDrawColor(nativeCanvas, color, mode);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawColor(
+ long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode) {
+ BaseCanvasNatives.nDrawColor(nativeCanvas, nativeColorSpace, color, mode);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawPaint(long nativeCanvas, long nativePaint) {
+ BaseCanvasNatives.nDrawPaint(nativeCanvas, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawPoint(long canvasHandle, float x, float y, long paintHandle) {
+ BaseCanvasNatives.nDrawPoint(canvasHandle, x, y, paintHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawPoints(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle) {
+ BaseCanvasNatives.nDrawPoints(canvasHandle, pts, offset, count, paintHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawLine(
+ long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint) {
+ BaseCanvasNatives.nDrawLine(nativeCanvas, startX, startY, stopX, stopY, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawLines(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle) {
+ BaseCanvasNatives.nDrawLines(canvasHandle, pts, offset, count, paintHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawRect(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint) {
+ BaseCanvasNatives.nDrawRect(nativeCanvas, left, top, right, bottom, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawOval(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint) {
+ BaseCanvasNatives.nDrawOval(nativeCanvas, left, top, right, bottom, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawCircle(
+ long nativeCanvas, float cx, float cy, float radius, long nativePaint) {
+ BaseCanvasNatives.nDrawCircle(nativeCanvas, cx, cy, radius, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawArc(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweep,
+ boolean useCenter,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawArc(
+ nativeCanvas, left, top, right, bottom, startAngle, sweep, useCenter, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawRoundRect(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawRoundRect(nativeCanvas, left, top, right, bottom, rx, ry, nativePaint);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float outerRx,
+ float outerRy,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float innerRx,
+ float innerRy,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawDoubleRoundRect(
+ nativeCanvas,
+ outerLeft,
+ outerTop,
+ outerRight,
+ outerBottom,
+ outerRx,
+ outerRy,
+ innerLeft,
+ innerTop,
+ innerRight,
+ innerBottom,
+ innerRx,
+ innerRy,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float[] outerRadii,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float[] innerRadii,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawDoubleRoundRect(
+ nativeCanvas,
+ outerLeft,
+ outerTop,
+ outerRight,
+ outerBottom,
+ outerRadii,
+ innerLeft,
+ innerTop,
+ innerRight,
+ innerBottom,
+ innerRadii,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawPath(long nativeCanvas, long nativePath, long nativePaint) {
+ BaseCanvasNatives.nDrawPath(nativeCanvas, nativePath, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint) {
+ BaseCanvasNatives.nDrawRegion(nativeCanvas, nativeRegion, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawNinePatch(
+ long nativeCanvas,
+ long nativeBitmap,
+ long ninePatch,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseCanvasNatives.nDrawNinePatch(
+ nativeCanvas,
+ nativeBitmap,
+ ninePatch,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ nativePaintOrZero,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawBitmapMatrix(
+ long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint) {
+ BaseCanvasNatives.nDrawBitmapMatrix(nativeCanvas, bitmapHandle, nativeMatrix, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nDrawBitmapMatrix(
+ long nativeCanvas, Bitmap bitmap, long nativeMatrix, long nativePaint) {
+ BaseCanvasNatives.nDrawBitmapMatrix(
+ nativeCanvas, bitmap.getNativeInstance(), nativeMatrix, nativePaint);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nDrawBitmapMesh(
+ long nativeCanvas,
+ long bitmapHandle,
+ int meshWidth,
+ int meshHeight,
+ float[] verts,
+ int vertOffset,
+ int[] colors,
+ int colorOffset,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawBitmapMesh(
+ nativeCanvas,
+ bitmapHandle,
+ meshWidth,
+ meshHeight,
+ verts,
+ vertOffset,
+ colors,
+ colorOffset,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nDrawBitmapMesh(
+ long nativeCanvas,
+ Bitmap bitmap,
+ int meshWidth,
+ int meshHeight,
+ float[] verts,
+ int vertOffset,
+ int[] colors,
+ int colorOffset,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawBitmapMesh(
+ nativeCanvas,
+ bitmap.getNativeInstance(),
+ meshWidth,
+ meshHeight,
+ verts,
+ vertOffset,
+ colors,
+ colorOffset,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDrawVertices(
+ long nativeCanvas,
+ int mode,
+ int n,
+ float[] verts,
+ int vertOffset,
+ float[] texs,
+ int texOffset,
+ int[] colors,
+ int colorOffset,
+ short[] indices,
+ int indexOffset,
+ int indexCount,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawVertices(
+ nativeCanvas,
+ mode,
+ n,
+ verts,
+ vertOffset,
+ texs,
+ texOffset,
+ colors,
+ colorOffset,
+ indices,
+ indexOffset,
+ indexCount,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nDrawGlyphs(
+ long nativeCanvas,
+ int[] glyphIds,
+ float[] positions,
+ int glyphIdStart,
+ int positionStart,
+ int glyphCount,
+ long nativeFont,
+ long nativePaint) {
+ BaseCanvasNatives.nDrawGlyphs(
+ nativeCanvas,
+ glyphIds,
+ positions,
+ glyphIdStart,
+ positionStart,
+ glyphCount,
+ nativeFont,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawText(nativeCanvas, text, index, count, x, y, flags, nativePaint);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawText(nativeCanvas, text, start, end, x, y, flags, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawText(
+ nativeCanvas, text, index, count, x, y, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawText(
+ nativeCanvas, text, start, end, x, y, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextRun(
+ nativeCanvas, text, start, end, contextStart, contextEnd, x, y, isRtl, nativePaint);
+ }
+
+ /**
+ * The signature of this method is the same from SDK levels O and above, but the last native
+ * pointer changed from a Typeface pointer to a MeasuredParagraph pointer in P.
+ */
+ @Implementation(minSdk = O)
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypefaceOrPrecomputedText) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ if (RuntimeEnvironment.getApiLevel() >= P) {
+ BaseCanvasNatives.nDrawTextRun(
+ nativeCanvas,
+ text,
+ start,
+ count,
+ contextStart,
+ contextCount,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypefaceOrPrecomputedText);
+ } else {
+ BaseCanvasNatives.nDrawTextRunTypeface(
+ nativeCanvas,
+ text,
+ start,
+ count,
+ contextStart,
+ contextCount,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypefaceOrPrecomputedText);
+ }
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextRun(
+ nativeCanvas,
+ text,
+ start,
+ end,
+ contextStart,
+ contextEnd,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypeface);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, index, count, nativePath, hOffset, vOffset, bidiFlags, nativePaint);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, nativePath, hOffset, vOffset, flags, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint,
+ long nativeTypeface) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextOnPath(
+ nativeCanvas,
+ text,
+ index,
+ count,
+ nativePath,
+ hOffset,
+ vOffset,
+ bidiFlags,
+ nativePaint,
+ nativeTypeface);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ BaseCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, nativePath, hOffset, vOffset, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static void nPunchHole(
+ long renderer, float left, float top, float right, float bottom, float rx, float ry) {
+ BaseCanvasNatives.nPunchHole(renderer, left, top, right, bottom, rx, ry);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static void nPunchHole(
+ long renderer,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ float alpha) {
+ nPunchHole(renderer, left, top, right, bottom, rx, ry);
+ }
+
+ long getNativeCanvas() {
+ return reflector(BaseCanvasReflector.class, realBaseCanvas).getNativeCanvas();
+ }
+
+ @Override
+ public void appendDescription(String s) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public String getDescription() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getPathPaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getCirclePaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getArcPaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public boolean hasDrawnPath() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public boolean hasDrawnCircle() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public Paint getDrawnPathPaint(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public Path getDrawnPath(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public CirclePaintHistoryEvent getDrawnCircle(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public ArcPaintHistoryEvent getDrawnArc(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public void resetCanvasHistory() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public Paint getDrawnPaint() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public void setHeight(int height) {
+ throw new UnsupportedOperationException("setHeight is not supported in native Canvas");
+ }
+
+ @Override
+ public void setWidth(int width) {
+ throw new UnsupportedOperationException("setWidth is not supported in native Canvas");
+ }
+
+ @Override
+ public TextHistoryEvent getDrawnTextEvent(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getTextHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public RectPaintHistoryEvent getDrawnRect(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public RectPaintHistoryEvent getLastDrawnRect() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getRectPaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public RoundRectPaintHistoryEvent getDrawnRoundRect(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public RoundRectPaintHistoryEvent getLastDrawnRoundRect() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getRoundRectPaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public LinePaintHistoryEvent getDrawnLine(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getLinePaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public int getOvalPaintHistoryCount() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @Override
+ public OvalPaintHistoryEvent getDrawnOval(int i) {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowCanvas description APIs are not supported");
+ }
+
+ @ForType(BaseCanvas.class)
+ interface BaseCanvasReflector {
+ @Accessor("mNativeCanvasWrapper")
+ long getNativeCanvas();
+ }
+
+ /** Shadow picker for {@link BaseCanvas}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeBaseCanvas.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java
new file mode 100644
index 000000000..1f061b53e
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBaseRecordingCanvas.java
@@ -0,0 +1,597 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.ColorLong;
+import android.graphics.BaseRecordingCanvas;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.BaseRecordingCanvasNatives;
+import org.robolectric.shadows.ShadowNativeBaseRecordingCanvas.Picker;
+
+/** Shadow for {@link BaseRecordingCanvas} that is backed by native code */
+@Implements(
+ value = BaseRecordingCanvas.class,
+ minSdk = Q,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeBaseRecordingCanvas extends ShadowNativeCanvas {
+
+ @Implementation
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float left,
+ float top,
+ long nativePaintOrZero,
+ int canvasDensity,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseRecordingCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmapHandle,
+ left,
+ top,
+ nativePaintOrZero,
+ canvasDensity,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseRecordingCanvasNatives.nDrawBitmap(
+ nativeCanvas,
+ bitmapHandle,
+ srcLeft,
+ srcTop,
+ srcRight,
+ srcBottom,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ nativePaintOrZero,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation
+ protected static void nDrawBitmap(
+ long nativeCanvas,
+ int[] colors,
+ int offset,
+ int stride,
+ float x,
+ float y,
+ int width,
+ int height,
+ boolean hasAlpha,
+ long nativePaintOrZero) {
+ BaseRecordingCanvasNatives.nDrawBitmap(
+ nativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha, nativePaintOrZero);
+ }
+
+ @Implementation
+ protected static void nDrawColor(long nativeCanvas, int color, int mode) {
+ BaseRecordingCanvasNatives.nDrawColor(nativeCanvas, color, mode);
+ }
+
+ @Implementation
+ protected static void nDrawColor(
+ long nativeCanvas, long nativeColorSpace, @ColorLong long color, int mode) {
+ BaseRecordingCanvasNatives.nDrawColor(nativeCanvas, nativeColorSpace, color, mode);
+ }
+
+ @Implementation
+ protected static void nDrawPaint(long nativeCanvas, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawPaint(nativeCanvas, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawPoint(long canvasHandle, float x, float y, long paintHandle) {
+ BaseRecordingCanvasNatives.nDrawPoint(canvasHandle, x, y, paintHandle);
+ }
+
+ @Implementation
+ protected static void nDrawPoints(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle) {
+ BaseRecordingCanvasNatives.nDrawPoints(canvasHandle, pts, offset, count, paintHandle);
+ }
+
+ @Implementation
+ protected static void nDrawLine(
+ long nativeCanvas, float startX, float startY, float stopX, float stopY, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawLine(nativeCanvas, startX, startY, stopX, stopY, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawLines(
+ long canvasHandle, float[] pts, int offset, int count, long paintHandle) {
+ BaseRecordingCanvasNatives.nDrawLines(canvasHandle, pts, offset, count, paintHandle);
+ }
+
+ @Implementation
+ protected static void nDrawRect(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawRect(nativeCanvas, left, top, right, bottom, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawOval(
+ long nativeCanvas, float left, float top, float right, float bottom, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawOval(nativeCanvas, left, top, right, bottom, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawCircle(
+ long nativeCanvas, float cx, float cy, float radius, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawCircle(nativeCanvas, cx, cy, radius, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawArc(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweep,
+ boolean useCenter,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawArc(
+ nativeCanvas, left, top, right, bottom, startAngle, sweep, useCenter, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawRoundRect(
+ long nativeCanvas,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawRoundRect(
+ nativeCanvas, left, top, right, bottom, rx, ry, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float outerRx,
+ float outerRy,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float innerRx,
+ float innerRy,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawDoubleRoundRect(
+ nativeCanvas,
+ outerLeft,
+ outerTop,
+ outerRight,
+ outerBottom,
+ outerRx,
+ outerRy,
+ innerLeft,
+ innerTop,
+ innerRight,
+ innerBottom,
+ innerRx,
+ innerRy,
+ nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawDoubleRoundRect(
+ long nativeCanvas,
+ float outerLeft,
+ float outerTop,
+ float outerRight,
+ float outerBottom,
+ float[] outerRadii,
+ float innerLeft,
+ float innerTop,
+ float innerRight,
+ float innerBottom,
+ float[] innerRadii,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawDoubleRoundRect(
+ nativeCanvas,
+ outerLeft,
+ outerTop,
+ outerRight,
+ outerBottom,
+ outerRadii,
+ innerLeft,
+ innerTop,
+ innerRight,
+ innerBottom,
+ innerRadii,
+ nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawPath(long nativeCanvas, long nativePath, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawPath(nativeCanvas, nativePath, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawRegion(nativeCanvas, nativeRegion, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawNinePatch(
+ long nativeCanvas,
+ long nativeBitmap,
+ long ninePatch,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom,
+ long nativePaintOrZero,
+ int screenDensity,
+ int bitmapDensity) {
+ BaseRecordingCanvasNatives.nDrawNinePatch(
+ nativeCanvas,
+ nativeBitmap,
+ ninePatch,
+ dstLeft,
+ dstTop,
+ dstRight,
+ dstBottom,
+ nativePaintOrZero,
+ screenDensity,
+ bitmapDensity);
+ }
+
+ @Implementation
+ protected static void nDrawBitmapMatrix(
+ long nativeCanvas, long bitmapHandle, long nativeMatrix, long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawBitmapMatrix(
+ nativeCanvas, bitmapHandle, nativeMatrix, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawBitmapMesh(
+ long nativeCanvas,
+ long bitmapHandle,
+ int meshWidth,
+ int meshHeight,
+ float[] verts,
+ int vertOffset,
+ int[] colors,
+ int colorOffset,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawBitmapMesh(
+ nativeCanvas,
+ bitmapHandle,
+ meshWidth,
+ meshHeight,
+ verts,
+ vertOffset,
+ colors,
+ colorOffset,
+ nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawVertices(
+ long nativeCanvas,
+ int mode,
+ int n,
+ float[] verts,
+ int vertOffset,
+ float[] texs,
+ int texOffset,
+ int[] colors,
+ int colorOffset,
+ short[] indices,
+ int indexOffset,
+ int indexCount,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawVertices(
+ nativeCanvas,
+ mode,
+ n,
+ verts,
+ vertOffset,
+ texs,
+ texOffset,
+ colors,
+ colorOffset,
+ indices,
+ indexOffset,
+ indexCount,
+ nativePaint);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nDrawGlyphs(
+ long nativeCanvas,
+ int[] glyphIds,
+ float[] positions,
+ int glyphIdStart,
+ int positionStart,
+ int glyphCount,
+ long nativeFont,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawGlyphs(
+ nativeCanvas,
+ glyphIds,
+ positions,
+ glyphIdStart,
+ positionStart,
+ glyphCount,
+ nativeFont,
+ nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawText(
+ nativeCanvas, text, index, count, x, y, flags, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawText(nativeCanvas, text, start, end, x, y, flags, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawText(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ BaseRecordingCanvasNatives.nDrawText(
+ nativeCanvas, text, index, count, x, y, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawText(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ BaseRecordingCanvasNatives.nDrawText(
+ nativeCanvas, text, start, end, x, y, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawTextRun(
+ nativeCanvas, text, start, end, contextStart, contextEnd, x, y, isRtl, nativePaint);
+ }
+
+ /**
+ * The signature of this method is the same from SDK levels O and above, but the last native
+ * pointer changed from a Typeface pointer to a MeasuredParagraph pointer in P.
+ */
+ @Implementation(minSdk = O)
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ char[] text,
+ int start,
+ int count,
+ int contextStart,
+ int contextCount,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypefaceOrPrecomputedText) {
+ if (RuntimeEnvironment.getApiLevel() >= P) {
+ BaseRecordingCanvasNatives.nDrawTextRun(
+ nativeCanvas,
+ text,
+ start,
+ count,
+ contextStart,
+ contextCount,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypefaceOrPrecomputedText);
+ } else {
+ BaseRecordingCanvasNatives.nDrawTextRunTypeface(
+ nativeCanvas,
+ text,
+ start,
+ count,
+ contextStart,
+ contextCount,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypefaceOrPrecomputedText);
+ }
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextRun(
+ long nativeCanvas,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ float x,
+ float y,
+ boolean isRtl,
+ long nativePaint,
+ long nativeTypeface) {
+ BaseRecordingCanvasNatives.nDrawTextRun(
+ nativeCanvas,
+ text,
+ start,
+ end,
+ contextStart,
+ contextEnd,
+ x,
+ y,
+ isRtl,
+ nativePaint,
+ nativeTypeface);
+ }
+
+ @Implementation
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, index, count, nativePath, hOffset, vOffset, bidiFlags, nativePaint);
+ }
+
+ @Implementation
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint) {
+ BaseRecordingCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, nativePath, hOffset, vOffset, flags, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ char[] text,
+ int index,
+ int count,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int bidiFlags,
+ long nativePaint,
+ long nativeTypeface) {
+ BaseRecordingCanvasNatives.nDrawTextOnPath(
+ nativeCanvas,
+ text,
+ index,
+ count,
+ nativePath,
+ hOffset,
+ vOffset,
+ bidiFlags,
+ nativePaint,
+ nativeTypeface);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nDrawTextOnPath(
+ long nativeCanvas,
+ String text,
+ long nativePath,
+ float hOffset,
+ float vOffset,
+ int flags,
+ long nativePaint,
+ long nativeTypeface) {
+ BaseRecordingCanvasNatives.nDrawTextOnPath(
+ nativeCanvas, text, nativePath, hOffset, vOffset, flags, nativePaint, nativeTypeface);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static void nPunchHole(
+ long renderer, float left, float top, float right, float bottom, float rx, float ry) {
+ BaseRecordingCanvasNatives.nPunchHole(renderer, left, top, right, bottom, rx, ry);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static void nPunchHole(
+ long renderer,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float rx,
+ float ry,
+ float alpha) {
+ nPunchHole(renderer, left, top, right, bottom, rx, ry);
+ }
+
+ /** Shadow picker for {@link BaseRecordingCanvas}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeBaseRecordingCanvas.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmap.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmap.java
new file mode 100644
index 000000000..cddbd4440
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmap.java
@@ -0,0 +1,504 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.Build.VERSION_CODES.M;
+import static android.os.Build.VERSION_CODES.N;
+import static android.os.Build.VERSION_CODES.N_MR1;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.ColorSpace.Rgb.TransferParameters;
+import android.graphics.Matrix;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.Buffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.nativeruntime.BitmapNatives;
+import org.robolectric.nativeruntime.ColorSpaceRgbNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.NativeAllocationRegistryNatives;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+import org.robolectric.util.reflector.Static;
+
+/** Shadow for {@link Bitmap} that is backed by native code */
+@Implements(value = Bitmap.class, looseSignatures = true, minSdk = O, isInAndroidSdk = false)
+public class ShadowNativeBitmap extends ShadowBitmap {
+
+ @RealObject Bitmap realBitmap;
+
+ private int createdFromResId;
+
+ private static final List<Long> colorSpaceAllocationsP =
+ Collections.synchronizedList(new ArrayList<>());
+
+ /** Called by {@link ShadowNativeBitmapFactory}. */
+ void setCreatedFromResId(int createdFromResId) {
+ this.createdFromResId = createdFromResId;
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeCreate(
+ int[] colors,
+ int offset,
+ int stride,
+ int width,
+ int height,
+ int nativeConfig,
+ boolean mutable,
+ long nativeColorSpace) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return BitmapNatives.nativeCreate(
+ colors, offset, stride, width, height, nativeConfig, mutable, nativeColorSpace);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static Bitmap nativeCreate(
+ int[] colors,
+ int offset,
+ int stride,
+ int width,
+ int height,
+ int nativeConfig,
+ boolean mutable,
+ float[] xyzD50,
+ ColorSpace.Rgb.TransferParameters p) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ long colorSpacePtr = 0;
+ if (xyzD50 != null && p != null) {
+ colorSpacePtr =
+ ColorSpaceRgbNatives.nativeCreate(
+ (float) p.a,
+ (float) p.b,
+ (float) p.c,
+ (float) p.d,
+ (float) p.e,
+ (float) p.f,
+ (float) p.g,
+ xyzD50);
+ colorSpaceAllocationsP.add(colorSpacePtr);
+ }
+ return nativeCreate(
+ colors, offset, stride, width, height, nativeConfig, mutable, colorSpacePtr);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static Bitmap nativeCopy(long nativeSrcBitmap, int nativeConfig, boolean isMutable) {
+ return BitmapNatives.nativeCopy(nativeSrcBitmap, nativeConfig, isMutable);
+ }
+
+ @Implementation(minSdk = M)
+ protected static Bitmap nativeCopyAshmem(long nativeSrcBitmap) {
+ return BitmapNatives.nativeCopyAshmem(nativeSrcBitmap);
+ }
+
+ @Implementation(minSdk = N)
+ protected static Bitmap nativeCopyAshmemConfig(long nativeSrcBitmap, int nativeConfig) {
+ return BitmapNatives.nativeCopyAshmemConfig(nativeSrcBitmap, nativeConfig);
+ }
+
+ @Implementation(minSdk = N)
+ protected static long nativeGetNativeFinalizer() {
+ return BitmapNatives.nativeGetNativeFinalizer();
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static Object nativeRecycle(Object nativeBitmap) {
+ BitmapNatives.nativeRecycle((long) nativeBitmap);
+ return true;
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nativeReconfigure(
+ long nativeBitmap, int width, int height, int config, boolean isPremultiplied) {
+ BitmapNatives.nativeReconfigure(nativeBitmap, width, height, config, isPremultiplied);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static boolean nativeCompress(
+ long nativeBitmap, int format, int quality, OutputStream stream, byte[] tempStorage) {
+ return BitmapNatives.nativeCompress(nativeBitmap, format, quality, stream, tempStorage);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeErase(long nativeBitmap, int color) {
+ BitmapNatives.nativeErase(nativeBitmap, color);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nativeErase(long nativeBitmap, long colorSpacePtr, long color) {
+ BitmapNatives.nativeErase(nativeBitmap, colorSpacePtr, color);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static int nativeRowBytes(long nativeBitmap) {
+ return BitmapNatives.nativeRowBytes(nativeBitmap);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static int nativeConfig(long nativeBitmap) {
+ return BitmapNatives.nativeConfig(nativeBitmap);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static int nativeGetPixel(long nativeBitmap, int x, int y) {
+ return BitmapNatives.nativeGetPixel(nativeBitmap, x, y);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nativeGetColor(long nativeBitmap, int x, int y) {
+ return BitmapNatives.nativeGetColor(nativeBitmap, x, y);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeGetPixels(
+ long nativeBitmap,
+ int[] pixels,
+ int offset,
+ int stride,
+ int x,
+ int y,
+ int width,
+ int height) {
+ BitmapNatives.nativeGetPixels(nativeBitmap, pixels, offset, stride, x, y, width, height);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
+ BitmapNatives.nativeSetPixel(nativeBitmap, x, y, color);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeSetPixels(
+ long nativeBitmap,
+ int[] colors,
+ int offset,
+ int stride,
+ int x,
+ int y,
+ int width,
+ int height) {
+ BitmapNatives.nativeSetPixels(nativeBitmap, colors, offset, stride, x, y, width, height);
+ }
+
+ @Implementation
+ protected static void nativeCopyPixelsToBuffer(long nativeBitmap, Buffer dst) {
+ BitmapNatives.nativeCopyPixelsToBuffer(nativeBitmap, dst);
+ }
+
+ @Implementation
+ protected static void nativeCopyPixelsFromBuffer(long nativeBitmap, Buffer src) {
+ BitmapNatives.nativeCopyPixelsFromBuffer(nativeBitmap, src);
+ }
+
+ @Implementation
+ protected static int nativeGenerationId(long nativeBitmap) {
+ return BitmapNatives.nativeGenerationId(nativeBitmap);
+ }
+
+ // returns a new bitmap built from the native bitmap's alpha, and the paint
+ @Implementation
+ protected static Bitmap nativeExtractAlpha(long nativeBitmap, long nativePaint, int[] offsetXY) {
+ return BitmapNatives.nativeExtractAlpha(nativeBitmap, nativePaint, offsetXY);
+ }
+
+ @Implementation
+ protected static boolean nativeHasAlpha(long nativeBitmap) {
+ return BitmapNatives.nativeHasAlpha(nativeBitmap);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static boolean nativeIsPremultiplied(long nativeBitmap) {
+ return BitmapNatives.nativeIsPremultiplied(nativeBitmap);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeSetPremultiplied(long nativeBitmap, boolean isPremul) {
+ BitmapNatives.nativeSetPremultiplied(nativeBitmap, isPremul);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeSetHasAlpha(
+ long nativeBitmap, boolean hasAlpha, boolean requestPremul) {
+ BitmapNatives.nativeSetHasAlpha(nativeBitmap, hasAlpha, requestPremul);
+ }
+
+ @Implementation(minSdk = JELLY_BEAN_MR1)
+ protected static boolean nativeHasMipMap(long nativeBitmap) {
+ return BitmapNatives.nativeHasMipMap(nativeBitmap);
+ }
+
+ @Implementation(minSdk = JELLY_BEAN_MR1)
+ protected static void nativeSetHasMipMap(long nativeBitmap, boolean hasMipMap) {
+ BitmapNatives.nativeSetHasMipMap(nativeBitmap, hasMipMap);
+ }
+
+ @Implementation
+ protected static boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1) {
+ return BitmapNatives.nativeSameAs(nativeBitmap0, nativeBitmap1);
+ }
+
+ @Implementation(minSdk = N_MR1)
+ protected static void nativePrepareToDraw(long nativeBitmap) {
+ BitmapNatives.nativePrepareToDraw(nativeBitmap);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nativeGetAllocationByteCount(long nativeBitmap) {
+ return BitmapNatives.nativeGetAllocationByteCount(nativeBitmap);
+ }
+
+ @Implementation(minSdk = O)
+ protected static Bitmap nativeCopyPreserveInternalConfig(long nativeBitmap) {
+ return BitmapNatives.nativeCopyPreserveInternalConfig(nativeBitmap);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeWrapHardwareBufferBitmap(
+ HardwareBuffer buffer, long nativeColorSpace) {
+ return BitmapNatives.nativeWrapHardwareBufferBitmap(buffer, nativeColorSpace);
+ }
+
+ @Implementation(minSdk = R)
+ protected static HardwareBuffer nativeGetHardwareBuffer(long nativeBitmap) {
+ return BitmapNatives.nativeGetHardwareBuffer(nativeBitmap);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static boolean nativeGetColorSpace(long nativePtr, float[] xyz, float[] params) {
+ ColorSpace colorSpace = nativeComputeColorSpace(nativePtr);
+ if (colorSpace == null) {
+ return false;
+ }
+ // In Android P, 'nativeGetColorSpace' is responsible for filling out the 'xyz' and 'params'
+ // float arrays. However, in Q and above, 'nativeGetColorSpace' was removed, and
+ // 'nativeComputeColorSpace' returns the ColorSpace object itself. This means for P, we need to
+ // do the reverse operations and generate the float arrays given the detected color space.
+ if (colorSpace instanceof ColorSpace.Rgb) {
+ TransferParameters transferParameters = ((ColorSpace.Rgb) colorSpace).getTransferParameters();
+ params[0] = (float) transferParameters.a;
+ params[1] = (float) transferParameters.b;
+ params[2] = (float) transferParameters.c;
+ params[3] = (float) transferParameters.d;
+ params[4] = (float) transferParameters.e;
+ params[5] = (float) transferParameters.f;
+ params[6] = (float) transferParameters.g;
+ ColorSpace.Rgb rgb =
+ (ColorSpace.Rgb)
+ ColorSpace.adapt(
+ colorSpace, reflector(ColorSpaceReflector.class).getIlluminantD50XYZ());
+ rgb.getTransform(xyz);
+ }
+ return true;
+ }
+
+ @Implementation(minSdk = Q)
+ protected static ColorSpace nativeComputeColorSpace(long nativePtr) {
+ return BitmapNatives.nativeComputeColorSpace(nativePtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nativeSetColorSpace(long nativePtr, long nativeColorSpace) {
+ BitmapNatives.nativeSetColorSpace(nativePtr, nativeColorSpace);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeIsSRGB(long nativePtr) {
+ return BitmapNatives.nativeIsSRGB(nativePtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static boolean nativeIsSRGBLinear(long nativePtr) {
+ return BitmapNatives.nativeIsSRGBLinear(nativePtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nativeSetImmutable(long nativePtr) {
+ BitmapNatives.nativeSetImmutable(nativePtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static boolean nativeIsImmutable(long nativePtr) {
+ return BitmapNatives.nativeIsImmutable(nativePtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static boolean nativeIsBackedByAshmem(long nativePtr) {
+ return BitmapNatives.nativeIsBackedByAshmem(nativePtr);
+ }
+
+ @ForType(ColorSpace.class)
+ interface ColorSpaceReflector {
+ @Accessor("ILLUMINANT_D50_XYZ")
+ @Static
+ float[] getIlluminantD50XYZ();
+
+ @Accessor("sNamedColorSpaces")
+ ColorSpace[] getNamedColorSpaces();
+ }
+
+ @Implementation
+ protected void writeToParcel(Parcel p, int flags) {
+ // Modeled after
+ // https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:frameworks/base/libs/hwui/jni/Bitmap.cpp;l=872.
+ reflector(BitmapReflector.class, realBitmap).checkRecycled("Can't parcel a recycled bitmap");
+ int width = realBitmap.getWidth();
+ int height = realBitmap.getHeight();
+ p.writeInt(width);
+ p.writeInt(height);
+ p.writeInt(realBitmap.getDensity());
+ p.writeBoolean(realBitmap.isMutable());
+ p.writeSerializable(realBitmap.getConfig());
+ p.writeString(realBitmap.getColorSpace().getName());
+ p.writeBoolean(realBitmap.hasAlpha());
+ int[] pixels = new int[width * height];
+ realBitmap.getPixels(pixels, 0, width, 0, 0, width, height);
+ p.writeIntArray(pixels);
+ }
+
+ @Implementation
+ protected static Bitmap nativeCreateFromParcel(Parcel p) {
+ int parceledWidth = p.readInt();
+ int parceledHeight = p.readInt();
+ int density = p.readInt();
+ boolean mutable = p.readBoolean();
+ Bitmap.Config parceledConfig = (Bitmap.Config) p.readSerializable();
+ String colorSpaceName = p.readString();
+ boolean hasAlpha = p.readBoolean();
+ ColorSpace colorSpace = null;
+ ColorSpace[] namedColorSpaces = reflector(ColorSpaceReflector.class).getNamedColorSpaces();
+ for (ColorSpace named : namedColorSpaces) {
+ if (named.getName().equals(colorSpaceName)) {
+ colorSpace = named;
+ break;
+ }
+ }
+ int[] parceledColors = new int[parceledHeight * parceledWidth];
+ p.readIntArray(parceledColors);
+ Bitmap bitmap =
+ Bitmap.createBitmap(parceledWidth, parceledHeight, parceledConfig, hasAlpha, colorSpace);
+ bitmap.setPixels(parceledColors, 0, parceledWidth, 0, 0, parceledWidth, parceledHeight);
+ bitmap.setDensity(density);
+ if (!mutable) {
+ bitmap = bitmap.copy(parceledConfig, false);
+ }
+ return bitmap;
+ }
+
+ @ForType(Bitmap.class)
+ interface BitmapReflector {
+ void checkRecycled(String errorMessage);
+ }
+
+ @Override
+ public Bitmap getCreatedFromBitmap() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ /**
+ * Resource ID from which this Bitmap was created.
+ *
+ * @return Resource ID from which this Bitmap was created, or {@code 0} if this Bitmap was not
+ * created from a resource.
+ */
+ @Override
+ public int getCreatedFromResId() {
+ return createdFromResId;
+ }
+
+ @Override
+ public String getCreatedFromPath() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public InputStream getCreatedFromStream() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public byte[] getCreatedFromBytes() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public int getCreatedFromX() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public int getCreatedFromY() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public int getCreatedFromWidth() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public int getCreatedFromHeight() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public int[] getCreatedFromColors() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public Matrix getCreatedFromMatrix() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public boolean getCreatedFromFilter() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public void setMutable(boolean mutable) {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public void appendDescription(String s) {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public String getDescription() {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Override
+ public void setDescription(String s) {
+ throw new UnsupportedOperationException("Legacy ShadowBitmap APIs are not supported");
+ }
+
+ @Resetter
+ public static void reset() {
+ synchronized (colorSpaceAllocationsP) {
+ for (Long ptr : colorSpaceAllocationsP) {
+ NativeAllocationRegistryNatives.applyFreeFunction(
+ ColorSpaceRgbNatives.nativeGetNativeFinalizer(), ptr);
+ }
+ colorSpaceAllocationsP.clear();
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapDrawable.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapDrawable.java
new file mode 100644
index 000000000..11b0341e1
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapDrawable.java
@@ -0,0 +1,42 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowNativeBitmapDrawable.Picker;
+
+/** Disable the legacy ShadowBitmapDrawable as it fakes the draw logic. */
+@Implements(
+ value = BitmapDrawable.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeBitmapDrawable extends ShadowBitmapDrawable {
+ @RealObject BitmapDrawable bitmapDrawable;
+
+ @Override
+ public int getCreatedFromResId() {
+ return ((ShadowNativeBitmap) Shadow.extract(bitmapDrawable.getBitmap())).getCreatedFromResId();
+ }
+
+ @Override
+ protected void setCreatedFromResId(int createdFromResId, String resourceName) {
+ super.setCreatedFromResId(createdFromResId, resourceName);
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ if (bitmap != null && Shadow.extract(bitmap) instanceof ShadowNativeBitmap) {
+ ShadowNativeBitmap shadowNativeBitmap = Shadow.extract(bitmap);
+ shadowNativeBitmap.setCreatedFromResId(createdFromResId);
+ }
+ }
+
+ /** Shadow picker for {@link BitmapDrawable}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowBitmapDrawable.class, ShadowNativeBitmapDrawable.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapFactory.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapFactory.java
new file mode 100644
index 000000000..54213d87c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapFactory.java
@@ -0,0 +1,153 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Rect;
+import java.io.FileDescriptor;
+import java.io.InputStream;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.BitmapFactoryNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowNativeBitmapFactory.Picker;
+import org.robolectric.util.reflector.Direct;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link BitmapFactory} that is backed by native code */
+@Implements(
+ value = BitmapFactory.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeBitmapFactory {
+
+ static {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ }
+
+ @Implementation
+ protected static Bitmap decodeResource(Resources res, int id, BitmapFactory.Options options) {
+ Bitmap bitmap = reflector(BitmapFactoryReflector.class).decodeResource(res, id, options);
+ if (bitmap == null) {
+ return null;
+ }
+
+ ShadowNativeBitmap shadowNativeBitmap = Shadow.extract(bitmap);
+ shadowNativeBitmap.setCreatedFromResId(id);
+ return bitmap;
+ }
+
+ @Implementation
+ protected static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+ reflector(BitmapFactoryOptionsReflector.class).validate(opts);
+ Bitmap bitmap =
+ reflector(BitmapFactoryReflector.class).decodeStreamInternal(is, outPadding, opts);
+ reflector(BitmapFactoryReflector.class).setDensityFromOptions(bitmap, opts);
+ return bitmap;
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeDecodeStream(
+ InputStream is,
+ byte[] storage,
+ Rect padding,
+ Options opts,
+ long inBitmapHandle,
+ long colorSpaceHandle) {
+ return BitmapFactoryNatives.nativeDecodeStream(
+ is, storage, padding, opts, inBitmapHandle, colorSpaceHandle);
+ }
+
+ @Implementation(maxSdk = P)
+ protected static Bitmap nativeDecodeStream(
+ InputStream is, byte[] storage, Rect padding, Options opts) {
+ return nativeDecodeStream(is, storage, padding, opts, nativeInBitmap(opts), 0);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeDecodeFileDescriptor(
+ FileDescriptor fd, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
+ return BitmapFactoryNatives.nativeDecodeFileDescriptor(
+ fd, padding, opts, inBitmapHandle, colorSpaceHandle);
+ }
+
+ @Implementation(maxSdk = P)
+ protected static Bitmap nativeDecodeFileDescriptor(
+ FileDescriptor fd, Rect padding, Options opts) {
+ return nativeDecodeFileDescriptor(fd, padding, opts, nativeInBitmap(opts), 0);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeDecodeAsset(
+ long nativeAsset, Rect padding, Options opts, long inBitmapHandle, long colorSpaceHandle) {
+ return BitmapFactoryNatives.nativeDecodeAsset(
+ nativeAsset, padding, opts, inBitmapHandle, colorSpaceHandle);
+ }
+
+ @Implementation(minSdk = LOLLIPOP, maxSdk = P)
+ protected static Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts) {
+ return nativeDecodeAsset(nativeAsset, padding, opts, nativeInBitmap(opts), 0);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nativeDecodeByteArray(
+ byte[] data,
+ int offset,
+ int length,
+ Options opts,
+ long inBitmapHandle,
+ long colorSpaceHandle) {
+ return BitmapFactoryNatives.nativeDecodeByteArray(
+ data, offset, length, opts, inBitmapHandle, colorSpaceHandle);
+ }
+
+ @Implementation(maxSdk = P)
+ protected static Bitmap nativeDecodeByteArray(byte[] data, int offset, int length, Options opts) {
+ return nativeDecodeByteArray(data, offset, length, opts, nativeInBitmap(opts), 0);
+ }
+
+ @Implementation
+ protected static boolean nativeIsSeekable(FileDescriptor fd) {
+ return BitmapFactoryNatives.nativeIsSeekable(fd);
+ }
+
+ /** Helper for passing inBitmap's native pointer to native. */
+ static long nativeInBitmap(Options opts) {
+ if (opts == null || opts.inBitmap == null) {
+ return 0;
+ }
+
+ return opts.inBitmap.getNativeInstance();
+ }
+
+ @ForType(BitmapFactory.class)
+ interface BitmapFactoryReflector {
+ Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts);
+
+ void setDensityFromOptions(Bitmap outputBitmap, Options opts);
+
+ @Direct
+ Bitmap decodeResource(Resources res, int id, BitmapFactory.Options options);
+ }
+
+ @ForType(BitmapFactory.Options.class)
+ interface BitmapFactoryOptionsReflector {
+ void validate(Options opts);
+ }
+
+ /** Shadow picker for {@link BitmapFactory}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowBitmapFactory.class, ShadowNativeBitmapFactory.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapShader.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapShader.java
new file mode 100644
index 000000000..1c4aa80a9
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBitmapShader.java
@@ -0,0 +1,69 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.BitmapShaderNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeBitmapShader.Picker;
+
+/** Shadow for {@link BitmapShader} that is backed by native code */
+@Implements(value = BitmapShader.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeBitmapShader {
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nativeCreate(
+ long nativeMatrix, Bitmap bitmap, int shaderTileModeX, int shaderTileModeY) {
+ return nativeCreate(
+ nativeMatrix,
+ bitmap != null ? bitmap.getNativeInstance() : 0,
+ shaderTileModeX,
+ shaderTileModeY,
+ false);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = R)
+ protected static long nativeCreate(
+ long nativeMatrix, long bitmapHandle, int shaderTileModeX, int shaderTileModeY) {
+ return nativeCreate(nativeMatrix, bitmapHandle, shaderTileModeX, shaderTileModeY, false);
+ }
+
+ @Implementation(minSdk = S, maxSdk = S_V2)
+ protected static long nativeCreate(
+ long nativeMatrix,
+ long bitmapHandle,
+ int shaderTileModeX,
+ int shaderTileModeY,
+ boolean filter) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return BitmapShaderNatives.nativeCreate(
+ nativeMatrix, bitmapHandle, shaderTileModeX, shaderTileModeY, filter);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static long nativeCreate(
+ long nativeMatrix,
+ long bitmapHandle,
+ int shaderTileModeX,
+ int shaderTileModeY,
+ boolean filter,
+ boolean isDirectSampled) {
+ return nativeCreate(nativeMatrix, bitmapHandle, shaderTileModeX, shaderTileModeY, filter);
+ }
+
+ /** Shadow picker for {@link BitmapShader}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeBitmapShader.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlendModeColorFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlendModeColorFilter.java
new file mode 100644
index 000000000..f4cbf9901
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlendModeColorFilter.java
@@ -0,0 +1,29 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.BlendModeColorFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.BlendModeColorFilterNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeBlendModeColorFilter.Picker;
+
+/** Shadow for {@link BlendModeColorFilter} that is backed by native code */
+@Implements(value = BlendModeColorFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeBlendModeColorFilter {
+
+ @Implementation(minSdk = Q)
+ protected static long native_CreateBlendModeFilter(int srcColor, int blendmode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return BlendModeColorFilterNatives.native_CreateBlendModeFilter(srcColor, blendmode);
+ }
+
+ /** Shadow picker for {@link BlendModeColorFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeBlendModeColorFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlurMaskFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlurMaskFilter.java
new file mode 100644
index 000000000..77d4a9da2
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeBlurMaskFilter.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.BlurMaskFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.BlurMaskFilterNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeBlurMaskFilter.Picker;
+
+/** Shadow for {@link BlurMaskFilter} that is backed by native code */
+@Implements(value = BlurMaskFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeBlurMaskFilter {
+
+ @Implementation(minSdk = O)
+ protected static long nativeConstructor(float radius, int style) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return BlurMaskFilterNatives.nativeConstructor(radius, style);
+ }
+
+ /** Shadow picker for {@link BlurMaskFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeBlurMaskFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvas.java
new file mode 100644
index 000000000..c2dffb8eb
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvas.java
@@ -0,0 +1,209 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.CanvasNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+
+/** Shadow for {@link Canvas} that is backed by native code */
+@Implements(value = Canvas.class, minSdk = O, isInAndroidSdk = false)
+public class ShadowNativeCanvas extends ShadowNativeBaseCanvas {
+
+ @Implementation(minSdk = O)
+ protected static void nFreeCaches() {
+ CanvasNatives.nFreeCaches();
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nFreeTextLayoutCaches() {
+ CanvasNatives.nFreeTextLayoutCaches();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nGetNativeFinalizer() {
+ return CanvasNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nSetCompatibilityVersion(int apiLevel) {
+ CanvasNatives.nSetCompatibilityVersion(apiLevel);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nInitRaster(Bitmap bitmap) {
+ return nInitRaster(bitmap != null ? bitmap.getNativeInstance() : 0);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nInitRaster(long bitmapHandle) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return CanvasNatives.nInitRaster(bitmapHandle);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nSetBitmap(long canvasHandle, Bitmap bitmap) {
+ CanvasNatives.nSetBitmap(canvasHandle, bitmap != null ? bitmap.getNativeInstance() : 0);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetBitmap(long canvasHandle, long bitmapHandle) {
+ CanvasNatives.nSetBitmap(canvasHandle, bitmapHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nGetClipBounds(long nativeCanvas, Rect bounds) {
+ return CanvasNatives.nGetClipBounds(nativeCanvas, bounds);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsOpaque(long canvasHandle) {
+ return CanvasNatives.nIsOpaque(canvasHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetWidth(long canvasHandle) {
+ return CanvasNatives.nGetWidth(canvasHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetHeight(long canvasHandle) {
+ return CanvasNatives.nGetHeight(canvasHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nSave(long canvasHandle, int saveFlags) {
+ return CanvasNatives.nSave(canvasHandle, saveFlags);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nSaveLayer(
+ long nativeCanvas, float l, float t, float r, float b, long nativePaint) {
+ return CanvasNatives.nSaveLayer(nativeCanvas, l, t, r, b, nativePaint);
+ }
+
+ @Implementation(minSdk = O, maxSdk = R)
+ protected static int nSaveLayer(
+ long nativeCanvas, float l, float t, float r, float b, long nativePaint, int layerFlags) {
+ return nSaveLayer(nativeCanvas, l, t, r, b, nativePaint);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nSaveLayerAlpha(
+ long nativeCanvas, float l, float t, float r, float b, int alpha) {
+ return CanvasNatives.nSaveLayerAlpha(nativeCanvas, l, t, r, b, alpha);
+ }
+
+ @Implementation(minSdk = O, maxSdk = R)
+ protected static int nSaveLayerAlpha(
+ long nativeCanvas, float l, float t, float r, float b, int alpha, int layerFlags) {
+ return nSaveLayerAlpha(nativeCanvas, l, t, r, b, alpha);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static int nSaveUnclippedLayer(long nativeCanvas, int l, int t, int r, int b) {
+ return CanvasNatives.nSaveUnclippedLayer(nativeCanvas, l, t, r, b);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nRestoreUnclippedLayer(long nativeCanvas, int saveCount, long nativePaint) {
+ CanvasNatives.nRestoreUnclippedLayer(nativeCanvas, saveCount, nativePaint);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nRestore(long canvasHandle) {
+ return CanvasNatives.nRestore(canvasHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRestoreToCount(long canvasHandle, int saveCount) {
+ CanvasNatives.nRestoreToCount(canvasHandle, saveCount);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetSaveCount(long canvasHandle) {
+ return CanvasNatives.nGetSaveCount(canvasHandle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nTranslate(long canvasHandle, float dx, float dy) {
+ CanvasNatives.nTranslate(canvasHandle, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nScale(long canvasHandle, float sx, float sy) {
+ CanvasNatives.nScale(canvasHandle, sx, sy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRotate(long canvasHandle, float degrees) {
+ CanvasNatives.nRotate(canvasHandle, degrees);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSkew(long canvasHandle, float sx, float sy) {
+ CanvasNatives.nSkew(canvasHandle, sx, sy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nConcat(long nativeCanvas, long nativeMatrix) {
+ CanvasNatives.nConcat(nativeCanvas, nativeMatrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetMatrix(long nativeCanvas, long nativeMatrix) {
+ CanvasNatives.nSetMatrix(nativeCanvas, nativeMatrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nClipRect(
+ long nativeCanvas, float left, float top, float right, float bottom, int regionOp) {
+ return CanvasNatives.nClipRect(nativeCanvas, left, top, right, bottom, regionOp);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nClipPath(long nativeCanvas, long nativePath, int regionOp) {
+ return CanvasNatives.nClipPath(nativeCanvas, nativePath, regionOp);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
+ CanvasNatives.nSetDrawFilter(nativeCanvas, nativeFilter);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nGetMatrix(long nativeCanvas, long nativeMatrix) {
+ CanvasNatives.nGetMatrix(nativeCanvas, nativeMatrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nQuickReject(long nativeCanvas, long nativePath) {
+ return CanvasNatives.nQuickReject(nativeCanvas, nativePath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nQuickReject(
+ long nativeCanvas, float left, float top, float right, float bottom) {
+ return CanvasNatives.nQuickReject(nativeCanvas, left, top, right, bottom);
+ }
+
+ /**
+ * In Android P and below, Canvas.saveUnclippedLayer called {@link
+ * ShadowNativeCanvas#nSaveLayer(long, float, float, float, float, long)}.
+ *
+ * <p>However, in Android Q, a new native method was added specifically to save unclipped layers.
+ * Use this new method to fix things like ScrollView fade effects in P and below.
+ */
+ @Implementation(minSdk = P, maxSdk = P)
+ protected int saveUnclippedLayer(int left, int top, int right, int bottom) {
+ return nSaveUnclippedLayer(getNativeCanvas(), left, top, right, bottom);
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvasProperty.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvasProperty.java
new file mode 100644
index 000000000..8bb4f1698
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCanvasProperty.java
@@ -0,0 +1,38 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.CanvasProperty;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.CanvasPropertyNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeCanvasProperty.Picker;
+
+/** Shadow for {@link CanvasProperty} that is backed by native code */
+@Implements(
+ value = CanvasProperty.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeCanvasProperty<T> {
+
+ @Implementation
+ protected static long nCreateFloat(float initialValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return CanvasPropertyNatives.nCreateFloat(initialValue);
+ }
+
+ @Implementation
+ protected static long nCreatePaint(long initialValuePaintPtr) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return CanvasPropertyNatives.nCreatePaint(initialValuePaintPtr);
+ }
+
+ /** Shadow picker for {@link CanvasProperty}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeCanvasProperty.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColor.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColor.java
new file mode 100644
index 000000000..05e3aa3fa
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColor.java
@@ -0,0 +1,34 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Color;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ColorNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeColor.Picker;
+
+/** Shadow for {@link Color} that is backed by native code */
+@Implements(value = Color.class, minSdk = O, shadowPicker = Picker.class, isInAndroidSdk = false)
+public class ShadowNativeColor {
+
+ @Implementation(minSdk = O)
+ protected static void nativeRGBToHSV(int red, int greed, int blue, float[] hsv) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ ColorNatives.nativeRGBToHSV(red, greed, blue, hsv);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nativeHSVToColor(int alpha, float[] hsv) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ColorNatives.nativeHSVToColor(alpha, hsv);
+ }
+
+ /** Shadow picker for {@link Color}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowColor.class, ShadowNativeColor.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorFilter.java
new file mode 100644
index 000000000..ed641a2f0
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorFilter.java
@@ -0,0 +1,32 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+
+import android.graphics.ColorFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ColorFilterNatives;
+import org.robolectric.shadows.ShadowNativeColorFilter.Picker;
+
+/** Shadow for {@link ColorFilter} that is backed by native code */
+@Implements(value = ColorFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeColorFilter {
+
+ @Implementation(minSdk = O_MR1)
+ protected static long nativeGetFinalizer() {
+ return ColorFilterNatives.nativeGetFinalizer();
+ }
+
+ @Implementation(minSdk = O, maxSdk = O)
+ protected static void nSafeUnref(long nativeInstance) {
+ ColorFilterNatives.nSafeUnref(nativeInstance);
+ }
+
+ /** Shadow picker for {@link ColorFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeColorFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorMatrixColorFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorMatrixColorFilter.java
new file mode 100644
index 000000000..307303bea
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorMatrixColorFilter.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.ColorMatrixColorFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ColorMatrixColorFilterNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeColorMatrixColorFilter.Picker;
+
+/** Shadow for {@link ColorMatrixColorFilter} that is backed by native code */
+@Implements(value = ColorMatrixColorFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeColorMatrixColorFilter {
+
+ @Implementation(minSdk = O)
+ protected static long nativeColorMatrixFilter(float[] array) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ColorMatrixColorFilterNatives.nativeColorMatrixFilter(array);
+ }
+
+ /** Shadow picker for {@link ColorMatrixColorFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeColorMatrixColorFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorSpaceRgb.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorSpaceRgb.java
new file mode 100644
index 000000000..6bb12eeb2
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeColorSpaceRgb.java
@@ -0,0 +1,39 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.ColorSpace;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ColorSpaceRgbNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeColorSpaceRgb.Picker;
+
+/** Shadow for {@link ColorSpace.Rgb} that is backed by native code */
+@Implements(
+ value = ColorSpace.Rgb.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeColorSpaceRgb {
+
+ @Implementation(minSdk = Q)
+ protected static long nativeGetNativeFinalizer() {
+ return ColorSpaceRgbNatives.nativeGetNativeFinalizer();
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nativeCreate(
+ float a, float b, float c, float d, float e, float f, float g, float[] xyz) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ColorSpaceRgbNatives.nativeCreate(a, b, c, d, e, f, g, xyz);
+ }
+
+ /** Shadow picker for {@link ColorSpace.Rgb}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowColorSpaceRgb.class, ShadowNativeColorSpaceRgb.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposePathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposePathEffect.java
new file mode 100644
index 000000000..6bf1e3ff4
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposePathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.ComposePathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ComposePathEffectNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeComposePathEffect.Picker;
+
+/** Shadow for {@link ComposePathEffect} that is backed by native code */
+@Implements(value = ComposePathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeComposePathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(long nativeOuterpe, long nativeInnerpe) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ComposePathEffectNatives.nativeCreate(nativeOuterpe, nativeInnerpe);
+ }
+
+ /** Shadow picker for {@link ComposePathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeComposePathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposeShader.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposeShader.java
new file mode 100644
index 000000000..b5e5b4a25
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeComposeShader.java
@@ -0,0 +1,30 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.ComposeShader;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.ComposeShaderNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeComposeShader.Picker;
+
+/** Shadow for {@link ComposeShader} that is backed by native code */
+@Implements(value = ComposeShader.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeComposeShader {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(
+ long nativeMatrix, long nativeShaderA, long nativeShaderB, int porterDuffMode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ComposeShaderNatives.nativeCreate(
+ nativeMatrix, nativeShaderA, nativeShaderB, porterDuffMode);
+ }
+
+ /** Shadow picker for {@link ComposeShader}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeComposeShader.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCornerPathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCornerPathEffect.java
new file mode 100644
index 000000000..7c306298b
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeCornerPathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.CornerPathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.CornerPathEffectNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeCornerPathEffect.Picker;
+
+/** Shadow for {@link CornerPathEffect} that is backed by native code */
+@Implements(value = CornerPathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeCornerPathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(float radius) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return CornerPathEffectNatives.nativeCreate(radius);
+ }
+
+ /** Shadow picker for {@link CornerPathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeCornerPathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDashPathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDashPathEffect.java
new file mode 100644
index 000000000..d8ad1eb3f
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDashPathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.DashPathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DashPathEffectNatives;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.shadows.ShadowNativeDashPathEffect.Picker;
+
+/** Shadow for {@link DashPathEffect} that is backed by native code */
+@Implements(value = DashPathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeDashPathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(float[] intervals, float phase) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return DashPathEffectNatives.nativeCreate(intervals, phase);
+ }
+
+ /** Shadow picker for {@link DashPathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeDashPathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDiscretePathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDiscretePathEffect.java
new file mode 100644
index 000000000..b7f5e3ed6
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDiscretePathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.DiscretePathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.DiscretePathEffectNatives;
+import org.robolectric.shadows.ShadowNativeDiscretePathEffect.Picker;
+
+/** Shadow for {@link DiscretePathEffect} that is backed by native code */
+@Implements(value = DiscretePathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeDiscretePathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(float length, float deviation) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return DiscretePathEffectNatives.nativeCreate(length, deviation);
+ }
+
+ /** Shadow picker for {@link DiscretePathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeDiscretePathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDisplayListCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDisplayListCanvas.java
new file mode 100644
index 000000000..f369ca517
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeDisplayListCanvas.java
@@ -0,0 +1,72 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RecordingCanvasNatives;
+import org.robolectric.shadows.ShadowNativeDisplayListCanvas.Picker;
+
+/** Shadow for {@link android.view.DisplayListCanvas} that is backed by native code */
+@Implements(
+ className = "android.view.DisplayListCanvas",
+ minSdk = O,
+ maxSdk = P,
+ shadowPicker = Picker.class)
+public class ShadowNativeDisplayListCanvas extends ShadowNativeRecordingCanvas {
+
+ @Implementation
+ protected static long nCreateDisplayListCanvas(long node, int width, int height) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RecordingCanvasNatives.nCreateDisplayListCanvas(node, width, height);
+ }
+
+ @Implementation
+ protected static void nResetDisplayListCanvas(long canvas, long node, int width, int height) {
+ RecordingCanvasNatives.nResetDisplayListCanvas(canvas, node, width, height);
+ }
+
+ @Implementation
+ protected static int nGetMaximumTextureWidth() {
+ return RecordingCanvasNatives.nGetMaximumTextureWidth();
+ }
+
+ @Implementation
+ protected static int nGetMaximumTextureHeight() {
+ return RecordingCanvasNatives.nGetMaximumTextureHeight();
+ }
+
+ @Implementation
+ protected static void nDrawRenderNode(long renderer, long renderNode) {
+ RecordingCanvasNatives.nDrawRenderNode(renderer, renderNode);
+ }
+
+ @Implementation
+ protected static void nDrawCircle(
+ long renderer, long propCx, long propCy, long propRadius, long propPaint) {
+ RecordingCanvasNatives.nDrawCircle(renderer, propCx, propCy, propRadius, propPaint);
+ }
+
+ @Implementation
+ protected static void nDrawRoundRect(
+ long renderer,
+ long propLeft,
+ long propTop,
+ long propRight,
+ long propBottom,
+ long propRx,
+ long propRy,
+ long propPaint) {
+ RecordingCanvasNatives.nDrawRoundRect(
+ renderer, propLeft, propTop, propRight, propBottom, propRx, propRy, propPaint);
+ }
+
+ /** Shadow picker for {@link android.view.DisplayListCanvas}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowDisplayListCanvas.class, ShadowNativeDisplayListCanvas.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeEmbossMaskFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeEmbossMaskFilter.java
new file mode 100644
index 000000000..52ed28e42
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeEmbossMaskFilter.java
@@ -0,0 +1,29 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.EmbossMaskFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.EmbossMaskFilterNatives;
+import org.robolectric.shadows.ShadowNativeEmbossMaskFilter.Picker;
+
+/** Shadow for {@link EmbossMaskFilter} that is backed by native code */
+@Implements(value = EmbossMaskFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeEmbossMaskFilter {
+
+ @Implementation(minSdk = O)
+ protected static long nativeConstructor(
+ float[] direction, float ambient, float specular, float blurRadius) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return EmbossMaskFilterNatives.nativeConstructor(direction, ambient, specular, blurRadius);
+ }
+
+ /** Shadow picker for {@link EmbossMaskFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeEmbossMaskFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFont.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFont.java
new file mode 100644
index 000000000..2c64de3cd
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFont.java
@@ -0,0 +1,281 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.fonts.Font;
+import android.util.TypedValue;
+import com.google.common.base.Ascii;
+import com.google.common.base.Preconditions;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.FontBuilderNatives;
+import org.robolectric.nativeruntime.FontNatives;
+import org.robolectric.shadows.ShadowNativeFont.Picker;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link Font} that is backed by native code */
+@Implements(value = Font.class, minSdk = P, shadowPicker = Picker.class, isInAndroidSdk = false)
+public class ShadowNativeFont {
+ @Implementation(minSdk = S)
+ protected static long nGetMinikinFontPtr(long font) {
+ return FontNatives.nGetMinikinFontPtr(font);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nCloneFont(long font) {
+ return FontNatives.nCloneFont(font);
+ }
+
+ @Implementation(minSdk = S)
+ protected static ByteBuffer nNewByteBuffer(long font) {
+ return FontNatives.nNewByteBuffer(font);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nGetBufferAddress(long font) {
+ return FontNatives.nGetBufferAddress(font);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nGetSourceId(long font) {
+ return FontNatives.nGetSourceId(font);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nGetReleaseNativeFont() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontNatives.nGetReleaseNativeFont();
+ }
+
+ @Implementation(minSdk = S)
+ protected static float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect) {
+ return FontNatives.nGetGlyphBounds(font, glyphId, paint, rect);
+ }
+
+ @Implementation(minSdk = S)
+ protected static float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics) {
+ return FontNatives.nGetFontMetrics(font, paint, metrics);
+ }
+
+ @Implementation(minSdk = S)
+ protected static String nGetFontPath(long fontPtr) {
+ return FontNatives.nGetFontPath(fontPtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static String nGetLocaleList(long familyPtr) {
+ return FontNatives.nGetLocaleList(familyPtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nGetPackedStyle(long fontPtr) {
+ return FontNatives.nGetPackedStyle(fontPtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nGetIndex(long fontPtr) {
+ return FontNatives.nGetIndex(fontPtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nGetAxisCount(long fontPtr) {
+ return FontNatives.nGetAxisCount(fontPtr);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nGetAxisInfo(long fontPtr, int i) {
+ return FontNatives.nGetAxisInfo(fontPtr, i);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long[] nGetAvailableFontSet() {
+ return FontNatives.nGetAvailableFontSet();
+ }
+
+ /** Shadow for {@link Font.Builder} that is backed by native code */
+ @Implements(
+ value = Font.Builder.class,
+ minSdk = P,
+ shadowPicker = ShadowNativeFontBuilder.Picker.class,
+ isInAndroidSdk = false)
+ public static class ShadowNativeFontBuilder {
+
+ @RealObject Font.Builder realFontBuilder;
+
+ @Implementation(minSdk = Q, maxSdk = Q)
+ protected void __constructor__(AssetManager am, String path, boolean isAsset, int cookie) {
+ // In Android Q, this method uses native methods that do not exist in later versions, so
+ // they need to be re-implemented using logic from S.
+ reflector(FontBuilderReflector.class, realFontBuilder).setWeight(-1);
+ reflector(FontBuilderReflector.class, realFontBuilder).setItalic(-1);
+ reflector(FontBuilderReflector.class, realFontBuilder).setLocaleList("");
+ try {
+ ByteBuffer buf = createBuffer(am, path, isAsset, cookie);
+ reflector(FontBuilderReflector.class, realFontBuilder).setBuffer(buf);
+ } catch (IOException e) {
+ reflector(FontBuilderReflector.class, realFontBuilder).setException(e);
+ }
+ }
+
+ @Implementation(minSdk = Q, maxSdk = Q)
+ protected void __constructor__(Resources res, int resId) {
+ // In Android Q, this method uses native methods that do not exist in later versions, so
+ // they need to be re-implemented using logic from S.
+ reflector(FontBuilderReflector.class, realFontBuilder).setWeight(-1);
+ reflector(FontBuilderReflector.class, realFontBuilder).setItalic(-1);
+ reflector(FontBuilderReflector.class, realFontBuilder).setLocaleList("");
+ final TypedValue value = new TypedValue();
+ res.getValue(resId, value, true);
+ if (value.string == null) {
+ reflector(FontBuilderReflector.class, realFontBuilder)
+ .setException(new FileNotFoundException(resId + " not found"));
+ return;
+ }
+ final String str = value.string.toString();
+ if (Ascii.toLowerCase(str).endsWith(".xml")) {
+ reflector(FontBuilderReflector.class, realFontBuilder)
+ .setException(new FileNotFoundException(resId + " must be font file."));
+ return;
+ }
+ try {
+ ByteBuffer buf = createBuffer(res.getAssets(), str, false, value.assetCookie);
+ reflector(FontBuilderReflector.class, realFontBuilder).setBuffer(buf);
+ } catch (IOException e) {
+ reflector(FontBuilderReflector.class, realFontBuilder).setException(e);
+ }
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nInitBuilder() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontBuilderNatives.nInitBuilder();
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nAddAxis(long builderPtr, int tag, float value) {
+ FontBuilderNatives.nAddAxis(builderPtr, tag, value);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nBuild(
+ long builderPtr,
+ ByteBuffer buffer,
+ String filePath,
+ String localeList,
+ int weight,
+ boolean italic,
+ int ttcIndex) {
+ return FontBuilderNatives.nBuild(
+ builderPtr, buffer, filePath, localeList, weight, italic, ttcIndex);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = R)
+ protected static long nBuild(
+ long builderPtr,
+ ByteBuffer buffer,
+ String filePath,
+ int weight,
+ boolean italic,
+ int ttcIndex) {
+ return nBuild(builderPtr, buffer, filePath, "", weight, italic, ttcIndex);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = TIRAMISU)
+ protected static long nGetReleaseNativeFont() {
+ // Starting in S, nGetReleaseNativeFont was moved from Font.Builder to Font, and despite
+ // existing in S, Font.Builder.nGetReleaseNativeFont does not get registered with a native
+ // method.
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontNatives.nGetReleaseNativeFont();
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nClone(
+ long fontPtr, long builderPtr, int weight, boolean italic, int ttcIndex) {
+ return FontBuilderNatives.nClone(fontPtr, builderPtr, weight, italic, ttcIndex);
+ }
+
+ /**
+ * The Android implementation attempts to call {@link java.nio.ByteBuffer#array()} on a direct
+ * byte buffer. This is supported in Libcore but not the JVM. Use an implementation that copies
+ * the data from the asset into a direct buffer.
+ */
+ @Implementation(minSdk = R)
+ protected static ByteBuffer createBuffer(
+ AssetManager am, String path, boolean isAsset, int cookie) throws IOException {
+ return assetToBuffer(am, path, isAsset, cookie);
+ }
+
+ @ForType(Font.Builder.class)
+ interface FontBuilderReflector {
+ @Accessor("mBuffer")
+ void setBuffer(ByteBuffer buffer);
+
+ @Accessor("mException")
+ void setException(IOException e);
+
+ @Accessor("mWeight")
+ void setWeight(int weight);
+
+ @Accessor("mItalic")
+ void setItalic(int italic);
+
+ @Accessor("mLocaleList")
+ void setLocaleList(String localeList);
+ }
+
+ /** Shadow picker for {@link Font.Builder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowFontBuilder.class, ShadowNativeFontBuilder.class);
+ }
+ }
+ }
+
+ static ByteBuffer assetToBuffer(AssetManager am, String path, boolean isAsset, int cookie)
+ throws IOException {
+ Preconditions.checkNotNull(am, "assetManager can not be null");
+ Preconditions.checkNotNull(path, "path can not be null");
+ try (InputStream assetStream =
+ isAsset
+ ? am.open(path, AssetManager.ACCESS_BUFFER)
+ : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
+ int capacity = assetStream.available();
+ ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ byte[] buf = new byte[8 * 1024]; // 8k
+ int bytesRead;
+ while ((bytesRead = assetStream.read(buf)) != -1) {
+ buffer.put(buf, 0, bytesRead);
+ }
+ if (assetStream.read() != -1) {
+ throw new IOException("Unable to access full contents of " + path);
+ }
+ return buffer;
+ }
+ }
+
+ /** Shadow picker for {@link Font}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowFont.class, ShadowNativeFont.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFamily.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFamily.java
new file mode 100644
index 000000000..7a78e471f
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFamily.java
@@ -0,0 +1,96 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.content.res.AssetManager;
+import android.graphics.FontFamily;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.FontFamilyNatives;
+import org.robolectric.shadows.ShadowNativeFontFamily.Picker;
+
+/** Shadow for {@link FontFamily} that is backed by native code */
+@Implements(
+ value = FontFamily.class,
+ minSdk = O,
+ isInAndroidSdk = false,
+ shadowPicker = Picker.class)
+public class ShadowNativeFontFamily {
+ @Implementation(minSdk = O)
+ public static long nInitBuilder(String langs, int variant) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFamilyNatives.nInitBuilder(langs, variant);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nAllowUnsupportedFont(long builderPtr) {
+ FontFamilyNatives.nAllowUnsupportedFont(builderPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateFamily(long mBuilderPtr) {
+ return FontFamilyNatives.nCreateFamily(mBuilderPtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static long nGetBuilderReleaseFunc() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFamilyNatives.nGetBuilderReleaseFunc();
+ }
+
+ @Implementation(minSdk = P)
+ protected static long nGetFamilyReleaseFunc() {
+ return FontFamilyNatives.nGetFamilyReleaseFunc();
+ }
+
+ // By passing -1 to weight argument, the weight value is resolved by OS/2 table in the font.
+ // By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
+ @Implementation(minSdk = O)
+ protected static boolean nAddFont(
+ long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic) {
+ return FontFamilyNatives.nAddFont(builderPtr, font, ttcIndex, weight, isItalic);
+ }
+
+ @Implementation(minSdk = O, maxSdk = Q)
+ protected static boolean nAddFontFromAssetManager(
+ long builderPtr,
+ AssetManager mgr,
+ String path,
+ int cookie,
+ boolean isAsset,
+ int ttcIndex,
+ int weight,
+ int isItalic) {
+ try {
+ ByteBuffer byteBuffer = ShadowNativeFont.assetToBuffer(mgr, path, isAsset, cookie);
+ return nAddFont(builderPtr, byteBuffer, ttcIndex, weight, isItalic);
+ } catch (IOException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nAddFontWeightStyle(
+ long builderPtr, ByteBuffer font, int ttcIndex, int weight, int isItalic) {
+ return FontFamilyNatives.nAddFontWeightStyle(builderPtr, font, ttcIndex, weight, isItalic);
+ }
+
+ // The added axis values are only valid for the next nAddFont* method call.
+ @Implementation(minSdk = O)
+ protected static void nAddAxisValue(long builderPtr, int tag, float value) {
+ FontFamilyNatives.nAddAxisValue(builderPtr, tag, value);
+ }
+
+ /** Shadow picker for {@link FontFamily}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowFontFamily.class, ShadowNativeFontFamily.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFileUtil.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFileUtil.java
new file mode 100644
index 000000000..a38e280aa
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontFileUtil.java
@@ -0,0 +1,45 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.fonts.FontFileUtil;
+import java.nio.ByteBuffer;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.FontFileUtilNatives;
+import org.robolectric.shadows.ShadowNativeFontFileUtil.Picker;
+
+/** Shadow for {@link FontFileUtil} that is backed by native code */
+@Implements(
+ value = FontFileUtil.class,
+ isInAndroidSdk = false,
+ minSdk = Q,
+ shadowPicker = Picker.class)
+public class ShadowNativeFontFileUtil {
+ @Implementation(minSdk = S)
+ protected static long nGetFontRevision(ByteBuffer buffer, int index) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFileUtilNatives.nGetFontRevision(buffer, index);
+ }
+
+ @Implementation(minSdk = S)
+ protected static String nGetFontPostScriptName(ByteBuffer buffer, int index) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFileUtilNatives.nGetFontPostScriptName(buffer, index);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nIsPostScriptType1Font(ByteBuffer buffer, int index) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFileUtilNatives.nIsPostScriptType1Font(buffer, index);
+ }
+
+ /** Shadow picker for {@link FontFileUtil}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeFontFileUtil.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java
new file mode 100644
index 000000000..360f61365
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeFontsFontFamily.java
@@ -0,0 +1,86 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.fonts.FontFamily;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.FontFamilyBuilderNatives;
+import org.robolectric.nativeruntime.FontsFontFamilyNatives;
+import org.robolectric.shadows.ShadowNativeFontsFontFamily.Picker;
+
+/** Shadow for {@link FontFamily} that is backed by native code */
+@Implements(
+ value = FontFamily.class,
+ minSdk = Q,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeFontsFontFamily {
+ @Implementation(minSdk = S)
+ protected static int nGetFontSize(long family) {
+ return FontsFontFamilyNatives.nGetFontSize(family);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nGetFont(long family, int i) {
+ return FontsFontFamilyNatives.nGetFont(family, i);
+ }
+
+ @Implementation(minSdk = S)
+ protected static String nGetLangTags(long family) {
+ return FontsFontFamilyNatives.nGetLangTags(family);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nGetVariant(long family) {
+ return FontsFontFamilyNatives.nGetVariant(family);
+ }
+
+ /** Shadow for {@link FontFamily.Builder} that is backed by native code */
+ @Implements(
+ value = FontFamily.Builder.class,
+ minSdk = Q,
+ shadowPicker = ShadowNativeFontFamilyBuilder.Picker.class,
+ isInAndroidSdk = false)
+ public static class ShadowNativeFontFamilyBuilder {
+ @Implementation
+ protected static long nInitBuilder() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return FontFamilyBuilderNatives.nInitBuilder();
+ }
+
+ @Implementation
+ protected static void nAddFont(long builderPtr, long fontPtr) {
+ FontFamilyBuilderNatives.nAddFont(builderPtr, fontPtr);
+ }
+
+ @Implementation
+ protected static long nBuild(
+ long builderPtr, String langTags, int variant, boolean isCustomFallback) {
+ return FontFamilyBuilderNatives.nBuild(builderPtr, langTags, variant, isCustomFallback);
+ }
+
+ @Implementation
+ protected static long nGetReleaseNativeFamily() {
+ return FontFamilyBuilderNatives.nGetReleaseNativeFamily();
+ }
+
+ /** Shadow picker for {@link FontFamily.Builder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(
+ ShadowFontsFontFamily.ShadowFontsFontFamilyBuilder.class,
+ ShadowNativeFontFamilyBuilder.class);
+ }
+ }
+ }
+
+ /** Shadow picker for {@link FontFamily}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowFontsFontFamily.class, ShadowNativeFontsFontFamily.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java
new file mode 100644
index 000000000..263522d84
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRenderer.java
@@ -0,0 +1,396 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
+import android.graphics.HardwareRenderer.ASurfaceTransactionCallback;
+import android.graphics.HardwareRenderer.FrameCompleteCallback;
+import android.graphics.HardwareRenderer.FrameDrawingCallback;
+import android.graphics.HardwareRenderer.PictureCapturedCallback;
+import android.graphics.HardwareRenderer.PrepareSurfaceControlForWebviewCallback;
+import android.view.Surface;
+import java.io.FileDescriptor;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.HardwareRendererNatives;
+import org.robolectric.shadows.ShadowNativeHardwareRenderer.Picker;
+
+/** Shadow for {@link HardwareRenderer} that is backed by native code */
+@Implements(
+ value = HardwareRenderer.class,
+ minSdk = Q,
+ looseSignatures = true,
+ shadowPicker = Picker.class)
+public class ShadowNativeHardwareRenderer {
+ @Implementation
+ protected static void disableVsync() {
+ HardwareRendererNatives.disableVsync();
+ }
+
+ @Implementation
+ protected static void preload() {
+ HardwareRendererNatives.preload();
+ }
+
+ @Implementation(minSdk = S)
+ protected static boolean isWebViewOverlaysEnabled() {
+ return HardwareRendererNatives.isWebViewOverlaysEnabled();
+ }
+
+ @Implementation
+ protected static void setupShadersDiskCache(String cacheFile, String skiaCacheFile) {
+ HardwareRendererNatives.setupShadersDiskCache(cacheFile, skiaCacheFile);
+ }
+
+ @Implementation
+ protected static void nRotateProcessStatsBuffer() {
+ HardwareRendererNatives.nRotateProcessStatsBuffer();
+ }
+
+ @Implementation
+ protected static void nSetProcessStatsBuffer(int fd) {
+ HardwareRendererNatives.nSetProcessStatsBuffer(fd);
+ }
+
+ @Implementation
+ protected static int nGetRenderThreadTid(long nativeProxy) {
+ return HardwareRendererNatives.nGetRenderThreadTid(nativeProxy);
+ }
+
+ @Implementation
+ protected static long nCreateRootRenderNode() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return HardwareRendererNatives.nCreateRootRenderNode();
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nCreateProxy(boolean translucent, long rootRenderNode) {
+ return HardwareRendererNatives.nCreateProxy(translucent, rootRenderNode);
+ }
+
+ @Implementation(minSdk = R, maxSdk = R)
+ protected static long nCreateProxy(
+ boolean translucent, boolean isWideGamut, long rootRenderNode) {
+ return nCreateProxy(true, rootRenderNode);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = Q)
+ protected static Object nCreateProxy(Object translucent, Object rootRenderNode) {
+ return nCreateProxy((boolean) translucent, (long) rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nDeleteProxy(long nativeProxy) {
+ HardwareRendererNatives.nDeleteProxy(nativeProxy);
+ }
+
+ @Implementation
+ protected static boolean nLoadSystemProperties(long nativeProxy) {
+ return HardwareRendererNatives.nLoadSystemProperties(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nSetName(long nativeProxy, String name) {
+ HardwareRendererNatives.nSetName(nativeProxy, name);
+ }
+
+ @Implementation(minSdk = R)
+ protected static void nSetSurface(long nativeProxy, Surface window, boolean discardBuffer) {
+ HardwareRendererNatives.nSetSurface(nativeProxy, window, discardBuffer);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetSurfaceControl(long nativeProxy, long nativeSurfaceControl) {
+ HardwareRendererNatives.nSetSurfaceControl(nativeProxy, nativeSurfaceControl);
+ }
+
+ @Implementation
+ protected static boolean nPause(long nativeProxy) {
+ return HardwareRendererNatives.nPause(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nSetStopped(long nativeProxy, boolean stopped) {
+ HardwareRendererNatives.nSetStopped(nativeProxy, stopped);
+ }
+
+ @Implementation
+ protected static void nSetLightGeometry(
+ long nativeProxy, float lightX, float lightY, float lightZ, float lightRadius) {
+ HardwareRendererNatives.nSetLightGeometry(nativeProxy, lightX, lightY, lightZ, lightRadius);
+ }
+
+ @Implementation
+ protected static void nSetLightAlpha(
+ long nativeProxy, float ambientShadowAlpha, float spotShadowAlpha) {
+ HardwareRendererNatives.nSetLightAlpha(nativeProxy, ambientShadowAlpha, spotShadowAlpha);
+ }
+
+ @Implementation
+ protected static void nSetOpaque(long nativeProxy, boolean opaque) {
+ HardwareRendererNatives.nSetOpaque(nativeProxy, opaque);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetColorMode(long nativeProxy, int colorMode) {
+ HardwareRendererNatives.nSetColorMode(nativeProxy, colorMode);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetSdrWhitePoint(long nativeProxy, float whitePoint) {
+ HardwareRendererNatives.nSetSdrWhitePoint(nativeProxy, whitePoint);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetIsHighEndGfx(boolean isHighEndGfx) {
+ HardwareRendererNatives.nSetIsHighEndGfx(isHighEndGfx);
+ }
+
+ @Implementation
+ protected static int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size) {
+ return HardwareRendererNatives.nSyncAndDrawFrame(nativeProxy, frameInfo, size);
+ }
+
+ @Implementation
+ protected static void nDestroy(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nDestroy(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode) {
+ HardwareRendererNatives.nRegisterAnimatingRenderNode(rootRenderNode, animatingNode);
+ }
+
+ @Implementation
+ protected static void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator) {
+ HardwareRendererNatives.nRegisterVectorDrawableAnimator(rootRenderNode, animator);
+ }
+
+ @Implementation
+ protected static long nCreateTextureLayer(long nativeProxy) {
+ return HardwareRendererNatives.nCreateTextureLayer(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nBuildLayer(long nativeProxy, long node) {
+ HardwareRendererNatives.nBuildLayer(nativeProxy, node);
+ }
+
+ @Implementation
+ protected static boolean nCopyLayerInto(long nativeProxy, long layer, long bitmapHandle) {
+ return HardwareRendererNatives.nCopyLayerInto(nativeProxy, layer, bitmapHandle);
+ }
+
+ @Implementation
+ protected static void nPushLayerUpdate(long nativeProxy, long layer) {
+ HardwareRendererNatives.nPushLayerUpdate(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nCancelLayerUpdate(long nativeProxy, long layer) {
+ HardwareRendererNatives.nCancelLayerUpdate(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nDetachSurfaceTexture(long nativeProxy, long layer) {
+ HardwareRendererNatives.nDetachSurfaceTexture(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nDestroyHardwareResources(long nativeProxy) {
+ HardwareRendererNatives.nDestroyHardwareResources(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nTrimMemory(int level) {
+ HardwareRendererNatives.nTrimMemory(level);
+ }
+
+ @Implementation
+ protected static void nOverrideProperty(String name, String value) {
+ HardwareRendererNatives.nOverrideProperty(name, value);
+ }
+
+ @Implementation
+ protected static void nFence(long nativeProxy) {
+ HardwareRendererNatives.nFence(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nStopDrawing(long nativeProxy) {
+ HardwareRendererNatives.nStopDrawing(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nNotifyFramePending(long nativeProxy) {
+ HardwareRendererNatives.nNotifyFramePending(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nDumpProfileInfo(long nativeProxy, FileDescriptor fd, int dumpFlags) {
+ HardwareRendererNatives.nDumpProfileInfo(nativeProxy, fd, dumpFlags);
+ }
+
+ @Implementation
+ protected static void nAddRenderNode(long nativeProxy, long rootRenderNode, boolean placeFront) {
+ HardwareRendererNatives.nAddRenderNode(nativeProxy, rootRenderNode, placeFront);
+ }
+
+ @Implementation
+ protected static void nRemoveRenderNode(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nRemoveRenderNode(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nDrawRenderNode(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nDrawRenderNode(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nSetContentDrawBounds(
+ long nativeProxy, int left, int top, int right, int bottom) {
+ HardwareRendererNatives.nSetContentDrawBounds(nativeProxy, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static void nSetPictureCaptureCallback(
+ long nativeProxy, PictureCapturedCallback callback) {
+ HardwareRendererNatives.nSetPictureCaptureCallback(nativeProxy, callback);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetASurfaceTransactionCallback(Object nativeProxy, Object callback) {
+ // Requires looseSignatures because ASurfaceTransactionCallback is S+.
+ HardwareRendererNatives.nSetASurfaceTransactionCallback(
+ (long) nativeProxy, (ASurfaceTransactionCallback) callback);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetPrepareSurfaceControlForWebviewCallback(
+ Object nativeProxy, Object callback) {
+ // Need to use loose signatures here as PrepareSurfaceControlForWebviewCallback is S+.
+ HardwareRendererNatives.nSetPrepareSurfaceControlForWebviewCallback(
+ (long) nativeProxy, (PrepareSurfaceControlForWebviewCallback) callback);
+ }
+
+ @Implementation
+ protected static void nSetFrameCallback(long nativeProxy, FrameDrawingCallback callback) {
+ HardwareRendererNatives.nSetFrameCallback(nativeProxy, callback);
+ }
+
+ @Implementation
+ protected static void nSetFrameCompleteCallback(
+ long nativeProxy, FrameCompleteCallback callback) {
+ HardwareRendererNatives.nSetFrameCompleteCallback(nativeProxy, callback);
+ }
+
+ @Implementation(minSdk = R)
+ protected static void nAddObserver(long nativeProxy, long nativeObserver) {
+ HardwareRendererNatives.nAddObserver(nativeProxy, nativeObserver);
+ }
+
+ @Implementation(minSdk = R)
+ protected static void nRemoveObserver(long nativeProxy, long nativeObserver) {
+ HardwareRendererNatives.nRemoveObserver(nativeProxy, nativeObserver);
+ }
+
+ @Implementation(maxSdk = TIRAMISU)
+ protected static int nCopySurfaceInto(
+ Surface surface, int srcLeft, int srcTop, int srcRight, int srcBottom, long bitmapHandle) {
+ return HardwareRendererNatives.nCopySurfaceInto(
+ surface, srcLeft, srcTop, srcRight, srcBottom, bitmapHandle);
+ }
+
+ @Implementation
+ protected static Bitmap nCreateHardwareBitmap(long renderNode, int width, int height) {
+ return HardwareRendererNatives.nCreateHardwareBitmap(renderNode, width, height);
+ }
+
+ @Implementation
+ protected static void nSetHighContrastText(boolean enabled) {
+ HardwareRendererNatives.nSetHighContrastText(enabled);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = S)
+ protected static void nHackySetRTAnimationsEnabled(boolean enabled) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ HardwareRendererNatives.nHackySetRTAnimationsEnabled(enabled);
+ }
+
+ @Implementation
+ protected static void nSetDebuggingEnabled(boolean enabled) {
+ HardwareRendererNatives.nSetDebuggingEnabled(enabled);
+ }
+
+ @Implementation
+ protected static void nSetIsolatedProcess(boolean enabled) {
+ HardwareRendererNatives.nSetIsolatedProcess(enabled);
+ }
+
+ @Implementation
+ protected static void nSetContextPriority(int priority) {
+ HardwareRendererNatives.nSetContextPriority(priority);
+ }
+
+ @Implementation
+ protected static void nAllocateBuffers(long nativeProxy) {
+ HardwareRendererNatives.nAllocateBuffers(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nSetForceDark(long nativeProxy, boolean enabled) {
+ HardwareRendererNatives.nSetForceDark(nativeProxy, enabled);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nSetDisplayDensityDpi(int densityDpi) {
+ HardwareRendererNatives.nSetDisplayDensityDpi(densityDpi);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static void nInitDisplayInfo(
+ int width,
+ int height,
+ float refreshRate,
+ int wideColorDataspace,
+ long appVsyncOffsetNanos,
+ long presentationDeadlineNanos) {
+ HardwareRendererNatives.nInitDisplayInfo(
+ width,
+ height,
+ refreshRate,
+ wideColorDataspace,
+ appVsyncOffsetNanos,
+ presentationDeadlineNanos);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static void nInitDisplayInfo(
+ int width,
+ int height,
+ float refreshRate,
+ int wideColorDataspace,
+ long appVsyncOffsetNanos,
+ long presentationDeadlineNanos,
+ boolean supportsFp16ForHdr) {
+ nInitDisplayInfo(
+ width,
+ height,
+ refreshRate,
+ wideColorDataspace,
+ appVsyncOffsetNanos,
+ presentationDeadlineNanos);
+ }
+
+ /** Shadow picker for {@link HardwareRenderer}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowHardwareRenderer.class, ShadowNativeHardwareRenderer.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRendererObserver.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRendererObserver.java
new file mode 100644
index 000000000..97b05eb18
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeHardwareRendererObserver.java
@@ -0,0 +1,60 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.HardwareRendererObserver;
+import java.lang.ref.WeakReference;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.HardwareRendererObserverNatives;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowNativeHardwareRendererObserver.Picker;
+
+/** Shadow for {@link HardwareRendererObserver} that is backed by native code */
+@Implements(
+ value = HardwareRendererObserver.class,
+ minSdk = R,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeHardwareRendererObserver {
+
+ public HardwareRendererObserverNatives hardwareRendererObserverNatives =
+ new HardwareRendererObserverNatives();
+
+ @Implementation
+ protected static int nGetNextBuffer(long nativePtr, long[] data) {
+ return HardwareRendererObserverNatives.nGetNextBuffer(nativePtr, data);
+ }
+
+ @Implementation(minSdk = R, maxSdk = R)
+ protected long nCreateObserver() {
+ return nCreateObserver(false);
+ }
+
+ @Implementation(minSdk = S, maxSdk = S_V2)
+ protected long nCreateObserver(boolean waitForPresentTime) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return hardwareRendererObserverNatives.nCreateObserver(waitForPresentTime);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static long nCreateObserver(
+ WeakReference<HardwareRendererObserver> observer, boolean waitForPresentTime) {
+ HardwareRendererObserver hardwareRendererObserver = observer.get();
+ ShadowNativeHardwareRendererObserver shadowNativeHardwareRendererObserver =
+ Shadow.extract(hardwareRendererObserver);
+ return shadowNativeHardwareRendererObserver.hardwareRendererObserverNatives.nCreateObserver(
+ waitForPresentTime);
+ }
+
+ /** Shadow picker for {@link HardwareRendererObserver}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeHardwareRendererObserver.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeImageDecoder.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeImageDecoder.java
new file mode 100644
index 000000000..4913c9f24
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeImageDecoder.java
@@ -0,0 +1,211 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.content.res.AssetManager.AssetInputStream;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.ImageDecoder;
+import android.graphics.ImageDecoder.Source;
+import android.graphics.Rect;
+import android.util.Size;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.ImageDecoderNatives;
+import org.robolectric.shadows.ShadowNativeImageDecoder.Picker;
+
+/** Shadow for {@link android.graphics.ImageDecoder} that is backed by native code */
+@Implements(value = ImageDecoder.class, minSdk = P, shadowPicker = Picker.class)
+public class ShadowNativeImageDecoder {
+
+ static {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ }
+
+ @Implementation(minSdk = P, maxSdk = Q)
+ protected static ImageDecoder createFromAsset(AssetInputStream ais, Source source)
+ throws IOException {
+ return createFromAsset(ais, false, source);
+ }
+
+ @Implementation(minSdk = R)
+ protected static ImageDecoder createFromAsset(
+ AssetInputStream ais, boolean preferAnimation, Source source) throws IOException {
+ int capacity = ais.available();
+ ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ byte[] buf = new byte[8 * 1024]; // 8k
+ int bytesRead;
+ while ((bytesRead = ais.read(buf)) != -1) {
+ buffer.put(buf, 0, bytesRead);
+ }
+ if (ais.read() != -1) {
+ throw new IOException("Unable to access full contents of asset");
+ }
+ return nCreate(buffer, 0, bytesRead, preferAnimation, source);
+ }
+
+ @Implementation(minSdk = P, maxSdk = Q)
+ protected static ImageDecoder nCreate(long asset, Source src) throws IOException {
+ return nCreate(asset, false, src);
+ }
+
+ @Implementation(minSdk = R)
+ protected static ImageDecoder nCreate(long asset, boolean preferAnimation, Source src)
+ throws IOException {
+ throw new UnsupportedEncodingException();
+ }
+
+ @Implementation(minSdk = P, maxSdk = Q)
+ protected static ImageDecoder nCreate(ByteBuffer buffer, int position, int limit, Source src)
+ throws IOException {
+ return nCreate(buffer, position, limit, false, src);
+ }
+
+ @Implementation(minSdk = R)
+ protected static ImageDecoder nCreate(
+ ByteBuffer buffer, int position, int limit, boolean preferAnimation, Source src)
+ throws IOException {
+ return ImageDecoderNatives.nCreate(buffer, position, limit, preferAnimation, src);
+ }
+
+ @Implementation(minSdk = P, maxSdk = Q)
+ protected static ImageDecoder nCreate(byte[] data, int offset, int length, Source src)
+ throws IOException {
+ return nCreate(data, offset, length, false, src);
+ }
+
+ @Implementation(minSdk = R)
+ protected static ImageDecoder nCreate(
+ byte[] data, int offset, int length, boolean preferAnimation, Source src) throws IOException {
+ return ImageDecoderNatives.nCreate(data, offset, length, preferAnimation, src);
+ }
+
+ @Implementation(minSdk = P, maxSdk = Q)
+ protected static ImageDecoder nCreate(InputStream is, byte[] storage, Source src)
+ throws IOException {
+ return nCreate(is, storage, false, src);
+ }
+
+ @Implementation(minSdk = R)
+ protected static ImageDecoder nCreate(
+ InputStream is, byte[] storage, boolean preferAnimation, Source src) throws IOException {
+ return ImageDecoderNatives.nCreate(is, storage, preferAnimation, src);
+ }
+
+ @Implementation(maxSdk = Q)
+ protected static ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException {
+ throw new UnsupportedEncodingException();
+ }
+
+ @Implementation(minSdk = S)
+ protected static ImageDecoder nCreate(
+ FileDescriptor fd, long length, boolean preferAnimation, Source src) throws IOException {
+ return ImageDecoderNatives.nCreate(fd, length, preferAnimation, src);
+ }
+
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static Bitmap nDecodeBitmap(
+ long nativePtr,
+ ImageDecoder decoder,
+ boolean doPostProcess,
+ int width,
+ int height,
+ Rect cropRect,
+ boolean mutable,
+ int allocator,
+ boolean unpremulRequired,
+ boolean conserveMemory,
+ boolean decodeAsAlphaMask,
+ ColorSpace desiredColorSpace)
+ throws IOException {
+ return nDecodeBitmap(
+ nativePtr,
+ decoder,
+ doPostProcess,
+ width,
+ height,
+ cropRect,
+ mutable,
+ allocator,
+ unpremulRequired,
+ conserveMemory,
+ decodeAsAlphaMask,
+ /* desiredColorSpace = */ 0, // Desired color space is currently not supported in P.
+ /* extended = */ false);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static Bitmap nDecodeBitmap(
+ long nativePtr,
+ ImageDecoder decoder,
+ boolean doPostProcess,
+ int width,
+ int height,
+ Rect cropRect,
+ boolean mutable,
+ int allocator,
+ boolean unpremulRequired,
+ boolean conserveMemory,
+ boolean decodeAsAlphaMask,
+ long desiredColorSpace,
+ boolean extended)
+ throws IOException {
+ return ImageDecoderNatives.nDecodeBitmap(
+ nativePtr,
+ decoder,
+ doPostProcess,
+ width,
+ height,
+ cropRect,
+ mutable,
+ allocator,
+ unpremulRequired,
+ conserveMemory,
+ decodeAsAlphaMask,
+ desiredColorSpace,
+ extended);
+ }
+
+ @Implementation
+ protected static Size nGetSampledSize(long nativePtr, int sampleSize) {
+ return ImageDecoderNatives.nGetSampledSize(nativePtr, sampleSize);
+ }
+
+ @Implementation
+ protected static void nGetPadding(long nativePtr, Rect outRect) {
+ ImageDecoderNatives.nGetPadding(nativePtr, outRect);
+ }
+
+ @Implementation
+ protected static void nClose(long nativePtr) {
+ ImageDecoderNatives.nClose(nativePtr);
+ }
+
+ @Implementation
+ protected static String nGetMimeType(long nativePtr) {
+ return ImageDecoderNatives.nGetMimeType(nativePtr);
+ }
+
+ @Implementation
+ protected static ColorSpace nGetColorSpace(long nativePtr) {
+ return ImageDecoderNatives.nGetColorSpace(nativePtr);
+ }
+
+ /** Shadow picker for {@link ImageDecoder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowImageDecoder.class, ShadowNativeImageDecoder.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeInterpolator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeInterpolator.java
new file mode 100644
index 000000000..21a292c89
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeInterpolator.java
@@ -0,0 +1,55 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Interpolator;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.InterpolatorNatives;
+import org.robolectric.shadows.ShadowNativeInterpolator.Picker;
+
+/** Shadow for {@link Interpolator} that is backed by native code */
+@Implements(value = Interpolator.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeInterpolator {
+
+ @Implementation
+ protected static long nativeConstructor(int valueCount, int frameCount) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return InterpolatorNatives.nativeConstructor(valueCount, frameCount);
+ }
+
+ @Implementation
+ protected static void nativeDestructor(long nativeInstance) {
+ InterpolatorNatives.nativeDestructor(nativeInstance);
+ }
+
+ @Implementation
+ protected static void nativeReset(long nativeInstance, int valueCount, int frameCount) {
+ InterpolatorNatives.nativeReset(nativeInstance, valueCount, frameCount);
+ }
+
+ @Implementation
+ protected static void nativeSetKeyFrame(
+ long nativeInstance, int index, int msec, float[] values, float[] blend) {
+ InterpolatorNatives.nativeSetKeyFrame(nativeInstance, index, msec, values, blend);
+ }
+
+ @Implementation
+ protected static void nativeSetRepeatMirror(
+ long nativeInstance, float repeatCount, boolean mirror) {
+ InterpolatorNatives.nativeSetRepeatMirror(nativeInstance, repeatCount, mirror);
+ }
+
+ @Implementation
+ protected static int nativeTimeToValues(long nativeInstance, int msec, float[] values) {
+ return InterpolatorNatives.nativeTimeToValues(nativeInstance, msec, values);
+ }
+
+ /** Shadow picker for {@link Interpolator}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeInterpolator.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLightingColorFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLightingColorFilter.java
new file mode 100644
index 000000000..88e4ea5c3
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLightingColorFilter.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.LightingColorFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.LightingColorFilterNatives;
+import org.robolectric.shadows.ShadowNativeLightingColorFilter.Picker;
+
+/** Shadow for {@link LightingColorFilter} that is backed by native code */
+@Implements(value = LightingColorFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeLightingColorFilter {
+
+ @Implementation(minSdk = O)
+ protected static long native_CreateLightingFilter(int mul, int add) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LightingColorFilterNatives.native_CreateLightingFilter(mul, add);
+ }
+
+ /** Shadow picker for {@link LightingColorFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeLightingColorFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLineBreaker.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLineBreaker.java
new file mode 100644
index 000000000..f5d029cce
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLineBreaker.java
@@ -0,0 +1,97 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.graphics.text.LineBreaker;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.LineBreakerNatives;
+import org.robolectric.shadows.ShadowNativeLineBreaker.Picker;
+
+/** Shadow for {@link LineBreaker} that is backed by native code */
+@Implements(value = LineBreaker.class, minSdk = Q, shadowPicker = Picker.class)
+public class ShadowNativeLineBreaker {
+ @Implementation
+ protected static long nInit(
+ int breakStrategy, int hyphenationFrequency, boolean isJustified, int[] indents) {
+ return LineBreakerNatives.nInit(breakStrategy, hyphenationFrequency, isJustified, indents);
+ }
+
+ @Implementation
+ protected static long nGetReleaseFunc() {
+ // Called first by the static initializer.
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LineBreakerNatives.nGetReleaseFunc();
+ }
+
+ @Implementation
+ protected static long nComputeLineBreaks(
+ long nativePtr,
+ char[] text,
+ long measuredTextPtr,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ float[] variableTabStops,
+ float defaultTabStop,
+ @IntRange(from = 0) int indentsOffset) {
+ return LineBreakerNatives.nComputeLineBreaks(
+ nativePtr,
+ text,
+ measuredTextPtr,
+ length,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ defaultTabStop,
+ indentsOffset);
+ }
+
+ // Result accessors
+ @Implementation
+ protected static int nGetLineCount(long ptr) {
+ return LineBreakerNatives.nGetLineCount(ptr);
+ }
+
+ @Implementation
+ protected static int nGetLineBreakOffset(long ptr, int idx) {
+ return LineBreakerNatives.nGetLineBreakOffset(ptr, idx);
+ }
+
+ @Implementation
+ protected static float nGetLineWidth(long ptr, int idx) {
+ return LineBreakerNatives.nGetLineWidth(ptr, idx);
+ }
+
+ @Implementation
+ protected static float nGetLineAscent(long ptr, int idx) {
+ return LineBreakerNatives.nGetLineAscent(ptr, idx);
+ }
+
+ @Implementation
+ protected static float nGetLineDescent(long ptr, int idx) {
+ return LineBreakerNatives.nGetLineDescent(ptr, idx);
+ }
+
+ @Implementation
+ protected static int nGetLineFlag(long ptr, int idx) {
+ return LineBreakerNatives.nGetLineFlag(ptr, idx);
+ }
+
+ @Implementation
+ protected static long nGetReleaseResultFunc() {
+ return LineBreakerNatives.nGetReleaseResultFunc();
+ }
+
+ /** Shadow picker for {@link LineBreaker}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLineBreaker.class, ShadowNativeLineBreaker.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLinearGradient.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLinearGradient.java
new file mode 100644
index 000000000..c3458fcf5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeLinearGradient.java
@@ -0,0 +1,60 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.LinearGradient;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.LinearGradientNatives;
+import org.robolectric.shadows.ShadowNativeLinearGradient.Picker;
+
+/** Shadow for {@link LinearGradient} that is backed by native code */
+@Implements(value = LinearGradient.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeLinearGradient {
+ @Implementation(minSdk = Q)
+ protected long nativeCreate(
+ long matrix,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ long[] colors,
+ float[] positions,
+ int tileMode,
+ long colorSpaceHandle) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LinearGradientNatives.nativeCreate(
+ matrix, x0, y0, x1, y1, colors, positions, tileMode, colorSpaceHandle);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected long nativeCreate1(
+ long matrix,
+ float x0,
+ float y0,
+ float x1,
+ float y1,
+ int[] colors,
+ float[] positions,
+ int tileMode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LinearGradientNatives.nativeCreate1(matrix, x0, y0, x1, y1, colors, positions, tileMode);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected long nativeCreate2(
+ long matrix, float x0, float y0, float x1, float y1, int color0, int color1, int tileMode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LinearGradientNatives.nativeCreate2(matrix, x0, y0, x1, y1, color0, color1, tileMode);
+ }
+
+ /** Shadow picker for {@link LinearGradient}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeLinearGradient.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMaskFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMaskFilter.java
new file mode 100644
index 000000000..97b18ac52
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMaskFilter.java
@@ -0,0 +1,26 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.MaskFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.MaskFilterNatives;
+import org.robolectric.shadows.ShadowNativeMaskFilter.Picker;
+
+/** Shadow for {@link MaskFilter} that is backed by native code */
+@Implements(value = MaskFilter.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeMaskFilter {
+
+ @Implementation(minSdk = O)
+ protected static void nativeDestructor(long nativeFilter) {
+ MaskFilterNatives.nativeDestructor(nativeFilter);
+ }
+
+ /** Shadow picker for {@link MaskFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeMaskFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMatrix.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMatrix.java
new file mode 100644
index 000000000..e840968db
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMatrix.java
@@ -0,0 +1,264 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.Build.VERSION_CODES.N_MR1;
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import java.util.List;
+import java.util.Map;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.MatrixNatives;
+
+/** Shadow for {@link Matrix} that is backed by native code */
+@Implements(value = Matrix.class, minSdk = O, isInAndroidSdk = false)
+public class ShadowNativeMatrix extends ShadowMatrix {
+
+ @Implementation(minSdk = LOLLIPOP, maxSdk = N_MR1)
+ protected static long native_create(long nSrcOrZero) {
+ return nCreate(nSrcOrZero);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreate(long nSrcOrZero) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return MatrixNatives.nCreate(nSrcOrZero);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nGetNativeFinalizer() {
+ return MatrixNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nSetRectToRect(long nObject, RectF src, RectF dst, int stf) {
+ return MatrixNatives.nSetRectToRect(nObject, src, dst, stf);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nSetPolyToPoly(
+ long nObject, float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount) {
+ return MatrixNatives.nSetPolyToPoly(nObject, src, srcIndex, dst, dstIndex, pointCount);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nMapPoints(
+ long nObject,
+ float[] dst,
+ int dstIndex,
+ float[] src,
+ int srcIndex,
+ int ptCount,
+ boolean isPts) {
+ MatrixNatives.nMapPoints(nObject, dst, dstIndex, src, srcIndex, ptCount, isPts);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nMapRect(long nObject, RectF dst, RectF src) {
+ return MatrixNatives.nMapRect(nObject, dst, src);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nGetValues(long nObject, float[] values) {
+ MatrixNatives.nGetValues(nObject, values);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetValues(long nObject, float[] values) {
+ MatrixNatives.nSetValues(nObject, values);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsIdentity(long nObject) {
+ return MatrixNatives.nIsIdentity(nObject);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsAffine(long nObject) {
+ return MatrixNatives.nIsAffine(nObject);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nRectStaysRect(long nObject) {
+ return MatrixNatives.nRectStaysRect(nObject);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nReset(long nObject) {
+ MatrixNatives.nReset(nObject);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSet(long nObject, long nOther) {
+ MatrixNatives.nSet(nObject, nOther);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTranslate(long nObject, float dx, float dy) {
+ MatrixNatives.nSetTranslate(nObject, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetScale(long nObject, float sx, float sy, float px, float py) {
+ MatrixNatives.nSetScale(nObject, sx, sy, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetScale(long nObject, float sx, float sy) {
+ MatrixNatives.nSetScale(nObject, sx, sy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetRotate(long nObject, float degrees, float px, float py) {
+ MatrixNatives.nSetRotate(nObject, degrees, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetRotate(long nObject, float degrees) {
+ MatrixNatives.nSetRotate(nObject, degrees);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetSinCos(
+ long nObject, float sinValue, float cosValue, float px, float py) {
+ MatrixNatives.nSetSinCos(nObject, sinValue, cosValue, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetSinCos(long nObject, float sinValue, float cosValue) {
+ MatrixNatives.nSetSinCos(nObject, sinValue, cosValue);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetSkew(long nObject, float kx, float ky, float px, float py) {
+ MatrixNatives.nSetSkew(nObject, kx, ky, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetSkew(long nObject, float kx, float ky) {
+ MatrixNatives.nSetSkew(nObject, kx, ky);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetConcat(long nObject, long nA, long nB) {
+ MatrixNatives.nSetConcat(nObject, nA, nB);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreTranslate(long nObject, float dx, float dy) {
+ MatrixNatives.nPreTranslate(nObject, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreScale(long nObject, float sx, float sy, float px, float py) {
+ MatrixNatives.nPreScale(nObject, sx, sy, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreScale(long nObject, float sx, float sy) {
+ MatrixNatives.nPreScale(nObject, sx, sy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreRotate(long nObject, float degrees, float px, float py) {
+ MatrixNatives.nPreRotate(nObject, degrees, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreRotate(long nObject, float degrees) {
+ MatrixNatives.nPreRotate(nObject, degrees);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreSkew(long nObject, float kx, float ky, float px, float py) {
+ MatrixNatives.nPreSkew(nObject, kx, ky, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreSkew(long nObject, float kx, float ky) {
+ MatrixNatives.nPreSkew(nObject, kx, ky);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPreConcat(long nObject, long nOtherMatrix) {
+ MatrixNatives.nPreConcat(nObject, nOtherMatrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostTranslate(long nObject, float dx, float dy) {
+ MatrixNatives.nPostTranslate(nObject, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostScale(long nObject, float sx, float sy, float px, float py) {
+ MatrixNatives.nPostScale(nObject, sx, sy, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostScale(long nObject, float sx, float sy) {
+ MatrixNatives.nPostScale(nObject, sx, sy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostRotate(long nObject, float degrees, float px, float py) {
+ MatrixNatives.nPostRotate(nObject, degrees, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostRotate(long nObject, float degrees) {
+ MatrixNatives.nPostRotate(nObject, degrees);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostSkew(long nObject, float kx, float ky, float px, float py) {
+ MatrixNatives.nPostSkew(nObject, kx, ky, px, py);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostSkew(long nObject, float kx, float ky) {
+ MatrixNatives.nPostSkew(nObject, kx, ky);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nPostConcat(long nObject, long nOtherMatrix) {
+ MatrixNatives.nPostConcat(nObject, nOtherMatrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nInvert(long nObject, long nInverse) {
+ return MatrixNatives.nInvert(nObject, nInverse);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nMapRadius(long nObject, float radius) {
+ return MatrixNatives.nMapRadius(nObject, radius);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nEquals(long nA, long nB) {
+ return MatrixNatives.nEquals(nA, nB);
+ }
+
+ @Override
+ public List<String> getPreOperations() {
+ throw new UnsupportedOperationException("Legacy ShadowMatrix APIs are not supported");
+ }
+
+ @Override
+ public List<String> getPostOperations() {
+ throw new UnsupportedOperationException("Legacy ShadowMatrix APIs are not supported");
+ }
+
+ @Override
+ public Map<String, String> getSetOperations() {
+ throw new UnsupportedOperationException("Legacy ShadowMatrix APIs are not supported");
+ }
+
+ @Override
+ public String getDescription() {
+ throw new UnsupportedOperationException("Legacy ShadowMatrix APIs are not supported");
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredParagraph.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredParagraph.java
new file mode 100644
index 000000000..130cd7b9e
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredParagraph.java
@@ -0,0 +1,73 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.P;
+
+import android.graphics.Rect;
+import android.text.MeasuredParagraph;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.MeasuredTextBuilderNatives;
+import org.robolectric.nativeruntime.MeasuredTextNatives;
+import org.robolectric.shadows.ShadowNativeMeasuredParagraph.Picker;
+
+/** Shadow for {@link MeasuredParagraph} that is backed by native code */
+@Implements(value = MeasuredParagraph.class, minSdk = P, maxSdk = P, shadowPicker = Picker.class)
+public class ShadowNativeMeasuredParagraph {
+ @Implementation
+ protected static long nInitBuilder() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return MeasuredTextBuilderNatives.nInitBuilder();
+ }
+
+ @Implementation
+ protected static void nAddStyleRun(
+ long nativeBuilderPtr, long paintPtr, int start, int end, boolean isRtl) {
+ MeasuredTextBuilderNatives.nAddStyleRun(nativeBuilderPtr, paintPtr, start, end, isRtl);
+ }
+
+ @Implementation
+ protected static void nAddReplacementRun(
+ long nativeBuilderPtr, long paintPtr, int start, int end, float width) {
+ MeasuredTextBuilderNatives.nAddReplacementRun(nativeBuilderPtr, paintPtr, start, end, width);
+ }
+
+ @Implementation
+ protected static long nBuildNativeMeasuredParagraph(
+ long nativeBuilderPtr, char[] text, boolean computeHyphenation, boolean computeLayout) {
+ return MeasuredTextBuilderNatives.nBuildMeasuredText(
+ nativeBuilderPtr, 0, text, computeHyphenation, computeLayout);
+ }
+
+ @Implementation
+ protected static void nFreeBuilder(long nativeBuilderPtr) {
+ MeasuredTextBuilderNatives.nFreeBuilder(nativeBuilderPtr);
+ }
+
+ @Implementation
+ protected static float nGetWidth(long nativePtr, int start, int end) {
+ return MeasuredTextNatives.nGetWidth(nativePtr, start, end);
+ }
+
+ @Implementation
+ protected static long nGetReleaseFunc() {
+ return MeasuredTextNatives.nGetReleaseFunc();
+ }
+
+ @Implementation
+ protected static int nGetMemoryUsage(long nativePtr) {
+ return MeasuredTextNatives.nGetMemoryUsage(nativePtr);
+ }
+
+ @Implementation
+ protected static void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect) {
+ MeasuredTextNatives.nGetBounds(nativePtr, buf, start, end, rect);
+ }
+
+ /** Shadow picker for {@link MeasuredParagraph}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowMeasuredParagraph.class, ShadowNativeMeasuredParagraph.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredText.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredText.java
new file mode 100644
index 000000000..5b82a6cf5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeMeasuredText.java
@@ -0,0 +1,135 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S_V2;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.graphics.Rect;
+import android.graphics.text.MeasuredText;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.MeasuredTextBuilderNatives;
+import org.robolectric.nativeruntime.MeasuredTextNatives;
+import org.robolectric.shadows.ShadowNativeMeasuredText.Picker;
+
+/** Shadow for {@link MeasuredText} that is backed by native code */
+@Implements(value = MeasuredText.class, minSdk = Q, shadowPicker = Picker.class)
+public class ShadowNativeMeasuredText {
+ @Implementation
+ protected static float nGetWidth(
+ /* Non Zero */ long nativePtr, @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+ return MeasuredTextNatives.nGetWidth(nativePtr, start, end);
+ }
+
+ @Implementation
+ protected static /* Non Zero */ long nGetReleaseFunc() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return MeasuredTextNatives.nGetReleaseFunc();
+ }
+
+ @Implementation
+ protected static int nGetMemoryUsage(/* Non Zero */ long nativePtr) {
+ return MeasuredTextNatives.nGetMemoryUsage(nativePtr);
+ }
+
+ @Implementation
+ protected static void nGetBounds(long nativePtr, char[] buf, int start, int end, Rect rect) {
+ MeasuredTextNatives.nGetBounds(nativePtr, buf, start, end, rect);
+ }
+
+ @Implementation
+ protected static float nGetCharWidthAt(long nativePtr, int offset) {
+ return MeasuredTextNatives.nGetCharWidthAt(nativePtr, offset);
+ }
+
+ /** Shadow for {@link MeasuredText.Builder} that is backed by native code */
+ @Implements(
+ value = MeasuredText.Builder.class,
+ minSdk = Q,
+ shadowPicker = ShadowNativeMeasuredTextBuilder.Picker.class)
+ public static class ShadowNativeMeasuredTextBuilder {
+ @Implementation
+ protected static /* Non Zero */ long nInitBuilder() {
+ return MeasuredTextBuilderNatives.nInitBuilder();
+ }
+
+ @Implementation(maxSdk = S_V2)
+ protected static void nAddStyleRun(
+ /* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl) {
+ MeasuredTextBuilderNatives.nAddStyleRun(nativeBuilderPtr, paintPtr, start, end, isRtl);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static void nAddStyleRun(
+ /* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ int lineBreakStyle,
+ int lineBreakWordStyle,
+ int start,
+ int end,
+ boolean isRtl) {
+ MeasuredTextBuilderNatives.nAddStyleRun(nativeBuilderPtr, paintPtr, start, end, isRtl);
+ }
+
+ @Implementation
+ protected static void nAddReplacementRun(
+ /* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width) {
+ MeasuredTextBuilderNatives.nAddReplacementRun(nativeBuilderPtr, paintPtr, start, end, width);
+ }
+
+ @Implementation(maxSdk = S_V2)
+ protected static long nBuildMeasuredText(
+ /* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
+ char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout) {
+ return MeasuredTextBuilderNatives.nBuildMeasuredText(
+ nativeBuilderPtr, hintMtPtr, text, computeHyphenation, computeLayout);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static long nBuildMeasuredText(
+ /* Non Zero */ long nativeBuilderPtr,
+ long hintMtPtr,
+ char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout,
+ boolean fastHyphenationMode) {
+ return MeasuredTextBuilderNatives.nBuildMeasuredText(
+ nativeBuilderPtr, hintMtPtr, text, computeHyphenation, computeLayout);
+ }
+
+ @Implementation
+ protected static void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr) {
+ MeasuredTextBuilderNatives.nFreeBuilder(nativeBuilderPtr);
+ }
+
+ /** Shadow picker for {@link MeasuredText.Builder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(
+ org.robolectric.shadows.ShadowMeasuredTextBuilder.class,
+ ShadowNativeMeasuredText.ShadowNativeMeasuredTextBuilder.class);
+ }
+ }
+ }
+
+ /** Shadow picker for {@link MeasuredText}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeMeasuredText.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNativeInterpolatorFactory.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNativeInterpolatorFactory.java
new file mode 100644
index 000000000..21c80e5c1
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNativeInterpolatorFactory.java
@@ -0,0 +1,85 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.R;
+
+import android.graphics.animation.NativeInterpolatorFactory;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.NativeInterpolatorFactoryNatives;
+import org.robolectric.shadows.ShadowNativeNativeInterpolatorFactory.Picker;
+
+/** Shadow for {@link NativeInterpolatorFactory} that is backed by native code */
+@Implements(
+ value = NativeInterpolatorFactory.class,
+ minSdk = R,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeNativeInterpolatorFactory {
+
+ static {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ }
+
+ @Implementation
+ protected static long createAccelerateDecelerateInterpolator() {
+ return NativeInterpolatorFactoryNatives.createAccelerateDecelerateInterpolator();
+ }
+
+ @Implementation
+ protected static long createAccelerateInterpolator(float factor) {
+ return NativeInterpolatorFactoryNatives.createAccelerateInterpolator(factor);
+ }
+
+ @Implementation
+ protected static long createAnticipateInterpolator(float tension) {
+ return NativeInterpolatorFactoryNatives.createAnticipateInterpolator(tension);
+ }
+
+ @Implementation
+ protected static long createAnticipateOvershootInterpolator(float tension) {
+ return NativeInterpolatorFactoryNatives.createAnticipateOvershootInterpolator(tension);
+ }
+
+ @Implementation
+ protected static long createBounceInterpolator() {
+ return NativeInterpolatorFactoryNatives.createBounceInterpolator();
+ }
+
+ @Implementation
+ protected static long createCycleInterpolator(float cycles) {
+ return NativeInterpolatorFactoryNatives.createCycleInterpolator(cycles);
+ }
+
+ @Implementation
+ protected static long createDecelerateInterpolator(float factor) {
+ return NativeInterpolatorFactoryNatives.createDecelerateInterpolator(factor);
+ }
+
+ @Implementation
+ protected static long createLinearInterpolator() {
+ return NativeInterpolatorFactoryNatives.createLinearInterpolator();
+ }
+
+ @Implementation
+ protected static long createOvershootInterpolator(float tension) {
+ return NativeInterpolatorFactoryNatives.createOvershootInterpolator(tension);
+ }
+
+ @Implementation
+ protected static long createPathInterpolator(float[] x, float[] y) {
+ return NativeInterpolatorFactoryNatives.createPathInterpolator(x, y);
+ }
+
+ @Implementation
+ protected static long createLutInterpolator(float[] values) {
+ return NativeInterpolatorFactoryNatives.createLutInterpolator(values);
+ }
+
+ /** Shadow picker for {@link NativeInterpolatorFactory}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeNativeInterpolatorFactory.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNinePatch.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNinePatch.java
new file mode 100644
index 000000000..11e5ba867
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeNinePatch.java
@@ -0,0 +1,50 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.NinePatch;
+import android.graphics.Rect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.NinePatchNatives;
+import org.robolectric.shadows.ShadowNativeNinePatch.Picker;
+
+/** Shadow for {@link NinePatch} that is backed by native code */
+@Implements(
+ value = NinePatch.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeNinePatch {
+
+ @Implementation
+ protected static boolean isNinePatchChunk(byte[] chunk) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return NinePatchNatives.isNinePatchChunk(chunk);
+ }
+
+ @Implementation
+ protected static long validateNinePatchChunk(byte[] chunk) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return NinePatchNatives.validateNinePatchChunk(chunk);
+ }
+
+ @Implementation
+ protected static void nativeFinalize(long chunk) {
+ NinePatchNatives.nativeFinalize(chunk);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nativeGetTransparentRegion(long bitmapHandle, long chunk, Rect location) {
+ return NinePatchNatives.nativeGetTransparentRegion(bitmapHandle, chunk, location);
+ }
+
+ /** Shadow picker for {@link NinePatch}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowNinePatch.class, ShadowNativeNinePatch.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePaint.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePaint.java
new file mode 100644
index 000000000..94fadb5ab
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePaint.java
@@ -0,0 +1,847 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetrics;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.Rect;
+import androidx.annotation.ColorInt;
+import androidx.annotation.ColorLong;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PaintNatives;
+import org.robolectric.shadows.ShadowNativePaint.Picker;
+
+/** Shadow for {@link Paint} that is backed by native code */
+@Implements(
+ minSdk = O,
+ value = Paint.class,
+ looseSignatures = true,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativePaint {
+
+ // nGetTextRunCursor methods are non-static
+ private PaintNatives paintNatives = new PaintNatives();
+
+ @Implementation(minSdk = O)
+ protected static long nGetNativeFinalizer() {
+ return PaintNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nInit() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ // This native code calls Typeface::resolveDefault, which requires Typeface clinit to have run.
+ ShadowNativeTypeface.ensureInitialized();
+ return PaintNatives.nInit();
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static int nGetHyphenEdit(long paintPtr) {
+ return PaintNatives.nGetEndHyphenEdit(paintPtr);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nSetHyphenEdit(long paintPtr, int hyphen) {
+ PaintNatives.nSetStartHyphenEdit(paintPtr, 0);
+ PaintNatives.nSetEndHyphenEdit(paintPtr, hyphen);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nInitWithPaint(long paint) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PaintNatives.nInitWithPaint(paint);
+ }
+
+ @Implementation(minSdk = P)
+ protected static int nBreakText(
+ long nObject,
+ char[] text,
+ int index,
+ int count,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth) {
+ return PaintNatives.nBreakText(nObject, text, index, count, maxWidth, bidiFlags, measuredWidth);
+ }
+
+ @Implementation(minSdk = P)
+ protected static int nBreakText(
+ long nObject,
+ String text,
+ boolean measureForwards,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth) {
+ return PaintNatives.nBreakText(
+ nObject, text, measureForwards, maxWidth, bidiFlags, measuredWidth);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static int nBreakText(
+ long nObject,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth) {
+ return PaintNatives.nBreakText(
+ nObject, typefacePtr, text, index, count, maxWidth, bidiFlags, measuredWidth);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static int nBreakText(
+ long nObject,
+ long typefacePtr,
+ String text,
+ boolean measureForwards,
+ float maxWidth,
+ int bidiFlags,
+ float[] measuredWidth) {
+ return PaintNatives.nBreakText(
+ nObject, typefacePtr, text, measureForwards, maxWidth, bidiFlags, measuredWidth);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetTextAdvances(
+ long paintPtr,
+ char[] text,
+ int index,
+ int count,
+ int contextIndex,
+ int contextCount,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex) {
+ return PaintNatives.nGetTextAdvances(
+ paintPtr,
+ text,
+ index,
+ count,
+ contextIndex,
+ contextCount,
+ bidiFlags,
+ advances,
+ advancesIndex);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetTextAdvances(
+ long paintPtr,
+ String text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex) {
+ return PaintNatives.nGetTextAdvances(
+ paintPtr, text, start, end, contextStart, contextEnd, bidiFlags, advances, advancesIndex);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nGetTextAdvances(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ int contextIndex,
+ int contextCount,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex) {
+ return PaintNatives.nGetTextAdvances(
+ paintPtr,
+ typefacePtr,
+ text,
+ index,
+ count,
+ contextIndex,
+ contextCount,
+ bidiFlags,
+ advances,
+ advancesIndex);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nGetTextAdvances(
+ long paintPtr,
+ long typefacePtr,
+ String text,
+ int index,
+ int count,
+ int contextIndex,
+ int contextCount,
+ int bidiFlags,
+ float[] advances,
+ int advancesIndex) {
+ return PaintNatives.nGetTextAdvances(
+ paintPtr,
+ typefacePtr,
+ text,
+ index,
+ count,
+ contextIndex,
+ contextCount,
+ bidiFlags,
+ advances,
+ advancesIndex);
+ }
+
+ @Implementation(minSdk = P)
+ protected int nGetTextRunCursor(
+ long paintPtr,
+ char[] text,
+ int contextStart,
+ int contextLength,
+ int dir,
+ int offset,
+ int cursorOpt) {
+ return paintNatives.nGetTextRunCursor(
+ paintPtr, text, contextStart, contextLength, dir, offset, cursorOpt);
+ }
+
+ @Implementation(minSdk = P)
+ protected int nGetTextRunCursor(
+ long paintPtr,
+ String text,
+ int contextStart,
+ int contextEnd,
+ int dir,
+ int offset,
+ int cursorOpt) {
+ return paintNatives.nGetTextRunCursor(
+ paintPtr, text, contextStart, contextEnd, dir, offset, cursorOpt);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected int nGetTextRunCursor(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int contextStart,
+ int contextLength,
+ int dir,
+ int offset,
+ int cursorOpt) {
+ return paintNatives.nGetTextRunCursor(
+ paintPtr, typefacePtr, text, contextStart, contextLength, dir, offset, cursorOpt);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected int nGetTextRunCursor(
+ long paintPtr,
+ long typefacePtr,
+ String text,
+ int contextStart,
+ int contextEnd,
+ int dir,
+ int offset,
+ int cursorOpt) {
+ return paintNatives.nGetTextRunCursor(
+ paintPtr, typefacePtr, text, contextStart, contextEnd, dir, offset, cursorOpt);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nGetTextPath(
+ long paintPtr,
+ int bidiFlags,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ long path) {
+ PaintNatives.nGetTextPath(paintPtr, bidiFlags, text, index, count, x, y, path);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nGetTextPath(
+ long paintPtr, int bidiFlags, String text, int start, int end, float x, float y, long path) {
+ PaintNatives.nGetTextPath(paintPtr, bidiFlags, text, start, end, x, y, path);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nGetTextPath(
+ long paintPtr,
+ long typefacePtr,
+ int bidiFlags,
+ char[] text,
+ int index,
+ int count,
+ float x,
+ float y,
+ long path) {
+ PaintNatives.nGetTextPath(paintPtr, typefacePtr, bidiFlags, text, index, count, x, y, path);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nGetTextPath(
+ long paintPtr,
+ long typefacePtr,
+ int bidiFlags,
+ String text,
+ int start,
+ int end,
+ float x,
+ float y,
+ long path) {
+ PaintNatives.nGetTextPath(paintPtr, typefacePtr, bidiFlags, text, start, end, x, y, path);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nGetStringBounds(
+ long nativePaint, String text, int start, int end, int bidiFlags, Rect bounds) {
+ PaintNatives.nGetStringBounds(nativePaint, text, start, end, bidiFlags, bounds);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nGetStringBounds(
+ long nativePaint,
+ long typefacePtr,
+ String text,
+ int start,
+ int end,
+ int bidiFlags,
+ Rect bounds) {
+ PaintNatives.nGetStringBounds(nativePaint, typefacePtr, text, start, end, bidiFlags, bounds);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static int nGetColor(long paintPtr) {
+ return PaintNatives.nGetColor(paintPtr);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static int nGetAlpha(long paintPtr) {
+ return PaintNatives.nGetAlpha(paintPtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nGetCharArrayBounds(
+ long nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds) {
+ PaintNatives.nGetCharArrayBounds(nativePaint, text, index, count, bidiFlags, bounds);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nGetCharArrayBounds(
+ long nativePaint,
+ long typefacePtr,
+ char[] text,
+ int index,
+ int count,
+ int bidiFlags,
+ Rect bounds) {
+ PaintNatives.nGetCharArrayBounds(
+ nativePaint, typefacePtr, text, index, count, bidiFlags, bounds);
+ }
+
+ @Implementation(minSdk = P)
+ protected static boolean nHasGlyph(long paintPtr, int bidiFlags, String string) {
+ return PaintNatives.nHasGlyph(paintPtr, bidiFlags, string);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static boolean nHasGlyph(
+ long paintPtr, long typefacePtr, int bidiFlags, String string) {
+ return PaintNatives.nHasGlyph(paintPtr, typefacePtr, bidiFlags, string);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetRunAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset) {
+ return PaintNatives.nGetRunAdvance(
+ paintPtr, text, start, end, contextStart, contextEnd, isRtl, offset);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nGetRunAdvance(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset) {
+ return PaintNatives.nGetRunAdvance(
+ paintPtr, text, start, end, contextStart, contextEnd, isRtl, offset);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static int nGetOffsetForAdvance(
+ long paintPtr,
+ long typefacePtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ float advance) {
+ return PaintNatives.nGetOffsetForAdvance(
+ paintPtr, typefacePtr, text, start, end, contextStart, contextEnd, isRtl, advance);
+ }
+
+ @Implementation(minSdk = P)
+ protected static int nGetOffsetForAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ float advance) {
+ return PaintNatives.nGetOffsetForAdvance(
+ paintPtr, text, start, end, contextStart, contextEnd, isRtl, advance);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nSetTextLocales(long paintPtr, String locales) {
+ return PaintNatives.nSetTextLocales(paintPtr, locales);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFontFeatureSettings(long paintPtr, String settings) {
+ PaintNatives.nSetFontFeatureSettings(paintPtr, settings);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetFontMetrics(long paintPtr, FontMetrics metrics) {
+ return PaintNatives.nGetFontMetrics(paintPtr, metrics);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nGetFontMetrics(long paintPtr, long typefacePtr, FontMetrics metrics) {
+ return PaintNatives.nGetFontMetrics(paintPtr, typefacePtr, metrics);
+ }
+
+ @Implementation(minSdk = P)
+ protected static int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi) {
+ return PaintNatives.nGetFontMetricsInt(paintPtr, fmi);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static int nGetFontMetricsInt(long paintPtr, long typefacePtr, FontMetricsInt fmi) {
+ return PaintNatives.nGetFontMetricsInt(paintPtr, typefacePtr, fmi);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nReset(long paintPtr) {
+ PaintNatives.nReset(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSet(long paintPtrDest, long paintPtrSrc) {
+ PaintNatives.nSet(paintPtrDest, paintPtrSrc);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetStyle(long paintPtr) {
+ return PaintNatives.nGetStyle(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStyle(long paintPtr, int style) {
+ PaintNatives.nSetStyle(paintPtr, style);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetStrokeCap(long paintPtr) {
+ return PaintNatives.nGetStrokeCap(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeCap(long paintPtr, int cap) {
+ PaintNatives.nSetStrokeCap(paintPtr, cap);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetStrokeJoin(long paintPtr) {
+ return PaintNatives.nGetStrokeJoin(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeJoin(long paintPtr, int join) {
+ PaintNatives.nSetStrokeJoin(paintPtr, join);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nGetFillPath(long paintPtr, long src, long dst) {
+ return PaintNatives.nGetFillPath(paintPtr, src, dst);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nSetShader(long paintPtr, long shader) {
+ return PaintNatives.nSetShader(paintPtr, shader);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nSetColorFilter(long paintPtr, long filter) {
+ return PaintNatives.nSetColorFilter(paintPtr, filter);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetXfermode(long paintPtr, int xfermode) {
+ PaintNatives.nSetXfermode(paintPtr, xfermode);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nSetPathEffect(long paintPtr, long effect) {
+ return PaintNatives.nSetPathEffect(paintPtr, effect);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nSetMaskFilter(long paintPtr, long maskfilter) {
+ return PaintNatives.nSetMaskFilter(paintPtr, maskfilter);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nSetTypeface(long paintPtr, long typeface) {
+ PaintNatives.nSetTypeface(paintPtr, typeface);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static Object nSetTypeface(Object paintPtr, Object typeface) {
+ PaintNatives.nSetTypeface((long) paintPtr, (long) typeface);
+ return paintPtr;
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetTextAlign(long paintPtr) {
+ return PaintNatives.nGetTextAlign(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTextAlign(long paintPtr, int align) {
+ PaintNatives.nSetTextAlign(paintPtr, align);
+ }
+
+ @Implementation(minSdk = P)
+ protected static void nSetTextLocalesByMinikinLocaleListId(
+ long paintPtr, int mMinikinLocaleListId) {
+ PaintNatives.nSetTextLocalesByMinikinLocaleListId(paintPtr, mMinikinLocaleListId);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetShadowLayer(
+ long paintPtr,
+ float radius,
+ float dx,
+ float dy,
+ long colorSpaceHandle,
+ @ColorLong long shadowColor) {
+ PaintNatives.nSetShadowLayer(paintPtr, radius, dx, dy, colorSpaceHandle, shadowColor);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static void nSetShadowLayer(
+ long paintPtr, float radius, float dx, float dy, int color) {
+ PaintNatives.nSetShadowLayer(paintPtr, radius, dx, dy, color);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nHasShadowLayer(long paintPtr) {
+ return PaintNatives.nHasShadowLayer(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetLetterSpacing(long paintPtr) {
+ return PaintNatives.nGetLetterSpacing(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetLetterSpacing(long paintPtr, float letterSpacing) {
+ PaintNatives.nSetLetterSpacing(paintPtr, letterSpacing);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetWordSpacing(long paintPtr) {
+ return PaintNatives.nGetWordSpacing(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetWordSpacing(long paintPtr, float wordSpacing) {
+ PaintNatives.nSetWordSpacing(paintPtr, wordSpacing);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static int nGetStartHyphenEdit(long paintPtr) {
+ return PaintNatives.nGetStartHyphenEdit(paintPtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static int nGetEndHyphenEdit(long paintPtr) {
+ return PaintNatives.nGetEndHyphenEdit(paintPtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetStartHyphenEdit(long paintPtr, int hyphen) {
+ PaintNatives.nSetStartHyphenEdit(paintPtr, hyphen);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetEndHyphenEdit(long paintPtr, int hyphen) {
+ PaintNatives.nSetEndHyphenEdit(paintPtr, hyphen);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeMiter(long paintPtr, float miter) {
+ PaintNatives.nSetStrokeMiter(paintPtr, miter);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetStrokeMiter(long paintPtr) {
+ return PaintNatives.nGetStrokeMiter(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeWidth(long paintPtr, float width) {
+ PaintNatives.nSetStrokeWidth(paintPtr, width);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetStrokeWidth(long paintPtr) {
+ return PaintNatives.nGetStrokeWidth(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetAlpha(long paintPtr, int a) {
+ PaintNatives.nSetAlpha(paintPtr, a);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetDither(long paintPtr, boolean dither) {
+ PaintNatives.nSetDither(paintPtr, dither);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetFlags(long paintPtr) {
+ return PaintNatives.nGetFlags(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFlags(long paintPtr, int flags) {
+ PaintNatives.nSetFlags(paintPtr, flags);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetHinting(long paintPtr) {
+ return PaintNatives.nGetHinting(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetHinting(long paintPtr, int mode) {
+ PaintNatives.nSetHinting(paintPtr, mode);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetAntiAlias(long paintPtr, boolean aa) {
+ PaintNatives.nSetAntiAlias(paintPtr, aa);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetLinearText(long paintPtr, boolean linearText) {
+ PaintNatives.nSetLinearText(paintPtr, linearText);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetSubpixelText(long paintPtr, boolean subpixelText) {
+ PaintNatives.nSetSubpixelText(paintPtr, subpixelText);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetUnderlineText(long paintPtr, boolean underlineText) {
+ PaintNatives.nSetUnderlineText(paintPtr, underlineText);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFakeBoldText(long paintPtr, boolean fakeBoldText) {
+ PaintNatives.nSetFakeBoldText(paintPtr, fakeBoldText);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFilterBitmap(long paintPtr, boolean filter) {
+ PaintNatives.nSetFilterBitmap(paintPtr, filter);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetColor(long paintPtr, long colorSpaceHandle, @ColorLong long color) {
+ PaintNatives.nSetColor(paintPtr, colorSpaceHandle, color);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetColor(long paintPtr, @ColorInt int color) {
+ PaintNatives.nSetColor(paintPtr, color);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrikeThruText(long paintPtr, boolean strikeThruText) {
+ PaintNatives.nSetStrikeThruText(paintPtr, strikeThruText);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsElegantTextHeight(long paintPtr) {
+ return PaintNatives.nIsElegantTextHeight(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetElegantTextHeight(long paintPtr, boolean elegant) {
+ PaintNatives.nSetElegantTextHeight(paintPtr, elegant);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTextSize(long paintPtr) {
+ return PaintNatives.nGetTextSize(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTextScaleX(long paintPtr) {
+ return PaintNatives.nGetTextScaleX(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTextScaleX(long paintPtr, float scaleX) {
+ PaintNatives.nSetTextScaleX(paintPtr, scaleX);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTextSkewX(long paintPtr) {
+ return PaintNatives.nGetTextSkewX(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTextSkewX(long paintPtr, float skewX) {
+ PaintNatives.nSetTextSkewX(paintPtr, skewX);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nAscent(long paintPtr) {
+ return PaintNatives.nAscent(paintPtr);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nAscent(long paintPtr, long typefacePtr) {
+ return PaintNatives.nAscent(paintPtr, typefacePtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nDescent(long paintPtr) {
+ return PaintNatives.nDescent(paintPtr);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nDescent(long paintPtr, long typefacePtr) {
+ return PaintNatives.nDescent(paintPtr, typefacePtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetUnderlinePosition(long paintPtr) {
+ return PaintNatives.nGetUnderlinePosition(paintPtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetUnderlineThickness(long paintPtr) {
+ return PaintNatives.nGetUnderlineThickness(paintPtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetStrikeThruPosition(long paintPtr) {
+ return PaintNatives.nGetStrikeThruPosition(paintPtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static float nGetStrikeThruThickness(long paintPtr) {
+ return PaintNatives.nGetStrikeThruThickness(paintPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTextSize(long paintPtr, float textSize) {
+ PaintNatives.nSetTextSize(paintPtr, textSize);
+ }
+
+ @Implementation(minSdk = P)
+ protected static boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr) {
+ return PaintNatives.nEqualsForTextMeasurement(leftPaintPtr, rightPaintPtr);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static void nGetFontMetricsIntForText(
+ long paintPtr,
+ char[] text,
+ int start,
+ int count,
+ int ctxStart,
+ int ctxCount,
+ boolean isRtl,
+ FontMetricsInt outMetrics) {
+ PaintNatives.nGetFontMetricsIntForText(
+ paintPtr, text, start, count, ctxStart, ctxCount, isRtl, outMetrics);
+ }
+
+ @Implementation(minSdk = TIRAMISU)
+ protected static void nGetFontMetricsIntForText(
+ long paintPtr,
+ String text,
+ int start,
+ int count,
+ int ctxStart,
+ int ctxCount,
+ boolean isRtl,
+ FontMetricsInt outMetrics) {
+ PaintNatives.nGetFontMetricsIntForText(
+ paintPtr, text, start, count, ctxStart, ctxCount, isRtl, outMetrics);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static float nGetRunCharacterAdvance(
+ long paintPtr,
+ char[] text,
+ int start,
+ int end,
+ int contextStart,
+ int contextEnd,
+ boolean isRtl,
+ int offset,
+ float[] advances,
+ int advancesIndex) {
+ return PaintNatives.nGetRunCharacterAdvance(
+ paintPtr,
+ text,
+ start,
+ end,
+ contextStart,
+ contextEnd,
+ isRtl,
+ offset,
+ advances,
+ advancesIndex);
+ }
+
+ /** Shadow picker for {@link Paint}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowPaint.class, ShadowNativePaint.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePath.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePath.java
new file mode 100644
index 000000000..c162df5a5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePath.java
@@ -0,0 +1,243 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+
+import android.graphics.Path;
+import android.graphics.RectF;
+import java.util.List;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PathNatives;
+
+/** Shadow for {@link Path} that is backed by native code */
+@Implements(value = Path.class, minSdk = O, isInAndroidSdk = false)
+public class ShadowNativePath extends ShadowPath {
+
+ @Implementation(minSdk = O)
+ protected static long nInit() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PathNatives.nInit();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nInit(long nPath) {
+ // Required for pre-P.
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PathNatives.nInit(nPath);
+ }
+
+ @Implementation(minSdk = P)
+ protected static long nGetFinalizer() {
+ // Required for pre-P.
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PathNatives.nGetFinalizer();
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSet(long nativeDst, long nSrc) {
+ PathNatives.nSet(nativeDst, nSrc);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nComputeBounds(long nPath, RectF bounds) {
+ PathNatives.nComputeBounds(nPath, bounds);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nIncReserve(long nPath, int extraPtCount) {
+ PathNatives.nIncReserve(nPath, extraPtCount);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nMoveTo(long nPath, float x, float y) {
+ PathNatives.nMoveTo(nPath, x, y);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRMoveTo(long nPath, float dx, float dy) {
+ PathNatives.nRMoveTo(nPath, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nLineTo(long nPath, float x, float y) {
+ PathNatives.nLineTo(nPath, x, y);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRLineTo(long nPath, float dx, float dy) {
+ PathNatives.nRLineTo(nPath, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nQuadTo(long nPath, float x1, float y1, float x2, float y2) {
+ PathNatives.nQuadTo(nPath, x1, y1, x2, y2);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRQuadTo(long nPath, float dx1, float dy1, float dx2, float dy2) {
+ PathNatives.nRQuadTo(nPath, dx1, dy1, dx2, dy2);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nCubicTo(
+ long nPath, float x1, float y1, float x2, float y2, float x3, float y3) {
+ PathNatives.nCubicTo(nPath, x1, y1, x2, y2, x3, y3);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRCubicTo(
+ long nPath, float x1, float y1, float x2, float y2, float x3, float y3) {
+ PathNatives.nRCubicTo(nPath, x1, y1, x2, y2, x3, y3);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nArcTo(
+ long nPath,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweepAngle,
+ boolean forceMoveTo) {
+ PathNatives.nArcTo(nPath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nClose(long nPath) {
+ PathNatives.nClose(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddRect(
+ long nPath, float left, float top, float right, float bottom, int dir) {
+ PathNatives.nAddRect(nPath, left, top, right, bottom, dir);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddOval(
+ long nPath, float left, float top, float right, float bottom, int dir) {
+ PathNatives.nAddOval(nPath, left, top, right, bottom, dir);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddCircle(long nPath, float x, float y, float radius, int dir) {
+ PathNatives.nAddCircle(nPath, x, y, radius, dir);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddArc(
+ long nPath,
+ float left,
+ float top,
+ float right,
+ float bottom,
+ float startAngle,
+ float sweepAngle) {
+ PathNatives.nAddArc(nPath, left, top, right, bottom, startAngle, sweepAngle);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddRoundRect(
+ long nPath, float left, float top, float right, float bottom, float rx, float ry, int dir) {
+ PathNatives.nAddRoundRect(nPath, left, top, right, bottom, rx, ry, dir);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddRoundRect(
+ long nPath, float left, float top, float right, float bottom, float[] radii, int dir) {
+ PathNatives.nAddRoundRect(nPath, left, top, right, bottom, radii, dir);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddPath(long nPath, long src, float dx, float dy) {
+ PathNatives.nAddPath(nPath, src, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddPath(long nPath, long src) {
+ PathNatives.nAddPath(nPath, src);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddPath(long nPath, long src, long matrix) {
+ PathNatives.nAddPath(nPath, src, matrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nOffset(long nPath, float dx, float dy) {
+ PathNatives.nOffset(nPath, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetLastPoint(long nPath, float dx, float dy) {
+ PathNatives.nSetLastPoint(nPath, dx, dy);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nTransform(long nPath, long matrix, long dstPath) {
+ PathNatives.nTransform(nPath, matrix, dstPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nTransform(long nPath, long matrix) {
+ PathNatives.nTransform(nPath, matrix);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nOp(long path1, long path2, int op, long result) {
+ return PathNatives.nOp(path1, path2, op, result);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsRect(long nPath, RectF rect) {
+ return PathNatives.nIsRect(nPath, rect);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nReset(long nPath) {
+ PathNatives.nReset(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nRewind(long nPath) {
+ PathNatives.nRewind(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsEmpty(long nPath) {
+ return PathNatives.nIsEmpty(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nIsConvex(long nPath) {
+ return PathNatives.nIsConvex(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetFillType(long nPath) {
+ return PathNatives.nGetFillType(nPath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFillType(long nPath, int ft) {
+ PathNatives.nSetFillType(nPath, ft);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float[] nApproximate(long nPath, float error) {
+ return PathNatives.nApproximate(nPath, error);
+ }
+
+ @Override
+ public List<Point> getPoints() {
+ throw new UnsupportedOperationException("Legacy ShadowPath description APIs are not supported");
+ }
+
+ @Override
+ public void fillBounds(RectF bounds) {
+ throw new UnsupportedOperationException("Legacy ShadowPath description APIs are not supported");
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathDashPathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathDashPathEffect.java
new file mode 100644
index 000000000..b72b0b231
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathDashPathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.PathDashPathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PathDashPathEffectNatives;
+import org.robolectric.shadows.ShadowNativePathDashPathEffect.Picker;
+
+/** Shadow for {@link PathDashPathEffect} that is backed by native code */
+@Implements(value = PathDashPathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativePathDashPathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(long nativePath, float advance, float phase, int nativeStyle) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PathDashPathEffectNatives.nativeCreate(nativePath, advance, phase, nativeStyle);
+ }
+
+ /** Shadow picker for {@link PathDashPathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativePathDashPathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathEffect.java
new file mode 100644
index 000000000..440eb2642
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathEffect.java
@@ -0,0 +1,26 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.PathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.PathEffectNatives;
+import org.robolectric.shadows.ShadowNativePathEffect.Picker;
+
+/** Shadow for {@link PathEffect} that is backed by native code */
+@Implements(value = PathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativePathEffect {
+
+ @Implementation(minSdk = O)
+ protected static void nativeDestructor(long nativePatheffect) {
+ PathEffectNatives.nativeDestructor(nativePatheffect);
+ }
+
+ /** Shadow picker for {@link PathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativePathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathMeasure.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathMeasure.java
new file mode 100644
index 000000000..fd450e912
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathMeasure.java
@@ -0,0 +1,76 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.PathMeasure;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PathMeasureNatives;
+import org.robolectric.shadows.ShadowNativePathMeasure.Picker;
+
+/** Shadow for {@link PathMeasure} that is backed by native code */
+@Implements(
+ value = PathMeasure.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativePathMeasure {
+
+ @Implementation(minSdk = O)
+ protected static long native_create(long nativePath, boolean forceClosed) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PathMeasureNatives.native_create(nativePath, forceClosed);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void native_setPath(long nativeInstance, long nativePath, boolean forceClosed) {
+ PathMeasureNatives.native_setPath(nativeInstance, nativePath, forceClosed);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float native_getLength(long nativeInstance) {
+ return PathMeasureNatives.native_getLength(nativeInstance);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean native_getPosTan(
+ long nativeInstance, float distance, float[] pos, float[] tan) {
+ return PathMeasureNatives.native_getPosTan(nativeInstance, distance, pos, tan);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean native_getMatrix(
+ long nativeInstance, float distance, long nativeMatrix, int flags) {
+ return PathMeasureNatives.native_getMatrix(nativeInstance, distance, nativeMatrix, flags);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean native_getSegment(
+ long nativeInstance, float startD, float stopD, long nativePath, boolean startWithMoveTo) {
+ return PathMeasureNatives.native_getSegment(
+ nativeInstance, startD, stopD, nativePath, startWithMoveTo);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean native_isClosed(long nativeInstance) {
+ return PathMeasureNatives.native_isClosed(nativeInstance);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean native_nextContour(long nativeInstance) {
+ return PathMeasureNatives.native_nextContour(nativeInstance);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void native_destroy(long nativeInstance) {
+ PathMeasureNatives.native_destroy(nativeInstance);
+ }
+
+ /** Shadow picker for {@link PathMeasure}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowPathMeasure.class, ShadowNativePathMeasure.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathParser.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathParser.java
new file mode 100644
index 000000000..cf83491ad
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePathParser.java
@@ -0,0 +1,76 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.util.PathParser;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PathParserNatives;
+import org.robolectric.shadows.ShadowNativePathParser.Picker;
+
+/** Shadow for {@link PathParser} that is backed by native code */
+@Implements(
+ value = PathParser.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativePathParser {
+
+ static {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nParseStringForPath(long pathPtr, String pathString, int stringLength) {
+ PathParserNatives.nParseStringForPath(pathPtr, pathString, stringLength);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreatePathDataFromString(String pathString, int stringLength) {
+ return PathParserNatives.nCreatePathDataFromString(pathString, stringLength);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nCreatePathFromPathData(long outPathPtr, long pathData) {
+ PathParserNatives.nCreatePathFromPathData(outPathPtr, pathData);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateEmptyPathData() {
+ return PathParserNatives.nCreateEmptyPathData();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreatePathData(long nativePtr) {
+ return PathParserNatives.nCreatePathData(nativePtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nInterpolatePathData(
+ long outDataPtr, long fromDataPtr, long toDataPtr, float fraction) {
+ return PathParserNatives.nInterpolatePathData(outDataPtr, fromDataPtr, toDataPtr, fraction);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nFinalize(long nativePtr) {
+ PathParserNatives.nFinalize(nativePtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nCanMorph(long fromDataPtr, long toDataPtr) {
+ return PathParserNatives.nCanMorph(fromDataPtr, toDataPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetPathData(long outDataPtr, long fromDataPtr) {
+ PathParserNatives.nSetPathData(outDataPtr, fromDataPtr);
+ }
+
+ /** Shadow picker for {@link PathParser}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowPathParser.class, ShadowNativePathParser.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePicture.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePicture.java
new file mode 100644
index 000000000..493076c28
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePicture.java
@@ -0,0 +1,72 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Picture;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PictureNatives;
+import org.robolectric.shadows.ShadowNativePicture.Picker;
+
+/** Shadow for {@link Picture} that is backed by native code */
+@Implements(value = Picture.class, minSdk = O, shadowPicker = Picker.class, isInAndroidSdk = false)
+public class ShadowNativePicture {
+
+ @Implementation
+ protected static long nativeConstructor(long nativeSrcOr0) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PictureNatives.nativeConstructor(nativeSrcOr0);
+ }
+
+ @Implementation
+ protected static long nativeCreateFromStream(InputStream stream, byte[] storage) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PictureNatives.nativeCreateFromStream(stream, storage);
+ }
+
+ @Implementation
+ protected static int nativeGetWidth(long nativePicture) {
+ return PictureNatives.nativeGetWidth(nativePicture);
+ }
+
+ @Implementation
+ protected static int nativeGetHeight(long nativePicture) {
+ return PictureNatives.nativeGetHeight(nativePicture);
+ }
+
+ @Implementation
+ protected static long nativeBeginRecording(long nativeCanvas, int w, int h) {
+ return PictureNatives.nativeBeginRecording(nativeCanvas, w, h);
+ }
+
+ @Implementation
+ protected static void nativeEndRecording(long nativeCanvas) {
+ PictureNatives.nativeEndRecording(nativeCanvas);
+ }
+
+ @Implementation
+ protected static void nativeDraw(long nativeCanvas, long nativePicture) {
+ PictureNatives.nativeDraw(nativeCanvas, nativePicture);
+ }
+
+ @Implementation
+ protected static boolean nativeWriteToStream(
+ long nativePicture, OutputStream stream, byte[] storage) {
+ return PictureNatives.nativeWriteToStream(nativePicture, stream, storage);
+ }
+
+ @Implementation
+ protected static void nativeDestructor(long nativePicture) {
+ PictureNatives.nativeDestructor(nativePicture);
+ }
+
+ /** Shadow picker for {@link Picture}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowPicture.class, ShadowNativePicture.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePorterDuffColorFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePorterDuffColorFilter.java
new file mode 100644
index 000000000..837ab51d3
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePorterDuffColorFilter.java
@@ -0,0 +1,39 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.PorterDuffColorFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.PorterDuffColorFilterNatives;
+import org.robolectric.shadows.ShadowNativePorterDuffColorFilter.Picker;
+
+/** Shadow for {@link PorterDuffColorFilter} that is backed by native code */
+@Implements(
+ value = PorterDuffColorFilter.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativePorterDuffColorFilter extends ShadowPorterDuffColorFilter {
+
+ @Implementation(minSdk = Q)
+ protected static long native_CreateBlendModeFilter(int srcColor, int blendmode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return PorterDuffColorFilterNatives.native_CreateBlendModeFilter(srcColor, blendmode);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
+ return native_CreateBlendModeFilter(srcColor, porterDuffMode);
+ }
+
+ /** Shadow picker for {@link PorterDuffColorFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<ShadowPorterDuffColorFilter> {
+ public Picker() {
+ super(ShadowPorterDuffColorFilter.class, ShadowNativePorterDuffColorFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePropertyValuesHolder.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePropertyValuesHolder.java
new file mode 100644
index 000000000..d9a8f7d5d
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativePropertyValuesHolder.java
@@ -0,0 +1,85 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.animation.PropertyValuesHolder;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.PropertyValuesHolderNatives;
+import org.robolectric.shadows.ShadowNativePropertyValuesHolder.Picker;
+
+/** Shadow for {@link PropertyValuesHolder} that is backed by native code */
+@Implements(value = PropertyValuesHolder.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativePropertyValuesHolder {
+
+ @Implementation
+ protected static long nGetIntMethod(Class<?> targetClass, String methodName) {
+ return PropertyValuesHolderNatives.nGetIntMethod(targetClass, methodName);
+ }
+
+ @Implementation
+ protected static long nGetFloatMethod(Class<?> targetClass, String methodName) {
+ return PropertyValuesHolderNatives.nGetFloatMethod(targetClass, methodName);
+ }
+
+ @Implementation
+ protected static long nGetMultipleIntMethod(
+ Class<?> targetClass, String methodName, int numParams) {
+ return PropertyValuesHolderNatives.nGetMultipleIntMethod(targetClass, methodName, numParams);
+ }
+
+ @Implementation
+ protected static long nGetMultipleFloatMethod(
+ Class<?> targetClass, String methodName, int numParams) {
+ return PropertyValuesHolderNatives.nGetMultipleFloatMethod(targetClass, methodName, numParams);
+ }
+
+ @Implementation
+ protected static void nCallIntMethod(Object target, long methodID, int arg) {
+ PropertyValuesHolderNatives.nCallIntMethod(target, methodID, arg);
+ }
+
+ @Implementation
+ protected static void nCallFloatMethod(Object target, long methodID, float arg) {
+ PropertyValuesHolderNatives.nCallFloatMethod(target, methodID, arg);
+ }
+
+ @Implementation
+ protected static void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2) {
+ PropertyValuesHolderNatives.nCallTwoIntMethod(target, methodID, arg1, arg2);
+ }
+
+ @Implementation
+ protected static void nCallFourIntMethod(
+ Object target, long methodID, int arg1, int arg2, int arg3, int arg4) {
+ PropertyValuesHolderNatives.nCallFourIntMethod(target, methodID, arg1, arg2, arg3, arg4);
+ }
+
+ @Implementation
+ protected static void nCallMultipleIntMethod(Object target, long methodID, int[] args) {
+ PropertyValuesHolderNatives.nCallMultipleIntMethod(target, methodID, args);
+ }
+
+ @Implementation
+ protected static void nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2) {
+ PropertyValuesHolderNatives.nCallTwoFloatMethod(target, methodID, arg1, arg2);
+ }
+
+ @Implementation
+ protected static void nCallFourFloatMethod(
+ Object target, long methodID, float arg1, float arg2, float arg3, float arg4) {
+ PropertyValuesHolderNatives.nCallFourFloatMethod(target, methodID, arg1, arg2, arg3, arg4);
+ }
+
+ @Implementation
+ protected static void nCallMultipleFloatMethod(Object target, long methodID, float[] args) {
+ PropertyValuesHolderNatives.nCallMultipleFloatMethod(target, methodID, args);
+ }
+
+ /** Shadow picker for {@link PropertyValuesHolder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativePropertyValuesHolder.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRadialGradient.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRadialGradient.java
new file mode 100644
index 000000000..07d2a724c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRadialGradient.java
@@ -0,0 +1,83 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.RadialGradient;
+import androidx.annotation.ColorLong;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RadialGradientNatives;
+import org.robolectric.shadows.ShadowNativeRadialGradient.Picker;
+
+/** Shadow for {@link RadialGradient} that is backed by native code */
+@Implements(value = RadialGradient.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeRadialGradient {
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreate(
+ long matrix,
+ float startX,
+ float startY,
+ float startRadius,
+ float endX,
+ float endY,
+ float endRadius,
+ @ColorLong long[] colors,
+ float[] positions,
+ int tileMode,
+ long colorSpaceHandle) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RadialGradientNatives.nativeCreate(
+ matrix,
+ startX,
+ startY,
+ startRadius,
+ endX,
+ endY,
+ endRadius,
+ colors,
+ positions,
+ tileMode,
+ colorSpaceHandle);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = R)
+ protected static long nativeCreate(
+ long matrix,
+ float x,
+ float y,
+ float radius,
+ @ColorLong long[] colors,
+ float[] positions,
+ int tileMode,
+ long colorSpaceHandle) {
+ return nativeCreate(
+ matrix, x, y, 0, x, y, radius, colors, positions, tileMode, colorSpaceHandle);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nativeCreate1(
+ long matrix, float x, float y, float radius, int[] colors, float[] positions, int tileMode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RadialGradientNatives.nativeCreate1(matrix, x, y, radius, colors, positions, tileMode);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nativeCreate2(
+ long matrix, float x, float y, float radius, int color0, int color1, int tileMode) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RadialGradientNatives.nativeCreate2(matrix, x, y, radius, color0, color1, tileMode);
+ }
+
+ /** Shadow picker for {@link RadialGradient}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeRadialGradient.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRecordingCanvas.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRecordingCanvas.java
new file mode 100644
index 000000000..537419c86
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRecordingCanvas.java
@@ -0,0 +1,112 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.RecordingCanvas;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RecordingCanvasNatives;
+import org.robolectric.shadows.ShadowNativeRecordingCanvas.Picker;
+
+/** Shadow for {@link RecordingCanvas} that is backed by native code */
+@Implements(value = RecordingCanvas.class, minSdk = Q, shadowPicker = Picker.class)
+public class ShadowNativeRecordingCanvas extends ShadowNativeBaseRecordingCanvas {
+
+ @Implementation
+ protected static long nCreateDisplayListCanvas(long node, int width, int height) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RecordingCanvasNatives.nCreateDisplayListCanvas(node, width, height);
+ }
+
+ @Implementation
+ protected static void nResetDisplayListCanvas(long canvas, long node, int width, int height) {
+ RecordingCanvasNatives.nResetDisplayListCanvas(canvas, node, width, height);
+ }
+
+ @Implementation
+ protected static int nGetMaximumTextureWidth() {
+ return RecordingCanvasNatives.nGetMaximumTextureWidth();
+ }
+
+ @Implementation
+ protected static int nGetMaximumTextureHeight() {
+ return RecordingCanvasNatives.nGetMaximumTextureHeight();
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nEnableZ(long renderer, boolean enableZ) {
+ RecordingCanvasNatives.nEnableZ(renderer, enableZ);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nFinishRecording(long renderer, long renderNode) {
+ RecordingCanvasNatives.nFinishRecording(renderer, renderNode);
+ }
+
+ @Implementation
+ protected static void nDrawRenderNode(long renderer, long renderNode) {
+ RecordingCanvasNatives.nDrawRenderNode(renderer, renderNode);
+ }
+
+ @Implementation
+ protected static void nDrawTextureLayer(long renderer, long layer) {
+ RecordingCanvasNatives.nDrawTextureLayer(renderer, layer);
+ }
+
+ @Implementation
+ protected static void nDrawCircle(
+ long renderer, long propCx, long propCy, long propRadius, long propPaint) {
+ RecordingCanvasNatives.nDrawCircle(renderer, propCx, propCy, propRadius, propPaint);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nDrawRipple(
+ long renderer,
+ long propCx,
+ long propCy,
+ long propRadius,
+ long propPaint,
+ long propProgress,
+ long turbulencePhase,
+ int color,
+ long runtimeEffect) {
+ RecordingCanvasNatives.nDrawRipple(
+ renderer,
+ propCx,
+ propCy,
+ propRadius,
+ propPaint,
+ propProgress,
+ turbulencePhase,
+ color,
+ runtimeEffect);
+ }
+
+ @Implementation
+ protected static void nDrawRoundRect(
+ long renderer,
+ long propLeft,
+ long propTop,
+ long propRight,
+ long propBottom,
+ long propRx,
+ long propRy,
+ long propPaint) {
+ RecordingCanvasNatives.nDrawRoundRect(
+ renderer, propLeft, propTop, propRight, propBottom, propRx, propRy, propPaint);
+ }
+
+ @Implementation
+ protected static void nDrawWebViewFunctor(long canvas, int functor) {
+ RecordingCanvasNatives.nDrawWebViewFunctor(canvas, functor);
+ }
+
+ /** Shadow picker for {@link RecordingCanvas}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRecordingCanvas.class, ShadowNativeRecordingCanvas.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegion.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegion.java
new file mode 100644
index 000000000..6c855c48b
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegion.java
@@ -0,0 +1,184 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static org.robolectric.shadow.api.Shadow.invokeConstructor;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Parcel;
+import com.google.errorprone.annotations.DoNotCall;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RegionNatives;
+import org.robolectric.shadows.ShadowNativeRegion.Picker;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link Region} that is backed by native code */
+@Implements(value = Region.class, minSdk = O, shadowPicker = Picker.class, isInAndroidSdk = false)
+public class ShadowNativeRegion {
+
+ RegionNatives regionNatives = new RegionNatives();
+ @RealObject Region realRegion;
+
+ @Implementation(minSdk = O)
+ protected void __constructor__(long ni) {
+ invokeConstructor(Region.class, realRegion, ClassParameter.from(long.class, ni));
+ regionNatives.mNativeRegion = ni;
+ }
+
+ @Implementation(minSdk = O)
+ protected void __constructor__(int left, int top, int right, int bottom) {
+ invokeConstructor(
+ Region.class,
+ realRegion,
+ ClassParameter.from(int.class, left),
+ ClassParameter.from(int.class, top),
+ ClassParameter.from(int.class, right),
+ ClassParameter.from(int.class, bottom));
+ regionNatives.mNativeRegion = reflector(RegionReflector.class, realRegion).getNativeRegion();
+ }
+
+ @Implementation(minSdk = O)
+ protected void __constructor__(Rect rect) {
+ invokeConstructor(Region.class, realRegion, ClassParameter.from(Rect.class, rect));
+ regionNatives.mNativeRegion = reflector(RegionReflector.class, realRegion).getNativeRegion();
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeEquals(long nativeR1, long nativeR2) {
+ return RegionNatives.nativeEquals(nativeR1, nativeR2);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nativeConstructor() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RegionNatives.nativeConstructor();
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nativeDestructor(long nativeRegion) {
+ RegionNatives.nativeDestructor(nativeRegion);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nativeSetRegion(long nativeDst, long nativeSrc) {
+ RegionNatives.nativeSetRegion(nativeDst, nativeSrc);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeSetRect(long nativeDst, int left, int top, int right, int bottom) {
+ return RegionNatives.nativeSetRect(nativeDst, left, top, right, bottom);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeSetPath(long nativeDst, long nativePath, long nativeClip) {
+ return RegionNatives.nativeSetPath(nativeDst, nativePath, nativeClip);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeGetBounds(long nativeRegion, Rect rect) {
+ return RegionNatives.nativeGetBounds(nativeRegion, rect);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeGetBoundaryPath(long nativeRegion, long nativePath) {
+ return RegionNatives.nativeGetBoundaryPath(nativeRegion, nativePath);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeOp(
+ long nativeDst, int left, int top, int right, int bottom, int op) {
+ return RegionNatives.nativeOp(nativeDst, left, top, right, bottom, op);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeOp(long nativeDst, Rect rect, long nativeRegion, int op) {
+ return RegionNatives.nativeOp(nativeDst, rect, nativeRegion, op);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeOp(
+ long nativeDst, long nativeRegion1, long nativeRegion2, int op) {
+ return RegionNatives.nativeOp(nativeDst, nativeRegion1, nativeRegion2, op);
+ }
+
+ @DoNotCall("Always throws java.lang.UnsupportedOperationException")
+ @Implementation(minSdk = O)
+ protected static long nativeCreateFromParcel(Parcel p) {
+ throw new UnsupportedOperationException();
+ }
+
+ @DoNotCall("Always throws java.lang.UnsupportedOperationException")
+ @Implementation(minSdk = O)
+ protected static boolean nativeWriteToParcel(long nativeRegion, Parcel p) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Implementation(minSdk = O)
+ protected static String nativeToString(long nativeRegion) {
+ return RegionNatives.nativeToString(nativeRegion);
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean isEmpty() {
+ return regionNatives.isEmpty();
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean isRect() {
+ return regionNatives.isRect();
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean isComplex() {
+ return regionNatives.isComplex();
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean contains(int x, int y) {
+ return regionNatives.contains(x, y);
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean quickContains(int left, int top, int right, int bottom) {
+ return regionNatives.quickContains(left, top, right, bottom);
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean quickReject(int left, int top, int right, int bottom) {
+ return regionNatives.quickReject(left, top, right, bottom);
+ }
+
+ @Implementation(minSdk = O)
+ protected boolean quickReject(Region rgn) {
+ return regionNatives.quickReject(rgn);
+ }
+
+ @Implementation(minSdk = O)
+ protected void translate(int dx, int dy, Region dst) {
+ regionNatives.translate(dx, dy, dst);
+ }
+
+ @Implementation(minSdk = O)
+ protected void scale(float scale, Region dst) {
+ regionNatives.scale(scale, dst);
+ }
+
+ @ForType(Region.class)
+ interface RegionReflector {
+ @Accessor("mNativeRegion")
+ long getNativeRegion();
+ }
+
+ /** Shadow picker for {@link Region}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRegion.class, ShadowNativeRegion.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegionIterator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegionIterator.java
new file mode 100644
index 000000000..b47bd420c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRegionIterator.java
@@ -0,0 +1,39 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Rect;
+import android.graphics.RegionIterator;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RegionIteratorNatives;
+import org.robolectric.shadows.ShadowNativeRegionIterator.Picker;
+
+/** Shadow for {@link RegionIterator} that is backed by native code */
+@Implements(value = RegionIterator.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeRegionIterator {
+
+ @Implementation(minSdk = O)
+ protected static long nativeConstructor(long nativeRegion) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RegionIteratorNatives.nativeConstructor(nativeRegion);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nativeDestructor(long nativeIter) {
+ RegionIteratorNatives.nativeDestructor(nativeIter);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nativeNext(long nativeIter, Rect r) {
+ return RegionIteratorNatives.nativeNext(nativeIter, r);
+ }
+
+ /** Shadow picker for {@link RegionIterator}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeRegionIterator.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderEffect.java
new file mode 100644
index 000000000..0535803db
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderEffect.java
@@ -0,0 +1,77 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.RenderEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RenderEffectNatives;
+import org.robolectric.shadows.ShadowNativeRenderEffect.Picker;
+
+/** Shadow for {@link RenderEffect} that is backed by native code */
+@Implements(value = RenderEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeRenderEffect {
+ static {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateOffsetEffect(float offsetX, float offsetY, long nativeInput) {
+ return RenderEffectNatives.nativeCreateOffsetEffect(offsetX, offsetY, nativeInput);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateBlurEffect(
+ float radiusX, float radiusY, long nativeInput, int edgeTreatment) {
+ return RenderEffectNatives.nativeCreateBlurEffect(radiusX, radiusY, nativeInput, edgeTreatment);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateBitmapEffect(
+ long bitmapHandle,
+ float srcLeft,
+ float srcTop,
+ float srcRight,
+ float srcBottom,
+ float dstLeft,
+ float dstTop,
+ float dstRight,
+ float dstBottom) {
+ return RenderEffectNatives.nativeCreateBitmapEffect(
+ bitmapHandle, srcLeft, srcTop, srcRight, srcBottom, dstLeft, dstTop, dstRight, dstBottom);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateColorFilterEffect(long colorFilter, long nativeInput) {
+ return RenderEffectNatives.nativeCreateColorFilterEffect(colorFilter, nativeInput);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateBlendModeEffect(long dst, long src, int blendmode) {
+ return RenderEffectNatives.nativeCreateBlendModeEffect(dst, src, blendmode);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateChainEffect(long outer, long inner) {
+ return RenderEffectNatives.nativeCreateChainEffect(outer, inner);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateShaderEffect(long shader) {
+ return RenderEffectNatives.nativeCreateShaderEffect(shader);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeGetFinalizer() {
+ return RenderEffectNatives.nativeGetFinalizer();
+ }
+
+ /** Shadow picker for {@link RenderEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeRenderEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNode.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNode.java
new file mode 100644
index 000000000..4b11355f7
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNode.java
@@ -0,0 +1,468 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
+
+import android.graphics.RenderNode;
+import android.graphics.RenderNode.PositionUpdateListener;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RenderNodeNatives;
+import org.robolectric.shadows.ShadowNativeRenderNode.Picker;
+
+/** Shadow for {@link RenderNode} that is backed by native code */
+@Implements(value = RenderNode.class, minSdk = Q, shadowPicker = Picker.class)
+public class ShadowNativeRenderNode {
+ @Implementation
+ protected static long nCreate(String name) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeNatives.nCreate(name);
+ }
+
+ @Implementation
+ protected static long nGetNativeFinalizer() {
+ return RenderNodeNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation
+ protected static void nOutput(long renderNode) {
+ RenderNodeNatives.nOutput(renderNode);
+ }
+
+ @Implementation(minSdk = R)
+ protected static int nGetUsageSize(long renderNode) {
+ return RenderNodeNatives.nGetUsageSize(renderNode);
+ }
+
+ @Implementation(minSdk = R)
+ protected static int nGetAllocatedSize(long renderNode) {
+ return RenderNodeNatives.nGetAllocatedSize(renderNode);
+ }
+
+ @Implementation(maxSdk = S_V2)
+ protected static void nRequestPositionUpdates(long renderNode, PositionUpdateListener callback) {
+ RenderNodeNatives.nRequestPositionUpdates(renderNode, callback);
+ }
+
+ @Implementation
+ protected static void nAddAnimator(long renderNode, long animatorPtr) {
+ RenderNodeNatives.nAddAnimator(renderNode, animatorPtr);
+ }
+
+ @Implementation
+ protected static void nEndAllAnimators(long renderNode) {
+ RenderNodeNatives.nEndAllAnimators(renderNode);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nDiscardDisplayList(long renderNode) {
+ RenderNodeNatives.nDiscardDisplayList(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nIsValid(long renderNode) {
+ return RenderNodeNatives.nIsValid(renderNode);
+ }
+
+ @Implementation
+ protected static void nGetTransformMatrix(long renderNode, long nativeMatrix) {
+ RenderNodeNatives.nGetTransformMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static void nGetInverseTransformMatrix(long renderNode, long nativeMatrix) {
+ RenderNodeNatives.nGetInverseTransformMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static boolean nHasIdentityMatrix(long renderNode) {
+ return RenderNodeNatives.nHasIdentityMatrix(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nOffsetTopAndBottom(long renderNode, int offset) {
+ return RenderNodeNatives.nOffsetTopAndBottom(renderNode, offset);
+ }
+
+ @Implementation
+ protected static boolean nOffsetLeftAndRight(long renderNode, int offset) {
+ return RenderNodeNatives.nOffsetLeftAndRight(renderNode, offset);
+ }
+
+ @Implementation
+ protected static boolean nSetLeftTopRightBottom(
+ long renderNode, int left, int top, int right, int bottom) {
+ return RenderNodeNatives.nSetLeftTopRightBottom(renderNode, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static boolean nSetLeft(long renderNode, int left) {
+ return RenderNodeNatives.nSetLeft(renderNode, left);
+ }
+
+ @Implementation
+ protected static boolean nSetTop(long renderNode, int top) {
+ return RenderNodeNatives.nSetTop(renderNode, top);
+ }
+
+ @Implementation
+ protected static boolean nSetRight(long renderNode, int right) {
+ return RenderNodeNatives.nSetRight(renderNode, right);
+ }
+
+ @Implementation
+ protected static boolean nSetBottom(long renderNode, int bottom) {
+ return RenderNodeNatives.nSetBottom(renderNode, bottom);
+ }
+
+ @Implementation
+ protected static int nGetLeft(long renderNode) {
+ return RenderNodeNatives.nGetLeft(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetTop(long renderNode) {
+ return RenderNodeNatives.nGetTop(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetRight(long renderNode) {
+ return RenderNodeNatives.nGetRight(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetBottom(long renderNode) {
+ return RenderNodeNatives.nGetBottom(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetCameraDistance(long renderNode, float distance) {
+ return RenderNodeNatives.nSetCameraDistance(renderNode, distance);
+ }
+
+ @Implementation
+ protected static boolean nSetPivotY(long renderNode, float pivotY) {
+ return RenderNodeNatives.nSetPivotY(renderNode, pivotY);
+ }
+
+ @Implementation
+ protected static boolean nSetPivotX(long renderNode, float pivotX) {
+ return RenderNodeNatives.nSetPivotX(renderNode, pivotX);
+ }
+
+ @Implementation
+ protected static boolean nResetPivot(long renderNode) {
+ return RenderNodeNatives.nResetPivot(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetLayerType(long renderNode, int layerType) {
+ return RenderNodeNatives.nSetLayerType(renderNode, layerType);
+ }
+
+ @Implementation
+ protected static int nGetLayerType(long renderNode) {
+ return RenderNodeNatives.nGetLayerType(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetLayerPaint(long renderNode, long paint) {
+ return RenderNodeNatives.nSetLayerPaint(renderNode, paint);
+ }
+
+ @Implementation
+ protected static boolean nSetClipToBounds(long renderNode, boolean clipToBounds) {
+ return RenderNodeNatives.nSetClipToBounds(renderNode, clipToBounds);
+ }
+
+ @Implementation
+ protected static boolean nGetClipToBounds(long renderNode) {
+ return RenderNodeNatives.nGetClipToBounds(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetClipBounds(
+ long renderNode, int left, int top, int right, int bottom) {
+ return RenderNodeNatives.nSetClipBounds(renderNode, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static boolean nSetClipBoundsEmpty(long renderNode) {
+ return RenderNodeNatives.nSetClipBoundsEmpty(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetProjectBackwards(long renderNode, boolean shouldProject) {
+ return RenderNodeNatives.nSetProjectBackwards(renderNode, shouldProject);
+ }
+
+ @Implementation
+ protected static boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive) {
+ return RenderNodeNatives.nSetProjectionReceiver(renderNode, shouldReceive);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineRoundRect(
+ long renderNode, int left, int top, int right, int bottom, float radius, float alpha) {
+ return RenderNodeNatives.nSetOutlineRoundRect(
+ renderNode, left, top, right, bottom, radius, alpha);
+ }
+
+ @Implementation(minSdk = R)
+ protected static boolean nSetOutlinePath(long renderNode, long nativePath, float alpha) {
+ return RenderNodeNatives.nSetOutlinePath(renderNode, nativePath, alpha);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineEmpty(long renderNode) {
+ return RenderNodeNatives.nSetOutlineEmpty(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineNone(long renderNode) {
+ return RenderNodeNatives.nSetOutlineNone(renderNode);
+ }
+
+ @Implementation(minSdk = S)
+ protected static boolean nClearStretch(long renderNode) {
+ return RenderNodeNatives.nClearStretch(renderNode);
+ }
+
+ @Implementation(minSdk = S)
+ protected static boolean nStretch(
+ long renderNode, float vecX, float vecY, float maxStretchX, float maxStretchY) {
+ return RenderNodeNatives.nStretch(renderNode, vecX, vecY, maxStretchX, maxStretchY);
+ }
+
+ @Implementation
+ protected static boolean nHasShadow(long renderNode) {
+ return RenderNodeNatives.nHasShadow(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetSpotShadowColor(long renderNode, int color) {
+ return RenderNodeNatives.nSetSpotShadowColor(renderNode, color);
+ }
+
+ @Implementation
+ protected static boolean nSetAmbientShadowColor(long renderNode, int color) {
+ return RenderNodeNatives.nSetAmbientShadowColor(renderNode, color);
+ }
+
+ @Implementation
+ protected static int nGetSpotShadowColor(long renderNode) {
+ return RenderNodeNatives.nGetSpotShadowColor(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetAmbientShadowColor(long renderNode) {
+ return RenderNodeNatives.nGetAmbientShadowColor(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetClipToOutline(long renderNode, boolean clipToOutline) {
+ return RenderNodeNatives.nSetClipToOutline(renderNode, clipToOutline);
+ }
+
+ @Implementation
+ protected static boolean nSetRevealClip(
+ long renderNode, boolean shouldClip, float x, float y, float radius) {
+ return RenderNodeNatives.nSetRevealClip(renderNode, shouldClip, x, y, radius);
+ }
+
+ @Implementation
+ protected static boolean nSetAlpha(long renderNode, float alpha) {
+ return RenderNodeNatives.nSetAlpha(renderNode, alpha);
+ }
+
+ @Implementation(minSdk = S)
+ protected static boolean nSetRenderEffect(long renderNode, long renderEffect) {
+ return RenderNodeNatives.nSetRenderEffect(renderNode, renderEffect);
+ }
+
+ @Implementation
+ protected static boolean nSetHasOverlappingRendering(
+ long renderNode, boolean hasOverlappingRendering) {
+ return RenderNodeNatives.nSetHasOverlappingRendering(renderNode, hasOverlappingRendering);
+ }
+
+ @Implementation
+ protected static void nSetUsageHint(long renderNode, int usageHint) {
+ RenderNodeNatives.nSetUsageHint(renderNode, usageHint);
+ }
+
+ @Implementation
+ protected static boolean nSetElevation(long renderNode, float lift) {
+ return RenderNodeNatives.nSetElevation(renderNode, lift);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationX(long renderNode, float translationX) {
+ return RenderNodeNatives.nSetTranslationX(renderNode, translationX);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationY(long renderNode, float translationY) {
+ return RenderNodeNatives.nSetTranslationY(renderNode, translationY);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationZ(long renderNode, float translationZ) {
+ return RenderNodeNatives.nSetTranslationZ(renderNode, translationZ);
+ }
+
+ @Implementation
+ protected static boolean nSetRotation(long renderNode, float rotation) {
+ return RenderNodeNatives.nSetRotation(renderNode, rotation);
+ }
+
+ @Implementation
+ protected static boolean nSetRotationX(long renderNode, float rotationX) {
+ return RenderNodeNatives.nSetRotationX(renderNode, rotationX);
+ }
+
+ @Implementation
+ protected static boolean nSetRotationY(long renderNode, float rotationY) {
+ return RenderNodeNatives.nSetRotationY(renderNode, rotationY);
+ }
+
+ @Implementation
+ protected static boolean nSetScaleX(long renderNode, float scaleX) {
+ return RenderNodeNatives.nSetScaleX(renderNode, scaleX);
+ }
+
+ @Implementation
+ protected static boolean nSetScaleY(long renderNode, float scaleY) {
+ return RenderNodeNatives.nSetScaleY(renderNode, scaleY);
+ }
+
+ @Implementation
+ protected static boolean nSetStaticMatrix(long renderNode, long nativeMatrix) {
+ return RenderNodeNatives.nSetStaticMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static boolean nSetAnimationMatrix(long renderNode, long animationMatrix) {
+ return RenderNodeNatives.nSetAnimationMatrix(renderNode, animationMatrix);
+ }
+
+ @Implementation
+ protected static boolean nHasOverlappingRendering(long renderNode) {
+ return RenderNodeNatives.nHasOverlappingRendering(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nGetAnimationMatrix(long renderNode, long animationMatrix) {
+ return RenderNodeNatives.nGetAnimationMatrix(renderNode, animationMatrix);
+ }
+
+ @Implementation
+ protected static boolean nGetClipToOutline(long renderNode) {
+ return RenderNodeNatives.nGetClipToOutline(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetAlpha(long renderNode) {
+ return RenderNodeNatives.nGetAlpha(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetCameraDistance(long renderNode) {
+ return RenderNodeNatives.nGetCameraDistance(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetScaleX(long renderNode) {
+ return RenderNodeNatives.nGetScaleX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetScaleY(long renderNode) {
+ return RenderNodeNatives.nGetScaleY(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetElevation(long renderNode) {
+ return RenderNodeNatives.nGetElevation(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationX(long renderNode) {
+ return RenderNodeNatives.nGetTranslationX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationY(long renderNode) {
+ return RenderNodeNatives.nGetTranslationY(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationZ(long renderNode) {
+ return RenderNodeNatives.nGetTranslationZ(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotation(long renderNode) {
+ return RenderNodeNatives.nGetRotation(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotationX(long renderNode) {
+ return RenderNodeNatives.nGetRotationX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotationY(long renderNode) {
+ return RenderNodeNatives.nGetRotationY(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nIsPivotExplicitlySet(long renderNode) {
+ return RenderNodeNatives.nIsPivotExplicitlySet(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetPivotX(long renderNode) {
+ return RenderNodeNatives.nGetPivotX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetPivotY(long renderNode) {
+ return RenderNodeNatives.nGetPivotY(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetWidth(long renderNode) {
+ return RenderNodeNatives.nGetWidth(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetHeight(long renderNode) {
+ return RenderNodeNatives.nGetHeight(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetAllowForceDark(long renderNode, boolean allowForceDark) {
+ return RenderNodeNatives.nSetAllowForceDark(renderNode, allowForceDark);
+ }
+
+ @Implementation
+ protected static boolean nGetAllowForceDark(long renderNode) {
+ return RenderNodeNatives.nGetAllowForceDark(renderNode);
+ }
+
+ @Implementation
+ protected static long nGetUniqueId(long renderNode) {
+ return RenderNodeNatives.nGetUniqueId(renderNode);
+ }
+
+ /** Shadow picker for {@link RenderNode}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRenderNodeQ.class, ShadowNativeRenderNode.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimator.java
new file mode 100644
index 000000000..5b3c32a1c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimator.java
@@ -0,0 +1,96 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.R;
+
+import android.graphics.animation.RenderNodeAnimator;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RenderNodeAnimatorNatives;
+import org.robolectric.shadows.ShadowNativeRenderNodeAnimator.Picker;
+
+/** Shadow for {@link RenderNodeAnimator} that is backed by native code */
+@Implements(
+ value = RenderNodeAnimator.class,
+ minSdk = R,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeRenderNodeAnimator {
+ @Implementation
+ protected static long nCreateAnimator(int property, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateAnimator(property, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateCanvasPropertyFloatAnimator(long canvasProperty, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateCanvasPropertyFloatAnimator(canvasProperty, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateCanvasPropertyPaintAnimator(
+ canvasProperty, paintField, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateRevealAnimator(int x, int y, float startRadius, float endRadius) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateRevealAnimator(x, y, startRadius, endRadius);
+ }
+
+ @Implementation
+ protected static void nSetStartValue(long nativePtr, float startValue) {
+ RenderNodeAnimatorNatives.nSetStartValue(nativePtr, startValue);
+ }
+
+ @Implementation
+ protected static void nSetDuration(long nativePtr, long duration) {
+ RenderNodeAnimatorNatives.nSetDuration(nativePtr, duration);
+ }
+
+ @Implementation
+ protected static long nGetDuration(long nativePtr) {
+ return RenderNodeAnimatorNatives.nGetDuration(nativePtr);
+ }
+
+ @Implementation
+ protected static void nSetStartDelay(long nativePtr, long startDelay) {
+ RenderNodeAnimatorNatives.nSetStartDelay(nativePtr, startDelay);
+ }
+
+ @Implementation
+ protected static void nSetInterpolator(long animPtr, long interpolatorPtr) {
+ RenderNodeAnimatorNatives.nSetInterpolator(animPtr, interpolatorPtr);
+ }
+
+ @Implementation
+ protected static void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync) {
+ RenderNodeAnimatorNatives.nSetAllowRunningAsync(animPtr, mayRunAsync);
+ }
+
+ @Implementation
+ protected static void nSetListener(long animPtr, RenderNodeAnimator listener) {
+ RenderNodeAnimatorNatives.nSetListener(animPtr, listener);
+ }
+
+ @Implementation
+ protected static void nStart(long animPtr) {
+ RenderNodeAnimatorNatives.nStart(animPtr);
+ }
+
+ @Implementation
+ protected static void nEnd(long animPtr) {
+ RenderNodeAnimatorNatives.nEnd(animPtr);
+ }
+
+ /** Shadow picker for {@link RenderNodeAnimator}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRenderNodeAnimatorR.class, ShadowNativeRenderNodeAnimator.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimatorQ.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimatorQ.java
new file mode 100644
index 000000000..f83378285
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeAnimatorQ.java
@@ -0,0 +1,100 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.Q;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RenderNodeAnimatorNatives;
+import org.robolectric.shadows.ShadowNativeRenderNodeAnimatorQ.Picker;
+
+/**
+ * Shadow for {@link android.view.RenderNodeAnimator} for Android Q and below that is backed by
+ * native code
+ */
+@Implements(
+ className = "android.view.RenderNodeAnimator",
+ minSdk = O,
+ maxSdk = Q,
+ looseSignatures = true,
+ shadowPicker = Picker.class)
+public class ShadowNativeRenderNodeAnimatorQ {
+ @Implementation
+ protected static long nCreateAnimator(int property, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateAnimator(property, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateCanvasPropertyFloatAnimator(long canvasProperty, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateCanvasPropertyFloatAnimator(canvasProperty, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateCanvasPropertyPaintAnimator(
+ long canvasProperty, int paintField, float finalValue) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateCanvasPropertyPaintAnimator(
+ canvasProperty, paintField, finalValue);
+ }
+
+ @Implementation
+ protected static long nCreateRevealAnimator(int x, int y, float startRadius, float endRadius) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeAnimatorNatives.nCreateRevealAnimator(x, y, startRadius, endRadius);
+ }
+
+ @Implementation
+ protected static void nSetStartValue(long nativePtr, float startValue) {
+ RenderNodeAnimatorNatives.nSetStartValue(nativePtr, startValue);
+ }
+
+ @Implementation
+ protected static void nSetDuration(long nativePtr, long duration) {
+ RenderNodeAnimatorNatives.nSetDuration(nativePtr, duration);
+ }
+
+ @Implementation
+ protected static long nGetDuration(long nativePtr) {
+ return RenderNodeAnimatorNatives.nGetDuration(nativePtr);
+ }
+
+ @Implementation
+ protected static void nSetStartDelay(long nativePtr, long startDelay) {
+ RenderNodeAnimatorNatives.nSetStartDelay(nativePtr, startDelay);
+ }
+
+ @Implementation
+ protected static void nSetInterpolator(long animPtr, long interpolatorPtr) {
+ RenderNodeAnimatorNatives.nSetInterpolator(animPtr, interpolatorPtr);
+ }
+
+ @Implementation
+ protected static void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync) {
+ RenderNodeAnimatorNatives.nSetAllowRunningAsync(animPtr, mayRunAsync);
+ }
+
+ @Implementation
+ protected static void nSetListener(Object animPtr, Object listener) {
+ RenderNodeAnimatorNatives.nSetListener((long) animPtr, listener);
+ }
+
+ @Implementation
+ protected static void nStart(long animPtr) {
+ RenderNodeAnimatorNatives.nStart(animPtr);
+ }
+
+ @Implementation
+ protected static void nEnd(long animPtr) {
+ RenderNodeAnimatorNatives.nEnd(animPtr);
+ }
+
+ /** Shadow picker for {@link android.view.RenderNodeAnimator}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRenderNodeAnimator.class, ShadowNativeRenderNodeAnimatorQ.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeOP.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeOP.java
new file mode 100644
index 000000000..f17650ec0
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRenderNodeOP.java
@@ -0,0 +1,465 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.graphics.Canvas;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RenderNodeNatives;
+import org.robolectric.shadows.ShadowNativeRenderNodeOP.Picker;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/** Shadow for {@link android.view.RenderNode} that is backed by native code */
+@Implements(
+ className = "android.view.RenderNode",
+ minSdk = O,
+ maxSdk = P,
+ looseSignatures = true,
+ shadowPicker = Picker.class)
+public class ShadowNativeRenderNodeOP {
+ @RealObject Object realRenderNode;
+
+ @Implementation
+ protected static long nCreate(String name) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RenderNodeNatives.nCreate(name);
+ }
+
+ @Implementation
+ protected static long nGetNativeFinalizer() {
+ return RenderNodeNatives.nGetNativeFinalizer();
+ }
+
+ @Implementation
+ protected static void nOutput(long renderNode) {
+ RenderNodeNatives.nOutput(renderNode);
+ }
+
+ @Implementation
+ protected static void nAddAnimator(long renderNode, long animatorPtr) {
+ RenderNodeNatives.nAddAnimator(renderNode, animatorPtr);
+ }
+
+ @Implementation
+ protected static void nEndAllAnimators(long renderNode) {
+ RenderNodeNatives.nEndAllAnimators(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nIsValid(long renderNode) {
+ return RenderNodeNatives.nIsValid(renderNode);
+ }
+
+ @Implementation
+ protected static void nGetTransformMatrix(long renderNode, long nativeMatrix) {
+ RenderNodeNatives.nGetTransformMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static void nGetInverseTransformMatrix(long renderNode, long nativeMatrix) {
+ RenderNodeNatives.nGetInverseTransformMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static boolean nHasIdentityMatrix(long renderNode) {
+ return RenderNodeNatives.nHasIdentityMatrix(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nOffsetTopAndBottom(long renderNode, int offset) {
+ return RenderNodeNatives.nOffsetTopAndBottom(renderNode, offset);
+ }
+
+ @Implementation
+ protected static boolean nOffsetLeftAndRight(long renderNode, int offset) {
+ return RenderNodeNatives.nOffsetLeftAndRight(renderNode, offset);
+ }
+
+ @Implementation
+ protected static boolean nSetLeftTopRightBottom(
+ long renderNode, int left, int top, int right, int bottom) {
+ return RenderNodeNatives.nSetLeftTopRightBottom(renderNode, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static boolean nSetLeft(long renderNode, int left) {
+ return RenderNodeNatives.nSetLeft(renderNode, left);
+ }
+
+ @Implementation
+ protected static boolean nSetTop(long renderNode, int top) {
+ return RenderNodeNatives.nSetTop(renderNode, top);
+ }
+
+ @Implementation
+ protected static boolean nSetRight(long renderNode, int right) {
+ return RenderNodeNatives.nSetRight(renderNode, right);
+ }
+
+ @Implementation
+ protected static boolean nSetBottom(long renderNode, int bottom) {
+ return RenderNodeNatives.nSetBottom(renderNode, bottom);
+ }
+
+ @Implementation
+ protected static int nGetLeft(long renderNode) {
+ return RenderNodeNatives.nGetLeft(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetTop(long renderNode) {
+ return RenderNodeNatives.nGetTop(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetRight(long renderNode) {
+ return RenderNodeNatives.nGetRight(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetBottom(long renderNode) {
+ return RenderNodeNatives.nGetBottom(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetCameraDistance(long renderNode, float distance) {
+ return RenderNodeNatives.nSetCameraDistance(renderNode, distance);
+ }
+
+ @Implementation
+ protected static boolean nSetPivotY(long renderNode, float pivotY) {
+ return RenderNodeNatives.nSetPivotY(renderNode, pivotY);
+ }
+
+ @Implementation
+ protected static boolean nSetPivotX(long renderNode, float pivotX) {
+ return RenderNodeNatives.nSetPivotX(renderNode, pivotX);
+ }
+
+ @Implementation
+ protected static boolean nResetPivot(long renderNode) {
+ return RenderNodeNatives.nResetPivot(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetLayerType(long renderNode, int layerType) {
+ return RenderNodeNatives.nSetLayerType(renderNode, layerType);
+ }
+
+ @Implementation
+ protected static int nGetLayerType(long renderNode) {
+ return RenderNodeNatives.nGetLayerType(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetLayerPaint(long renderNode, long paint) {
+ return RenderNodeNatives.nSetLayerPaint(renderNode, paint);
+ }
+
+ @Implementation
+ protected static boolean nSetClipToBounds(long renderNode, boolean clipToBounds) {
+ return RenderNodeNatives.nSetClipToBounds(renderNode, clipToBounds);
+ }
+
+ @Implementation
+ protected static boolean nGetClipToBounds(long renderNode) {
+ return RenderNodeNatives.nGetClipToBounds(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetClipBounds(
+ long renderNode, int left, int top, int right, int bottom) {
+ return RenderNodeNatives.nSetClipBounds(renderNode, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static boolean nSetClipBoundsEmpty(long renderNode) {
+ return RenderNodeNatives.nSetClipBoundsEmpty(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetProjectBackwards(long renderNode, boolean shouldProject) {
+ return RenderNodeNatives.nSetProjectBackwards(renderNode, shouldProject);
+ }
+
+ @Implementation
+ protected static boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive) {
+ return RenderNodeNatives.nSetProjectionReceiver(renderNode, shouldReceive);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineRoundRect(
+ long renderNode, int left, int top, int right, int bottom, float radius, float alpha) {
+ return RenderNodeNatives.nSetOutlineRoundRect(
+ renderNode, left, top, right, bottom, radius, alpha);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineEmpty(long renderNode) {
+ return RenderNodeNatives.nSetOutlineEmpty(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetOutlineNone(long renderNode) {
+ return RenderNodeNatives.nSetOutlineNone(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nHasShadow(long renderNode) {
+ return RenderNodeNatives.nHasShadow(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetSpotShadowColor(long renderNode, int color) {
+ return RenderNodeNatives.nSetSpotShadowColor(renderNode, color);
+ }
+
+ @Implementation
+ protected static boolean nSetAmbientShadowColor(long renderNode, int color) {
+ return RenderNodeNatives.nSetAmbientShadowColor(renderNode, color);
+ }
+
+ @Implementation
+ protected static int nGetSpotShadowColor(long renderNode) {
+ return RenderNodeNatives.nGetSpotShadowColor(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetAmbientShadowColor(long renderNode) {
+ return RenderNodeNatives.nGetAmbientShadowColor(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetClipToOutline(long renderNode, boolean clipToOutline) {
+ return RenderNodeNatives.nSetClipToOutline(renderNode, clipToOutline);
+ }
+
+ @Implementation
+ protected static boolean nSetRevealClip(
+ long renderNode, boolean shouldClip, float x, float y, float radius) {
+ return RenderNodeNatives.nSetRevealClip(renderNode, shouldClip, x, y, radius);
+ }
+
+ @Implementation
+ protected static boolean nSetAlpha(long renderNode, float alpha) {
+ return RenderNodeNatives.nSetAlpha(renderNode, alpha);
+ }
+
+ @Implementation
+ protected static boolean nSetHasOverlappingRendering(
+ long renderNode, boolean hasOverlappingRendering) {
+ return RenderNodeNatives.nSetHasOverlappingRendering(renderNode, hasOverlappingRendering);
+ }
+
+ @Implementation
+ protected static void nSetUsageHint(long renderNode, int usageHint) {
+ RenderNodeNatives.nSetUsageHint(renderNode, usageHint);
+ }
+
+ @Implementation
+ protected static boolean nSetElevation(long renderNode, float lift) {
+ return RenderNodeNatives.nSetElevation(renderNode, lift);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationX(long renderNode, float translationX) {
+ return RenderNodeNatives.nSetTranslationX(renderNode, translationX);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationY(long renderNode, float translationY) {
+ return RenderNodeNatives.nSetTranslationY(renderNode, translationY);
+ }
+
+ @Implementation
+ protected static boolean nSetTranslationZ(long renderNode, float translationZ) {
+ return RenderNodeNatives.nSetTranslationZ(renderNode, translationZ);
+ }
+
+ @Implementation
+ protected static boolean nSetRotation(long renderNode, float rotation) {
+ return RenderNodeNatives.nSetRotation(renderNode, rotation);
+ }
+
+ @Implementation
+ protected static boolean nSetRotationX(long renderNode, float rotationX) {
+ return RenderNodeNatives.nSetRotationX(renderNode, rotationX);
+ }
+
+ @Implementation
+ protected static boolean nSetRotationY(long renderNode, float rotationY) {
+ return RenderNodeNatives.nSetRotationY(renderNode, rotationY);
+ }
+
+ @Implementation
+ protected static boolean nSetScaleX(long renderNode, float scaleX) {
+ return RenderNodeNatives.nSetScaleX(renderNode, scaleX);
+ }
+
+ @Implementation
+ protected static boolean nSetScaleY(long renderNode, float scaleY) {
+ return RenderNodeNatives.nSetScaleY(renderNode, scaleY);
+ }
+
+ @Implementation
+ protected static boolean nSetStaticMatrix(long renderNode, long nativeMatrix) {
+ return RenderNodeNatives.nSetStaticMatrix(renderNode, nativeMatrix);
+ }
+
+ @Implementation
+ protected static boolean nSetAnimationMatrix(long renderNode, long animationMatrix) {
+ return RenderNodeNatives.nSetAnimationMatrix(renderNode, animationMatrix);
+ }
+
+ @Implementation
+ protected static boolean nHasOverlappingRendering(long renderNode) {
+ return RenderNodeNatives.nHasOverlappingRendering(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nGetAnimationMatrix(long renderNode, long animationMatrix) {
+ return RenderNodeNatives.nGetAnimationMatrix(renderNode, animationMatrix);
+ }
+
+ @Implementation
+ protected static boolean nGetClipToOutline(long renderNode) {
+ return RenderNodeNatives.nGetClipToOutline(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetAlpha(long renderNode) {
+ return RenderNodeNatives.nGetAlpha(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetCameraDistance(long renderNode) {
+ return RenderNodeNatives.nGetCameraDistance(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetScaleX(long renderNode) {
+ return RenderNodeNatives.nGetScaleX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetScaleY(long renderNode) {
+ return RenderNodeNatives.nGetScaleY(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetElevation(long renderNode) {
+ return RenderNodeNatives.nGetElevation(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationX(long renderNode) {
+ return RenderNodeNatives.nGetTranslationX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationY(long renderNode) {
+ return RenderNodeNatives.nGetTranslationY(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetTranslationZ(long renderNode) {
+ return RenderNodeNatives.nGetTranslationZ(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotation(long renderNode) {
+ return RenderNodeNatives.nGetRotation(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotationX(long renderNode) {
+ return RenderNodeNatives.nGetRotationX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetRotationY(long renderNode) {
+ return RenderNodeNatives.nGetRotationY(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nIsPivotExplicitlySet(long renderNode) {
+ return RenderNodeNatives.nIsPivotExplicitlySet(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetPivotX(long renderNode) {
+ return RenderNodeNatives.nGetPivotX(renderNode);
+ }
+
+ @Implementation
+ protected static float nGetPivotY(long renderNode) {
+ return RenderNodeNatives.nGetPivotY(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetWidth(long renderNode) {
+ return RenderNodeNatives.nGetWidth(renderNode);
+ }
+
+ @Implementation
+ protected static int nGetHeight(long renderNode) {
+ return RenderNodeNatives.nGetHeight(renderNode);
+ }
+
+ @Implementation
+ protected static boolean nSetAllowForceDark(long renderNode, boolean allowForceDark) {
+ return RenderNodeNatives.nSetAllowForceDark(renderNode, allowForceDark);
+ }
+
+ @Implementation
+ protected static boolean nGetAllowForceDark(long renderNode) {
+ return RenderNodeNatives.nGetAllowForceDark(renderNode);
+ }
+
+ @Implementation
+ protected static long nGetUniqueId(long renderNode) {
+ return RenderNodeNatives.nGetUniqueId(renderNode);
+ }
+
+ // In APIs Q+, RenderNodes are used to maintain DisplayLists instead of through DisplayListCanvas.
+ // In APIs O-P, this function would call the version of nFinishRecording that didn't use a
+ // RenderNode at all and instead returned a DisplayList that would need to be moved.
+ // To bridge the two implementations, the end(..) function here uses the API Q+ version so that
+ // the RenderNode is marked as valid when isValid() is called.
+ @Implementation
+ protected void end(Object canvas) {
+ long nativeRenderNode =
+ reflector(RenderNodeOpReflector.class, realRenderNode).getNativeRenderNode();
+ long nativeCanvasWrapper = reflector(CanvasReflector.class, canvas).getNativeCanvasWrapper();
+ ShadowNativeRecordingCanvas.nFinishRecording(nativeCanvasWrapper, nativeRenderNode);
+ reflector(DisplayListCanvasReflector.class, canvas).recycle();
+ }
+
+ @ForType(className = "android.view.RenderNode")
+ interface RenderNodeOpReflector {
+ @Accessor("mNativeRenderNode")
+ long getNativeRenderNode();
+ }
+
+ @ForType(className = "android.view.DisplayListCanvas")
+ interface DisplayListCanvasReflector {
+ void recycle();
+ }
+
+ @ForType(Canvas.class)
+ interface CanvasReflector {
+ long getNativeCanvasWrapper();
+ }
+
+ /** Shadow picker for {@link android.view.RenderNode}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowRenderNode.class, ShadowNativeRenderNodeOP.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRuntimeShader.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRuntimeShader.java
new file mode 100644
index 000000000..9793d1d68
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeRuntimeShader.java
@@ -0,0 +1,180 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.S_V2;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+
+import android.graphics.RuntimeShader;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.RuntimeShaderNatives;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowNativeRuntimeShader.Picker;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+/** Shadow for {@link RuntimeShader} that is backed by native code */
+@Implements(value = RuntimeShader.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeRuntimeShader {
+
+ @RealObject RuntimeShader runtimeShader;
+
+ private static final String RIPPLE_SHADER_UNIFORMS_31 =
+ "uniform vec2 in_origin;\n"
+ + "uniform vec2 in_touch;\n"
+ + "uniform float in_progress;\n"
+ + "uniform float in_maxRadius;\n"
+ + "uniform vec2 in_resolutionScale;\n"
+ + "uniform vec2 in_noiseScale;\n"
+ + "uniform float in_hasMask;\n"
+ + "uniform float in_noisePhase;\n"
+ + "uniform float in_turbulencePhase;\n"
+ + "uniform vec2 in_tCircle1;\n"
+ + "uniform vec2 in_tCircle2;\n"
+ + "uniform vec2 in_tCircle3;\n"
+ + "uniform vec2 in_tRotation1;\n"
+ + "uniform vec2 in_tRotation2;\n"
+ + "uniform vec2 in_tRotation3;\n"
+ + "uniform vec4 in_color;\n"
+ + "uniform vec4 in_sparkleColor;\n"
+ + "uniform shader in_shader;\n";
+ private static final String RIPPLE_SHADER_LIB_31 =
+ "float triangleNoise(vec2 n) {\n"
+ + " n = fract(n * vec2(5.3987, 5.4421));\n"
+ + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n"
+ + " float xy = n.x * n.y;\n"
+ + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n"
+ + "}"
+ + "const float PI = 3.1415926535897932384626;\n"
+ + "\n"
+ + "float threshold(float v, float l, float h) {\n"
+ + " return step(l, v) * (1.0 - step(h, v));\n"
+ + "}\n"
+ + "float sparkles(vec2 uv, float t) {\n"
+ + " float n = triangleNoise(uv);\n"
+ + " float s = 0.0;\n"
+ + " for (float i = 0; i < 4; i += 1) {\n"
+ + " float l = i * 0.1;\n"
+ + " float h = l + 0.05;\n"
+ + " float o = sin(PI * (t + 0.35 * i));\n"
+ + " s += threshold(n + o, l, h);\n"
+ + " }\n"
+ + " return saturate(s) * in_sparkleColor.a;\n"
+ + "}\n"
+ + "float softCircle(vec2 uv, vec2 xy, float radius, float blur) {\n"
+ + " float blurHalf = blur * 0.5;\n"
+ + " float d = distance(uv, xy);\n"
+ + " return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
+ + "}\n"
+ + "float softRing(vec2 uv, vec2 xy, float radius, float progress, float blur) {\n"
+ + " float thickness = 0.05 * radius;\n"
+ + " float currentRadius = radius * progress;\n"
+ + " float circle_outer = softCircle(uv, xy, currentRadius + thickness, blur);\n"
+ + " float circle_inner = softCircle(uv, xy, max(currentRadius - thickness, 0.), "
+ + " blur);\n"
+ + " return saturate(circle_outer - circle_inner);\n"
+ + "}\n"
+ + "float subProgress(float start, float end, float progress) {\n"
+ + " float sub = clamp(progress, start, end);\n"
+ + " return (sub - start) / (end - start); \n"
+ + "}\n"
+ + "mat2 rotate2d(vec2 rad){\n"
+ + " return mat2(rad.x, -rad.y, rad.y, rad.x);\n"
+ + "}\n"
+ + "float circle_grid(vec2 resolution, vec2 coord, float time, vec2 center,\n"
+ + " vec2 rotation, float cell_diameter) {\n"
+ + " coord = rotate2d(rotation) * (center - coord) + center;\n"
+ + " coord = mod(coord, cell_diameter) / resolution;\n"
+ + " float normal_radius = cell_diameter / resolution.y * 0.5;\n"
+ + " float radius = 0.65 * normal_radius;\n"
+ + " return softCircle(coord, vec2(normal_radius), radius, radius * 50.0);\n"
+ + "}\n"
+ + "float turbulence(vec2 uv, float t) {\n"
+ + " const vec2 scale = vec2(0.8);\n"
+ + " uv = uv * scale;\n"
+ + " float g1 = circle_grid(scale, uv, t, in_tCircle1, in_tRotation1, 0.17);\n"
+ + " float g2 = circle_grid(scale, uv, t, in_tCircle2, in_tRotation2, 0.2);\n"
+ + " float g3 = circle_grid(scale, uv, t, in_tCircle3, in_tRotation3, 0.275);\n"
+ + " float v = (g1 * g1 + g2 - g3) * 0.5;\n"
+ + " return saturate(0.45 + 0.8 * v);\n"
+ + "}\n";
+ private static final String RIPPLE_SHADER_MAIN_31 =
+ "vec4 main(vec2 p) {\n"
+ + " float fadeIn = subProgress(0., 0.13, in_progress);\n"
+ + " float scaleIn = subProgress(0., 1.0, in_progress);\n"
+ + " float fadeOutNoise = subProgress(0.4, 0.5, in_progress);\n"
+ + " float fadeOutRipple = subProgress(0.4, 1., in_progress);\n"
+ + " vec2 center = mix(in_touch, in_origin, saturate(in_progress * 2.0));\n"
+ + " float ring = softRing(p, center, in_maxRadius, scaleIn, 1.);\n"
+ + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
+ + " vec2 uv = p * in_resolutionScale;\n"
+ + " vec2 densityUv = uv - mod(uv, in_noiseScale);\n"
+ + " float turbulence = turbulence(uv, in_turbulencePhase);\n"
+ + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha "
+ + "* turbulence;\n"
+ + " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
+ + " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 1.) * fade "
+ + "* in_color.a;\n"
+ + " vec4 waveColor = vec4(in_color.rgb * waveAlpha, waveAlpha);\n"
+ + " vec4 sparkleColor = vec4(in_sparkleColor.rgb * in_sparkleColor.a, "
+ + "in_sparkleColor.a);\n"
+ + " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n"
+ + " return mix(waveColor, sparkleColor, sparkleAlpha) * mask;\n"
+ + "}";
+ private static final String RIPPLE_SHADER_31 =
+ RIPPLE_SHADER_UNIFORMS_31 + RIPPLE_SHADER_LIB_31 + RIPPLE_SHADER_MAIN_31;
+
+ @Implementation(minSdk = TIRAMISU)
+ protected void __constructor__(String sksl) {
+ // This is a workaround for supporting RippleShader from T+ with the native code from S.
+ // There were some new capabilities added to SKSL in T which are not available in S. Use the
+ // RippleShader SKSL from T in S.
+ // TODO(hoisie): Delete this shadow method when RNG is updated to use native libraries from T+.
+ try {
+ if (Class.forName("android.graphics.drawable.RippleShader").isInstance(runtimeShader)) {
+ sksl = RIPPLE_SHADER_31;
+ }
+ } catch (ClassNotFoundException e) {
+ throw new AssertionError(e);
+ }
+ Shadow.invokeConstructor(
+ RuntimeShader.class, runtimeShader, ClassParameter.from(String.class, sksl));
+ }
+
+ @Implementation(minSdk = R)
+ protected static long nativeGetFinalizer() {
+ return RuntimeShaderNatives.nativeGetFinalizer();
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateBuilder(String sksl) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return RuntimeShaderNatives.nativeCreateBuilder(sksl);
+ }
+
+ @Implementation(minSdk = S, maxSdk = S_V2)
+ protected static long nativeCreateShader(long shaderBuilder, long matrix, boolean isOpaque) {
+ return RuntimeShaderNatives.nativeCreateShader(shaderBuilder, matrix, isOpaque);
+ }
+
+ @Implementation(minSdk = S, maxSdk = S_V2)
+ protected static void nativeUpdateUniforms(
+ long shaderBuilder, String uniformName, float[] uniforms) {
+ RuntimeShaderNatives.nativeUpdateUniforms(shaderBuilder, uniformName, uniforms);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nativeUpdateShader(long shaderBuilder, String shaderName, long shader) {
+ RuntimeShaderNatives.nativeUpdateShader(shaderBuilder, shaderName, shader);
+ }
+
+ /** Shadow picker for {@link RuntimeShader}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeRuntimeShader.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeShader.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeShader.java
new file mode 100644
index 000000000..1a216f8ab
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeShader.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.Shader;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.ShaderNatives;
+import org.robolectric.shadows.ShadowNativeShader.Picker;
+
+/** Shadow for {@link Shader} that is backed by native code */
+@Implements(value = Shader.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeShader {
+
+ @Implementation(minSdk = O)
+ protected static long nativeGetFinalizer() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return ShaderNatives.nativeGetFinalizer();
+ }
+
+ /** Shadow picker for {@link Shader}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeShader.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeStaticLayout.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeStaticLayout.java
new file mode 100644
index 000000000..04504d30e
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeStaticLayout.java
@@ -0,0 +1,330 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.graphics.Paint;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.LineBreakerNatives;
+import org.robolectric.nativeruntime.MeasuredTextBuilderNatives;
+import org.robolectric.nativeruntime.MeasuredTextNatives;
+import org.robolectric.nativeruntime.NativeAllocationRegistryNatives;
+import org.robolectric.res.android.NativeObjRegistry;
+import org.robolectric.shadows.ShadowNativeStaticLayout.Picker;
+import org.robolectric.util.reflector.Accessor;
+import org.robolectric.util.reflector.ForType;
+
+/**
+ * Shadow for {@link StaticLayout} that is backed by native code for Android O-P. In Android Q, the
+ * native methods relate to text layout were heavily refactored and moved to MeasuredText and
+ * LineBreaker.
+ */
+@Implements(
+ value = StaticLayout.class,
+ minSdk = O,
+ maxSdk = P,
+ looseSignatures = true,
+ shadowPicker = Picker.class)
+public class ShadowNativeStaticLayout {
+
+ // Only used for the O/O_MR1 adapter logic.
+ static final NativeObjRegistry<NativeStaticLayoutSetup> nativeObjectRegistry =
+ new NativeObjRegistry<>(NativeStaticLayoutSetup.class);
+
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static long nInit(
+ int breakStrategy,
+ int hyphenationFrequency,
+ boolean isJustified,
+ int[] indents,
+ int[] leftPaddings,
+ int[] rightPaddings) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return LineBreakerNatives.nInit(breakStrategy, hyphenationFrequency, isJustified, indents);
+ }
+
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static void nFinish(long nativePtr) {
+ LineBreakerNatives.nFinishP(nativePtr);
+ }
+
+ /**
+ * This has to use looseSignatures due to {@code recycle} param with non-public type {@code
+ * android.text.StaticLayout$LineBreaks}.
+ */
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static int nComputeLineBreaks(
+ Object nativePtr,
+ Object text,
+ Object measuredTextPtr,
+ Object length,
+ Object firstWidth,
+ Object firstWidthLineCount,
+ Object restWidth,
+ Object variableTabStopsObject,
+ Object defaultTabStop,
+ Object indentsOffset,
+ Object recycle,
+ Object recycleLength,
+ Object recycleBreaks,
+ Object recycleWidths,
+ Object recycleAscents,
+ Object recycleDescents,
+ Object recycleFlags,
+ Object charWidths) {
+
+ return LineBreakerNatives.nComputeLineBreaksP(
+ (long) nativePtr,
+ (char[]) text,
+ (long) measuredTextPtr,
+ (int) length,
+ (float) firstWidth,
+ (int) firstWidthLineCount,
+ (float) restWidth,
+ intsToFloat((int[]) variableTabStopsObject),
+ ((Number) defaultTabStop).floatValue(),
+ (int) indentsOffset,
+ recycle,
+ (int) recycleLength,
+ (int[]) recycleBreaks,
+ (float[]) recycleWidths,
+ (float[]) recycleAscents,
+ (float[]) recycleDescents,
+ (int[]) recycleFlags,
+ (float[]) charWidths);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static long nNewBuilder() {
+ return nativeObjectRegistry.register(new NativeStaticLayoutSetup());
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nFreeBuilder(long nativePtr) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+
+ NativeAllocationRegistryNatives.applyFreeFunction(
+ LineBreakerNatives.nGetReleaseResultFunc(), setup.lineBreakerResultPtr);
+
+ nativeObjectRegistry.unregister(nativePtr);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nFinishBuilder(long nativePtr) {
+ // No-op
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static long nLoadHyphenator(ByteBuffer buf, int offset, int minPrefix, int minSuffix) {
+ // nLoadHyphenator is not supported
+ return 0;
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nSetLocale(long nativePtr, String locale, long nativeHyphenator) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ setup.localePaint.setTextLocale(Locale.forLanguageTag(locale));
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nSetIndents(long nativePtr, int[] indents) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ setup.indents = indents;
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static void nSetupParagraph(
+ long nativePtr,
+ char[] text,
+ int length,
+ float firstWidth,
+ int firstWidthLineCount,
+ float restWidth,
+ int[] variableTabStops,
+ int defaultTabStop,
+ int breakStrategy,
+ int hyphenationFrequency,
+ boolean isJustified) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ setup.text = text;
+ setup.length = length;
+ setup.firstWidth = firstWidth;
+ setup.firstWidthLineCount = firstWidthLineCount;
+ setup.restWidth = restWidth;
+ setup.variableTabStops = variableTabStops;
+ setup.defaultTabStop = defaultTabStop;
+ setup.breakStrategy = breakStrategy;
+ setup.hyphenationFrequency = hyphenationFrequency;
+ setup.isJustified = isJustified;
+ setup.measuredTextBuilderPtr = MeasuredTextBuilderNatives.nInitBuilder();
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static float nAddStyleRun(
+ long nativePtr, long nativePaint, long nativeTypeface, int start, int end, boolean isRtl) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+
+ MeasuredTextBuilderNatives.nAddStyleRun(
+ setup.measuredTextBuilderPtr, nativePaint, start, end, isRtl);
+ return 0f;
+ }
+
+ @Implementation
+ protected static void nAddMeasuredRun(long nativePtr, int start, int end, float[] widths) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ MeasuredTextBuilderNatives.nAddStyleRun(
+ setup.measuredTextBuilderPtr, setup.localePaint.getNativeInstance(), start, end, false);
+ }
+
+ @Implementation
+ protected static void nAddReplacementRun(long nativePtr, int start, int end, float width) {
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ MeasuredTextBuilderNatives.nAddReplacementRun(
+ setup.measuredTextBuilderPtr, setup.localePaint.getNativeInstance(), start, end, width);
+ }
+
+ @Implementation
+ protected static void nGetWidths(long nativePtr, float[] widths) {
+ // Returns the width of each char in the text.
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject(nativePtr);
+ setup.measuredTextPtr =
+ MeasuredTextBuilderNatives.nBuildMeasuredText(
+ setup.measuredTextBuilderPtr, 0, setup.text, false, false);
+ for (int i = 0; i < setup.text.length; i++) {
+ widths[i] = MeasuredTextNatives.nGetCharWidthAt(setup.measuredTextPtr, i);
+ }
+ MeasuredTextBuilderNatives.nFreeBuilder(setup.measuredTextBuilderPtr);
+ }
+
+ /**
+ * This has to use looseSignatures due to {@code recycle} param with non-public type {@code
+ * android.text.StaticLayout$LineBreaks}.
+ */
+ @Implementation
+ protected static int nComputeLineBreaks(
+ Object /*long*/ nativePtr,
+ Object /*LineBreaks*/ recycle,
+ Object /*int[]*/ recycleBreaksObject,
+ Object /*float[]*/ recycleWidthsObject,
+ Object /*int[]*/ recycleFlagsObject,
+ Object /*int*/ recycleLength) {
+
+ int[] recycleBreaks = (int[]) recycleBreaksObject;
+ float[] recycleWidths = (float[]) recycleWidthsObject;
+ int[] recycleFlags = (int[]) recycleFlagsObject;
+
+ NativeStaticLayoutSetup setup = nativeObjectRegistry.getNativeObject((long) nativePtr);
+
+ long lineBreakerBuilderPtr =
+ LineBreakerNatives.nInit(
+ setup.breakStrategy, setup.hyphenationFrequency, setup.isJustified, setup.indents);
+
+ setup.lineBreakerResultPtr =
+ LineBreakerNatives.nComputeLineBreaks(
+ lineBreakerBuilderPtr,
+ setup.text,
+ setup.measuredTextPtr,
+ setup.length,
+ setup.firstWidth,
+ setup.firstWidthLineCount,
+ setup.restWidth,
+ intsToFloat(setup.variableTabStops),
+ (float) setup.defaultTabStop,
+ 0);
+
+ int lineCount = LineBreakerNatives.nGetLineCount(setup.lineBreakerResultPtr);
+
+ if (lineCount > recycleBreaks.length) {
+ // resize the recycle objects
+ recycleBreaks = new int[lineCount];
+ recycleWidths = new float[lineCount];
+ recycleFlags = new int[lineCount];
+ reflector(LineBreaksReflector.class, recycle).setBreaks(recycleBreaks);
+ reflector(LineBreaksReflector.class, recycle).setWidths(recycleWidths);
+ reflector(LineBreaksReflector.class, recycle).setFlags(recycleFlags);
+ }
+
+ for (int i = 0; i < lineCount; i++) {
+ recycleBreaks[i] = LineBreakerNatives.nGetLineBreakOffset(setup.lineBreakerResultPtr, i);
+ recycleWidths[i] = LineBreakerNatives.nGetLineWidth(setup.lineBreakerResultPtr, i);
+ recycleFlags[i] = LineBreakerNatives.nGetLineFlag(setup.lineBreakerResultPtr, i);
+ }
+
+ // Release the pointers used for the builder, the result pointer is the only relevant pointer
+ // now.
+ NativeAllocationRegistryNatives.applyFreeFunction(
+ LineBreakerNatives.nGetReleaseFunc(), lineBreakerBuilderPtr);
+
+ NativeAllocationRegistryNatives.applyFreeFunction(
+ MeasuredTextNatives.nGetReleaseFunc(), setup.measuredTextPtr);
+
+ return lineCount;
+ }
+
+ static final class NativeStaticLayoutSetup {
+
+ char[] text;
+ int length;
+ float firstWidth;
+ int firstWidthLineCount;
+ float restWidth;
+ int[] variableTabStops;
+ int defaultTabStop;
+ int breakStrategy;
+ int hyphenationFrequency;
+ boolean isJustified;
+ int[] indents;
+ Paint localePaint = new TextPaint(); // TODO(hoisie): use `mPaint` from StaticLayout.Builder
+ long measuredTextBuilderPtr;
+ long measuredTextPtr;
+ long lineBreakerResultPtr;
+ }
+
+ private static float[] intsToFloat(int[] intArray) {
+ if (intArray == null) {
+ return null;
+ }
+ float[] floatArray = new float[intArray.length];
+
+ for (int i = 0; i < floatArray.length; i++) {
+ floatArray[i] = intArray[i];
+ }
+ return floatArray;
+ }
+
+ @ForType(className = "android.text.StaticLayout$LineBreaks")
+ interface LineBreaksReflector {
+ @Accessor("breaks")
+ int[] getBreaks();
+
+ @Accessor("breaks")
+ void setBreaks(int[] breaks);
+
+ @Accessor("widths")
+ float[] getWidths();
+
+ @Accessor("widths")
+ void setWidths(float[] widths);
+
+ @Accessor("flags")
+ int[] getFlags();
+
+ @Accessor("flags")
+ void setFlags(int[] flags);
+ }
+
+ /** Shadow picker for {@link StaticLayout}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowStaticLayout.class, ShadowNativeStaticLayout.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSumPathEffect.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSumPathEffect.java
new file mode 100644
index 000000000..2d436ae23
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSumPathEffect.java
@@ -0,0 +1,28 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.SumPathEffect;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.SumPathEffectNatives;
+import org.robolectric.shadows.ShadowNativeSumPathEffect.Picker;
+
+/** Shadow for {@link SumPathEffect} that is backed by native code */
+@Implements(value = SumPathEffect.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeSumPathEffect {
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreate(long first, long second) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SumPathEffectNatives.nativeCreate(first, second);
+ }
+
+ /** Shadow picker for {@link SumPathEffect}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeSumPathEffect.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSurface.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSurface.java
new file mode 100644
index 000000000..fe789981a
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSurface.java
@@ -0,0 +1,147 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.SurfaceNatives;
+import org.robolectric.shadows.ShadowNativeSurface.Picker;
+
+/** Shadow for {@link Surface} that is backed by native code */
+@Implements(value = Surface.class, minSdk = O, shadowPicker = Picker.class, isInAndroidSdk = false)
+public class ShadowNativeSurface {
+ @Implementation
+ protected static long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
+ throws OutOfResourcesException {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SurfaceNatives.nativeCreateFromSurfaceTexture(surfaceTexture);
+ }
+
+ @Implementation
+ protected static long nativeCreateFromSurfaceControl(long surfaceControlNativeObject) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SurfaceNatives.nativeCreateFromSurfaceControl(surfaceControlNativeObject);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static long nativeGetFromSurfaceControl(
+ long surfaceObject, long surfaceControlNativeObject) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SurfaceNatives.nativeGetFromSurfaceControl(surfaceObject, surfaceControlNativeObject);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeGetFromBlastBufferQueue(
+ long surfaceObject, long blastBufferQueueNativeObject) {
+ return SurfaceNatives.nativeGetFromBlastBufferQueue(
+ surfaceObject, blastBufferQueueNativeObject);
+ }
+
+ @Implementation
+ protected static long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
+ throws OutOfResourcesException {
+ return SurfaceNatives.nativeLockCanvas(nativeObject, canvas, dirty);
+ }
+
+ @Implementation
+ protected static void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas) {
+ SurfaceNatives.nativeUnlockCanvasAndPost(nativeObject, canvas);
+ }
+
+ @Implementation
+ protected static void nativeRelease(long nativeObject) {
+ SurfaceNatives.nativeRelease(nativeObject);
+ }
+
+ @Implementation
+ protected static boolean nativeIsValid(long nativeObject) {
+ return SurfaceNatives.nativeIsValid(nativeObject);
+ }
+
+ @Implementation
+ protected static boolean nativeIsConsumerRunningBehind(long nativeObject) {
+ return SurfaceNatives.nativeIsConsumerRunningBehind(nativeObject);
+ }
+
+ @Implementation
+ protected static long nativeReadFromParcel(long nativeObject, Parcel source) {
+ return SurfaceNatives.nativeReadFromParcel(nativeObject, source);
+ }
+
+ @Implementation
+ protected static void nativeWriteToParcel(long nativeObject, Parcel dest) {
+ SurfaceNatives.nativeWriteToParcel(nativeObject, dest);
+ }
+
+ @Implementation
+ protected static void nativeAllocateBuffers(long nativeObject) {
+ SurfaceNatives.nativeAllocateBuffers(nativeObject);
+ }
+
+ @Implementation
+ protected static int nativeGetWidth(long nativeObject) {
+ return SurfaceNatives.nativeGetWidth(nativeObject);
+ }
+
+ @Implementation
+ protected static int nativeGetHeight(long nativeObject) {
+ return SurfaceNatives.nativeGetHeight(nativeObject);
+ }
+
+ @Implementation
+ protected static long nativeGetNextFrameNumber(long nativeObject) {
+ return SurfaceNatives.nativeGetNextFrameNumber(nativeObject);
+ }
+
+ @Implementation
+ protected static int nativeSetScalingMode(long nativeObject, int scalingMode) {
+ return SurfaceNatives.nativeSetScalingMode(nativeObject, scalingMode);
+ }
+
+ @Implementation
+ protected static int nativeForceScopedDisconnect(long nativeObject) {
+ return SurfaceNatives.nativeForceScopedDisconnect(nativeObject);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nativeAttachAndQueueBufferWithColorSpace(
+ long nativeObject, HardwareBuffer buffer, int colorSpaceId) {
+ return SurfaceNatives.nativeAttachAndQueueBufferWithColorSpace(
+ nativeObject, buffer, colorSpaceId);
+ }
+
+ @Implementation(minSdk = O_MR1)
+ protected static int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled) {
+ return SurfaceNatives.nativeSetSharedBufferModeEnabled(nativeObject, enabled);
+ }
+
+ @Implementation(minSdk = O_MR1)
+ protected static int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled) {
+ return SurfaceNatives.nativeSetAutoRefreshEnabled(nativeObject, enabled);
+ }
+
+ @Implementation(minSdk = S)
+ protected static int nativeSetFrameRate(
+ long nativeObject, float frameRate, int compatibility, int changeFrameRateStrategy) {
+ return SurfaceNatives.nativeSetFrameRate(
+ nativeObject, frameRate, compatibility, changeFrameRateStrategy);
+ }
+
+ /** Shadow picker for {@link Surface}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowSurface.class, ShadowNativeSurface.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSweepGradient.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSweepGradient.java
new file mode 100644
index 000000000..d51300f1c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSweepGradient.java
@@ -0,0 +1,44 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.SweepGradient;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.SweepGradientNatives;
+import org.robolectric.shadows.ShadowNativeSweepGradient.Picker;
+
+/** Shadow for {@link SweepGradient} that is backed by native code */
+@Implements(value = SweepGradient.class, minSdk = O, shadowPicker = Picker.class)
+public class ShadowNativeSweepGradient {
+
+ @Implementation(minSdk = Q)
+ protected static long nativeCreate(
+ long matrix, float x, float y, long[] colors, float[] positions, long colorSpaceHandle) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SweepGradientNatives.nativeCreate(matrix, x, y, colors, positions, colorSpaceHandle);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nativeCreate1(
+ long matrix, float x, float y, int[] colors, float[] positions) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SweepGradientNatives.nativeCreate1(matrix, x, y, colors, positions);
+ }
+
+ @Implementation(minSdk = O, maxSdk = P)
+ protected static long nativeCreate2(long matrix, float x, float y, int color0, int color1) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return SweepGradientNatives.nativeCreate2(matrix, x, y, color0, color1);
+ }
+
+ /** Shadow picker for {@link SweepGradient}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeSweepGradient.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSystemFonts.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSystemFonts.java
new file mode 100644
index 000000000..f154df3f0
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeSystemFonts.java
@@ -0,0 +1,125 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.fonts.Font;
+import android.graphics.fonts.FontCustomizationParser;
+import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.SystemFonts;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.text.FontConfig;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Map;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowNativeSystemFonts.Picker;
+import org.robolectric.util.reflector.Direct;
+import org.robolectric.util.reflector.ForType;
+import org.robolectric.util.reflector.Static;
+
+/**
+ * Shadow for {@link SystemFonts} for the Robolectric native runtime. It supports getting system
+ * font config using a custom fonts path.
+ */
+@Implements(
+ value = SystemFonts.class,
+ minSdk = Build.VERSION_CODES.Q,
+ isInAndroidSdk = false,
+ shadowPicker = Picker.class)
+public class ShadowNativeSystemFonts {
+ @Implementation(minSdk = S)
+ protected static FontConfig getSystemFontConfigInternal(
+ String fontsXml,
+ String systemFontDir,
+ String oemXml,
+ String productFontDir,
+ Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion) {
+ String fontDir = System.getProperty("robolectric.nativeruntime.fontdir");
+ Preconditions.checkNotNull(fontDir);
+ Preconditions.checkState(new File(fontDir).isDirectory(), "Missing fonts directory");
+ Preconditions.checkState(fontDir.endsWith("/"), "Fonts directory must end with a slash");
+ return reflector(SystemFontsReflector.class)
+ .getSystemFontConfigInternal(
+ fontDir + "fonts.xml",
+ fontDir,
+ null,
+ null,
+ updatableFontMap,
+ lastModifiedDate,
+ configVersion);
+ }
+
+ @Implementation(maxSdk = VERSION_CODES.R)
+ public static FontConfig.Alias[] buildSystemFallback(
+ String xmlPath,
+ String systemFontDir,
+ FontCustomizationParser.Result oemCustomization,
+ ArrayMap<String, FontFamily[]> fallbackMap,
+ ArrayList<Font> availableFonts) {
+ String fontDir = System.getProperty("robolectric.nativeruntime.fontdir");
+ Preconditions.checkNotNull(fontDir);
+ Preconditions.checkState(new File(fontDir).isDirectory(), "Missing fonts directory");
+ Preconditions.checkState(fontDir.endsWith("/"), "Fonts directory must end with a slash");
+ return reflector(SystemFontsReflector.class)
+ .buildSystemFallback(
+ fontDir + "fonts.xml", fontDir, oemCustomization, fallbackMap, availableFonts);
+ }
+
+ @Implementation(minSdk = Q, maxSdk = Q)
+ @Nullable
+ protected static ByteBuffer mmap(@NonNull String fullPath) {
+ try (FileInputStream file = new FileInputStream(fullPath)) {
+ final FileChannel fileChannel = file.getChannel();
+ final long fontSize = fileChannel.size();
+ return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+ } catch (IOException e) {
+ Log.w("SystemFonts", e.getMessage());
+ return null;
+ }
+ }
+
+ @ForType(SystemFonts.class)
+ interface SystemFontsReflector {
+ @Static
+ @Direct
+ FontConfig getSystemFontConfigInternal(
+ String fontsXml,
+ String systemFontDir,
+ String oemXml,
+ String productFontDir,
+ Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion);
+
+ @Static
+ @Direct
+ FontConfig.Alias[] buildSystemFallback(
+ String xmlPath,
+ String fontDir,
+ FontCustomizationParser.Result oemCustomization,
+ ArrayMap<String, FontFamily[]> fallbackMap,
+ ArrayList<Font> availableFonts);
+ }
+
+ /** Shadow picker for {@link SystemFonts}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeSystemFonts.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTableMaskFilter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTableMaskFilter.java
new file mode 100644
index 000000000..7d7c0a34c
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTableMaskFilter.java
@@ -0,0 +1,44 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import android.graphics.TableMaskFilter;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.TableMaskFilterNatives;
+import org.robolectric.shadows.ShadowNativeTableMaskFilter.Picker;
+
+/** Shadow for {@link TableMaskFilter} that is backed by native code */
+@Implements(
+ value = TableMaskFilter.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeTableMaskFilter {
+
+ @Implementation(minSdk = O)
+ protected static long nativeNewTable(byte[] table) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return TableMaskFilterNatives.nativeNewTable(table);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nativeNewClip(int min, int max) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return TableMaskFilterNatives.nativeNewClip(min, max);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nativeNewGamma(float gamma) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return TableMaskFilterNatives.nativeNewGamma(gamma);
+ }
+
+ /** Shadow picker for {@link TableMaskFilter}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(null, ShadowNativeTableMaskFilter.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeThreadedRenderer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeThreadedRenderer.java
new file mode 100644
index 000000000..e9ea4645e
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeThreadedRenderer.java
@@ -0,0 +1,183 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.P;
+
+import android.graphics.Bitmap;
+import android.view.ThreadedRenderer;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.HardwareRendererNatives;
+import org.robolectric.shadows.ShadowNativeThreadedRenderer.Picker;
+
+/** Shadow for {@link ThreadedRenderer} that is backed by native code */
+@Implements(value = ThreadedRenderer.class, minSdk = O, maxSdk = P, shadowPicker = Picker.class)
+public class ShadowNativeThreadedRenderer {
+
+ // ThreadedRenderer specific functions. These do not exist in HardwareRenderer
+ @Implementation
+ protected static boolean nSupportsOpenGL() {
+ return false;
+ }
+
+ // HardwareRenderer methods. These exist in both ThreadedRenderer and HardwareRenderer.
+ @Implementation
+ protected static void nRotateProcessStatsBuffer() {
+ HardwareRendererNatives.nRotateProcessStatsBuffer();
+ }
+
+ @Implementation
+ protected static void nSetProcessStatsBuffer(int fd) {
+ HardwareRendererNatives.nSetProcessStatsBuffer(fd);
+ }
+
+ @Implementation
+ protected static int nGetRenderThreadTid(long nativeProxy) {
+ return HardwareRendererNatives.nGetRenderThreadTid(nativeProxy);
+ }
+
+ @Implementation
+ protected static long nCreateRootRenderNode() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return HardwareRendererNatives.nCreateRootRenderNode();
+ }
+
+ @Implementation
+ protected static long nCreateProxy(boolean translucent, long rootRenderNode) {
+ return HardwareRendererNatives.nCreateProxy(translucent, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nDeleteProxy(long nativeProxy) {
+ HardwareRendererNatives.nDeleteProxy(nativeProxy);
+ }
+
+ @Implementation
+ protected static boolean nLoadSystemProperties(long nativeProxy) {
+ return HardwareRendererNatives.nLoadSystemProperties(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nSetName(long nativeProxy, String name) {
+ HardwareRendererNatives.nSetName(nativeProxy, name);
+ }
+
+ @Implementation
+ protected static void nSetStopped(long nativeProxy, boolean stopped) {
+ HardwareRendererNatives.nSetStopped(nativeProxy, stopped);
+ }
+
+ @Implementation
+ protected static void nSetOpaque(long nativeProxy, boolean opaque) {
+ HardwareRendererNatives.nSetOpaque(nativeProxy, opaque);
+ }
+
+ @Implementation
+ protected static int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size) {
+ return HardwareRendererNatives.nSyncAndDrawFrame(nativeProxy, frameInfo, size);
+ }
+
+ @Implementation
+ protected static void nDestroy(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nDestroy(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode) {
+ HardwareRendererNatives.nRegisterAnimatingRenderNode(rootRenderNode, animatingNode);
+ }
+
+ @Implementation
+ protected static void nRegisterVectorDrawableAnimator(long rootRenderNode, long animator) {
+ HardwareRendererNatives.nRegisterVectorDrawableAnimator(rootRenderNode, animator);
+ }
+
+ @Implementation
+ protected static long nCreateTextureLayer(long nativeProxy) {
+ return HardwareRendererNatives.nCreateTextureLayer(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nBuildLayer(long nativeProxy, long node) {
+ HardwareRendererNatives.nBuildLayer(nativeProxy, node);
+ }
+
+ @Implementation
+ protected static void nPushLayerUpdate(long nativeProxy, long layer) {
+ HardwareRendererNatives.nPushLayerUpdate(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nCancelLayerUpdate(long nativeProxy, long layer) {
+ HardwareRendererNatives.nCancelLayerUpdate(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nDetachSurfaceTexture(long nativeProxy, long layer) {
+ HardwareRendererNatives.nDetachSurfaceTexture(nativeProxy, layer);
+ }
+
+ @Implementation
+ protected static void nDestroyHardwareResources(long nativeProxy) {
+ HardwareRendererNatives.nDestroyHardwareResources(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nTrimMemory(int level) {
+ HardwareRendererNatives.nTrimMemory(level);
+ }
+
+ @Implementation
+ protected static void nOverrideProperty(String name, String value) {
+ HardwareRendererNatives.nOverrideProperty(name, value);
+ }
+
+ @Implementation
+ protected static void nFence(long nativeProxy) {
+ HardwareRendererNatives.nFence(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nStopDrawing(long nativeProxy) {
+ HardwareRendererNatives.nStopDrawing(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nNotifyFramePending(long nativeProxy) {
+ HardwareRendererNatives.nNotifyFramePending(nativeProxy);
+ }
+
+ @Implementation
+ protected static void nAddRenderNode(long nativeProxy, long rootRenderNode, boolean placeFront) {
+ HardwareRendererNatives.nAddRenderNode(nativeProxy, rootRenderNode, placeFront);
+ }
+
+ @Implementation
+ protected static void nRemoveRenderNode(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nRemoveRenderNode(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nDrawRenderNode(long nativeProxy, long rootRenderNode) {
+ HardwareRendererNatives.nDrawRenderNode(nativeProxy, rootRenderNode);
+ }
+
+ @Implementation
+ protected static void nSetContentDrawBounds(
+ long nativeProxy, int left, int top, int right, int bottom) {
+ HardwareRendererNatives.nSetContentDrawBounds(nativeProxy, left, top, right, bottom);
+ }
+
+ @Implementation
+ protected static Bitmap nCreateHardwareBitmap(long renderNode, int width, int height) {
+ return HardwareRendererNatives.nCreateHardwareBitmap(renderNode, width, height);
+ }
+
+ /** Shadow picker for {@link ThreadedRenderer}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowThreadedRenderer.class, ShadowNativeThreadedRenderer.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTypeface.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTypeface.java
new file mode 100644
index 000000000..0c7fcf630
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeTypeface.java
@@ -0,0 +1,291 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.O_MR1;
+import static android.os.Build.VERSION_CODES.P;
+import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import static android.os.Build.VERSION_CODES.TIRAMISU;
+import static org.robolectric.util.reflector.Reflector.reflector;
+
+import android.graphics.FontFamily;
+import android.graphics.Typeface;
+import android.graphics.fonts.FontVariationAxis;
+import android.text.FontConfig;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.google.common.base.Preconditions;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.List;
+import java.util.Map;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.TypefaceNatives;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.util.reflector.Direct;
+import org.robolectric.util.reflector.ForType;
+import org.robolectric.util.reflector.Static;
+
+/** Shadow for {@link Typeface} that is backed by native code */
+@Implements(value = Typeface.class, looseSignatures = true, minSdk = O, isInAndroidSdk = false)
+public class ShadowNativeTypeface extends ShadowTypeface {
+
+ private static final String TAG = "ShadowNativeTypeface";
+
+ // Style value for building typeface.
+ private static final int STYLE_NORMAL = 0;
+ private static final int STYLE_ITALIC = 1;
+
+ @Implementation(minSdk = S)
+ protected static void __staticInitializer__() {
+ Shadow.directInitialize(Typeface.class);
+ // Initialize the system font map. In real Android this is done as part of Application startup
+ // and uses a more complex SharedMemory system not supported in Robolectric.
+ Typeface.loadPreinstalledSystemFontMap();
+ }
+
+ @Implementation(minSdk = P, maxSdk = P)
+ protected static void buildSystemFallback(
+ String xmlPath,
+ String systemFontDir,
+ ArrayMap<String, Typeface> fontMap,
+ ArrayMap<String, FontFamily[]> fallbackMap) {
+ String fontDir = System.getProperty("robolectric.nativeruntime.fontdir");
+ Preconditions.checkNotNull(fontDir);
+ Preconditions.checkState(new File(fontDir).isDirectory(), "Missing fonts directory");
+ Preconditions.checkState(fontDir.endsWith("/"), "Fonts directory must end with a slash");
+ reflector(TypefaceReflector.class)
+ .buildSystemFallback(fontDir + "fonts.xml", fontDir, fontMap, fallbackMap);
+ }
+
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static File getSystemFontConfigLocation() {
+ // Ensure that the Robolectric native runtime is loaded in ordere to ensure that the
+ // `robolectric.nativeruntime.fontdir` system property is valid.
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ String fontDir = System.getProperty("robolectric.nativeruntime.fontdir");
+ Preconditions.checkNotNull(fontDir);
+ Preconditions.checkState(new File(fontDir).isDirectory(), "Missing fonts directory");
+ Preconditions.checkState(fontDir.endsWith("/"), "Fonts directory must end with a slash");
+ return new File(fontDir);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Implementation(minSdk = O, maxSdk = O_MR1)
+ protected static Object makeFamilyFromParsed(Object family, Object bufferForPathMap) {
+ FontConfigFamilyReflector reflector = reflector(FontConfigFamilyReflector.class, family);
+ Map<String, ByteBuffer> bufferForPath = (Map<String, ByteBuffer>) bufferForPathMap;
+
+ FontFamily fontFamily =
+ Shadow.newInstance(
+ FontFamily.class,
+ new Class<?>[] {String.class, int.class},
+ new Object[] {reflector.getLanguage(), reflector.getVariant()});
+ for (FontConfig.Font font : reflector.getFonts()) {
+ String fullPathName =
+ System.getProperty("robolectric.nativeruntime.fontdir")
+ + reflector(FontConfigFontReflector.class, font).getFontName();
+ ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
+ if (fontBuffer == null) {
+ try (FileInputStream file = new FileInputStream(fullPathName)) {
+ FileChannel fileChannel = file.getChannel();
+ long fontSize = fileChannel.size();
+ fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+ bufferForPath.put(fullPathName, fontBuffer);
+ } catch (IOException e) {
+ Log.w(TAG, "Error mapping font file " + fullPathName);
+ continue;
+ }
+ }
+ if (!fontFamily.addFontFromBuffer(
+ fontBuffer,
+ font.getTtcIndex(),
+ font.getAxes(),
+ font.getWeight(),
+ font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
+ Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
+ }
+ }
+ if (!fontFamily.freeze()) {
+ // Treat as system error since reaching here means that a system pre-installed font
+ // can't be used by our font stack.
+ Log.w(TAG, "Unable to load Family: " + reflector.getName() + ":" + reflector.getLanguage());
+ return null;
+ }
+ return fontFamily;
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static long nativeCreateFromTypeface(long nativeInstance, int style) {
+ return TypefaceNatives.nativeCreateFromTypeface(nativeInstance, style);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreateFromTypefaceWithExactStyle(
+ long nativeInstance, int weight, boolean italic) {
+ return TypefaceNatives.nativeCreateFromTypefaceWithExactStyle(nativeInstance, weight, italic);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nativeCreateFromTypefaceWithVariation(
+ long nativeInstance, List<FontVariationAxis> axes) {
+ return TypefaceNatives.nativeCreateFromTypefaceWithVariation(nativeInstance, axes);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static long nativeCreateWeightAlias(long nativeInstance, int weight) {
+ return TypefaceNatives.nativeCreateWeightAlias(nativeInstance, weight);
+ }
+
+ @Implementation(minSdk = O, maxSdk = R)
+ protected static long nativeCreateFromArray(long[] familyArray, int weight, int italic) {
+ return TypefaceNatives.nativeCreateFromArray(familyArray, 0, weight, italic);
+ }
+
+ @Implementation(minSdk = S)
+ protected static long nativeCreateFromArray(
+ long[] familyArray, long fallbackTypeface, int weight, int italic) {
+ return TypefaceNatives.nativeCreateFromArray(familyArray, fallbackTypeface, weight, italic);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int[] nativeGetSupportedAxes(long nativeInstance) {
+ return TypefaceNatives.nativeGetSupportedAxes(nativeInstance);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static void nativeSetDefault(long nativePtr) {
+ TypefaceNatives.nativeSetDefault(nativePtr);
+ }
+
+ @Implementation(minSdk = LOLLIPOP)
+ protected static int nativeGetStyle(long nativePtr) {
+ return TypefaceNatives.nativeGetStyle(nativePtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nativeGetWeight(long nativePtr) {
+ return TypefaceNatives.nativeGetWeight(nativePtr);
+ }
+
+ @Implementation(minSdk = P)
+ protected static long nativeGetReleaseFunc() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return TypefaceNatives.nativeGetReleaseFunc();
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static int nativeGetFamilySize(long nativePtr) {
+ return TypefaceNatives.nativeGetFamilySize(nativePtr);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static long nativeGetFamily(long nativePtr, int index) {
+ return TypefaceNatives.nativeGetFamily(nativePtr, index);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nativeRegisterGenericFamily(String str, long nativePtr) {
+ TypefaceNatives.nativeRegisterGenericFamily(str, nativePtr);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static int nativeWriteTypefaces(ByteBuffer buffer, long[] nativePtrs) {
+ return TypefaceNatives.nativeWriteTypefaces(buffer, nativePtrs);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static int nativeWriteTypefaces(ByteBuffer buffer, int position, long[] nativePtrs) {
+ return nativeWriteTypefaces(buffer, nativePtrs);
+ }
+
+ @Implementation(minSdk = S, maxSdk = TIRAMISU)
+ protected static long[] nativeReadTypefaces(ByteBuffer buffer) {
+ return TypefaceNatives.nativeReadTypefaces(buffer);
+ }
+
+ @Implementation(minSdk = 10000)
+ protected static long[] nativeReadTypefaces(ByteBuffer buffer, int position) {
+ return nativeReadTypefaces(buffer);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nativeForceSetStaticFinalField(String fieldName, Typeface typeface) {
+ TypefaceNatives.nativeForceSetStaticFinalField(fieldName, typeface);
+ }
+
+ @Implementation(minSdk = S)
+ protected static void nativeAddFontCollections(long nativePtr) {
+ TypefaceNatives.nativeAddFontCollections(nativePtr);
+ }
+
+ static void ensureInitialized() {
+ try {
+ // Forces static initialization. This should be called before any native code that calls
+ // Typeface::resolveDefault.
+ Class.forName("android.graphics.Typeface");
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError("Unable to load Typeface", e);
+ }
+ }
+
+ @Override
+ public FontDesc getFontDescription() {
+ throw new UnsupportedOperationException(
+ "Legacy ShadowTypeface description APIs are not supported");
+ }
+
+ /**
+ * Shadow for {@link Typeface.Builder}. It is empty to avoid using the legacy {@link
+ * Typeface.Builder} shadow.
+ */
+ @Implements(
+ value = Typeface.Builder.class,
+ minSdk = P,
+ shadowPicker = ShadowNativeTypefaceBuilder.Picker.class,
+ isInAndroidSdk = false)
+ public static class ShadowNativeTypefaceBuilder {
+ /** Shadow picker for {@link Typeface.Builder}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyTypeface.ShadowBuilder.class, ShadowNativeTypefaceBuilder.class);
+ }
+ }
+ }
+
+ @ForType(Typeface.class)
+ interface TypefaceReflector {
+ @CanIgnoreReturnValue
+ @Static
+ @Direct
+ FontConfig.Alias[] buildSystemFallback(
+ String xmlPath,
+ String fontDir,
+ ArrayMap<String, Typeface> fontMap,
+ ArrayMap<String, FontFamily[]> fallbackMap);
+ }
+
+ @ForType(className = "android.text.FontConfig$Family")
+ interface FontConfigFamilyReflector {
+ String getLanguage();
+
+ int getVariant();
+
+ FontConfig.Font[] getFonts();
+
+ String getName();
+ }
+
+ @ForType(className = "android.text.FontConfig$Font")
+ interface FontConfigFontReflector {
+ String getFontName();
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVectorDrawable.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVectorDrawable.java
new file mode 100644
index 000000000..88a4a76d5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVectorDrawable.java
@@ -0,0 +1,343 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+import static android.os.Build.VERSION_CODES.Q;
+
+import android.graphics.Rect;
+import android.graphics.drawable.VectorDrawable;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader;
+import org.robolectric.nativeruntime.VectorDrawableNatives;
+import org.robolectric.shadows.ShadowNativeVectorDrawable.Picker;
+
+/** Shadow for {@link VectorDrawable} that is backed by native code */
+@Implements(
+ value = VectorDrawable.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeVectorDrawable extends ShadowDrawable {
+
+ @Implementation(minSdk = O)
+ protected static int nDraw(
+ long rendererPtr,
+ long canvasWrapperPtr,
+ long colorFilterPtr,
+ Rect bounds,
+ boolean needsMirroring,
+ boolean canReuseCache) {
+ return VectorDrawableNatives.nDraw(
+ rendererPtr, canvasWrapperPtr, colorFilterPtr, bounds, needsMirroring, canReuseCache);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nGetFullPathProperties(long pathPtr, byte[] properties, int length) {
+ return VectorDrawableNatives.nGetFullPathProperties(pathPtr, properties, length);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetName(long nodePtr, String name) {
+ VectorDrawableNatives.nSetName(nodePtr, name);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nGetGroupProperties(long groupPtr, float[] properties, int length) {
+ return VectorDrawableNatives.nGetGroupProperties(groupPtr, properties, length);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetPathString(long pathPtr, String pathString, int length) {
+ VectorDrawableNatives.nSetPathString(pathPtr, pathString, length);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateTree(long rootGroupPtr) {
+ return VectorDrawableNatives.nCreateTree(rootGroupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateTreeFromCopy(long treeToCopy, long rootGroupPtr) {
+ return VectorDrawableNatives.nCreateTreeFromCopy(treeToCopy, rootGroupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetRendererViewportSize(
+ long rendererPtr, float viewportWidth, float viewportHeight) {
+ VectorDrawableNatives.nSetRendererViewportSize(rendererPtr, viewportWidth, viewportHeight);
+ }
+
+ @Implementation(minSdk = O)
+ protected static boolean nSetRootAlpha(long rendererPtr, float alpha) {
+ return VectorDrawableNatives.nSetRootAlpha(rendererPtr, alpha);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetRootAlpha(long rendererPtr) {
+ return VectorDrawableNatives.nGetRootAlpha(rendererPtr);
+ }
+
+ @Implementation(minSdk = Q)
+ protected static void nSetAntiAlias(long rendererPtr, boolean aa) {
+ VectorDrawableNatives.nSetAntiAlias(rendererPtr, aa);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetAllowCaching(long rendererPtr, boolean allowCaching) {
+ VectorDrawableNatives.nSetAllowCaching(rendererPtr, allowCaching);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateFullPath() {
+ return VectorDrawableNatives.nCreateFullPath();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateFullPath(long nativeFullPathPtr) {
+ return VectorDrawableNatives.nCreateFullPath(nativeFullPathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nUpdateFullPathProperties(
+ long pathPtr,
+ float strokeWidth,
+ int strokeColor,
+ float strokeAlpha,
+ int fillColor,
+ float fillAlpha,
+ float trimPathStart,
+ float trimPathEnd,
+ float trimPathOffset,
+ float strokeMiterLimit,
+ int strokeLineCap,
+ int strokeLineJoin,
+ int fillType) {
+ VectorDrawableNatives.nUpdateFullPathProperties(
+ pathPtr,
+ strokeWidth,
+ strokeColor,
+ strokeAlpha,
+ fillColor,
+ fillAlpha,
+ trimPathStart,
+ trimPathEnd,
+ trimPathOffset,
+ strokeMiterLimit,
+ strokeLineCap,
+ strokeLineJoin,
+ fillType);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
+ VectorDrawableNatives.nUpdateFullPathFillGradient(pathPtr, fillGradientPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
+ VectorDrawableNatives.nUpdateFullPathStrokeGradient(pathPtr, strokeGradientPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateClipPath() {
+ return VectorDrawableNatives.nCreateClipPath();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateClipPath(long clipPathPtr) {
+ return VectorDrawableNatives.nCreateClipPath(clipPathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateGroup() {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return VectorDrawableNatives.nCreateGroup();
+ }
+
+ @Implementation(minSdk = O)
+ protected static long nCreateGroup(long groupPtr) {
+ DefaultNativeRuntimeLoader.injectAndLoad();
+ return VectorDrawableNatives.nCreateGroup(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nUpdateGroupProperties(
+ long groupPtr,
+ float rotate,
+ float pivotX,
+ float pivotY,
+ float scaleX,
+ float scaleY,
+ float translateX,
+ float translateY) {
+ VectorDrawableNatives.nUpdateGroupProperties(
+ groupPtr, rotate, pivotX, pivotY, scaleX, scaleY, translateX, translateY);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nAddChild(long groupPtr, long nodePtr) {
+ VectorDrawableNatives.nAddChild(groupPtr, nodePtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetRotation(long groupPtr) {
+ return VectorDrawableNatives.nGetRotation(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetRotation(long groupPtr, float rotation) {
+ VectorDrawableNatives.nSetRotation(groupPtr, rotation);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetPivotX(long groupPtr) {
+ return VectorDrawableNatives.nGetPivotX(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetPivotX(long groupPtr, float pivotX) {
+ VectorDrawableNatives.nSetPivotX(groupPtr, pivotX);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetPivotY(long groupPtr) {
+ return VectorDrawableNatives.nGetPivotY(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetPivotY(long groupPtr, float pivotY) {
+ VectorDrawableNatives.nSetPivotY(groupPtr, pivotY);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetScaleX(long groupPtr) {
+ return VectorDrawableNatives.nGetScaleX(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetScaleX(long groupPtr, float scaleX) {
+ VectorDrawableNatives.nSetScaleX(groupPtr, scaleX);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetScaleY(long groupPtr) {
+ return VectorDrawableNatives.nGetScaleY(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetScaleY(long groupPtr, float scaleY) {
+ VectorDrawableNatives.nSetScaleY(groupPtr, scaleY);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTranslateX(long groupPtr) {
+ return VectorDrawableNatives.nGetTranslateX(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTranslateX(long groupPtr, float translateX) {
+ VectorDrawableNatives.nSetTranslateX(groupPtr, translateX);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTranslateY(long groupPtr) {
+ return VectorDrawableNatives.nGetTranslateY(groupPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTranslateY(long groupPtr, float translateY) {
+ VectorDrawableNatives.nSetTranslateY(groupPtr, translateY);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetPathData(long pathPtr, long pathDataPtr) {
+ VectorDrawableNatives.nSetPathData(pathPtr, pathDataPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetStrokeWidth(long pathPtr) {
+ return VectorDrawableNatives.nGetStrokeWidth(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeWidth(long pathPtr, float width) {
+ VectorDrawableNatives.nSetStrokeWidth(pathPtr, width);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetStrokeColor(long pathPtr) {
+ return VectorDrawableNatives.nGetStrokeColor(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeColor(long pathPtr, int strokeColor) {
+ VectorDrawableNatives.nSetStrokeColor(pathPtr, strokeColor);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetStrokeAlpha(long pathPtr) {
+ return VectorDrawableNatives.nGetStrokeAlpha(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetStrokeAlpha(long pathPtr, float alpha) {
+ VectorDrawableNatives.nSetStrokeAlpha(pathPtr, alpha);
+ }
+
+ @Implementation(minSdk = O)
+ protected static int nGetFillColor(long pathPtr) {
+ return VectorDrawableNatives.nGetFillColor(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFillColor(long pathPtr, int fillColor) {
+ VectorDrawableNatives.nSetFillColor(pathPtr, fillColor);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetFillAlpha(long pathPtr) {
+ return VectorDrawableNatives.nGetFillAlpha(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetFillAlpha(long pathPtr, float fillAlpha) {
+ VectorDrawableNatives.nSetFillAlpha(pathPtr, fillAlpha);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTrimPathStart(long pathPtr) {
+ return VectorDrawableNatives.nGetTrimPathStart(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
+ VectorDrawableNatives.nSetTrimPathStart(pathPtr, trimPathStart);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTrimPathEnd(long pathPtr) {
+ return VectorDrawableNatives.nGetTrimPathEnd(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
+ VectorDrawableNatives.nSetTrimPathEnd(pathPtr, trimPathEnd);
+ }
+
+ @Implementation(minSdk = O)
+ protected static float nGetTrimPathOffset(long pathPtr) {
+ return VectorDrawableNatives.nGetTrimPathOffset(pathPtr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) {
+ VectorDrawableNatives.nSetTrimPathOffset(pathPtr, trimPathOffset);
+ }
+
+ /** Shadow picker for {@link VectorDrawable}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowVectorDrawable.class, ShadowNativeVectorDrawable.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVirtualRefBasePtr.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVirtualRefBasePtr.java
new file mode 100644
index 000000000..8343912c5
--- /dev/null
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowNativeVirtualRefBasePtr.java
@@ -0,0 +1,35 @@
+package org.robolectric.shadows;
+
+import static android.os.Build.VERSION_CODES.O;
+
+import com.android.internal.util.VirtualRefBasePtr;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.nativeruntime.VirtualRefBasePtrNatives;
+import org.robolectric.shadows.ShadowNativeVirtualRefBasePtr.Picker;
+
+/** Shadow for {@link VirtualRefBasePtr} that is backed by native code */
+@Implements(
+ value = VirtualRefBasePtr.class,
+ minSdk = O,
+ shadowPicker = Picker.class,
+ isInAndroidSdk = false)
+public class ShadowNativeVirtualRefBasePtr {
+
+ @Implementation(minSdk = O)
+ protected static void nIncStrong(long ptr) {
+ VirtualRefBasePtrNatives.nIncStrong(ptr);
+ }
+
+ @Implementation(minSdk = O)
+ protected static void nDecStrong(long ptr) {
+ VirtualRefBasePtrNatives.nDecStrong(ptr);
+ }
+
+ /** Shadow picker for {@link VirtualRefBasePtr}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowVirtualRefBasePtr.class, ShadowNativeVirtualRefBasePtr.class);
+ }
+ }
+}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPath.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPath.java
index 459130f28..de99c0123 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPath.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPath.java
@@ -5,7 +5,6 @@ import android.graphics.Path;
import android.graphics.RectF;
import java.util.List;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.ShadowPicker;
import org.robolectric.shadows.ShadowPath.Picker;
/** Base class for {@link ShadowPath} classes. */
@@ -81,11 +80,10 @@ public abstract class ShadowPath {
}
}
- /** A {@link ShadowPicker} that always selects the legacy ShadowPath */
- public static class Picker implements ShadowPicker<ShadowPath> {
- @Override
- public Class<? extends ShadowPath> pickShadowClass() {
- return ShadowLegacyPath.class;
+ /** Shadow picker for {@link Path}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyPath.class, ShadowNativePath.class);
}
}
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTypeface.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTypeface.java
index f9af04905..5db6c9d60 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTypeface.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTypeface.java
@@ -2,7 +2,6 @@ package org.robolectric.shadows;
import android.graphics.Typeface;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadow.api.ShadowPicker;
import org.robolectric.shadows.ShadowTypeface.Picker;
/** Base class for {@link ShadowTypeface} classes. */
@@ -65,11 +64,10 @@ public abstract class ShadowTypeface {
}
}
- /** A {@link ShadowPicker} that always selects the legacy ShadowTypeface. */
- public static class Picker implements ShadowPicker<ShadowTypeface> {
- @Override
- public Class<? extends ShadowTypeface> pickShadowClass() {
- return ShadowLegacyTypeface.class;
+ /** Shadow picker for {@link Typeface}. */
+ public static final class Picker extends GraphicsShadowPicker<Object> {
+ public Picker() {
+ super(ShadowLegacyTypeface.class, ShadowNativeTypeface.class);
}
}
}