From 9aa485d75152a2d3ce63639e61acfb850759ff15 Mon Sep 17 00:00:00 2001 From: Artem Kholodnyi Date: Wed, 16 Dec 2020 08:51:48 -0800 Subject: [PATCH] Simplify on fade listener Reviewed By: oprisnik Differential Revision: D24799754 fbshipit-source-id: b5a094c3fce11ab13c04e1bdc8868341b47e51e3 --- .../drawee/drawable/FadeDrawable.java | 67 +++--- .../drawable/FadeDrawableAllOnTest.java | 2 +- .../FadeDrawableOnFadeListenerTest.java | 218 ++++++++++++++++++ .../drawee/drawable/FadeDrawableTest.java | 2 +- 4 files changed, 252 insertions(+), 37 deletions(-) create mode 100644 drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableOnFadeListenerTest.java diff --git a/drawee/src/main/java/com/facebook/drawee/drawable/FadeDrawable.java b/drawee/src/main/java/com/facebook/drawee/drawable/FadeDrawable.java index 02423ff6ac..8d7609ec58 100644 --- a/drawee/src/main/java/com/facebook/drawee/drawable/FadeDrawable.java +++ b/drawee/src/main/java/com/facebook/drawee/drawable/FadeDrawable.java @@ -65,8 +65,7 @@ public class FadeDrawable extends ArrayDrawable { @VisibleForTesting int mPreventInvalidateCount; private @Nullable OnFadeListener mOnFadeListener; - private boolean mCallOnFadeStartedListener; - private boolean mCallOnFadeFinishedListener; + private boolean mIsFadingActualImage; /** * Creates a new fade drawable. The first layer is displayed with full opacity whereas all other @@ -98,7 +97,6 @@ public FadeDrawable(Drawable[] layers, boolean allLayersVisible, int actualImage mPreventInvalidateCount = 0; mDefaultLayerIsOn = allLayersVisible; mDefaultLayerAlpha = mDefaultLayerIsOn ? 255 : 0; - mCallOnFadeStartedListener = true; mActualImageLayer = actualImageLayer; resetInternal(); } @@ -162,9 +160,6 @@ public void reset() { * @param index the index of the layer to fade in. */ public void fadeInLayer(int index) { - mCallOnFadeFinishedListener = index == mActualImageLayer; - maybeNotifyOnFadeStarted(index); - mTransitionState = TRANSITION_STARTING; mIsLayerOn[index] = true; invalidateSelf(); @@ -176,10 +171,6 @@ public void fadeInLayer(int index) { * @param index the index of the layer to fade out. */ public void fadeOutLayer(int index) { - if (index == mActualImageLayer) { - mCallOnFadeStartedListener = true; - mCallOnFadeFinishedListener = true; - } mTransitionState = TRANSITION_STARTING; mIsLayerOn[index] = false; invalidateSelf(); @@ -299,11 +290,8 @@ public void draw(Canvas canvas) { ratio = (mDurationMs == 0) ? 1.0f : 0.0f; // if all the layers have reached their target opacity, transition is done done = updateAlphas(ratio); + onFadeStarted(); mTransitionState = done ? TRANSITION_NONE : TRANSITION_RUNNING; - - if (done) { - maybeNotifyOnFadeFinished(); - } break; case TRANSITION_RUNNING: @@ -313,17 +301,11 @@ public void draw(Canvas canvas) { // if all the layers have reached their target opacity, transition is done done = updateAlphas(ratio); mTransitionState = done ? TRANSITION_NONE : TRANSITION_RUNNING; - - if (done) { - maybeNotifyOnFadeFinished(); - } break; case TRANSITION_NONE: // there is no transition in progress and mAlphas should be left as is. done = true; - - maybeNotifyOnFadeFinished(); break; } @@ -331,25 +313,13 @@ public void draw(Canvas canvas) { drawDrawableWithAlpha(canvas, mLayers[i], (int) Math.ceil(mAlphas[i] * mAlpha / 255.0)); } - if (!done) { + if (done) { + onFadeFinished(); + } else { invalidateSelf(); } } - private void maybeNotifyOnFadeStarted(int index) { - if (mOnFadeListener != null && index == mActualImageLayer && mCallOnFadeStartedListener) { - mOnFadeListener.onFadeStarted(); - mCallOnFadeStartedListener = false; - } - } - - private void maybeNotifyOnFadeFinished() { - if (mOnFadeListener != null && mCallOnFadeFinishedListener) { - mOnFadeListener.onFadeFinished(); - mCallOnFadeFinishedListener = false; - } - } - private void drawDrawableWithAlpha(Canvas canvas, Drawable drawable, int alpha) { if (drawable != null && alpha > 0) { mPreventInvalidateCount++; @@ -403,6 +373,33 @@ public void setOnFadeListener(OnFadeListener onFadeListener) { mOnFadeListener = onFadeListener; } + private void onFadeStarted() { + if (mIsFadingActualImage) { + return; + } + + if (!mIsLayerOn[mActualImageLayer]) { + return; + } + + mIsFadingActualImage = true; + + if (mOnFadeListener != null) { + mOnFadeListener.onFadeStarted(); + } + } + + private void onFadeFinished() { + if (!mIsFadingActualImage) { + return; + } + mIsFadingActualImage = false; + + if (mOnFadeListener != null) { + mOnFadeListener.onFadeFinished(); + } + } + public interface OnFadeListener { void onFadeStarted(); diff --git a/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableAllOnTest.java b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableAllOnTest.java index 883bda363c..1d74a3137a 100644 --- a/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableAllOnTest.java +++ b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableAllOnTest.java @@ -414,7 +414,7 @@ private static class FakeFadeDrawable extends FadeDrawable { private long mCurrentTimeMs; public FakeFadeDrawable(Drawable[] layers) { - super(layers, true, -1); + super(layers, true, 0); mCurrentTimeMs = 0; } diff --git a/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableOnFadeListenerTest.java b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableOnFadeListenerTest.java new file mode 100644 index 0000000000..bcb3863dab --- /dev/null +++ b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableOnFadeListenerTest.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.drawee.drawable; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.SystemClock; +import com.facebook.testing.robolectric.v4.WithTestDefaultsRunner; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; + +/** Tests {@link FadeDrawable.OnFadeListener} */ +@RunWith(WithTestDefaultsRunner.class) +@PrepareForTest({ + // SystemClock.class, + Rect.class, +}) +public class FadeDrawableOnFadeListenerTest { + + public static final int DURATION = 1000; + public static final int ACTUAL_LAYER_INDEX = 1; + public static final int OTHER_LAYER_INDEX = 2; + + private Drawable[] mLayers = + new Drawable[] { + DrawableTestUtils.mockDrawable(), + DrawableTestUtils.mockDrawable(), + DrawableTestUtils.mockDrawable(), + }; + + private FadeDrawable mFadeDrawable; + private FadeDrawable.OnFadeListener mOnFadeListener = mock(FadeDrawable.OnFadeListener.class); + + private Canvas mCanvas = mock(Canvas.class); + + @Before + public void setUp() { + // PowerMockito.mockStatic(SystemClock.class); + mFadeDrawable = new FadeDrawable(mLayers, false, 1); + mFadeDrawable.setTransitionDuration(DURATION); + mFadeDrawable.setOnFadeListener(mOnFadeListener); + } + + @Test + public void testSimple() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.5)); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, never()).onFadeFinished(); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.5)); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testComplex() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.fadeOutLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.5)); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, never()).onFadeFinished(); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.1)); + // mFadeDrawable.fadeInAllLayers(); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testFinishImmediately() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + + mFadeDrawable.finishTransitionImmediately(); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testFadeInAll() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInAllLayers(); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.1)); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testFadeTheOtherLayer() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInLayer(OTHER_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.1)); + mFadeDrawable.draw(mCanvas); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testFadeOut() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + mFadeDrawable.fadeInAllLayers(); + // Notice we finish immediately before the draw call: + mFadeDrawable.finishTransitionImmediately(); + mFadeDrawable.draw(mCanvas); + + mFadeDrawable.fadeOutLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.1)); + mFadeDrawable.draw(mCanvas); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testFadeOut2() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + mFadeDrawable.fadeInAllLayers(); + // Notice we call draw before finishing immediately + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + mFadeDrawable.finishTransitionImmediately(); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } + + @Test + public void testMultipleFades() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeStarted(); + + mFadeDrawable.fadeInLayer(OTHER_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + verifyNoMoreInteractions(mOnFadeListener); + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.2)); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + } + + @Test + public void testLateListener() { + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.1)); + + // No listener set: + mFadeDrawable.setOnFadeListener(null); + + mFadeDrawable.fadeInLayer(ACTUAL_LAYER_INDEX); + mFadeDrawable.draw(mCanvas); + SystemClock.setCurrentTimeMillis((long) (DURATION * 0.5)); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + mFadeDrawable.draw(mCanvas); + + // Set the listener back + mFadeDrawable.setOnFadeListener(mOnFadeListener); + + SystemClock.setCurrentTimeMillis((long) (DURATION * 1.5)); + mFadeDrawable.draw(mCanvas); + verify(mOnFadeListener, times(1)).onFadeFinished(); + + verifyNoMoreInteractions(mOnFadeListener); + } +} diff --git a/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableTest.java b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableTest.java index 18cebf3e4e..37b69f68f9 100644 --- a/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableTest.java +++ b/drawee/src/test/java/com/facebook/drawee/drawable/FadeDrawableTest.java @@ -402,7 +402,7 @@ private static class FakeFadeDrawable extends FadeDrawable { private long mCurrentTimeMs; public FakeFadeDrawable(Drawable[] layers) { - super(layers); + super(layers, false, 0); mCurrentTimeMs = 0; }