diff options
-rw-r--r-- | graphics/java/android/graphics/drawable/LottieDrawable.java | 151 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 4 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/SkiaCanvas.h | 1 | ||||
-rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Canvas.h | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/LottieDrawable.cpp | 83 | ||||
-rw-r--r-- | libs/hwui/hwui/LottieDrawable.h | 67 | ||||
-rw-r--r-- | libs/hwui/jni/LottieDrawable.cpp | 91 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaDisplayList.cpp | 10 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaDisplayList.h | 3 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/SkiaRecordingCanvas.h | 1 | ||||
-rw-r--r-- | tests/VectorDrawableTest/Android.bp | 2 | ||||
-rw-r--r-- | tests/VectorDrawableTest/AndroidManifest.xml | 9 | ||||
-rw-r--r-- | tests/VectorDrawableTest/res/raw/lottie.json | 123 | ||||
-rw-r--r-- | tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java | 76 |
17 files changed, 634 insertions, 0 deletions
diff --git a/graphics/java/android/graphics/drawable/LottieDrawable.java b/graphics/java/android/graphics/drawable/LottieDrawable.java new file mode 100644 index 000000000000..c1f1b50cf339 --- /dev/null +++ b/graphics/java/android/graphics/drawable/LottieDrawable.java @@ -0,0 +1,151 @@ +/* + * 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 android.graphics.drawable; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; + +import dalvik.annotation.optimization.FastNative; + +import libcore.util.NativeAllocationRegistry; + +import java.io.IOException; + +/** + * {@link Drawable} for drawing Lottie files. + * + * <p>The framework handles decoding subsequent frames in another thread and + * updating when necessary. The drawable will only animate while it is being + * displayed.</p> + * + * @hide + */ +@SuppressLint("NotCloseable") +public class LottieDrawable extends Drawable implements Animatable { + private long mNativePtr; + + /** + * Create an animation from the provided JSON string + * @hide + */ + private LottieDrawable(@NonNull String animationJson) throws IOException { + mNativePtr = nCreate(animationJson); + if (mNativePtr == 0) { + throw new IOException("could not make LottieDrawable from json"); + } + + final long nativeSize = nNativeByteSize(mNativePtr); + NativeAllocationRegistry registry = new NativeAllocationRegistry( + LottieDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize); + registry.registerNativeAllocation(this, mNativePtr); + } + + /** + * Factory for LottieDrawable, throws IOException if native Skottie Builder fails to parse + */ + public static LottieDrawable makeLottieDrawable(@NonNull String animationJson) + throws IOException { + return new LottieDrawable(animationJson); + } + + + + /** + * Draw the current frame to the Canvas. + * @hide + */ + @Override + public void draw(@NonNull Canvas canvas) { + if (mNativePtr == 0) { + throw new IllegalStateException("called draw on empty LottieDrawable"); + } + + nDraw(mNativePtr, canvas.getNativeCanvasWrapper()); + } + + /** + * Start the animation. Needs to be called before draw calls. + * @hide + */ + @Override + public void start() { + if (mNativePtr == 0) { + throw new IllegalStateException("called start on empty LottieDrawable"); + } + + if (nStart(mNativePtr)) { + invalidateSelf(); + } + } + + /** + * Stops the animation playback. Does not release anything. + * @hide + */ + @Override + public void stop() { + if (mNativePtr == 0) { + throw new IllegalStateException("called stop on empty LottieDrawable"); + } + nStop(mNativePtr); + } + + /** + * Return whether the animation is currently running. + */ + @Override + public boolean isRunning() { + if (mNativePtr == 0) { + throw new IllegalStateException("called isRunning on empty LottieDrawable"); + } + return nIsRunning(mNativePtr); + } + + @Override + public int getOpacity() { + // We assume translucency to avoid checking each pixel. + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + //TODO + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + //TODO + } + + private static native long nCreate(String json); + private static native void nDraw(long nativeInstance, long nativeCanvas); + @FastNative + private static native long nGetNativeFinalizer(); + @FastNative + private static native long nNativeByteSize(long nativeInstance); + @FastNative + private static native boolean nIsRunning(long nativeInstance); + @FastNative + private static native boolean nStart(long nativeInstance); + @FastNative + private static native boolean nStop(long nativeInstance); + +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 3e3d77b89a9a..9c4ea0ecbe30 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -78,6 +78,7 @@ cc_defaults { "external/skia/src/utils", "external/skia/src/gpu", "external/skia/src/shaders", + "external/skia/modules/skottie", ], }, host: { @@ -374,6 +375,7 @@ cc_defaults { "external/skia/src/effects", "external/skia/src/image", "external/skia/src/images", + "external/skia/modules/skottie", ], shared_libs: [ @@ -400,6 +402,7 @@ cc_defaults { "jni/BitmapRegionDecoder.cpp", "jni/GIFMovie.cpp", "jni/GraphicsStatsService.cpp", + "jni/LottieDrawable.cpp", "jni/Movie.cpp", "jni/MovieImpl.cpp", "jni/pdf/PdfDocument.cpp", @@ -507,6 +510,7 @@ cc_defaults { "hwui/BlurDrawLooper.cpp", "hwui/Canvas.cpp", "hwui/ImageDecoder.cpp", + "hwui/LottieDrawable.cpp", "hwui/MinikinSkia.cpp", "hwui/MinikinUtils.cpp", "hwui/PaintImpl.cpp", diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index d83d78f650aa..a1c4b49b6742 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -736,6 +736,10 @@ double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { return imgDrawable->drawStaging(mCanvas); } +void SkiaCanvas::drawLottie(LottieDrawable* lottieDrawable) { + lottieDrawable->drawStaging(mCanvas); +} + void SkiaCanvas::drawVectorDrawable(VectorDrawableRoot* vectorDrawable) { vectorDrawable->drawStaging(this); } diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 31e3b4c3c7e2..fd8b6cdb7ca0 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -145,6 +145,7 @@ public: float dstTop, float dstRight, float dstBottom, const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; + virtual void drawLottie(LottieDrawable* lottieDrawable) override; virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index e6cfa7bcaf70..5a96174ebac0 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -37,6 +37,7 @@ extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env); extern int register_android_graphics_Graphics(JNIEnv* env); extern int register_android_graphics_ImageDecoder(JNIEnv*); extern int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv*); +extern int register_android_graphics_drawable_LottieDrawable(JNIEnv*); extern int register_android_graphics_Interpolator(JNIEnv* env); extern int register_android_graphics_MaskFilter(JNIEnv* env); extern int register_android_graphics_Movie(JNIEnv* env); @@ -116,6 +117,7 @@ extern int register_android_view_ThreadedRenderer(JNIEnv* env); REG_JNI(register_android_graphics_HardwareRendererObserver), REG_JNI(register_android_graphics_ImageDecoder), REG_JNI(register_android_graphics_drawable_AnimatedImageDrawable), + REG_JNI(register_android_graphics_drawable_LottieDrawable), REG_JNI(register_android_graphics_Interpolator), REG_JNI(register_android_graphics_MaskFilter), REG_JNI(register_android_graphics_Matrix), diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 2a2019199bda..07e2fe24c939 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -60,6 +60,7 @@ typedef uirenderer::VectorDrawable::Tree VectorDrawableRoot; typedef std::function<void(uint16_t* text, float* positions)> ReadGlyphFunc; class AnimatedImageDrawable; +class LottieDrawable; class Bitmap; class Paint; struct Typeface; @@ -242,6 +243,7 @@ public: const Paint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; + virtual void drawLottie(LottieDrawable* lottieDrawable) = 0; virtual void drawPicture(const SkPicture& picture) = 0; /** diff --git a/libs/hwui/hwui/LottieDrawable.cpp b/libs/hwui/hwui/LottieDrawable.cpp new file mode 100644 index 000000000000..92dc51e01a85 --- /dev/null +++ b/libs/hwui/hwui/LottieDrawable.cpp @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#include "LottieDrawable.h" + +#include <SkTime.h> +#include <log/log.h> +#include <pipeline/skia/SkiaUtils.h> + +namespace android { + +sk_sp<LottieDrawable> LottieDrawable::Make(sk_sp<skottie::Animation> animation, size_t bytesUsed) { + if (animation) { + return sk_sp<LottieDrawable>(new LottieDrawable(std::move(animation), bytesUsed)); + } + return nullptr; +} +LottieDrawable::LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytesUsed) + : mAnimation(std::move(animation)), mBytesUsed(bytesUsed) {} + +bool LottieDrawable::start() { + if (mRunning) { + return false; + } + + mRunning = true; + return true; +} + +bool LottieDrawable::stop() { + bool wasRunning = mRunning; + mRunning = false; + return wasRunning; +} + +bool LottieDrawable::isRunning() { + return mRunning; +} + +// TODO: Check to see if drawable is actually dirty +bool LottieDrawable::isDirty() { + return true; +} + +void LottieDrawable::onDraw(SkCanvas* canvas) { + if (mRunning) { + const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); + + nsecs_t t = 0; + if (mStartTime == 0) { + mStartTime = currentTime; + } else { + t = currentTime - mStartTime; + } + double seekTime = std::fmod((double)t * 1e-9, mAnimation->duration()); + mAnimation->seekFrameTime(seekTime); + mAnimation->render(canvas); + } +} + +void LottieDrawable::drawStaging(SkCanvas* canvas) { + onDraw(canvas); +} + +SkRect LottieDrawable::onGetBounds() { + // We do not actually know the bounds, so give a conservative answer. + return SkRectMakeLargest(); +} + +} // namespace android diff --git a/libs/hwui/hwui/LottieDrawable.h b/libs/hwui/hwui/LottieDrawable.h new file mode 100644 index 000000000000..9cc34bf12f4f --- /dev/null +++ b/libs/hwui/hwui/LottieDrawable.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#pragma once + +#include <SkDrawable.h> +#include <Skottie.h> +#include <utils/Timers.h> + +class SkCanvas; + +namespace android { + +/** + * Native component of android.graphics.drawable.LottieDrawable.java. + * This class can be drawn into Canvas.h and maintains the state needed to drive + * the animation from the RenderThread. + */ +class LottieDrawable : public SkDrawable { +public: + static sk_sp<LottieDrawable> Make(sk_sp<skottie::Animation> animation, size_t bytes); + + // Draw to software canvas + void drawStaging(SkCanvas* canvas); + + // Returns true if the animation was started; false otherwise (e.g. it was + // already running) + bool start(); + // Returns true if the animation was stopped; false otherwise (e.g. it was + // already stopped) + bool stop(); + bool isRunning(); + + // TODO: Is dirty should take in a time til next frame to determine if it is dirty + bool isDirty(); + + SkRect onGetBounds() override; + + size_t byteSize() const { return sizeof(*this) + mBytesUsed; } + +protected: + void onDraw(SkCanvas* canvas) override; + +private: + LottieDrawable(sk_sp<skottie::Animation> animation, size_t bytes_used); + + sk_sp<skottie::Animation> mAnimation; + bool mRunning = false; + // The start time for the drawable itself. + nsecs_t mStartTime = 0; + const size_t mBytesUsed = 0; +}; + +} // namespace android diff --git a/libs/hwui/jni/LottieDrawable.cpp b/libs/hwui/jni/LottieDrawable.cpp new file mode 100644 index 000000000000..fb6eede213a8 --- /dev/null +++ b/libs/hwui/jni/LottieDrawable.cpp @@ -0,0 +1,91 @@ +/* + * 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. + */ + +#include <SkRect.h> +#include <Skottie.h> +#include <hwui/Canvas.h> +#include <hwui/LottieDrawable.h> + +#include "GraphicsJNI.h" +#include "Utils.h" + +using namespace android; + +static jclass gLottieDrawableClass; + +static jlong LottieDrawable_nCreate(JNIEnv* env, jobject, jstring jjson) { + const ScopedUtfChars cstr(env, jjson); + // TODO(b/259267150) provide more accurate byteSize + size_t bytes = strlen(cstr.c_str()); + auto animation = skottie::Animation::Builder().make(cstr.c_str(), bytes); + sk_sp<LottieDrawable> drawable(LottieDrawable::Make(std::move(animation), bytes)); + if (!drawable) { + return 0; + } + return reinterpret_cast<jlong>(drawable.release()); +} + +static void LottieDrawable_destruct(LottieDrawable* drawable) { + SkSafeUnref(drawable); +} + +static jlong LottieDrawable_nGetNativeFinalizer(JNIEnv* /*env*/, jobject /*clazz*/) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&LottieDrawable_destruct)); +} + +static void LottieDrawable_nDraw(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jlong canvasPtr) { + auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr); + auto* canvas = reinterpret_cast<Canvas*>(canvasPtr); + canvas->drawLottie(drawable); +} + +static jboolean LottieDrawable_nIsRunning(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr); + return drawable->isRunning(); +} + +static jboolean LottieDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr); + return drawable->start(); +} + +static jboolean LottieDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr); + return drawable->stop(); +} + +static jlong LottieDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { + auto* drawable = reinterpret_cast<LottieDrawable*>(nativePtr); + return drawable->byteSize(); +} + +static const JNINativeMethod gLottieDrawableMethods[] = { + {"nCreate", "(Ljava/lang/String;)J", (void*)LottieDrawable_nCreate}, + {"nNativeByteSize", "(J)J", (void*)LottieDrawable_nNativeByteSize}, + {"nGetNativeFinalizer", "()J", (void*)LottieDrawable_nGetNativeFinalizer}, + {"nDraw", "(JJ)V", (void*)LottieDrawable_nDraw}, + {"nIsRunning", "(J)Z", (void*)LottieDrawable_nIsRunning}, + {"nStart", "(J)Z", (void*)LottieDrawable_nStart}, + {"nStop", "(J)Z", (void*)LottieDrawable_nStop}, +}; + +int register_android_graphics_drawable_LottieDrawable(JNIEnv* env) { + gLottieDrawableClass = reinterpret_cast<jclass>( + env->NewGlobalRef(FindClassOrDie(env, "android/graphics/drawable/LottieDrawable"))); + + return android::RegisterMethodsOrDie(env, "android/graphics/drawable/LottieDrawable", + gLottieDrawableMethods, NELEM(gLottieDrawableMethods)); +} diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index fcfc4f82abed..f0dc5eb4dd0e 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -146,6 +146,16 @@ bool SkiaDisplayList::prepareListAndChildren( } } + for (auto& lottie : mLotties) { + // If any animated image in the display list needs updated, then damage the node. + if (lottie->isDirty()) { + isDirty = true; + } + if (lottie->isRunning()) { + info.out.hasAnimations = true; + } + } + for (auto& [vectorDrawable, cachedMatrix] : mVectorDrawables) { // If any vector drawable in the display list needs update, damage the node. if (vectorDrawable->isDirty()) { diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 2a677344b7b2..39217fcf1a56 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -22,6 +22,7 @@ #include "RenderNodeDrawable.h" #include "TreeInfo.h" #include "hwui/AnimatedImageDrawable.h" +#include "hwui/LottieDrawable.h" #include "utils/LinearAllocator.h" #include "utils/Pair.h" @@ -186,6 +187,8 @@ public: return mHasHolePunches; } + // TODO(b/257304231): create common base class for Lotties and AnimatedImages + std::vector<LottieDrawable*> mLotties; std::vector<AnimatedImageDrawable*> mAnimatedImages; DisplayListData mDisplayList; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 1f87865f2672..db449d608c1f 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -188,6 +188,11 @@ void SkiaRecordingCanvas::drawWebViewFunctor(int functor) { #endif } +void SkiaRecordingCanvas::drawLottie(LottieDrawable* lottie) { + drawDrawable(lottie); + mDisplayList->mLotties.push_back(lottie); +} + void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) { mRecorder.drawVectorDrawable(tree); SkMatrix mat; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 7844e2cc2a73..c823d8d0a755 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -78,6 +78,7 @@ public: uirenderer::CanvasPropertyPaint* paint) override; virtual void drawRipple(const RippleDrawableParams& params) override; + virtual void drawLottie(LottieDrawable* lottieDrawable) override; virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void enableZ(bool enableZ) override; diff --git a/tests/VectorDrawableTest/Android.bp b/tests/VectorDrawableTest/Android.bp index 9da7c5fdbb17..099d874375a1 100644 --- a/tests/VectorDrawableTest/Android.bp +++ b/tests/VectorDrawableTest/Android.bp @@ -26,5 +26,7 @@ package { android_test { name: "VectorDrawableTest", srcs: ["**/*.java"], + // certificate set as platform to allow testing of @hidden APIs + certificate: "platform", platform_apis: true, } diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml index 5334dac57ca2..163e438e0677 100644 --- a/tests/VectorDrawableTest/AndroidManifest.xml +++ b/tests/VectorDrawableTest/AndroidManifest.xml @@ -158,6 +158,15 @@ <category android:name="com.android.test.dynamic.TEST"/> </intent-filter> </activity> + <activity android:name="LottieDrawableTest" + android:label="Lottie test bed" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="com.android.test.dynamic.TEST" /> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/VectorDrawableTest/res/raw/lottie.json b/tests/VectorDrawableTest/res/raw/lottie.json new file mode 100644 index 000000000000..fea571c6bedb --- /dev/null +++ b/tests/VectorDrawableTest/res/raw/lottie.json @@ -0,0 +1,123 @@ +{ + "v":"4.6.9", + "fr":60, + "ip":0, + "op":200, + "w":800, + "h":600, + "nm":"Loader 1 JSON", + "ddd":0, + + + "layers":[ + { + "ddd":0, + "ind":1, + "ty":4, + "nm":"Custom Path 1", + "ao": 0, + "ip": 0, + "op": 300, + "st": 0, + "sr": 1, + "bm": 0, + "ks": { + "o": { "a":0, "k":100 }, + "r": { "a":1, "k": [ + { "s": [ 0 ], "e": [ 360], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 }, + { "t": 200 } + ] }, + "p": { "a":0, "k":[ 300, 300, 0 ] }, + "a": { "a":0, "k":[ 100, 100, 0 ] }, + "s": { "a":1, "k":[ + { "s": [ 100, 100 ], "e": [ 200, 200 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 }, + { "s": [ 200, 200 ], "e": [ 100, 100 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 }, + { "t": 200 } + ] } + }, + + "shapes":[ + { + "ty":"gr", + "it":[ + { + "ty" : "sh", + "nm" : "Path 1", + "ks" : { + "a" : 1, + "k" : [ + { + "s": [ { + "i": [ [ 0, 50 ], [ -50, 0 ], [ 0, -50 ], [ 50, 0 ] ], + "o": [ [ 0, -50 ], [ 50, 0 ], [ 0, 50 ], [ -50, 0 ] ], + "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ], + "c": true + } ], + "e": [ { + "i": [ [ 50, 50 ], [ -50, 0 ], [ -50, -50 ], [ 50, 50 ] ], + "o": [ [ 50, -50 ], [ 50, 0 ], [ -50, 50 ], [ -50, 50 ] ], + "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ], + "c": true + } ], + "i": { "x":0.5, "y":0.5 }, + "o": { "x":0.5, "y":0.5 }, + "t": 0 + }, + { + "s": [ { + "i": [ [ 50, 50 ], [ -50, 0 ], [ -50, -50 ], [ 50, 50 ] ], + "o": [ [ 50, -50 ], [ 50, 0 ], [ -50, 50 ], [ -50, 50 ] ], + "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ], + "c": true + } ], + "e": [ { + "i": [ [ 0, 50 ], [ -50, 0 ], [ 0, -50 ], [ 50, 0 ] ], + "o": [ [ 0, -50 ], [ 50, 0 ], [ 0, 50 ], [ -50, 0 ] ], + "v": [ [ 0, 100 ], [ 100, 0 ], [ 200, 100 ], [ 100, 200 ] ], + "c": true + } ], + "i": { "x":0.5, "y":0.5 }, + "o": { "x":0.5, "y":0.5 }, + "t": 100 + }, + { + "t": 200 + } + ] + } + }, + + { + "ty": "st", + "nm": "Stroke 1", + "lc": 1, + "lj": 1, + "ml": 4, + "w" : { "a": 1, "k": [ + { "s": [ 30 ], "e": [ 50 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 }, + { "s": [ 50 ], "e": [ 30 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 }, + { "t": 200 } + ] }, + "o" : { "a": 0, "k": 100 }, + "c" : { "a": 1, "k": [ + { "s": [ 0, 1, 0 ], "e": [ 1, 0, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 0 }, + { "s": [ 1, 0, 0 ], "e": [ 0, 1, 0 ], "i": { "x":0.5, "y":0.5 }, "o": { "x":0.5, "y":0.5 }, "t": 100 }, + { "t": 200 } + ] } + }, + + { + "ty":"tr", + "p" : { "a":0, "k":[ 0, 0 ] }, + "a" : { "a":0, "k":[ 0, 0 ] }, + "s" : { "a":0, "k":[ 100, 100 ] }, + "r" : { "a":0, "k": 0 }, + "o" : { "a":0, "k":100 }, + "nm": "Transform" + } + ] + } + ] + } + ] + } diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java new file mode 100644 index 000000000000..05eae7b0e642 --- /dev/null +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/LottieDrawableTest.java @@ -0,0 +1,76 @@ +/* + * 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 com.android.test.dynamic; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.LottieDrawable; +import android.os.Bundle; +import android.view.View; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Scanner; + +@SuppressWarnings({"UnusedDeclaration"}) +public class LottieDrawableTest extends Activity { + private static final String TAG = "LottieDrawableTest"; + static final int BACKGROUND = 0xFFF44336; + + class LottieDrawableView extends View { + private Rect mLottieBounds; + + private LottieDrawable mLottie; + + LottieDrawableView(Context context, InputStream is) { + super(context); + Scanner s = new Scanner(is).useDelimiter("\\A"); + String json = s.hasNext() ? s.next() : ""; + try { + mLottie = LottieDrawable.makeLottieDrawable(json); + } catch (IOException e) { + throw new RuntimeException(TAG + ": error parsing test Lottie"); + } + mLottie.start(); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawColor(BACKGROUND); + + mLottie.setBounds(mLottieBounds); + mLottie.draw(canvas); + } + + public void setLottieSize(Rect bounds) { + mLottieBounds = bounds; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + InputStream is = getResources().openRawResource(R.raw.lottie); + + LottieDrawableView view = new LottieDrawableView(this, is); + view.setLottieSize(new Rect(0, 0, 900, 900)); + setContentView(view); + } +} |