blob: 88435c909388e88661ee4535e5fc6bbc676cfdc9 [file] [log] [blame]
/*
* Copyright (C) 2009 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.view.animation.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.res.XmlResourceParser;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import android.view.cts.R;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.PollingCheck;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test {@link Animation}.
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class AnimationTest {
private static final float COMPARISON_DELTA = 0.001f;
/** It is defined in R.anim.accelerate_alpha */
private static final int ACCELERATE_ALPHA_DURATION = 1000;
/** It is defined in R.anim.decelerate_alpha */
private static final int DECELERATE_ALPHA_DURATION = 2000;
private static final int CANCELATION_TIMEOUT = 5000;
private Instrumentation mInstrumentation;
private Activity mActivity;
@Rule
public ActivityTestRule<AnimationTestCtsActivity> mActivityRule =
new ActivityTestRule<>(AnimationTestCtsActivity.class);
@Before
public void setup() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
}
@Test
public void testConstructor() {
XmlResourceParser parser = mActivity.getResources().getAnimation(R.anim.alpha);
AttributeSet attrs = Xml.asAttributeSet(parser);
new Animation(mActivity, attrs) {
};
new Animation() {
};
}
@Test
public void testAccessInterpolator() {
// check default interpolator
MyAnimation myAnimation = new MyAnimation();
Interpolator interpolator = myAnimation.getInterpolator();
assertTrue(interpolator instanceof AccelerateDecelerateInterpolator); // issue 1561186.
myAnimation.ensureInterpolator();
assertSame(interpolator, myAnimation.getInterpolator());
myAnimation.setInterpolator(null);
assertNull(myAnimation.getInterpolator());
myAnimation.ensureInterpolator();
interpolator = myAnimation.getInterpolator();
assertTrue(interpolator instanceof AccelerateDecelerateInterpolator);
Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
interpolator = animation.getInterpolator();
assertNotNull(interpolator);
assertTrue(interpolator instanceof DecelerateInterpolator);
animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
interpolator = animation.getInterpolator();
assertNotNull(interpolator);
assertTrue(interpolator instanceof AccelerateInterpolator);
}
@Test
public void testDefaultFill() {
Animation animation = new Animation() {
};
assertTrue(animation.getFillBefore());
assertFalse(animation.getFillAfter());
}
@Test
public void testAccessFill() throws Throwable {
View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.accelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/accelerate_interpolator"
// android:fromAlpha="0.1"
// android:toAlpha="0.9"
// android:duration="1000" />
Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
assertFalse(animation.isFillEnabled());
assertTrue(animation.getFillBefore());
assertFalse(animation.getFillAfter());
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
animation);
// fillBefore and fillAfter are ignored when fillEnabled is false
Transformation transformation = new Transformation();
// check alpha before start
animation.getTransformation(animation.getStartTime() - 1, transformation);
float alpha = transformation.getAlpha();
assertEquals(0.1f, alpha, COMPARISON_DELTA); // issue 1698355
transformation = new Transformation();
// check alpha after the end
animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
transformation);
alpha = transformation.getAlpha();
assertEquals(0.9f, alpha, COMPARISON_DELTA); // issue 1698355
animation.setFillEnabled(true);
animation.setFillBefore(false);
assertTrue(animation.isFillEnabled());
assertFalse(animation.getFillBefore());
assertFalse(animation.getFillAfter());
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
animation);
transformation = new Transformation();
animation.getTransformation(animation.getStartTime() - 1, transformation);
alpha = transformation.getAlpha();
assertEquals(1.0f, alpha, COMPARISON_DELTA);
transformation = new Transformation();
animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
transformation);
alpha = transformation.getAlpha();
assertEquals(1.0f, alpha, COMPARISON_DELTA);
animation.setFillBefore(true);
animation.setFillAfter(true);
assertTrue(animation.isFillEnabled());
assertTrue(animation.getFillBefore());
assertTrue(animation.getFillAfter());
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
animation);
transformation = new Transformation();
animation.getTransformation(animation.getStartTime() - 1, transformation);
alpha = transformation.getAlpha();
assertEquals(0.1f, alpha, COMPARISON_DELTA);
transformation = new Transformation();
animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1,
transformation);
alpha = transformation.getAlpha();
assertEquals(0.9f, alpha, COMPARISON_DELTA);
}
@Test
public void testComputeDurationHint() {
// start offset is 0, duration is 2000, repeat count is 0.
Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
assertEquals(2000, animation.computeDurationHint());
// start offset is 0, duration is 2000, repeat count is 2.
animation.setRepeatCount(2);
assertEquals(6000, animation.computeDurationHint());
// start offset is 800, duration is 2000, repeat count is 2.
animation.setStartOffset(800);
assertEquals(8400, animation.computeDurationHint());
}
@Test
public void testRepeatAnimation() throws Throwable {
// check default repeatMode
Animation animation = new Animation() {
};
assertEquals(Animation.RESTART, animation.getRepeatMode());
final View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.decelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/decelerate_interpolator"
// android:fromAlpha="0.0"
// android:toAlpha="1.0"
// android:duration="2000" />
final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha);
assertEquals(Animation.RESTART, animation.getRepeatMode());
long duration = anim.getDuration();
assertEquals(DECELERATE_ALPHA_DURATION, duration);
// repeat count is 0, repeat mode does not make sense.
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
// test repeat mode REVERSE
anim.setRepeatCount(1);
anim.setRepeatMode(Animation.REVERSE);
// we have to PollingCheck the animation status on test thread,
// it cannot be done on UI thread, so we invoke runOnUiThread method here.
mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
// check whether animation has started
PollingCheck.waitFor(anim::hasStarted);
Transformation transformation = new Transformation();
long startTime = anim.getStartTime();
anim.getTransformation(startTime, transformation);
float alpha1 = transformation.getAlpha();
assertEquals(0.0f, alpha1, COMPARISON_DELTA);
anim.getTransformation(startTime + 1000, transformation);
float alpha2 = transformation.getAlpha();
anim.getTransformation(startTime + 2000, transformation);
float alpha3 = transformation.getAlpha();
assertEquals(1.0f, alpha3, COMPARISON_DELTA);
// wait for animation has ended.
// timeout is larger than duration, in case the system is sluggish
PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded);
// get start time of reversing.
startTime = anim.getStartTime();
anim.getTransformation(startTime + 3000, transformation);
float alpha4 = transformation.getAlpha();
anim.getTransformation(startTime + 4000, transformation);
float alpha5 = transformation.getAlpha();
assertEquals(0.0f, alpha5, COMPARISON_DELTA);
// check decelerating delta alpha when reverse. alpha should change form 0.0f to 1.0f
// and then from 1.0f to 0.0f
float delta1 = alpha2 - alpha1;
float delta2 = alpha3 - alpha2;
// the animation plays backward
float delta3 = alpha3 - alpha4;
float delta4 = alpha4 - alpha5;
assertTrue(delta1 > delta2);
assertTrue(delta3 > delta4);
// test repeat mode RESTART
anim.setRepeatMode(Animation.RESTART);
// we have to PollingCheck the animation status on test thread,
// it cannot be done on UI thread, so we invoke runOnUiThread method here.
mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
// check whether animation has started
PollingCheck.waitFor(anim::hasStarted);
transformation = new Transformation();
startTime = anim.getStartTime();
anim.getTransformation(startTime, transformation);
alpha1 = transformation.getAlpha();
assertEquals(0.0f, alpha1, COMPARISON_DELTA);
anim.getTransformation(startTime + 1000, transformation);
alpha2 = transformation.getAlpha();
anim.getTransformation(startTime + 2000, transformation);
alpha3 = transformation.getAlpha();
assertEquals(1.0f, alpha3, COMPARISON_DELTA);
// wait for animation has ended.
// timeout is larger than duration, in case the system is sluggish
PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded);
// get start time of restarting.
startTime = anim.getStartTime();
anim.getTransformation(startTime + 3000, transformation);
alpha4 = transformation.getAlpha();
anim.getTransformation(startTime + 4000, transformation);
alpha5 = transformation.getAlpha();
assertEquals(1.0f, alpha5, COMPARISON_DELTA);
// check decelerating delta alpha when restart. alpha should change form 0.0f to 1.0f
// and then from 0.0f to 1.0f again
delta1 = alpha2 - 0.0f;
delta2 = alpha3 - alpha2;
// the animation restarts from the beginning
delta3 = alpha4 - 0.0f;
delta4 = alpha5 - alpha4;
assertTrue(delta1 > delta2);
assertTrue(delta3 > delta4);
}
@Test
public void testAccessStartOffset() throws Throwable {
final long startOffset = 800;
// check default startOffset
Animation animation = new Animation() {
};
assertEquals(0, animation.getStartOffset());
View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.accelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/accelerate_interpolator"
// android:fromAlpha="0.1"
// android:toAlpha="0.9"
// android:duration="1000" />
animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
animation.setStartOffset(startOffset);
assertEquals(startOffset, animation.getStartOffset());
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow,
animation, ACCELERATE_ALPHA_DURATION + startOffset);
Transformation transformation = new Transformation();
long startTime = animation.getStartTime();
animation.getTransformation(startTime, transformation);
float alpha1 = transformation.getAlpha();
assertEquals(0.1f, alpha1, COMPARISON_DELTA);
animation.getTransformation(startTime + 400, transformation);
float alpha2 = transformation.getAlpha();
// alpha is 0.1f during start offset
assertEquals(0.1f, alpha2, COMPARISON_DELTA);
animation.getTransformation(startTime + startOffset, transformation);
float alpha3 = transformation.getAlpha();
// alpha is 0.1f during start offset
assertEquals(0.1f, alpha3, COMPARISON_DELTA);
animation.getTransformation(startTime + startOffset + 1, transformation);
float alpha4 = transformation.getAlpha();
// alpha is lager than 0.1f after start offset
assertTrue(alpha4 > 0.1f);
}
@Test
public void testRunAccelerateAlpha() throws Throwable {
// check default startTime
Animation animation = new Animation() {
};
assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime());
long currentTime = AnimationUtils.currentAnimationTimeMillis();
animation.setStartTime(currentTime);
assertEquals(currentTime, animation.getStartTime());
View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.accelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/accelerate_interpolator"
// android:fromAlpha="0.1"
// android:toAlpha="0.9"
// android:duration="1000" />
Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
assertEquals(Animation.START_ON_FIRST_FRAME, anim.getStartTime());
assertFalse(anim.hasStarted());
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
}
@Test
public void testGetTransformation() throws Throwable {
final View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.accelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/accelerate_interpolator"
// android:fromAlpha="0.1"
// android:toAlpha="0.9"
// android:duration="1000" />
final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
assertFalse(anim.hasStarted());
// we have to PollingCheck the animation status on test thread,
// it cannot be done on UI thread, so we invoke runOnUiThread method here.
mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
// check whether animation has started
PollingCheck.waitFor(anim::hasStarted);
// check transformation objects that is provided by the
// caller and will be filled in by the animation.
Transformation transformation = new Transformation();
long startTime = anim.getStartTime();
assertTrue(anim.getTransformation(startTime, transformation));
float alpha1 = transformation.getAlpha();
assertEquals(0.1f, alpha1, COMPARISON_DELTA);
assertTrue(anim.getTransformation(startTime + 250, transformation));
float alpha2 = transformation.getAlpha();
assertTrue(anim.getTransformation(startTime + 500, transformation));
float alpha3 = transformation.getAlpha();
assertTrue(anim.getTransformation(startTime + 750, transformation));
float alpha4 = transformation.getAlpha();
// wait for animation has ended.
// timeout is larger than duration, in case the system is sluggish
PollingCheck.waitFor(2000, anim::hasEnded);
assertFalse(anim.getTransformation(startTime + 1000, transformation));
float alpha5 = transformation.getAlpha();
assertEquals(0.9f, alpha5, COMPARISON_DELTA);
// check decelerating delta alpha
float delta1 = alpha2 - alpha1;
float delta2 = alpha3 - alpha2;
float delta3 = alpha4 - alpha3;
float delta4 = alpha5 - alpha4;
assertTrue(delta1 < delta2);
assertTrue(delta2 < delta3);
assertTrue(delta3 < delta4);
}
@Test
public void testAccessZAdjustment() {
// check default zAdjustment
Animation animation = new Animation() {
};
assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment());
animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment());
animation.setZAdjustment(Animation.ZORDER_TOP);
assertEquals(Animation.ZORDER_TOP, animation.getZAdjustment());
animation.setZAdjustment(Animation.ZORDER_BOTTOM);
assertEquals(Animation.ZORDER_BOTTOM, animation.getZAdjustment());
}
@Test
public void testInitialize() {
Animation animation = new Animation() {
};
assertFalse(animation.isInitialized());
animation.initialize(320, 480, 320, 480);
assertTrue(animation.isInitialized());
}
@Test
public void testResolveSize() {
MyAnimation myAnimation = new MyAnimation();
assertEquals(1.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 1.0f, 0, 0),
COMPARISON_DELTA);
assertEquals(2.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 2.0f, 0, 0),
COMPARISON_DELTA);
assertEquals(6.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 2, 0),
COMPARISON_DELTA);
assertEquals(9.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 3, 0),
COMPARISON_DELTA);
assertEquals(18.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 6),
COMPARISON_DELTA);
assertEquals(12.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 4),
COMPARISON_DELTA);
int unknownType = 7;
assertEquals(8.0f, myAnimation.resolveSize(unknownType, 8.0f, 3, 4), COMPARISON_DELTA);
assertEquals(10.0f, myAnimation.resolveSize(unknownType, 10.0f, 3, 4), COMPARISON_DELTA);
}
@Test
public void testRestrictDuration() {
Animation animation = new Animation() {
};
animation.setStartOffset(1000);
animation.restrictDuration(500);
assertEquals(500, animation.getStartOffset());
assertEquals(0, animation.getDuration());
assertEquals(0, animation.getRepeatCount());
animation.setStartOffset(1000);
animation.setDuration(1000);
animation.restrictDuration(1500);
assertEquals(500, animation.getDuration());
animation.setStartOffset(1000);
animation.setDuration(1000);
animation.setRepeatCount(3);
animation.restrictDuration(4500);
assertEquals(1, animation.getRepeatCount());
}
@Test
public void testScaleCurrentDuration() {
Animation animation = new Animation() {
};
animation.setDuration(10);
animation.scaleCurrentDuration(0);
assertEquals(0, animation.getDuration());
animation.setDuration(10);
animation.scaleCurrentDuration(2);
assertEquals(20, animation.getDuration());
animation.setDuration(10);
animation.scaleCurrentDuration(-1);
assertEquals(-10, animation.getDuration());
}
@LargeTest
@Test
public void testSetAnimationListener() throws Throwable {
final View animWindow = mActivity.findViewById(R.id.anim_window);
// XML file of R.anim.accelerate_alpha
// <alpha xmlns:android="http://schemas.android.com/apk/res/android"
// android:interpolator="@android:anim/accelerate_interpolator"
// android:fromAlpha="0.1"
// android:toAlpha="0.9"
// android:duration="1000" />
final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
final AnimationListener listener = mock(AnimationListener.class);
anim.setAnimationListener(listener);
verifyZeroInteractions(listener);
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim);
verify(listener, times(1)).onAnimationStart(anim);
verify(listener, times(1)).onAnimationEnd(anim);
verify(listener, never()).onAnimationRepeat(anim);
reset(listener);
anim.setRepeatCount(2);
anim.setRepeatMode(Animation.REVERSE);
AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim,
3000);
verify(listener, times(1)).onAnimationStart(anim);
verify(listener, times(2)).onAnimationRepeat(anim);
verify(listener, times(1)).onAnimationEnd(anim);
reset(listener);
// onAnimationEnd will not be invoked and animation should not end
anim.setRepeatCount(Animation.INFINITE);
mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim));
// Verify that our animation doesn't call listener's onAnimationEnd even after a long
// period of time. We just sleep and then verify what's happened with the listener.
SystemClock.sleep(4 * ACCELERATE_ALPHA_DURATION);
verify(listener, times(1)).onAnimationStart(anim);
verify(listener, atLeastOnce()).onAnimationRepeat(anim);
verify(listener, never()).onAnimationEnd(anim);
}
@Test
public void testStart() {
Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
animation.setStartTime(0);
animation.start();
assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime());
}
@Test
public void testStartNow() {
Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha);
animation.setStartTime(0);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
animation.startNow();
assertEquals(currentTime, animation.getStartTime(), 100);
}
@Test
public void testWillChangeBounds() {
Animation animation = new Animation() {
};
assertTrue(animation.willChangeBounds());
}
@Test
public void testWillChangeTransformationMatrix() {
Animation animation = new Animation() {
};
assertTrue(animation.willChangeTransformationMatrix());
}
@Test
public void testClone() throws CloneNotSupportedException {
MyAnimation myAnimation = new MyAnimation();
myAnimation.setDuration(3000);
myAnimation.setFillAfter(true);
myAnimation.setFillBefore(false);
myAnimation.setFillEnabled(true);
myAnimation.setStartTime(1000);
myAnimation.setRepeatCount(10);
myAnimation.setRepeatMode(Animation.REVERSE);
Animation cloneAnimation = myAnimation.clone();
assertNotSame(myAnimation, cloneAnimation);
assertEquals(myAnimation.getDuration(), cloneAnimation.getDuration());
assertEquals(myAnimation.getFillAfter(), cloneAnimation.getFillAfter());
assertEquals(myAnimation.getFillBefore(), cloneAnimation.getFillBefore());
assertEquals(myAnimation.isFillEnabled(), cloneAnimation.isFillEnabled());
assertEquals(myAnimation.getStartOffset(), cloneAnimation.getStartOffset());
assertEquals(myAnimation.getRepeatCount(), cloneAnimation.getRepeatCount());
assertEquals(myAnimation.getRepeatMode(), cloneAnimation.getRepeatMode());
}
@Test
public void testCancelImmediately() throws Throwable {
MyAnimation anim = new MyAnimation();
final CountDownLatch latch1 = new CountDownLatch(1);
runCanceledAnimation(anim, latch1, false, false);
assertTrue(latch1.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
assertFalse(anim.isStillAnimating());
}
@Test
public void testRepeatingCancelImmediately() throws Throwable {
MyAnimation anim = new MyAnimation();
final CountDownLatch latch2 = new CountDownLatch(1);
runCanceledAnimation(anim, latch2, true, false);
assertTrue(latch2.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
assertFalse(anim.isStillAnimating());
}
@Test
public void testCancelDelayed() throws Throwable {
MyAnimation anim = new MyAnimation();
final CountDownLatch latch3 = new CountDownLatch(1);
runCanceledAnimation(anim, latch3, false, true);
assertTrue(latch3.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
assertFalse(anim.isStillAnimating());
}
@Test
public void testRepeatingCancelDelayed() throws Throwable {
MyAnimation anim = new MyAnimation();
final CountDownLatch latch4 = new CountDownLatch(1);
runCanceledAnimation(anim, latch4, true, true);
assertTrue(latch4.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS));
assertFalse(anim.isStillAnimating());
}
private void runCanceledAnimation(final MyAnimation anim, final CountDownLatch latch,
final boolean repeating, final boolean delayed) throws Throwable {
// The idea behind this test is that canceling an Animation should result in
// it ending, which means not having its getTransformation() method called
// anymore. The trick is that cancel() will still allow one more frame to run,
// so we have to insert some delay between when we cancel and when we can check
// whether it is still animating.
final View view = mActivity.findViewById(R.id.anim_window);
mActivityRule.runOnUiThread(() -> {
anim.setDuration(delayed ? 150 : 100);
if (repeating) {
anim.setRepeatCount(Animation.INFINITE);
}
view.startAnimation(anim);
if (!delayed) {
anim.cancel();
} else {
view.postDelayed(anim::cancel, 50);
}
view.postDelayed(() -> {
anim.setStillAnimating(false);
view.postDelayed(latch::countDown, 50);
}, delayed ? 100 : 50);
});
}
private class MyAnimation extends Animation {
boolean mStillAnimating = false;
@Override
protected void ensureInterpolator() {
super.ensureInterpolator();
}
@Override
protected float resolveSize(int type, float value, int size, int parentSize) {
return super.resolveSize(type, value, size, parentSize);
}
@Override
protected Animation clone() throws CloneNotSupportedException {
return super.clone();
}
public void setStillAnimating(boolean value) {
mStillAnimating = value;
}
public boolean isStillAnimating() {
return mStillAnimating;
}
@Override
public boolean getTransformation(long currentTime, Transformation outTransformation) {
mStillAnimating = true;
return super.getTransformation(currentTime, outTransformation);
}
}
}