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