1 /*
2  * Copyright 2018 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 
17 package androidx.core.animation;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.content.Context;
25 import android.os.Build;
26 import android.view.InflateException;
27 
28 import androidx.core.animation.testapp.test.R;
29 import androidx.test.annotation.UiThreadTest;
30 import androidx.test.core.app.ApplicationProvider;
31 import androidx.test.ext.junit.runners.AndroidJUnit4;
32 import androidx.test.filters.SmallTest;
33 
34 import org.junit.ClassRule;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 @SmallTest
39 @RunWith(AndroidJUnit4.class)
40 public class AnimatorInflaterTest {
41 
42     private static final float EPSILON = 0.01f;
43 
44     @ClassRule
45     public static AnimatorTestRule sAnimatorTestRule = new AnimatorTestRule();
46 
47     @UiThreadTest
48     @Test
testLoadAnimator()49     public void testLoadAnimator() {
50         Context context = ApplicationProvider.getApplicationContext();
51         Animator anim = AnimatorInflater.loadAnimator(context, R.animator.animator_set_with_dimens);
52         assertEquals(100, anim.getTotalDuration());
53 
54         TestObject obj = new TestObject();
55         anim.setTarget(obj);
56         anim.start();
57 
58         assertTrue(anim.isRunning());
59         sAnimatorTestRule.advanceTimeBy(anim.getTotalDuration());
60         assertFalse(anim.isRunning());
61 
62         float targetX = context.getResources().getDimension(R.dimen.test_animator_target_x);
63         float targetY = context.getResources().getDimension(R.dimen.test_animator_target_y);
64 
65         assertEquals(targetX, obj.x, EPSILON);
66         assertEquals(targetY, obj.y, EPSILON);
67         assertEquals(2, obj.left);
68     }
69 
70 
71     @UiThreadTest
72     @Test
testLoadAnimatorAlongPath()73     public void testLoadAnimatorAlongPath() {
74         Context context = ApplicationProvider.getApplicationContext();
75         Animator anim = AnimatorInflater.loadAnimator(context, R.animator.animator_along_path);
76         assertTrue(anim.getInterpolator() instanceof LinearInterpolator);
77         assertEquals(100, anim.getDuration());
78         TestObject obj = new TestObject();
79         anim.setTarget(obj);
80         anim.start();
81 
82         // Check whether the animation is indeed running along the path.
83         int inc = 2;
84         for (int i = 0; i <= 100; i += inc) {
85             float y = i <= 50 ? 0 : 100;
86             assertEquals(i, obj.x, EPSILON);
87             assertEquals(y, obj.y, EPSILON);
88             sAnimatorTestRule.advanceTimeBy(inc);
89         }
90     }
91 
92     @Test
pathInterpolator()93     public void pathInterpolator() {
94         final Interpolator interpolator = AnimatorInflater.loadInterpolator(
95                 ApplicationProvider.getApplicationContext(),
96                 R.interpolator.path_interpolator
97         );
98         assertEquals(0.85f, interpolator.getInterpolation(0.5f), EPSILON);
99     }
100 
101     @Test
pathInterpolator_controlPoints()102     public void pathInterpolator_controlPoints() {
103         final Interpolator interpolator = AnimatorInflater.loadInterpolator(
104                 ApplicationProvider.getApplicationContext(),
105                 R.interpolator.control_points_interpolator
106         );
107         assertEquals(0.89f, interpolator.getInterpolation(0.5f), EPSILON);
108     }
109 
110     @Test
pathInterpolator_singleControlPoint()111     public void pathInterpolator_singleControlPoint() {
112         final Interpolator interpolator = AnimatorInflater.loadInterpolator(
113                 ApplicationProvider.getApplicationContext(),
114                 R.interpolator.single_control_point_interpolator
115         );
116         assertEquals(0.086f, interpolator.getInterpolation(0.5f), EPSILON);
117     }
118 
119     @Test
pathInterpolator_wrongControlPoint()120     public void pathInterpolator_wrongControlPoint() {
121         InflateException exception = assertThrows(InflateException.class, () -> {
122             AnimatorInflater.loadInterpolator(ApplicationProvider.getApplicationContext(),
123                     R.interpolator.wrong_control_point_interpolator);
124         });
125         assertEquals("pathInterpolator requires the controlY1 attribute", exception.getMessage());
126     }
127 
128     @Test
pathInterpolator_wrongControlPoints()129     public void pathInterpolator_wrongControlPoints() {
130 
131 
132         InflateException exception = assertThrows(InflateException.class, () -> {
133             AnimatorInflater.loadInterpolator(ApplicationProvider.getApplicationContext(),
134                     R.interpolator.wrong_control_points_interpolator);
135         });
136         assertEquals("pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.",
137                 exception.getMessage());
138     }
139 
140     @Test
pathInterpolator_wrongStartEnd()141     public void pathInterpolator_wrongStartEnd() {
142         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
143             AnimatorInflater.loadInterpolator(ApplicationProvider.getApplicationContext(),
144                     R.interpolator.wrong_path_interpolator_1);
145         });
146         assertEquals("The Path must start at (0,0) and end at (1,1)", exception.getMessage());
147     }
148 
149     @Test
pathInterpolator_loopBack()150     public void pathInterpolator_loopBack() {
151         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
152             AnimatorInflater.loadInterpolator(ApplicationProvider.getApplicationContext(),
153                     R.interpolator.wrong_path_interpolator_2);
154         });
155         assertEquals("The Path cannot loop back on itself.", exception.getMessage());
156 
157     }
158 
159     @Test
pathInterpolator_discontinuity()160     public void pathInterpolator_discontinuity() {
161         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
162             AnimatorInflater.loadInterpolator(ApplicationProvider.getApplicationContext(),
163                     R.interpolator.wrong_path_interpolator_3);
164         });
165         assertEquals(Build.VERSION.SDK_INT >= 26
166                 ? "The Path cannot have discontinuity in the X axis."
167                 // Older APIs don't detect discontinuity, but they report it as loop back.
168                 : "The Path cannot loop back on itself.", exception.getMessage());
169     }
170 
171     static class TestObject {
172 
173         public float x;
174         public float y;
175         public int left;
176 
getX()177         public float getX() {
178             return x;
179         }
180 
setX(float x)181         public void setX(float x) {
182             this.x = x;
183         }
184 
getY()185         public float getY() {
186             return y;
187         }
188 
setY(float y)189         public void setY(float y) {
190             this.y = y;
191         }
192 
setLeft(int left)193         public void setLeft(int left) {
194             this.left = left;
195         }
196 
getLeft()197         public int getLeft() {
198             return left;
199         }
200     }
201 
202 }
203