1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package androidx.dynamicanimation.tests; 18 19import static org.junit.Assert.assertEquals; 20import static org.junit.Assert.assertTrue; 21import static org.mockito.Matchers.eq; 22import static org.mockito.Matchers.floatThat; 23import static org.mockito.Mockito.mock; 24import static org.mockito.Mockito.timeout; 25import static org.mockito.Mockito.verify; 26 27import android.support.test.InstrumentationRegistry; 28import android.support.test.filters.MediumTest; 29import android.support.test.rule.ActivityTestRule; 30import android.support.test.runner.AndroidJUnit4; 31import android.view.View; 32 33import androidx.dynamicanimation.animation.DynamicAnimation; 34import androidx.dynamicanimation.animation.FlingAnimation; 35import androidx.dynamicanimation.animation.FloatPropertyCompat; 36import androidx.dynamicanimation.animation.FloatValueHolder; 37import androidx.dynamicanimation.test.R; 38 39import org.junit.Before; 40import org.junit.Rule; 41import org.junit.Test; 42import org.junit.rules.ExpectedException; 43import org.junit.runner.RunWith; 44import org.mockito.internal.matchers.GreaterThan; 45import org.mockito.internal.matchers.LessThan; 46 47@MediumTest 48@RunWith(AndroidJUnit4.class) 49public class FlingTests { 50 @Rule 51 public final ActivityTestRule<AnimationActivity> mActivityTestRule; 52 public View mView1; 53 public View mView2; 54 55 @Rule 56 public ExpectedException mExpectedException = ExpectedException.none(); 57 58 public FlingTests() { 59 mActivityTestRule = new ActivityTestRule<>(AnimationActivity.class); 60 } 61 62 @Before 63 public void setup() throws Exception { 64 mView1 = mActivityTestRule.getActivity().findViewById(R.id.anim_view); 65 mView2 = mActivityTestRule.getActivity().findViewById(R.id.anim_another_view); 66 } 67 68 /** 69 * Test that custom properties are supported. 70 */ 71 @Test 72 public void testCustomProperties() { 73 final Object animObj = new Object(); 74 FloatPropertyCompat property = new FloatPropertyCompat("") { 75 private float mValue = 0f; 76 @Override 77 public float getValue(Object object) { 78 assertEquals(animObj, object); 79 return mValue; 80 } 81 82 @Override 83 public void setValue(Object object, float value) { 84 assertEquals(animObj, object); 85 assertTrue(value > mValue); 86 assertTrue(value >= 100); 87 mValue = value; 88 } 89 }; 90 final FlingAnimation anim = new FlingAnimation(animObj, property); 91 DynamicAnimation.OnAnimationEndListener listener = mock( 92 DynamicAnimation.OnAnimationEndListener.class); 93 anim.addEndListener(listener); 94 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 95 @Override 96 public void run() { 97 anim.setStartValue(100).setStartVelocity(2000).start(); 98 } 99 }); 100 verify(listener, timeout(1000)).onAnimationEnd(eq(anim), eq(false), floatThat( 101 new GreaterThan(110f)), eq(0f)); 102 } 103 104 /** 105 * Test that spring animation can work with a single property without an object. 106 */ 107 @Test 108 public void testFloatValueHolder() { 109 FloatValueHolder floatValueHolder = new FloatValueHolder(); 110 assertEquals(0.0f, floatValueHolder.getValue(), 0.01f); 111 112 final FlingAnimation anim = new FlingAnimation(floatValueHolder).setStartVelocity(-2500); 113 114 DynamicAnimation.OnAnimationEndListener listener = mock( 115 DynamicAnimation.OnAnimationEndListener.class); 116 anim.addEndListener(listener); 117 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 118 @Override 119 public void run() { 120 anim.start(); 121 } 122 }); 123 verify(listener, timeout(1000)).onAnimationEnd(eq(anim), eq(false), floatThat( 124 new LessThan(-50f)), eq(0f)); 125 } 126 127 128 /** 129 * Test that friction does affect how fast the slow down happens. Fling animation with 130 * higher friction should finish first. 131 */ 132 @Test 133 public void testFriction() { 134 FloatValueHolder floatValueHolder = new FloatValueHolder(); 135 float lowFriction = 0.5f; 136 float highFriction = 2f; 137 final FlingAnimation animLowFriction = new FlingAnimation(floatValueHolder); 138 final FlingAnimation animHighFriction = new FlingAnimation(floatValueHolder); 139 140 animHighFriction.setFriction(highFriction); 141 animLowFriction.setFriction(lowFriction); 142 143 DynamicAnimation.OnAnimationEndListener listener = mock( 144 DynamicAnimation.OnAnimationEndListener.class); 145 animHighFriction.addEndListener(listener); 146 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 147 @Override 148 public void run() { 149 animHighFriction.setStartVelocity(5000).setStartValue(0).start(); 150 animLowFriction.setStartVelocity(5000).setStartValue(0).start(); 151 } 152 }); 153 154 verify(listener, timeout(1000)).onAnimationEnd(eq(animHighFriction), eq(false), floatThat( 155 new GreaterThan(200f)), eq(0f)); 156 // By the time high scalar animation finishes, the lower friction animation should still be 157 // running. 158 assertTrue(animLowFriction.isRunning()); 159 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 160 @Override 161 public void run() { 162 animLowFriction.cancel(); 163 } 164 }); 165 166 assertEquals(lowFriction, animLowFriction.getFriction(), 0f); 167 assertEquals(highFriction, animHighFriction.getFriction(), 0f); 168 169 } 170 171 /** 172 * Test that velocity threshold does affect how early fling animation ends. An animation with 173 * higher velocity threshold should finish first. 174 */ 175 @Test 176 public void testVelocityThreshold() { 177 FloatValueHolder floatValueHolder = new FloatValueHolder(); 178 float lowThreshold = 5f; 179 final float highThreshold = 10f; 180 final FlingAnimation animLowThreshold = new FlingAnimation(floatValueHolder); 181 final FlingAnimation animHighThreshold = new FlingAnimation(floatValueHolder); 182 183 animHighThreshold.setMinimumVisibleChange(highThreshold); 184 animHighThreshold.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { 185 @Override 186 public void onAnimationUpdate(DynamicAnimation animation, float value, float velocity) { 187 if (velocity != 0f) { 188 // Other than last frame, velocity should always be above threshold 189 assertTrue(velocity >= highThreshold); 190 } 191 } 192 }); 193 animLowThreshold.setMinimumVisibleChange(lowThreshold); 194 195 DynamicAnimation.OnAnimationEndListener listener = mock( 196 DynamicAnimation.OnAnimationEndListener.class); 197 animHighThreshold.addEndListener(listener); 198 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 199 @Override 200 public void run() { 201 animHighThreshold.setStartVelocity(2000).setStartValue(0).start(); 202 animLowThreshold.setStartVelocity(2000).setStartValue(0).start(); 203 } 204 }); 205 206 verify(listener, timeout(1000)).onAnimationEnd(eq(animHighThreshold), eq(false), floatThat( 207 new GreaterThan(200f)), eq(0f)); 208 // By the time high scalar animation finishes, the lower friction animation should still be 209 // running. 210 assertTrue(animLowThreshold.isRunning()); 211 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 212 @Override 213 public void run() { 214 animLowThreshold.cancel(); 215 } 216 }); 217 218 assertEquals(lowThreshold, animLowThreshold.getMinimumVisibleChange(), 0f); 219 assertEquals(highThreshold, animHighThreshold.getMinimumVisibleChange(), 0f); 220 221 } 222} 223