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