1 /*
2 * Copyright (C) 2014 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 package android.view.animation.cts;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertNotSame;
21 import static org.junit.Assert.assertSame;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.animation.Animator;
25 import android.animation.AnimatorInflater;
26 import android.animation.AnimatorListenerAdapter;
27 import android.animation.AnimatorSet;
28 import android.animation.ObjectAnimator;
29 import android.animation.StateListAnimator;
30 import android.app.Instrumentation;
31 import android.app.UiAutomation;
32 import android.content.Context;
33 import android.util.Log;
34 import android.view.Display;
35 import android.view.Surface;
36 import android.view.View;
37 import android.view.WindowManager;
38 import android.view.cts.R;
39 
40 import androidx.test.InstrumentationRegistry;
41 import androidx.test.filters.MediumTest;
42 import androidx.test.rule.ActivityTestRule;
43 import androidx.test.runner.AndroidJUnit4;
44 
45 import org.junit.After;
46 import org.junit.Before;
47 import org.junit.Rule;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 
51 import java.util.HashSet;
52 import java.util.Set;
53 import java.util.concurrent.CountDownLatch;
54 import java.util.concurrent.TimeUnit;
55 
56 @MediumTest
57 @RunWith(AndroidJUnit4.class)
58 public class AnimatorInflaterTest {
59     private static final String TAG = "AnimatorInflaterTest";
60 
61     private Instrumentation mInstrumentation;
62     private AnimationTestCtsActivity mActivity;
63     private View mTestView;
64     private int mUserRotation;
65 
66     Set<Integer> identityHashes = new HashSet<>();
67 
68     @Rule
69     public ActivityTestRule<AnimationTestCtsActivity> mActivityRule =
70             new ActivityTestRule<>(AnimationTestCtsActivity.class);
71 
72     @Before
setup()73     public void setup() {
74         mInstrumentation = InstrumentationRegistry.getInstrumentation();
75         mActivity = mActivityRule.getActivity();
76         mTestView = mActivity.findViewById(R.id.anim_window);
77         mUserRotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
78     }
79 
80     @After
tearDown()81     public void tearDown() {
82         mInstrumentation.getUiAutomation().setRotation(mUserRotation);
83     }
84 
assertUnique(Object object)85     private void assertUnique(Object object) {
86         assertUnique(object, "");
87     }
88 
assertUnique(Object object, String msg)89     private void assertUnique(Object object, String msg) {
90         final int code = System.identityHashCode(object);
91         assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
92     }
93 
94     @Test
testLoadAnimatorWithDifferentInterpolators()95     public void testLoadAnimatorWithDifferentInterpolators() throws Throwable {
96         Animator anim1 = AnimatorInflater .loadAnimator(mActivity, R.anim.changing_test_animator);
97         if (!rotate()) {
98             return;//cancel test
99         }
100         Animator anim2 = AnimatorInflater .loadAnimator(mActivity, R.anim.changing_test_animator);
101         assertNotSame(anim1, anim2);
102         assertNotSame("interpolater is orientation dependent, should change",
103                 anim1.getInterpolator(), anim2.getInterpolator());
104     }
105 
106     /**
107      * Tests animators with dimension references.
108      */
109     @Test
testLoadAnimator()110     public void testLoadAnimator() throws Throwable {
111         // to identify objects
112         Animator anim1 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
113         Animator anim2 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
114         assertNotSame("a different animation should be returned", anim1, anim2);
115         assertSame("interpolator should be shallow cloned", anim1.getInterpolator(),
116                 anim2.getInterpolator());
117         for (int i = 0; i < 2; i++) {
118             float targetX = mActivity.getResources()
119                     .getDimension(R.dimen.test_animator_target_x);
120             // y value changes in landscape orientation
121             float targetY = mActivity.getResources()
122                     .getDimension(R.dimen.test_animator_target_y);
123             for (Animator anim : new Animator[]{anim1, anim2}) {
124                 assertTrue(anim instanceof AnimatorSet);
125                 assertUnique(anim);
126                 AnimatorSet set = (AnimatorSet) anim;
127                 assertEquals("should have 3 sub animations", 3, set.getChildAnimations().size());
128                 for (Animator subAnim : set.getChildAnimations()) {
129                     assertUnique(subAnim);
130                     assertTrue(subAnim instanceof ObjectAnimator);
131                 }
132                 final ObjectAnimator child1 = (ObjectAnimator) set.getChildAnimations().get(0);
133                 final ObjectAnimator child2 = (ObjectAnimator) set.getChildAnimations().get(1);
134                 final DummyObject dummyObject = new DummyObject();
135                 mActivityRule.runOnUiThread(() -> {
136                     for (ObjectAnimator animator : new ObjectAnimator[]{child1, child2}) {
137                         animator.setTarget(dummyObject);
138                         animator.setupStartValues();
139                         animator.start();
140                         animator.end();
141                     }
142                 });
143                 assertEquals(targetX, dummyObject.x, 0.0f);
144                 assertEquals(targetY, dummyObject.y, 0.0f);
145             }
146             if (i == 0) {
147                 if (!rotate()) {
148                     return;//cancel test
149                 }
150             }
151             anim1 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
152             anim2 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
153 
154         }
155     }
156 
rotate()157     private boolean rotate() throws Throwable {
158         WindowManager mWindowManager = (WindowManager) mActivity
159                 .getSystemService(Context.WINDOW_SERVICE);
160         Display display = mWindowManager.getDefaultDisplay();
161         int orientation = mActivity.getResources().getConfiguration().orientation;
162 
163         Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
164                 mActivity.getClass().getName(), null, false);
165         mInstrumentation.addMonitor(monitor);
166         int nextRotation = 0;
167         switch (display.getRotation()) {
168             case Surface.ROTATION_0:
169             case Surface.ROTATION_180:
170                 nextRotation = UiAutomation.ROTATION_FREEZE_90;
171                 break;
172             case Surface.ROTATION_90:
173             case Surface.ROTATION_270:
174                 nextRotation = UiAutomation.ROTATION_FREEZE_0;
175                 break;
176             default:
177                 Log.e(TAG, "Cannot get rotation, test is canceled");
178                 return false;
179         }
180         boolean rotated = mInstrumentation.getUiAutomation().setRotation(nextRotation);
181         Thread.sleep(500);
182         if (!rotated) {
183             Log.e(TAG, "Rotation failed, test is canceled");
184         }
185         mInstrumentation.waitForIdleSync();
186         if (!mActivity.waitUntilVisible()) {
187             Log.e(TAG, "Activity failed to complete rotation, canceling test");
188             return false;
189         }
190         if (mActivity.getWindowManager().getDefaultDisplay().getRotation() != nextRotation) {
191             Log.e(TAG, "New activity orientation does not match. Canceling test");
192             return false;
193         }
194         if (mActivity.getResources().getConfiguration().orientation == orientation) {
195             Log.e(TAG, "Screen orientation didn't change, test is canceled");
196             return false;
197         }
198         return true;
199     }
200 
201     /**
202      * Simple state list animator test that checks for cloning
203      */
204     @Test
testLoadStateListAnimator()205     public void testLoadStateListAnimator() {
206         StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(mActivity,
207                 R.anim.test_state_list_animator);
208         StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(mActivity,
209                 R.anim.test_state_list_animator);
210         assertUnique(sla1);
211         assertUnique(sla2);
212     }
213 
214     /**
215      * Tests a state list animator which has an @anim reference that has different xmls per
216      * orientation
217      */
218     @Test
testLoadStateListAnimatorWithChangingResetState()219     public void testLoadStateListAnimatorWithChangingResetState() throws Throwable {
220         loadStateListAnimatorWithChangingResetStateTest();
221         if (!rotate()) {
222             return;//cancel test
223         }
224 
225         loadStateListAnimatorWithChangingResetStateTest();
226     }
227 
loadStateListAnimatorWithChangingResetStateTest()228     private void loadStateListAnimatorWithChangingResetStateTest() throws Throwable {
229         final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(mActivity,
230                 R.anim.test_state_list_animator_2);
231         mActivityRule.runOnUiThread(() -> {
232             mTestView.setStateListAnimator(sla);
233             mTestView.jumpDrawablesToCurrentState();
234         });
235         float resetValue = mActivity.getResources().getDimension(R.dimen.reset_state_value);
236         mInstrumentation.waitForIdleSync();
237         assertEquals(resetValue, mTestView.getX(), 0.0f);
238         assertEquals(resetValue, mTestView.getY(), 0.0f);
239         assertEquals(resetValue, mTestView.getZ(), 0.0f);
240     }
241 
242     /**
243      * Tests a state list animator which has different xml descriptions per orientation.
244      */
245     @Test
testLoadChangingStateListAnimator()246     public void testLoadChangingStateListAnimator() throws Throwable {
247         loadChangingStateListAnimatorTest();
248         if (!rotate()) {
249             return;//cancel test
250         }
251         loadChangingStateListAnimatorTest();
252     }
253 
loadChangingStateListAnimatorTest()254     private void loadChangingStateListAnimatorTest() throws Throwable {
255         final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(mActivity,
256                 R.anim.changing_state_list_animator);
257         mActivityRule.runOnUiThread(() -> {
258             mTestView.setStateListAnimator(sla);
259             mTestView.jumpDrawablesToCurrentState();
260         });
261         float targetValue = mActivity.getResources()
262                 .getDimension(R.dimen.changing_state_list_anim_target_x_value);
263         mInstrumentation.waitForIdleSync();
264         assertEquals(targetValue, mTestView.getX(), 0.0f);
265     }
266 
267     /**
268      * Tests that makes sure that reloaded animator is not affected by previous changes
269      */
270     @Test
testReloadedAnimatorIsNotModified()271     public void testReloadedAnimatorIsNotModified() throws Throwable {
272         final Animator anim1 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
273         final CountDownLatch mStarted = new CountDownLatch(1);
274         final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
275             @Override
276             public void onAnimationEnd(Animator animation) {
277                 super.onAnimationEnd(animation);
278             }
279 
280             @Override
281             public void onAnimationStart(Animator animation) {
282                 super.onAnimationStart(animation);
283                 mStarted.countDown();
284             }
285         };
286         mActivityRule.runOnUiThread(() -> {
287             anim1.setTarget(mTestView);
288             anim1.addListener(listener);
289             anim1.start();
290         });
291         Animator anim2 = AnimatorInflater.loadAnimator(mActivity, R.anim.test_animator);
292         assertTrue(anim1.isStarted());
293         assertFalse(anim2.isStarted());
294         assertFalse("anim2 should not include the listener",
295                 anim2.getListeners() != null && anim2.getListeners().contains(listener));
296         assertTrue("animator should start", mStarted.await(10, TimeUnit.SECONDS));
297         assertFalse(anim2.isRunning());
298 
299     }
300 
301     class DummyObject {
302 
303         float x;
304         float y;
305 
getX()306         public float getX() {
307             return x;
308         }
309 
setX(float x)310         public void setX(float x) {
311             this.x = x;
312         }
313 
getY()314         public float getY() {
315             return y;
316         }
317 
setY(float y)318         public void setY(float y) {
319             this.y = y;
320         }
321     }
322 }
323 
324