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 android.support.dynamicanimation.tests;
18
19import static junit.framework.Assert.assertEquals;
20import static junit.framework.Assert.assertTrue;
21
22import static org.mockito.Matchers.eq;
23import static org.mockito.Matchers.floatThat;
24import static org.mockito.Mockito.mock;
25import static org.mockito.Mockito.timeout;
26import static org.mockito.Mockito.verify;
27
28import android.support.animation.DynamicAnimation;
29import android.support.animation.FlingAnimation;
30import android.support.animation.FloatPropertyCompat;
31import android.support.animation.FloatValueHolder;
32import android.support.dynamicanimation.test.R;
33import android.support.test.InstrumentationRegistry;
34import android.support.test.filters.MediumTest;
35import android.support.test.rule.ActivityTestRule;
36import android.support.test.runner.AndroidJUnit4;
37import android.view.View;
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());
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