| /* |
| * Copyright (C) 2012 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.animation.cts; |
| |
| import static com.android.compatibility.common.util.CtsMockitoUtils.within; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Mockito.any; |
| 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 android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.AnimatorSet; |
| import android.animation.ObjectAnimator; |
| import android.animation.TimeInterpolator; |
| import android.animation.ValueAnimator; |
| import android.os.SystemClock; |
| import android.support.test.InstrumentationRegistry; |
| import android.support.test.filters.MediumTest; |
| import android.support.test.rule.ActivityTestRule; |
| import android.support.test.runner.AndroidJUnit4; |
| import android.view.animation.AccelerateDecelerateInterpolator; |
| import android.view.animation.AccelerateInterpolator; |
| import android.view.animation.LinearInterpolator; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| |
| @MediumTest |
| @RunWith(AndroidJUnit4.class) |
| public class AnimatorSetTest { |
| private AnimationActivity mActivity; |
| private AnimatorSet mAnimatorSet; |
| private float mPreviousDurationScale = 1.0f; |
| private long mDuration = 1000; |
| private Object object; |
| private ObjectAnimator yAnimator; |
| private ObjectAnimator xAnimator; |
| Set<Integer> identityHashes = new HashSet<>(); |
| private static final float EPSILON = 0.001f; |
| |
| @Rule |
| public ActivityTestRule<AnimationActivity> mActivityRule = |
| new ActivityTestRule<>(AnimationActivity.class); |
| |
| @Before |
| public void setup() { |
| InstrumentationRegistry.getInstrumentation().setInTouchMode(false); |
| mActivity = mActivityRule.getActivity(); |
| mPreviousDurationScale = ValueAnimator.getDurationScale(); |
| ValueAnimator.setDurationScale(1.0f); |
| object = mActivity.view.newBall; |
| yAnimator = getYAnimator(object); |
| xAnimator = getXAnimator(object); |
| } |
| |
| @After |
| public void tearDown() { |
| ValueAnimator.setDurationScale(mPreviousDurationScale); |
| } |
| |
| @Test |
| public void testPlaySequentially() throws Throwable { |
| xAnimator.setRepeatCount(0); |
| yAnimator.setRepeatCount(0); |
| xAnimator.setDuration(50); |
| yAnimator.setDuration(50); |
| List<Animator> animators = new ArrayList<Animator>(); |
| animators.add(xAnimator); |
| animators.add(yAnimator); |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.playSequentially(animators); |
| verifySequentialPlayOrder(mAnimatorSet, new Animator[] {xAnimator, yAnimator}); |
| |
| ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 1f); |
| ValueAnimator anim2 = ValueAnimator.ofInt(0, 100); |
| anim1.setDuration(50); |
| anim2.setDuration(50); |
| AnimatorSet set = new AnimatorSet(); |
| set.playSequentially(anim1, anim2); |
| verifySequentialPlayOrder(set, new Animator[] {anim1, anim2}); |
| } |
| |
| /** |
| * Start the animator, and verify the animators are played sequentially in the order that is |
| * defined in the array. |
| * |
| * @param set AnimatorSet to be started and verified |
| * @param animators animators that we put in the AnimatorSet, in the order that they'll play |
| */ |
| private void verifySequentialPlayOrder(final AnimatorSet set, Animator[] animators) |
| throws Throwable { |
| |
| final MyListener[] listeners = new MyListener[animators.length]; |
| for (int i = 0; i < animators.length; i++) { |
| if (i == 0) { |
| listeners[i] = new MyListener(); |
| } else { |
| final int current = i; |
| listeners[i] = new MyListener() { |
| @Override |
| public void onAnimationStart(Animator anim) { |
| super.onAnimationStart(anim); |
| // Check that the previous animator has finished. |
| assertTrue(listeners[current - 1].mEndIsCalled); |
| } |
| }; |
| } |
| animators[i].addListener(listeners[i]); |
| } |
| |
| final CountDownLatch startLatch = new CountDownLatch(1); |
| final CountDownLatch endLatch = new CountDownLatch(1); |
| |
| set.addListener(new MyListener() { |
| @Override |
| public void onAnimationEnd(Animator anim) { |
| endLatch.countDown(); |
| } |
| }); |
| |
| long totalDuration = set.getTotalDuration(); |
| assertFalse(set.isRunning()); |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| startLatch.countDown(); |
| }); |
| |
| // Set timeout to 100ms, if current count reaches 0 before the timeout, startLatch.await(...) |
| // will return immediately. |
| assertTrue(startLatch.await(100, TimeUnit.MILLISECONDS)); |
| assertTrue(set.isRunning()); |
| assertTrue(endLatch.await(totalDuration * 2, TimeUnit.MILLISECONDS)); |
| // Check that all the animators have finished. |
| for (int i = 0; i < listeners.length; i++) { |
| assertTrue(listeners[i].mEndIsCalled); |
| } |
| |
| // Now reverse the animations and verify whether the play order is reversed. |
| for (int i = 0; i < animators.length; i++) { |
| if (i == animators.length - 1) { |
| listeners[i] = new MyListener(); |
| } else { |
| final int current = i; |
| listeners[i] = new MyListener() { |
| @Override |
| public void onAnimationStart(Animator anim) { |
| super.onAnimationStart(anim); |
| // Check that the previous animator has finished. |
| assertTrue(listeners[current + 1].mEndIsCalled); |
| } |
| }; |
| } |
| animators[i].removeAllListeners(); |
| animators[i].addListener(listeners[i]); |
| } |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.reverse(); |
| startLatch.countDown(); |
| }); |
| |
| // Set timeout to 100ms, if current count reaches 0 before the timeout, startLatch.await(..) |
| // will return immediately. |
| assertTrue(startLatch.await(100, TimeUnit.MILLISECONDS)); |
| assertTrue(set.isRunning()); |
| assertTrue(endLatch.await(totalDuration * 2, TimeUnit.MILLISECONDS)); |
| |
| } |
| |
| @Test |
| public void testPlayTogether() throws Throwable { |
| xAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| Animator[] animatorArray = {xAnimator, yAnimator}; |
| |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.playTogether(animatorArray); |
| |
| assertFalse(mAnimatorSet.isRunning()); |
| assertFalse(xAnimator.isRunning()); |
| assertFalse(yAnimator.isRunning()); |
| startAnimation(mAnimatorSet); |
| SystemClock.sleep(100); |
| assertTrue(mAnimatorSet.isRunning()); |
| assertTrue(xAnimator.isRunning()); |
| assertTrue(yAnimator.isRunning()); |
| |
| // Now assemble another animator set |
| ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 100f); |
| ValueAnimator anim2 = ValueAnimator.ofFloat(10f, 100f); |
| AnimatorSet set = new AnimatorSet(); |
| set.playTogether(anim1, anim2); |
| |
| assertFalse(set.isRunning()); |
| assertFalse(anim1.isRunning()); |
| assertFalse(anim2.isRunning()); |
| startAnimation(set); |
| SystemClock.sleep(100); |
| assertTrue(set.isRunning()); |
| assertTrue(anim1.isRunning()); |
| assertTrue(anim2.isRunning()); |
| } |
| |
| @Test |
| public void testPlayBeforeAfter() throws Throwable { |
| xAnimator.setRepeatCount(0); |
| yAnimator.setRepeatCount(0); |
| final ValueAnimator zAnimator = ValueAnimator.ofFloat(0f, 100f); |
| |
| xAnimator.setDuration(50); |
| yAnimator.setDuration(50); |
| zAnimator.setDuration(50); |
| |
| AnimatorSet set = new AnimatorSet(); |
| set.play(yAnimator).before(zAnimator).after(xAnimator); |
| |
| verifySequentialPlayOrder(set, new Animator[] {xAnimator, yAnimator, zAnimator}); |
| } |
| |
| @Test |
| public void testListenerCallbackOnEmptySet() throws Throwable { |
| // Create an AnimatorSet that only contains one empty AnimatorSet, and checks the callback |
| // sequence by checking the time stamps of the callbacks. |
| final AnimatorSet emptySet = new AnimatorSet(); |
| final AnimatorSet set = new AnimatorSet(); |
| set.play(emptySet); |
| MyListener listener = new MyListener() { |
| long startTime = 0; |
| long endTime = 0; |
| @Override |
| public void onAnimationStart(Animator animation) { |
| super.onAnimationStart(animation); |
| startTime = SystemClock.currentThreadTimeMillis(); |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| super.onAnimationEnd(animation); |
| endTime = SystemClock.currentThreadTimeMillis(); |
| assertTrue(endTime >= startTime); |
| assertTrue(startTime != 0); |
| } |
| }; |
| set.addListener(listener); |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| }); |
| assertTrue(listener.mStartIsCalled); |
| assertTrue(listener.mEndIsCalled); |
| } |
| |
| @Test |
| public void testPauseAndResume() throws Throwable { |
| final AnimatorSet set = new AnimatorSet(); |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f); |
| a1.setDuration(50); |
| ValueAnimator a2 = ValueAnimator.ofFloat(0f, 100f); |
| a2.setDuration(50); |
| a1.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationStart(Animator animation) { |
| // Pause non-delayed set once the child animator starts |
| set.pause(); |
| } |
| }); |
| set.playTogether(a1, a2); |
| |
| final AnimatorSet delayedSet = new AnimatorSet(); |
| ValueAnimator a3 = ValueAnimator.ofFloat(0f, 100f); |
| a3.setDuration(50); |
| ValueAnimator a4 = ValueAnimator.ofFloat(0f, 100f); |
| a4.setDuration(50); |
| delayedSet.playSequentially(a3, a4); |
| delayedSet.setStartDelay(50); |
| |
| MyListener l1 = new MyListener(); |
| MyListener l2 = new MyListener(); |
| set.addListener(l1); |
| delayedSet.addListener(l2); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| delayedSet.start(); |
| |
| // Pause the delayed set during start delay |
| delayedSet.pause(); |
| }); |
| |
| // Sleep long enough so that if the sets are not properly paused, they would have |
| // finished. |
| SystemClock.sleep(300); |
| // Verify that both sets have been paused and *not* finished. |
| assertTrue(set.isPaused()); |
| assertTrue(delayedSet.isPaused()); |
| assertTrue(l1.mStartIsCalled); |
| assertTrue(l2.mStartIsCalled); |
| assertFalse(l1.mEndIsCalled); |
| assertFalse(l2.mEndIsCalled); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.resume(); |
| delayedSet.resume(); |
| }); |
| SystemClock.sleep(300); |
| |
| assertFalse(set.isPaused()); |
| assertFalse(delayedSet.isPaused()); |
| assertTrue(l1.mEndIsCalled); |
| assertTrue(l2.mEndIsCalled); |
| } |
| |
| @Test |
| public void testPauseBeforeStart() throws Throwable { |
| final AnimatorSet set = new AnimatorSet(); |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f); |
| a1.setDuration(50); |
| ValueAnimator a2 = ValueAnimator.ofFloat(0f, 100f); |
| a2.setDuration(50); |
| set.setStartDelay(50); |
| set.playSequentially(a1, a2); |
| |
| final MyListener listener = new MyListener(); |
| set.addListener(listener); |
| |
| mActivityRule.runOnUiThread(() -> { |
| // Pause animator set before calling start() |
| set.pause(); |
| // Verify that pause should have no effect on a not-yet-started animator. |
| assertFalse(set.isPaused()); |
| set.start(); |
| }); |
| SystemClock.sleep(300); |
| |
| // Animator set should finish running by now since it's not paused. |
| assertTrue(listener.mStartIsCalled); |
| assertTrue(listener.mEndIsCalled); |
| } |
| |
| @Test |
| public void testDuration() throws Throwable { |
| xAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| Animator[] animatorArray = { xAnimator, yAnimator }; |
| |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.playTogether(animatorArray); |
| mAnimatorSet.setDuration(1000); |
| |
| startAnimation(mAnimatorSet); |
| SystemClock.sleep(100); |
| assertEquals(mAnimatorSet.getDuration(), 1000); |
| } |
| |
| @Test |
| public void testStartDelay() throws Throwable { |
| xAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| Animator[] animatorArray = { xAnimator, yAnimator }; |
| |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.playTogether(animatorArray); |
| mAnimatorSet.setStartDelay(10); |
| |
| startAnimation(mAnimatorSet); |
| SystemClock.sleep(100); |
| assertEquals(mAnimatorSet.getStartDelay(), 10); |
| } |
| |
| /** |
| * This test sets up an AnimatorSet with start delay. One of the child animators also has |
| * start delay. We then verify that start delay was handled correctly on both AnimatorSet |
| * and individual animator level. |
| */ |
| @Test |
| public void testReverseWithStartDelay() throws Throwable { |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f); |
| a1.setDuration(200); |
| Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class); |
| a1.addListener(listener1); |
| |
| ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f); |
| a2.setDuration(200); |
| // Set start delay on a2 so that the delay is passed 100ms after a1 is finished. |
| a2.setStartDelay(300); |
| Animator.AnimatorListener listener = mock(AnimatorListenerAdapter.class); |
| a2.addListener(listener); |
| |
| a2.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation, boolean inReverse) { |
| assertTrue(inReverse); |
| // By the time a2 finishes reversing, a1 should not have started. |
| assertFalse(a1.isStarted()); |
| } |
| }); |
| |
| AnimatorSet set = new AnimatorSet(); |
| set.playTogether(a1, a2); |
| set.setStartDelay(1000); |
| Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| mActivityRule.runOnUiThread(() -> { |
| set.reverse(); |
| assertTrue(a2.isStarted()); |
| assertTrue(a2.isRunning()); |
| }); |
| |
| // a2 should finish 200ms after reverse started |
| verify(listener, within(300)).onAnimationEnd(a2, true); |
| // When a2 finishes, a1 should not have started yet |
| verify(listener1, never()).onAnimationStart(a1, true); |
| |
| // The whole set should finish within 500ms, i.e. 300ms after a2 is finished. This verifies |
| // that the AnimatorSet didn't mistakenly use its start delay in the reverse run. |
| verify(setListener, within(400)).onAnimationEnd(set, true); |
| verify(listener1, times(1)).onAnimationEnd(a1, true); |
| |
| } |
| |
| /** |
| * Test that duration scale is handled correctly in the AnimatorSet. |
| */ |
| @Test |
| public void testZeroDurationScale() throws Throwable { |
| ValueAnimator.setDurationScale(0); |
| |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f); |
| a1.setDuration(200); |
| Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class); |
| a1.addListener(listener1); |
| |
| ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f); |
| a2.setDuration(200); |
| // Set start delay on a2 so that the delay is passed 100ms after a1 is finished. |
| a2.setStartDelay(300); |
| Animator.AnimatorListener listener2 = mock(AnimatorListenerAdapter.class); |
| a2.addListener(listener2); |
| |
| AnimatorSet set = new AnimatorSet(); |
| set.playSequentially(a1, a2); |
| set.setStartDelay(1000); |
| Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| verify(setListener, times(0)).onAnimationEnd(any(AnimatorSet.class), |
| any(boolean.class)); |
| }); |
| verify(setListener, within(100)).onAnimationEnd(set, false); |
| verify(listener1, times(1)).onAnimationEnd(a1, false); |
| verify(listener2, times(1)).onAnimationEnd(a2, false); |
| } |
| |
| /** |
| * Test that non-zero duration scale is handled correctly in the AnimatorSet. |
| */ |
| @Test |
| public void testDurationScale() throws Throwable { |
| // Change the duration scale to 3 |
| ValueAnimator.setDurationScale(3f); |
| |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f); |
| a1.setDuration(100); |
| Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class); |
| a1.addListener(listener1); |
| |
| ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f); |
| a2.setDuration(100); |
| // Set start delay on a2 so that the delay is passed 100ms after a1 is finished. |
| a2.setStartDelay(200); |
| Animator.AnimatorListener listener2 = mock(AnimatorListenerAdapter.class); |
| a2.addListener(listener2); |
| |
| AnimatorSet set = new AnimatorSet(); |
| set.playSequentially(a1, a2); |
| Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| set.setStartDelay(200); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| }); |
| |
| // Sleep for part of the start delay and check that no child animator has started, to verify |
| // that the duration scale has been properly scaled. |
| SystemClock.sleep(400); |
| // start delay of the set should be scaled to 600ms |
| verify(listener1, never()).onAnimationStart(a1, false); |
| verify(listener2, never()).onAnimationStart(a2, false); |
| |
| verify(listener1, within(400)).onAnimationStart(a1, false); |
| // Sleep for part of a2's start delay and verify that a2 is still in the delayed stage. This |
| // is to make sure child animator's start delay is also properly scaled. |
| SystemClock.sleep(400); |
| assertTrue(a2.isStarted()); |
| assertFalse(a2.isRunning()); |
| |
| // Sleep past the start delay |
| SystemClock.sleep(350); |
| assertTrue(a2.isRunning()); |
| |
| // Verify that the AnimatorSet has finished within 1650ms since the start of the animation. |
| // The duration of the set is 500ms, duration scale = 3. |
| verify(setListener, within(500)).onAnimationEnd(set, false); |
| verify(listener1, times(1)).onAnimationEnd(a1, false); |
| verify(listener2, times(1)).onAnimationEnd(a2, false); |
| } |
| |
| /** |
| * This test sets up 10 animators playing together. We expect the start time for all animators |
| * to be the same. |
| */ |
| @Test |
| public void testMultipleAnimatorsPlayTogether() throws Throwable { |
| Animator[] animators = new Animator[10]; |
| for (int i = 0; i < 10; i++) { |
| animators[i] = ValueAnimator.ofFloat(0f, 1f); |
| } |
| AnimatorSet set = new AnimatorSet(); |
| set.playTogether(animators); |
| set.setStartDelay(80); |
| |
| Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| }); |
| SystemClock.sleep(150); |
| for (int i = 0; i < 10; i++) { |
| assertTrue(animators[i].isRunning()); |
| } |
| |
| verify(setListener, within(400)).onAnimationEnd(set, false); |
| } |
| |
| @Test |
| public void testGetChildAnimations() throws Throwable { |
| Animator[] animatorArray = { xAnimator, yAnimator }; |
| |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.getChildAnimations(); |
| assertEquals(0, mAnimatorSet.getChildAnimations().size()); |
| mAnimatorSet.playSequentially(animatorArray); |
| assertEquals(2, mAnimatorSet.getChildAnimations().size()); |
| } |
| |
| @Test |
| public void testSetInterpolator() throws Throwable { |
| xAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| Animator[] animatorArray = {xAnimator, yAnimator}; |
| TimeInterpolator interpolator = new AccelerateDecelerateInterpolator(); |
| mAnimatorSet = new AnimatorSet(); |
| mAnimatorSet.playTogether(animatorArray); |
| mAnimatorSet.setInterpolator(interpolator); |
| |
| assertFalse(mAnimatorSet.isRunning()); |
| startAnimation(mAnimatorSet); |
| SystemClock.sleep(100); |
| |
| ArrayList<Animator> animatorList = mAnimatorSet.getChildAnimations(); |
| assertEquals(interpolator, animatorList.get(0).getInterpolator()); |
| assertEquals(interpolator, animatorList.get(1).getInterpolator()); |
| } |
| |
| private ObjectAnimator getXAnimator(Object object) { |
| String propertyX = "x"; |
| float startX = mActivity.mStartX; |
| float endX = mActivity.mStartX + mActivity.mDeltaX; |
| ObjectAnimator xAnimator = ObjectAnimator.ofFloat(object, propertyX, startX, endX); |
| xAnimator.setDuration(mDuration); |
| xAnimator.setRepeatCount(ValueAnimator.INFINITE); |
| xAnimator.setInterpolator(new AccelerateInterpolator()); |
| xAnimator.setRepeatMode(ValueAnimator.REVERSE); |
| return xAnimator; |
| } |
| |
| private ObjectAnimator getYAnimator(Object object) { |
| String property = "y"; |
| float startY = mActivity.mStartY; |
| float endY = mActivity.mStartY + mActivity.mDeltaY; |
| ObjectAnimator yAnimator = ObjectAnimator.ofFloat(object, property, startY, endY); |
| yAnimator.setDuration(mDuration); |
| yAnimator.setRepeatCount(2); |
| yAnimator.setInterpolator(new AccelerateInterpolator()); |
| yAnimator.setRepeatMode(ValueAnimator.REVERSE); |
| return yAnimator; |
| } |
| |
| private void startAnimation(final AnimatorSet animatorSet) throws Throwable { |
| mActivityRule.runOnUiThread(() -> mActivity.startAnimatorSet(animatorSet)); |
| } |
| |
| private void assertUnique(Object object) { |
| assertUnique(object, ""); |
| } |
| |
| private void assertUnique(Object object, String msg) { |
| final int code = System.identityHashCode(object); |
| assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code)); |
| |
| } |
| |
| @Test |
| public void testClone() throws Throwable { |
| final AnimatorSet set1 = new AnimatorSet(); |
| final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {}; |
| set1.addListener(setListener); |
| ObjectAnimator animator1 = new ObjectAnimator(); |
| animator1.setDuration(100); |
| animator1.setPropertyName("x"); |
| animator1.setIntValues(5); |
| animator1.setInterpolator(new LinearInterpolator()); |
| AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){}; |
| AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){}; |
| animator1.addListener(listener1); |
| |
| ObjectAnimator animator2 = new ObjectAnimator(); |
| animator2.setDuration(100); |
| animator2.setInterpolator(new LinearInterpolator()); |
| animator2.addListener(listener2); |
| animator2.setPropertyName("y"); |
| animator2.setIntValues(10); |
| |
| set1.playTogether(animator1, animator2); |
| |
| AnimateObject target = new AnimateObject(); |
| set1.setTarget(target); |
| mActivityRule.runOnUiThread(set1::start); |
| assertTrue(set1.isStarted()); |
| |
| animator1.getListeners(); |
| AnimatorSet set2 = set1.clone(); |
| assertFalse(set2.isStarted()); |
| |
| assertUnique(set1); |
| assertUnique(animator1); |
| assertUnique(animator2); |
| |
| assertUnique(set2); |
| assertEquals(2, set2.getChildAnimations().size()); |
| |
| Animator clone1 = set2.getChildAnimations().get(0); |
| Animator clone2 = set2.getChildAnimations().get(1); |
| |
| for (Animator animator : set2.getChildAnimations()) { |
| assertUnique(animator); |
| } |
| |
| assertTrue(clone1.getListeners().contains(listener1)); |
| assertTrue(clone2.getListeners().contains(listener2)); |
| |
| assertTrue(set2.getListeners().contains(setListener)); |
| |
| for (Animator.AnimatorListener listener : set1.getListeners()) { |
| assertTrue(set2.getListeners().contains(listener)); |
| } |
| |
| assertEquals(animator1.getDuration(), clone1.getDuration()); |
| assertEquals(animator2.getDuration(), clone2.getDuration()); |
| assertSame(animator1.getInterpolator(), clone1.getInterpolator()); |
| assertSame(animator2.getInterpolator(), clone2.getInterpolator()); |
| } |
| |
| /** |
| * Testing seeking in an AnimatorSet containing sequential animators. |
| */ |
| @Test |
| public void testSeeking() throws Throwable { |
| final AnimatorSet set = new AnimatorSet(); |
| final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 150f); |
| a1.setDuration(150); |
| final ValueAnimator a2 = ValueAnimator.ofFloat(150f, 250f); |
| a2.setDuration(100); |
| final ValueAnimator a3 = ValueAnimator.ofFloat(250f, 300f); |
| a3.setDuration(50); |
| |
| a1.setInterpolator(null); |
| a2.setInterpolator(null); |
| a3.setInterpolator(null); |
| |
| set.playSequentially(a1, a2, a3); |
| |
| set.setCurrentPlayTime(100); |
| assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(150f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| set.setCurrentPlayTime(280); |
| assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(280f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| super.onAnimationEnd(animation); |
| assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(300f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| } |
| }; |
| AnimatorListenerAdapter mockListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| set.addListener(mockListener); |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| }); |
| |
| verify(mockListener, within(300)).onAnimationEnd(set, false); |
| |
| // Seek after a run to the middle-ish, and verify the first animator is at the end |
| // value and the 3rd at beginning value, and the 2nd animator is at the seeked value. |
| set.setCurrentPlayTime(200); |
| assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(200f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON); |
| } |
| |
| /** |
| * Testing seeking in an AnimatorSet containing infinite animators. |
| */ |
| @Test |
| public void testSeekingInfinite() { |
| final AnimatorSet set = new AnimatorSet(); |
| final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f); |
| a1.setDuration(100); |
| final ValueAnimator a2 = ValueAnimator.ofFloat(100f, 200f); |
| a2.setDuration(100); |
| a2.setRepeatCount(ValueAnimator.INFINITE); |
| a2.setRepeatMode(ValueAnimator.RESTART); |
| |
| final ValueAnimator a3 = ValueAnimator.ofFloat(100f, 200f); |
| a3.setDuration(100); |
| a3.setRepeatCount(ValueAnimator.INFINITE); |
| a3.setRepeatMode(ValueAnimator.REVERSE); |
| |
| a1.setInterpolator(null); |
| a2.setInterpolator(null); |
| a3.setInterpolator(null); |
| set.play(a1).before(a2); |
| set.play(a1).before(a3); |
| |
| set.setCurrentPlayTime(50); |
| assertEquals(50f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| set.setCurrentPlayTime(100); |
| assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| // Seek to the 1st iteration of the infinite repeat animators, and they should have the |
| // same value. |
| set.setCurrentPlayTime(180); |
| assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(180f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| // Seek to the 2nd iteration of the infinite repeat animators, and they should have |
| // different values as they have different repeat mode. |
| set.setCurrentPlayTime(280); |
| assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON); |
| assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON); |
| assertEquals(120f, (Float) a3.getAnimatedValue(), EPSILON); |
| |
| } |
| |
| /** |
| * This test verifies that getCurrentPlayTime() returns the right value. |
| */ |
| @Test |
| public void testGetCurrentPlayTime() throws Throwable { |
| // Setup an AnimatorSet with start delay |
| final AnimatorSet set = new AnimatorSet(); |
| final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f).setDuration(300); |
| anim.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationStart(Animator animation, boolean inReverse) { |
| assertFalse(inReverse); |
| assertTrue(set.getCurrentPlayTime() >= 200); |
| } |
| }); |
| set.play(anim); |
| set.setStartDelay(100); |
| |
| Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class); |
| set.addListener(setListener); |
| |
| // Set a seek time and verify, before start |
| set.setCurrentPlayTime(20); |
| assertEquals(20, set.getCurrentPlayTime()); |
| |
| // Now start() should start right away from the seeked position, skipping the delay. |
| mActivityRule.runOnUiThread(() -> { |
| set.setCurrentPlayTime(200); |
| set.start(); |
| assertEquals(200, set.getCurrentPlayTime()); |
| }); |
| |
| // When animation is seeked to 200ms, it should take another 100ms to end. |
| verify(setListener, within(200)).onAnimationEnd(set, false); |
| } |
| |
| @Test |
| public void testNotifiesAfterEnd() throws Throwable { |
| final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); |
| Animator.AnimatorListener listener = new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationStart(Animator animation) { |
| assertTrue(animation.isStarted()); |
| assertTrue(animation.isRunning()); |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| assertFalse(animation.isRunning()); |
| assertFalse(animation.isStarted()); |
| super.onAnimationEnd(animation); |
| } |
| }; |
| animator.addListener(listener); |
| final AnimatorSet animatorSet = new AnimatorSet(); |
| animatorSet.playTogether(animator); |
| animatorSet.addListener(listener); |
| mActivityRule.runOnUiThread(() -> { |
| animatorSet.start(); |
| animator.end(); |
| assertFalse(animator.isStarted()); |
| }); |
| } |
| |
| /** |
| * |
| * This test verifies that custom ValueAnimators will be start()'ed in a set. |
| */ |
| @Test |
| public void testChildAnimatorStartCalled() throws Throwable { |
| MyValueAnimator a1 = new MyValueAnimator(); |
| MyValueAnimator a2 = new MyValueAnimator(); |
| AnimatorSet set = new AnimatorSet(); |
| set.playTogether(a1, a2); |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| assertTrue(a1.mStartCalled); |
| assertTrue(a2.mStartCalled); |
| }); |
| |
| } |
| |
| /** |
| * This test sets up an AnimatorSet that contains two sequential animations. The first animation |
| * is infinite, the second animation therefore has an infinite start time. This test verifies |
| * that the infinite start time is handled correctly. |
| */ |
| @Test |
| public void testInfiniteStartTime() throws Throwable { |
| ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f); |
| a1.setRepeatCount(ValueAnimator.INFINITE); |
| ValueAnimator a2 = ValueAnimator.ofFloat(0f, 1f); |
| |
| AnimatorSet set = new AnimatorSet(); |
| set.playSequentially(a1, a2); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.start(); |
| }); |
| |
| assertEquals(Animator.DURATION_INFINITE, set.getTotalDuration()); |
| |
| mActivityRule.runOnUiThread(() -> { |
| set.end(); |
| }); |
| } |
| |
| static class TargetObj { |
| public float value = 0; |
| |
| public void setVal(float value) { |
| this.value = value; |
| } |
| } |
| |
| class AnimateObject { |
| int x = 1; |
| int y = 2; |
| } |
| |
| static class MyListener extends AnimatorListenerAdapter { |
| boolean mStartIsCalled = false; |
| boolean mEndIsCalled = false; |
| |
| public void onAnimationStart(Animator animation) { |
| mStartIsCalled = true; |
| } |
| |
| public void onAnimationEnd(Animator animation) { |
| mEndIsCalled = true; |
| } |
| } |
| |
| static class MyValueAnimator extends ValueAnimator { |
| boolean mStartCalled = false; |
| @Override |
| public void start() { |
| // Do not call super intentionally. |
| mStartCalled = true; |
| } |
| } |
| } |