blob: d154ba710263713984e23bbdf5c62a333481e236 [file] [log] [blame]
/*
* Copyright (C) 2017 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 androidx.dynamicanimation.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.AdditionalMatchers.lt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.AndroidRuntimeException;
import android.view.View;
import androidx.core.view.ViewCompat;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import androidx.dynamicanimation.animation.FloatValueHolder;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.dynamicanimation.test.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class SpringTests {
@Rule public final ActivityTestRule<AnimationActivity> mActivityTestRule;
public View mView1;
public View mView2;
@Rule
public ExpectedException mExpectedException = ExpectedException.none();
public SpringTests() {
mActivityTestRule = new ActivityTestRule<>(AnimationActivity.class);
}
@Before
public void setup() throws Exception {
mView1 = mActivityTestRule.getActivity().findViewById(R.id.anim_view);
mView2 = mActivityTestRule.getActivity().findViewById(R.id.anim_another_view);
}
/**
* Test that custom properties are supported.
*/
@Test
public void testCustomProperties() {
final Object animObj = new Object();
FloatPropertyCompat property = new FloatPropertyCompat("") {
private float mValue = 0f;
@Override
public float getValue(Object object) {
assertEquals(animObj, object);
return mValue;
}
@Override
public void setValue(Object object, float value) {
assertEquals(animObj, object);
assertTrue(value >= mValue);
mValue = value;
}
};
final SpringAnimation anim = new SpringAnimation(animObj, property, 1f);
DynamicAnimation.OnAnimationEndListener listener = mock(
DynamicAnimation.OnAnimationEndListener.class);
anim.addEndListener(listener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.start();
}
});
verify(listener, timeout(1000)).onAnimationEnd(anim, false, 1f, 0f);
assertEquals(1f, property.getValue(animObj), 0f);
}
/**
* Test that spring animation can work with a single property without an object.
*/
@Test
public void testFloatValueHolder() {
final FloatValueHolder floatValueHolder = new FloatValueHolder(0f);
DynamicAnimation.OnAnimationUpdateListener updateListener =
new DynamicAnimation.OnAnimationUpdateListener() {
private float mLastValue = 0f;
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
// New value >= value from last frame
assertTrue(value >= mLastValue);
mLastValue = value;
assertEquals(value, floatValueHolder.getValue(), 0f);
}
};
DynamicAnimation.OnAnimationUpdateListener mockListener =
mock(DynamicAnimation.OnAnimationUpdateListener.class);
final SpringAnimation anim = new SpringAnimation(floatValueHolder)
.addUpdateListener(updateListener).addUpdateListener(mockListener);
anim.setSpring(new SpringForce(1000).setDampingRatio(1.2f));
DynamicAnimation.OnAnimationEndListener listener = mock(
DynamicAnimation.OnAnimationEndListener.class);
anim.addEndListener(listener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.setStartValue(0).start();
}
});
verify(mockListener, timeout(1000).atLeast(10)).onAnimationUpdate(eq(anim), lt(1000f),
any(float.class));
verify(listener, timeout(1000)).onAnimationEnd(anim, false, 1000f, 0f);
}
/**
* Check the final position of the default spring against what's being set through the
* constructor.
*/
@Test
public void testGetFinalPosition() {
SpringAnimation animation = new SpringAnimation(mView1, DynamicAnimation.TRANSLATION_X, 20);
assertEquals(20, animation.getSpring().getFinalPosition(), 0);
SpringForce spring = new SpringForce();
spring.setFinalPosition(25.0f);
assertEquals(25.0f, spring.getFinalPosition(), 0.0f);
}
/**
* Verify that for over-damped springs, the higher the damping ratio, the slower it is. Also
* verify that critically damped springs finish faster than overdamped springs.
*/
@Test
public void testDampingRatioOverAndCriticallyDamped() {
// Compare overdamped springs
final SpringAnimation anim1 = new SpringAnimation(mView1, DynamicAnimation.X, 0);
final SpringAnimation anim2 = new SpringAnimation(mView2, DynamicAnimation.Y, 0);
final SpringAnimation anim3 = new SpringAnimation(mView2, DynamicAnimation.Z, 0);
final DynamicAnimation.OnAnimationUpdateListener updateListener =
new DynamicAnimation.OnAnimationUpdateListener() {
public float position1 = 1000;
public float position2 = 1000;
public float position3 = 1000;
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value,
float velocity) {
if (animation == anim1) {
position1 = value;
if (position1 == 800) {
// first frame
assertEquals(position1, position2, 0);
assertEquals(position1, position3, 0);
} else {
assertTrue(position2 > position1);
assertTrue(position3 > position2);
assertTrue(800 > position3);
}
} else if (animation == anim2) {
position2 = value;
} else {
position3 = value;
}
}
};
final MyEndListener l1 = new MyEndListener();
final MyEndListener l2 = new MyEndListener();
final MyEndListener l3 = new MyEndListener();
final DynamicAnimation.OnAnimationEndListener mockListener =
mock(DynamicAnimation.OnAnimationEndListener.class);
anim1.getSpring().setStiffness(SpringForce.STIFFNESS_HIGH).setDampingRatio(1f);
anim2.getSpring().setStiffness(SpringForce.STIFFNESS_HIGH).setDampingRatio(1.5f);
anim3.getSpring().setStiffness(SpringForce.STIFFNESS_HIGH).setDampingRatio(2.0f);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim2.setStartValue(800).addUpdateListener(updateListener).addEndListener(l2)
.start();
anim3.setStartValue(800).addUpdateListener(updateListener).addEndListener(l3)
.addEndListener(mockListener).start();
anim1.setStartValue(800).addUpdateListener(updateListener).addEndListener(l1)
.start();
}
});
// The spring animation with critically-damped spring should return to rest position faster.
verify(mockListener, timeout(2000)).onAnimationEnd(anim3, false, 0, 0);
assertTrue(l1.endTime > 0);
assertTrue(l2.endTime > l1.endTime);
assertTrue(l3.endTime > l2.endTime);
}
/**
* Verify that more underdamped springs are bouncier, and that critically damped springs finish
* faster than underdamped springs.
*/
@Test
public void testDampingRatioUnderDamped() {
final SpringAnimation anim1 = new SpringAnimation(mView1, DynamicAnimation.ROTATION, 0);
final SpringAnimation anim2 = new SpringAnimation(mView2, DynamicAnimation.ROTATION_X, 0);
final SpringAnimation anim3 = new SpringAnimation(mView2, DynamicAnimation.ROTATION_Y, 0);
final DynamicAnimation.OnAnimationUpdateListener updateListener =
new DynamicAnimation.OnAnimationUpdateListener() {
public float bounceCount1 = 0;
public float bounceCount2 = 0;
public float velocity1 = 0;
public float velocity2 = 0;
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value,
float velocity) {
if (animation == anim1) {
if (velocity > 0 && velocity1 < 0) {
bounceCount1++;
}
velocity1 = velocity;
} else if (animation == anim2) {
velocity2 = velocity;
if (velocity > 0 && velocity2 < 0) {
bounceCount2++;
assertTrue(bounceCount1 > bounceCount2);
}
}
}
};
final MyEndListener l1 = new MyEndListener();
final MyEndListener l2 = new MyEndListener();
final MyEndListener l3 = new MyEndListener();
final DynamicAnimation.OnAnimationEndListener mockListener =
mock(DynamicAnimation.OnAnimationEndListener.class);
anim1.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM).setDampingRatio(0.3f);
anim2.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM).setDampingRatio(0.5f);
anim3.getSpring().setStiffness(SpringForce.STIFFNESS_MEDIUM).setDampingRatio(1f);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim1.setStartValue(360).addUpdateListener(updateListener).addEndListener(l1)
.start();
anim2.setStartValue(360).addUpdateListener(updateListener).addEndListener(l2)
.addEndListener(mockListener).start();
anim3.setStartValue(360).addEndListener(l3).start();
}
});
// The spring animation with critically-damped spring should return to rest position faster.
verify(mockListener, timeout(2000)).onAnimationEnd(anim2, false, 0, 0);
assertFalse(anim3.isRunning());
assertTrue(l3.endTime > 0);
assertTrue(l2.endTime > l3.endTime);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
if (anim1.isRunning()) {
anim1.cancel();
} else {
assertTrue(l1.endTime > l2.endTime);
}
}
});
}
/**
* Verify that stiffer spring animations finish sooner than less stiff spring animations. Run
* the same verification on different damping ratios.
*/
@LargeTest
@Test
public void testStiffness() {
float[] dampingRatios = {0.3f, 0.5f, 1f, 5f};
final float[] stiffness = {50f, 500f, 1500f, 5000f};
DynamicAnimation.ViewProperty[] viewProperties =
{DynamicAnimation.SCROLL_X, DynamicAnimation.TRANSLATION_X,
DynamicAnimation.TRANSLATION_Y, DynamicAnimation.TRANSLATION_Z};
assertEquals(viewProperties.length, stiffness.length);
final SpringAnimation[] springAnims = new SpringAnimation[stiffness.length];
SpringForce[] springs = new SpringForce[stiffness.length];
MyEndListener[] listeners = new MyEndListener[stiffness.length];
// Sets stiffness
for (int i = 0; i < stiffness.length; i++) {
springs[i] = new SpringForce(0).setStiffness(stiffness[i]);
listeners[i] = new MyEndListener();
springAnims[i] = new SpringAnimation(mView1, viewProperties[i]).setSpring(springs[i])
.addEndListener(listeners[i]);
}
for (int i = 0; i < dampingRatios.length; i++) {
for (int j = 0; j < stiffness.length; j++) {
springs[j].setDampingRatio(dampingRatios[i]);
springAnims[j].setStartValue(0).setStartVelocity(500);
listeners[j].endTime = -1;
}
DynamicAnimation.OnAnimationEndListener mockListener = mock(
DynamicAnimation.OnAnimationEndListener.class);
springAnims[1].addEndListener(mockListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
for (int j = 0; j < stiffness.length; j++) {
springAnims[j].start();
}
}
});
verify(mockListener, timeout(2000)).onAnimationEnd(springAnims[1], false, 0f, 0f);
if (springAnims[0].isRunning()) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
springAnims[0].cancel();
}
});
}
for (int j = 1; j < stiffness.length; j++) {
// The stiffer spring should finish no later than the less stiff spring.
assertTrue(listeners[j - 1].endTime > listeners[j].endTime);
}
}
}
/**
* Test negative stiffness and expect exception.
*/
@Test
public void testInvalidStiffness() {
SpringForce spring = new SpringForce();
mExpectedException.expect(IllegalArgumentException.class);
spring.setStiffness(-5f);
}
/**
* Test negative dampingRatio and expect exception.
*/
@Test
public void testInvalidDampingRatio() {
SpringForce spring = new SpringForce();
mExpectedException.expect(IllegalArgumentException.class);
spring.setDampingRatio(-5f);
}
/**
* Remove an update listener and an end listener, and check that there are no interaction after
* removal.
*/
@Test
public void testRemoveListeners() {
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.ALPHA, 0.5f);
DynamicAnimation.OnAnimationEndListener endListener = mock(
DynamicAnimation.OnAnimationEndListener.class);
DynamicAnimation.OnAnimationEndListener removedEndListener = mock(
DynamicAnimation.OnAnimationEndListener.class);
DynamicAnimation.OnAnimationUpdateListener updateListener = mock(
DynamicAnimation.OnAnimationUpdateListener.class);
DynamicAnimation.OnAnimationUpdateListener removedUpdateListener = mock(
DynamicAnimation.OnAnimationUpdateListener.class);
anim.addEndListener(removedEndListener);
anim.addEndListener(endListener);
anim.removeEndListener(removedEndListener);
anim.addUpdateListener(removedUpdateListener);
anim.addUpdateListener(updateListener);
anim.removeUpdateListener(removedUpdateListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.start();
}
});
verify(endListener, timeout(1000)).onAnimationEnd(anim, false, 0.5f, 0f);
verify(updateListener, atLeast(2)).onAnimationUpdate(eq(anim), any(float.class),
any(float.class));
verifyZeroInteractions(removedEndListener);
verifyZeroInteractions(removedUpdateListener);
}
/**
* Verifies stiffness getter returns the right value.
*/
@Test
public void testGetStiffness() {
SpringForce spring = new SpringForce();
spring.setStiffness(1.0f);
assertEquals(1.0f, spring.getStiffness(), 0.0f);
spring.setStiffness(2.0f);
assertEquals(2.0f, spring.getStiffness(), 0.0f);
}
/**
* Verifies damping ratio getter returns the right value.
*/
@Test
public void testGetDampingRatio() {
SpringForce spring = new SpringForce();
spring.setDampingRatio(1.0f);
assertEquals(1.0f, spring.getDampingRatio(), 0.0f);
spring.setDampingRatio(2.0f);
assertEquals(2.0f, spring.getDampingRatio(), 0.0f);
}
/**
* Verifies that once min and max value threshold does apply to the values in animation.
*/
@Test
public void testSetMinMax() {
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.SCALE_X, 0.0f);
anim.setMinValue(0.0f);
anim.setMaxValue(1.0f);
anim.getSpring().setStiffness(SpringForce.STIFFNESS_HIGH).setDampingRatio(
SpringForce.DAMPING_RATIO_HIGH_BOUNCY);
final DynamicAnimation.OnAnimationUpdateListener mockUpdateListener = mock(
DynamicAnimation.OnAnimationUpdateListener.class);
final DynamicAnimation.OnAnimationEndListener mockEndListener = mock(
DynamicAnimation.OnAnimationEndListener.class);
final DynamicAnimation.OnAnimationUpdateListener updateListener =
new DynamicAnimation.OnAnimationUpdateListener() {
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) {
assertTrue(value >= 0.0f);
assertTrue(value <= 1.0f);
}
};
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.setStartValue(1.0f).setStartVelocity(8000f)
.addEndListener(mockEndListener).addUpdateListener(mockUpdateListener)
.addUpdateListener(updateListener).start();
}});
verify(mockEndListener, timeout(2000)).onAnimationEnd(anim, false, 0f, 0f);
verify(mockUpdateListener, atLeast(2)).onAnimationUpdate(eq(anim), any(float.class),
any(float.class));
}
/**
* Verifies animateToFinalPosition works both when the anim hasn't started and when it's
* running.
*/
@Test
public void testAnimateToFinalPosition() throws InterruptedException {
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.SCALE_Y, 0.0f);
final DynamicAnimation.OnAnimationEndListener mockEndListener = mock(
DynamicAnimation.OnAnimationEndListener.class);
anim.addEndListener(mockEndListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.animateToFinalPosition(0.0f);
}
});
assertTrue(anim.isRunning());
Thread.sleep(100);
assertTrue(anim.isRunning());
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.animateToFinalPosition(1.0f);
}
});
assertTrue(anim.isRunning());
// Verify that it indeed ends at the value from the second animateToFinalPosition() call.
verify(mockEndListener, timeout(1500)).onAnimationEnd(anim, false, 1.0f, 0.0f);
}
/**
* Verifies that skip to end will stop the animation, and skips the value to the end value.
*/
@Test
public void testSkipToEnd() {
final float finalPosition = 10f;
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.SCROLL_X,
finalPosition);
final DynamicAnimation.OnAnimationEndListener mockListener =
mock(DynamicAnimation.OnAnimationEndListener.class);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.addEndListener(mockListener).setStartValue(200).start();
}
});
assertTrue(anim.isRunning());
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
int scrollX = mView1.getScrollX();
anim.skipToEnd();
// Expect no change in the animation values until next frame.
assertEquals(scrollX, mView1.getScrollX());
assertTrue(anim.isRunning());
}
});
verify(mockListener, timeout(100).times(1)).onAnimationEnd(anim, false, finalPosition, 0);
// Also make sure the skipToEnd() call doesn't affect next animation run.
final DynamicAnimation.OnAnimationEndListener mockListener2 =
mock(DynamicAnimation.OnAnimationEndListener.class);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.addEndListener(mockListener2);
anim.animateToFinalPosition(finalPosition + 1000f);
}
});
// Verify that the animation doesn't finish right away
verify(mockListener2, timeout(300).times(0)).onAnimationEnd(any(DynamicAnimation.class),
any(boolean.class), any(float.class), any(float.class));
// But the animation should eventually finish.
verify(mockListener, timeout(1000).times(1)).onAnimationEnd(anim, false,
finalPosition + 1000f, 0);
}
/**
* Check that the min visible change does affect how soon spring animations end.
*/
public void testScaleMinChange() {
FloatValueHolder valueHolder = new FloatValueHolder(0.5f);
final SpringAnimation anim = new SpringAnimation(valueHolder);
DynamicAnimation.OnAnimationUpdateListener mockListener =
mock(DynamicAnimation.OnAnimationUpdateListener.class);
anim.addUpdateListener(mockListener);
final DynamicAnimation.OnAnimationEndListener endListener =
mock(DynamicAnimation.OnAnimationEndListener.class);
anim.addEndListener(endListener);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.animateToFinalPosition(1f);
}
});
verify(endListener, timeout(500)).onAnimationEnd(anim, false, 0, 0);
verify(mockListener, atMost(5)).onAnimationUpdate(eq(anim), anyFloat(), anyFloat());
assertEquals(DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS, anim.getMinimumVisibleChange(),
0.01f);
// Set the right threshold and start again.
anim.setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE);
anim.setStartValue(0.5f);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
anim.animateToFinalPosition(1f);
}
});
verify(endListener, timeout(2000)).onAnimationEnd(anim, false, 0, 0);
verify(mockListener, atLeast(10)).onAnimationUpdate(eq(anim), anyFloat(), anyFloat());
}
/**
* Makes sure all the properties getter works.
*/
@Test
public void testAllProperties() {
final DynamicAnimation.ViewProperty[] properties = {
DynamicAnimation.ALPHA, DynamicAnimation.TRANSLATION_X,
DynamicAnimation.TRANSLATION_Y, DynamicAnimation.TRANSLATION_Z,
DynamicAnimation.SCALE_X, DynamicAnimation.SCALE_Y, DynamicAnimation.ROTATION,
DynamicAnimation.ROTATION_X, DynamicAnimation.ROTATION_Y,
DynamicAnimation.X, DynamicAnimation.Y, DynamicAnimation.Z,
DynamicAnimation.SCROLL_X, DynamicAnimation.SCROLL_Y,
};
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
mView1.setAlpha(0f);
mView1.setTranslationX(0f);
mView1.setTranslationY(0f);
ViewCompat.setTranslationZ(mView1, 0f);
mView1.setScaleX(0f);
mView1.setScaleY(0f);
mView1.setRotation(0f);
mView1.setRotationX(0f);
mView1.setRotationY(0f);
mView1.setX(0f);
mView1.setY(0f);
ViewCompat.setZ(mView1, 0f);
mView1.setScrollX(0);
mView1.setScrollY(0);
}
});
final SpringAnimation[] anims = new SpringAnimation[properties.length];
final DynamicAnimation.OnAnimationUpdateListener[] mockListeners =
new DynamicAnimation.OnAnimationUpdateListener[properties.length];
for (int i = 0; i < properties.length; i++) {
anims[i] = new SpringAnimation(mView1, properties[i], 1);
final int finalI = i;
anims[i].addUpdateListener(
new DynamicAnimation.OnAnimationUpdateListener() {
boolean mIsFirstFrame = true;
@Override
public void onAnimationUpdate(DynamicAnimation animation, float value,
float velocity) {
if (mIsFirstFrame) {
assertEquals(value, 0f, 0f);
}
mIsFirstFrame = false;
}
});
mockListeners[i] = mock(DynamicAnimation.OnAnimationUpdateListener.class);
anims[i].addUpdateListener(mockListeners[i]);
}
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
for (int i = properties.length - 1; i >= 0; i--) {
anims[i].start();
}
}
});
for (int i = 0; i < properties.length; i++) {
int timeout = i == 0 ? 100 : 0;
verify(mockListeners[i], timeout(timeout).atLeast(1)).onAnimationUpdate(
any(SpringAnimation.class), any(float.class), any(float.class));
}
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
for (int i = 0; i < properties.length; i++) {
anims[i].cancel();
}
}
});
}
/**
* Test start() on a test thread.
*/
@Test
public void testStartOnNonMainThread() {
mExpectedException.expect(AndroidRuntimeException.class);
SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.ALPHA, 0f);
anim.start();
}
/**
* Test cancel() on a test thread.
*/
@Test
public void testCancelOnNonMainThread() {
mExpectedException.expect(AndroidRuntimeException.class);
SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.ALPHA, 0f);
anim.cancel();
}
/**
* Test skipToEnd() on a test thread.
*/
@Test
public void testSkipToEndOnNonMainThread() {
mExpectedException.expect(AndroidRuntimeException.class);
SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.ALPHA, 0f);
anim.skipToEnd();
}
/**
* Test invalid start condition: no spring position specified, final position > max value,
* and final position < min. Expect exception in all these cases.
*/
@Test
public void testInvalidStartingCondition() {
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.X);
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
// Expect exception from not setting spring final position before calling start.
try {
anim.start();
fail("No exception is thrown when calling start() from non-main thread.");
} catch (UnsupportedOperationException e) {
}
// Expect exception from having a final position < min value
try {
anim.setMinValue(50);
// Final position < min value, expect exception.
anim.setStartValue(50).animateToFinalPosition(40);
fail("No exception is thrown when spring position is less than min value.");
} catch (UnsupportedOperationException e) {
}
// Expect exception from not setting spring final position before calling start.
try {
anim.setMaxValue(60);
// Final position < min value, expect exception.
anim.setStartValue(60).animateToFinalPosition(70);
fail("No exception is thrown when spring position is greater than max value.");
} catch (UnsupportedOperationException e) {
}
}
});
}
/**
* Try skipToEnd() on an undamped spring, and expect exception.
*/
@Test
public void testUndampedSpring() {
final SpringAnimation anim = new SpringAnimation(mView1, DynamicAnimation.Y);
anim.setSpring(new SpringForce(10).setDampingRatio(0));
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
// Expect exception for ending an undamped spring.
try {
anim.skipToEnd();
fail("No exception is thrown when calling skipToEnd() on an undamped spring");
} catch (UnsupportedOperationException e) {
}
}
});
}
static class MyEndListener implements DynamicAnimation.OnAnimationEndListener {
public long endTime = -1;
@Override
public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
float velocity) {
endTime = SystemClock.uptimeMillis();
}
}
}