Loading core/jni/android/graphics/AnimatedImageDrawable.cpp +46 −15 Original line number Diff line number Diff line Loading @@ -26,10 +26,11 @@ #include <SkPictureRecorder.h> #include <hwui/AnimatedImageDrawable.h> #include <hwui/Canvas.h> #include <utils/Looper.h> using namespace android; static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID; static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID; // Note: jpostProcess holds a handle to the ImageDecoder. static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, Loading Loading @@ -123,9 +124,9 @@ static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlo return drawable->start(); } static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); drawable->stop(); return drawable->stop(); } // Java's LOOP_INFINITE relies on this being the same. Loading @@ -137,33 +138,63 @@ static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, drawable->setRepetitionCount(loopCount); } class JniAnimationEndListener : public OnAnimationEndListener { class InvokeListener : public MessageHandler { public: JniAnimationEndListener(JNIEnv* env, jobject javaObject) { InvokeListener(JNIEnv* env, jobject javaObject) { LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); mJavaObject = env->NewGlobalRef(javaObject); // Hold a weak reference to break a cycle that would prevent GC. mWeakRef = env->NewWeakGlobalRef(javaObject); } ~JniAnimationEndListener() override { ~InvokeListener() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mJavaObject); env->DeleteWeakGlobalRef(mWeakRef); } void onAnimationEnd() override { virtual void handleMessage(const Message&) override { auto* env = get_env_or_die(mJvm); env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID); jobject localRef = env->NewLocalRef(mWeakRef); if (localRef) { env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID); } } private: JavaVM* mJvm; jobject mJavaObject; jweak mWeakRef; }; class JniAnimationEndListener : public OnAnimationEndListener { public: JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) { mListener = new InvokeListener(env, javaObject); mLooper = std::move(looper); } void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } private: sp<InvokeListener> mListener; sp<Looper> mLooper; }; static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jdrawable) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>( new JniAnimationEndListener(env, jdrawable))); if (!jdrawable) { drawable->setOnAnimationEndListener(nullptr); } else { sp<Looper> looper = Looper::getForThread(); if (!looper.get()) { doThrowISE(env, "Must set AnimatedImageDrawable's AnimationCallback on a thread with a " "looper!"); return; } drawable->setOnAnimationEndListener( std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable)); } } static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { Loading @@ -186,7 +217,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, { "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount }, { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, Loading @@ -195,7 +226,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V"); gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V"); return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); Loading graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +33 −6 Original line number Diff line number Diff line Loading @@ -348,7 +348,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { if (mState == null) { throw new IllegalStateException("called stop on empty AnimatedImageDrawable"); } nStop(mState.mNativePtr); if (nStop(mState.mNativePtr)) { postOnAnimationEnd(); } } // Animatable2 overrides Loading @@ -365,21 +367,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { nSetOnAnimationEndListener(mState.mNativePtr, this); } if (!mAnimationCallbacks.contains(callback)) { mAnimationCallbacks.add(callback); } } @Override public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) { if (callback == null || mAnimationCallbacks == null) { if (callback == null || mAnimationCallbacks == null || !mAnimationCallbacks.remove(callback)) { return false; } return mAnimationCallbacks.remove(callback); if (mAnimationCallbacks.isEmpty()) { clearAnimationCallbacks(); } return true; } @Override public void clearAnimationCallbacks() { if (mAnimationCallbacks != null) { mAnimationCallbacks = null; nSetOnAnimationEndListener(mState.mNativePtr, null); } } private void postOnAnimationStart() { Loading Loading @@ -413,6 +425,21 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { return mHandler; } /** * Called by JNI. * * The JNI code has already posted this to the thread that created the * callback, so no need to post. */ @SuppressWarnings("unused") private void onAnimationEnd() { if (mAnimationCallbacks != null) { for (Animatable2.AnimationCallback callback : mAnimationCallbacks) { callback.onAnimationEnd(this); } } } private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) Loading @@ -432,7 +459,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { @FastNative private static native boolean nStart(long nativePtr); @FastNative private static native void nStop(long nativePtr); private static native boolean nStop(long nativePtr); @FastNative private static native void nSetLoopCount(long nativePtr, int loopCount); // Pass the drawable down to native so it can call onAnimationEnd. Loading libs/hwui/hwui/AnimatedImageDrawable.cpp +3 −2 Original line number Diff line number Diff line Loading @@ -48,8 +48,10 @@ bool AnimatedImageDrawable::start() { return true; } void AnimatedImageDrawable::stop() { bool AnimatedImageDrawable::stop() { bool wasRunning = mRunning; mRunning = false; return wasRunning; } bool AnimatedImageDrawable::isRunning() { Loading Loading @@ -180,7 +182,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { if (finalFrame) { if (mEndListener) { mEndListener->onAnimationEnd(); mEndListener = nullptr; } } } Loading libs/hwui/hwui/AnimatedImageDrawable.h +3 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,9 @@ public: // Returns true if the animation was started; false otherwise (e.g. it was // already running) bool start(); void stop(); // Returns true if the animation was stopped; false otherwise (e.g. it was // already stopped) bool stop(); bool isRunning(); void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); } Loading Loading
core/jni/android/graphics/AnimatedImageDrawable.cpp +46 −15 Original line number Diff line number Diff line Loading @@ -26,10 +26,11 @@ #include <SkPictureRecorder.h> #include <hwui/AnimatedImageDrawable.h> #include <hwui/Canvas.h> #include <utils/Looper.h> using namespace android; static jmethodID gAnimatedImageDrawable_postOnAnimationEndMethodID; static jmethodID gAnimatedImageDrawable_onAnimationEndMethodID; // Note: jpostProcess holds a handle to the ImageDecoder. static jlong AnimatedImageDrawable_nCreate(JNIEnv* env, jobject /*clazz*/, Loading Loading @@ -123,9 +124,9 @@ static jboolean AnimatedImageDrawable_nStart(JNIEnv* env, jobject /*clazz*/, jlo return drawable->start(); } static void AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { static jboolean AnimatedImageDrawable_nStop(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); drawable->stop(); return drawable->stop(); } // Java's LOOP_INFINITE relies on this being the same. Loading @@ -137,33 +138,63 @@ static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, drawable->setRepetitionCount(loopCount); } class JniAnimationEndListener : public OnAnimationEndListener { class InvokeListener : public MessageHandler { public: JniAnimationEndListener(JNIEnv* env, jobject javaObject) { InvokeListener(JNIEnv* env, jobject javaObject) { LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJvm) != JNI_OK); mJavaObject = env->NewGlobalRef(javaObject); // Hold a weak reference to break a cycle that would prevent GC. mWeakRef = env->NewWeakGlobalRef(javaObject); } ~JniAnimationEndListener() override { ~InvokeListener() override { auto* env = get_env_or_die(mJvm); env->DeleteGlobalRef(mJavaObject); env->DeleteWeakGlobalRef(mWeakRef); } void onAnimationEnd() override { virtual void handleMessage(const Message&) override { auto* env = get_env_or_die(mJvm); env->CallVoidMethod(mJavaObject, gAnimatedImageDrawable_postOnAnimationEndMethodID); jobject localRef = env->NewLocalRef(mWeakRef); if (localRef) { env->CallVoidMethod(localRef, gAnimatedImageDrawable_onAnimationEndMethodID); } } private: JavaVM* mJvm; jobject mJavaObject; jweak mWeakRef; }; class JniAnimationEndListener : public OnAnimationEndListener { public: JniAnimationEndListener(sp<Looper>&& looper, JNIEnv* env, jobject javaObject) { mListener = new InvokeListener(env, javaObject); mLooper = std::move(looper); } void onAnimationEnd() override { mLooper->sendMessage(mListener, 0); } private: sp<InvokeListener> mListener; sp<Looper> mLooper; }; static void AnimatedImageDrawable_nSetOnAnimationEndListener(JNIEnv* env, jobject /*clazz*/, jlong nativePtr, jobject jdrawable) { auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr); drawable->setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener>( new JniAnimationEndListener(env, jdrawable))); if (!jdrawable) { drawable->setOnAnimationEndListener(nullptr); } else { sp<Looper> looper = Looper::getForThread(); if (!looper.get()) { doThrowISE(env, "Must set AnimatedImageDrawable's AnimationCallback on a thread with a " "looper!"); return; } drawable->setOnAnimationEndListener( std::make_unique<JniAnimationEndListener>(std::move(looper), env, jdrawable)); } } static long AnimatedImageDrawable_nNativeByteSize(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) { Loading @@ -186,7 +217,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { { "nSetColorFilter", "(JJ)V", (void*) AnimatedImageDrawable_nSetColorFilter }, { "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning }, { "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart }, { "nStop", "(J)V", (void*) AnimatedImageDrawable_nStop }, { "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop }, { "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount }, { "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener }, { "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize }, Loading @@ -195,7 +226,7 @@ static const JNINativeMethod gAnimatedImageDrawableMethods[] = { int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) { jclass animatedImageDrawable_class = FindClassOrDie(env, "android/graphics/drawable/AnimatedImageDrawable"); gAnimatedImageDrawable_postOnAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "postOnAnimationEnd", "()V"); gAnimatedImageDrawable_onAnimationEndMethodID = GetMethodIDOrDie(env, animatedImageDrawable_class, "onAnimationEnd", "()V"); return android::RegisterMethodsOrDie(env, "android/graphics/drawable/AnimatedImageDrawable", gAnimatedImageDrawableMethods, NELEM(gAnimatedImageDrawableMethods)); Loading
graphics/java/android/graphics/drawable/AnimatedImageDrawable.java +33 −6 Original line number Diff line number Diff line Loading @@ -348,7 +348,9 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { if (mState == null) { throw new IllegalStateException("called stop on empty AnimatedImageDrawable"); } nStop(mState.mNativePtr); if (nStop(mState.mNativePtr)) { postOnAnimationEnd(); } } // Animatable2 overrides Loading @@ -365,21 +367,31 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { nSetOnAnimationEndListener(mState.mNativePtr, this); } if (!mAnimationCallbacks.contains(callback)) { mAnimationCallbacks.add(callback); } } @Override public boolean unregisterAnimationCallback(@NonNull AnimationCallback callback) { if (callback == null || mAnimationCallbacks == null) { if (callback == null || mAnimationCallbacks == null || !mAnimationCallbacks.remove(callback)) { return false; } return mAnimationCallbacks.remove(callback); if (mAnimationCallbacks.isEmpty()) { clearAnimationCallbacks(); } return true; } @Override public void clearAnimationCallbacks() { if (mAnimationCallbacks != null) { mAnimationCallbacks = null; nSetOnAnimationEndListener(mState.mNativePtr, null); } } private void postOnAnimationStart() { Loading Loading @@ -413,6 +425,21 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { return mHandler; } /** * Called by JNI. * * The JNI code has already posted this to the thread that created the * callback, so no need to post. */ @SuppressWarnings("unused") private void onAnimationEnd() { if (mAnimationCallbacks != null) { for (Animatable2.AnimationCallback callback : mAnimationCallbacks) { callback.onAnimationEnd(this); } } } private static native long nCreate(long nativeImageDecoder, @Nullable ImageDecoder decoder, int width, int height, Rect cropRect) Loading @@ -432,7 +459,7 @@ public class AnimatedImageDrawable extends Drawable implements Animatable2 { @FastNative private static native boolean nStart(long nativePtr); @FastNative private static native void nStop(long nativePtr); private static native boolean nStop(long nativePtr); @FastNative private static native void nSetLoopCount(long nativePtr, int loopCount); // Pass the drawable down to native so it can call onAnimationEnd. Loading
libs/hwui/hwui/AnimatedImageDrawable.cpp +3 −2 Original line number Diff line number Diff line Loading @@ -48,8 +48,10 @@ bool AnimatedImageDrawable::start() { return true; } void AnimatedImageDrawable::stop() { bool AnimatedImageDrawable::stop() { bool wasRunning = mRunning; mRunning = false; return wasRunning; } bool AnimatedImageDrawable::isRunning() { Loading Loading @@ -180,7 +182,6 @@ void AnimatedImageDrawable::onDraw(SkCanvas* canvas) { if (finalFrame) { if (mEndListener) { mEndListener->onAnimationEnd(); mEndListener = nullptr; } } } Loading
libs/hwui/hwui/AnimatedImageDrawable.h +3 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,9 @@ public: // Returns true if the animation was started; false otherwise (e.g. it was // already running) bool start(); void stop(); // Returns true if the animation was stopped; false otherwise (e.g. it was // already stopped) bool stop(); bool isRunning(); void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); } Loading