diff options
author | Rex Hoffman <rexhoffman@google.com> | 2022-12-17 01:20:08 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-12-17 01:20:08 +0000 |
commit | dec7fbfd56318f64da1b81163754f2cdd7f457e2 (patch) | |
tree | b3411ee1d396479139d774bf3c633a49701740e6 | |
parent | 1125cabb041c59c353f985cc9af15756fed1017f (diff) | |
parent | 278071078a1e89942088142505cd9774b951c904 (diff) | |
download | robolectric-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>
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); } } } |