1 /* 2 * Copyright (C) 2009 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 android.graphics.drawable.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertTrue; 25 import static org.junit.Assert.fail; 26 27 import android.app.Activity; 28 import android.content.res.Resources; 29 import android.content.res.XmlResourceParser; 30 import android.graphics.cts.ImageViewCtsActivity; 31 import android.graphics.cts.R; 32 import android.graphics.drawable.AnimationDrawable; 33 import android.graphics.drawable.Drawable; 34 import android.graphics.drawable.DrawableContainer.DrawableContainerState; 35 import android.support.test.annotation.UiThreadTest; 36 import android.support.test.filters.LargeTest; 37 import android.support.test.rule.ActivityTestRule; 38 import android.support.test.runner.AndroidJUnit4; 39 import android.util.Xml; 40 import android.widget.ImageView; 41 42 import com.android.compatibility.common.util.PollingCheck; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.xmlpull.v1.XmlPullParser; 50 import org.xmlpull.v1.XmlPullParserException; 51 52 import java.io.IOException; 53 54 @LargeTest 55 @RunWith(AndroidJUnit4.class) 56 public class AnimationDrawableTest { 57 private static final int FRAMES_COUNT = 3; 58 private static final int FIRST_FRAME_INDEX = 0; 59 private static final int SECOND_FRAME_INDEX = 1; 60 private static final int THIRD_FRAME_INDEX = 2; 61 private static final long TOLERANCE = 500; 62 private static final long FIRST_FRAME_DURATION = 3000; 63 private static final long SECOND_FRAME_DURATION = 2000; 64 private static final long THIRD_FRAME_DURATION = 1000; 65 66 private AnimationDrawable mAnimationDrawable; 67 private Resources mResources; 68 private boolean mInitialOneShotValue; 69 70 @Rule 71 public ActivityTestRule<ImageViewCtsActivity> mActivityRule = 72 new ActivityTestRule<>(ImageViewCtsActivity.class); 73 74 @UiThreadTest 75 @Before 76 public void setup() throws Throwable { 77 final Activity activity = mActivityRule.getActivity(); 78 mResources = activity.getResources(); 79 80 try { 81 mActivityRule.runOnUiThread(new Runnable() { 82 public void run() { 83 ImageView imageView = (ImageView) activity.findViewById(R.id.imageview); 84 imageView.setBackgroundResource(R.drawable.animationdrawable); 85 mAnimationDrawable = (AnimationDrawable) imageView.getBackground(); 86 mInitialOneShotValue = mAnimationDrawable.isOneShot(); 87 } 88 }); 89 } catch (Throwable t) { 90 throw new Exception(t); 91 } 92 } 93 94 @After 95 public void tearDown() throws Exception { 96 mAnimationDrawable.setOneShot(mInitialOneShotValue); 97 } 98 99 @Test 100 public void testConstructor() { 101 AnimationDrawable animationDrawable = new AnimationDrawable(); 102 // Check the values set in the constructor 103 assertNotNull(animationDrawable.getConstantState()); 104 assertFalse(animationDrawable.isRunning()); 105 assertFalse(animationDrawable.isOneShot()); 106 } 107 108 @Test 109 public void testSetVisible() throws Throwable { 110 assertTrue(mAnimationDrawable.isVisible()); 111 mActivityRule.runOnUiThread(mAnimationDrawable::start); 112 assertTrue(mAnimationDrawable.isRunning()); 113 assertSame(mAnimationDrawable.getFrame(FIRST_FRAME_INDEX), mAnimationDrawable.getCurrent()); 114 115 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 116 117 mActivityRule.runOnUiThread(() -> assertTrue(mAnimationDrawable.setVisible(false, false))); 118 assertFalse(mAnimationDrawable.isVisible()); 119 assertFalse(mAnimationDrawable.isRunning()); 120 verifyStoppedAnimation(SECOND_FRAME_INDEX, SECOND_FRAME_DURATION); 121 122 // restart animation 123 mActivityRule.runOnUiThread(() -> assertTrue(mAnimationDrawable.setVisible(true, true))); 124 assertTrue(mAnimationDrawable.isVisible()); 125 assertTrue(mAnimationDrawable.isRunning()); 126 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 127 } 128 129 @Test 130 public void testStart() throws Throwable { 131 // animation should play repeat if do not stop it. 132 assertFalse(mAnimationDrawable.isOneShot()); 133 assertFalse(mAnimationDrawable.isRunning()); 134 mActivityRule.runOnUiThread(mAnimationDrawable::start); 135 136 assertTrue(mAnimationDrawable.isRunning()); 137 assertSame(mAnimationDrawable.getFrame(FIRST_FRAME_INDEX), 138 mAnimationDrawable.getCurrent()); 139 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 140 141 mActivityRule.runOnUiThread(mAnimationDrawable::start); 142 pollingCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION); 143 144 mActivityRule.runOnUiThread(mAnimationDrawable::stop); 145 assertFalse(mAnimationDrawable.isRunning()); 146 verifyStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION); 147 148 // This method has no effect if the animation is not running. 149 mActivityRule.runOnUiThread(mAnimationDrawable::stop); 150 assertFalse(mAnimationDrawable.isRunning()); 151 verifyStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION); 152 } 153 154 @Test 155 public void testRun() throws Throwable { 156 assertFalse(mAnimationDrawable.isRunning()); 157 mActivityRule.runOnUiThread(mAnimationDrawable::run); 158 159 assertTrue(mAnimationDrawable.isRunning()); 160 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 161 162 mActivityRule.runOnUiThread(() -> mAnimationDrawable.unscheduleSelf(mAnimationDrawable)); 163 } 164 165 @Test 166 public void testUnscheduleSelf() throws Throwable { 167 assertFalse(mAnimationDrawable.isRunning()); 168 mActivityRule.runOnUiThread(mAnimationDrawable::start); 169 170 assertTrue(mAnimationDrawable.isRunning()); 171 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 172 173 mActivityRule.runOnUiThread(() -> mAnimationDrawable.unscheduleSelf(mAnimationDrawable)); 174 assertFalse(mAnimationDrawable.isRunning()); 175 verifyStoppedAnimation(SECOND_FRAME_INDEX, SECOND_FRAME_DURATION); 176 } 177 178 @Test 179 public void testGetNumberOfFrames() { 180 AnimationDrawable mutated = (AnimationDrawable) mAnimationDrawable.mutate(); 181 assertEquals(FRAMES_COUNT, mutated.getNumberOfFrames()); 182 183 Drawable frame = mResources.getDrawable(R.drawable.failed); 184 mAnimationDrawable.addFrame(frame, 2000); 185 assertEquals(FRAMES_COUNT + 1, mutated.getNumberOfFrames()); 186 187 // add same frame with same duration 188 mAnimationDrawable.addFrame(frame, 2000); 189 assertEquals(FRAMES_COUNT + 2, mutated.getNumberOfFrames()); 190 191 try { 192 mAnimationDrawable.addFrame(null, 1000); 193 fail("Should throw NullPointerException if param frame is null."); 194 } catch (NullPointerException e) { 195 // expected 196 } 197 } 198 199 @Test 200 public void testGetFrame() { 201 Drawable frame = mAnimationDrawable.getFrame(FIRST_FRAME_INDEX); 202 Drawable drawable = mResources.getDrawable(R.drawable.testimage); 203 assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth()); 204 assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight()); 205 206 frame = mAnimationDrawable.getFrame(SECOND_FRAME_INDEX); 207 drawable = mResources.getDrawable(R.drawable.pass); 208 assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth()); 209 assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight()); 210 211 frame = mAnimationDrawable.getFrame(THIRD_FRAME_INDEX); 212 drawable = mResources.getDrawable(R.drawable.scenery); 213 assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth()); 214 assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight()); 215 216 assertNull(mAnimationDrawable.getFrame(THIRD_FRAME_INDEX + 1)); 217 } 218 219 @Test(expected=ArrayIndexOutOfBoundsException.class) 220 public void testGetFrameTooLow() { 221 mAnimationDrawable.getFrame(-1); 222 } 223 224 @Test(expected=ArrayIndexOutOfBoundsException.class) 225 public void testGetFrameTooHigh() { 226 mAnimationDrawable.getFrame(10); 227 } 228 229 @Test 230 public void testGetDuration() { 231 assertEquals(FIRST_FRAME_DURATION, mAnimationDrawable.getDuration(FIRST_FRAME_INDEX)); 232 assertEquals(SECOND_FRAME_DURATION, mAnimationDrawable.getDuration(SECOND_FRAME_INDEX)); 233 assertEquals(THIRD_FRAME_DURATION, mAnimationDrawable.getDuration(THIRD_FRAME_INDEX)); 234 assertEquals(0, mAnimationDrawable.getDuration(THIRD_FRAME_INDEX + 1)); 235 } 236 237 @Test(expected=ArrayIndexOutOfBoundsException.class) 238 public void testGetDurationTooLow() { 239 mAnimationDrawable.getDuration(-1); 240 } 241 242 @Test(expected=ArrayIndexOutOfBoundsException.class) 243 public void testGetDurationTooHigh() { 244 mAnimationDrawable.getDuration(10); 245 } 246 247 @Test 248 public void testAccessOneShot() throws Throwable { 249 // animation should play repeat if do not stop it. 250 assertFalse(mAnimationDrawable.isOneShot()); 251 mActivityRule.runOnUiThread(mAnimationDrawable::start); 252 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 253 pollingCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION); 254 // begin to repeat 255 pollingCheckDrawable(FIRST_FRAME_INDEX, THIRD_FRAME_DURATION); 256 257 mActivityRule.runOnUiThread(() -> { 258 mAnimationDrawable.stop(); 259 mAnimationDrawable.setOneShot(true); 260 assertTrue(mAnimationDrawable.isOneShot()); 261 mAnimationDrawable.start(); 262 }); 263 pollingCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION); 264 pollingCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION); 265 // do not repeat 266 verifyStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION); 267 // Set visible to false and restart to false 268 mActivityRule.runOnUiThread(() -> mAnimationDrawable.setVisible(false, false)); 269 // Check that animation drawable stays on the same frame 270 verifyStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION); 271 272 // Set visible to true and restart to false 273 mActivityRule.runOnUiThread(() -> mAnimationDrawable.setVisible(true, false)); 274 // Check that animation drawable stays on the same frame 275 verifyStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION); 276 } 277 278 @Test 279 public void testInflateCorrect() throws XmlPullParserException, IOException { 280 XmlResourceParser parser = getResourceParser(R.xml.anim_list_correct); 281 AnimationDrawable dr = new AnimationDrawable(); 282 dr.inflate(mResources, parser, Xml.asAttributeSet(parser)); 283 // android:visible="false" 284 assertFalse(dr.isVisible()); 285 // android:oneShot="true" 286 assertTrue(dr.isOneShot()); 287 // android:variablePadding="true" 288 DrawableContainerState state = 289 (DrawableContainerState) dr.getConstantState(); 290 assertNull(state.getConstantPadding()); 291 assertEquals(2, dr.getNumberOfFrames()); 292 assertEquals(2000, dr.getDuration(0)); 293 assertEquals(1000, dr.getDuration(1)); 294 assertSame(dr.getFrame(0), dr.getCurrent()); 295 } 296 297 @Test 298 public void testInflateMissingDrawable() throws XmlPullParserException, IOException { 299 XmlResourceParser parser = getResourceParser(R.xml.anim_list_missing_item_drawable); 300 AnimationDrawable dr = new AnimationDrawable(); 301 try { 302 dr.inflate(mResources, parser, Xml.asAttributeSet(parser)); 303 fail("Should throw XmlPullParserException if drawable of item is missing"); 304 } catch (XmlPullParserException e) { 305 // expected 306 } 307 } 308 309 @Test(expected=NullPointerException.class) 310 public void testInflateNullResources() throws XmlPullParserException, IOException { 311 XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable); 312 AnimationDrawable dr = new AnimationDrawable(); 313 // Should throw NullPointerException if resource is null 314 dr.inflate(null, parser, Xml.asAttributeSet(parser)); 315 } 316 317 @Test(expected=NullPointerException.class) 318 public void testInflateNullXmlPullParser() throws XmlPullParserException, IOException { 319 XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable); 320 AnimationDrawable dr = new AnimationDrawable(); 321 // Should throw NullPointerException if parser is null 322 dr.inflate(mResources, null, Xml.asAttributeSet(parser)); 323 } 324 325 @Test(expected=NullPointerException.class) 326 public void testInflateNullAttributeSet() throws XmlPullParserException, IOException { 327 XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable); 328 AnimationDrawable dr = new AnimationDrawable(); 329 // Should throw NullPointerException if AttributeSet is null 330 dr.inflate(mResources, parser, null); 331 } 332 333 @Test 334 public void testMutate() { 335 AnimationDrawable d1 = (AnimationDrawable) mResources 336 .getDrawable(R.drawable.animationdrawable); 337 // multiple instances inflated from the same resource do not share the state 338 // simply call mutate to make sure it does not throw an exception 339 d1.mutate(); 340 } 341 342 private XmlResourceParser getResourceParser(int resId) throws XmlPullParserException, 343 IOException { 344 XmlResourceParser parser = mResources.getXml(resId); 345 int type; 346 while ((type = parser.next()) != XmlPullParser.START_TAG 347 && type != XmlPullParser.END_DOCUMENT) { 348 // Empty loop 349 } 350 return parser; 351 } 352 353 /** 354 * Polling check specific frame should be current one in timeout. 355 * @param index - expected index of frame. 356 * @param timeout - timeout. 357 */ 358 private void pollingCheckDrawable(final int index, long timeout) { 359 final Drawable expected = mAnimationDrawable.getFrame(index); 360 PollingCheck.waitFor(timeout + TOLERANCE, 361 () -> mAnimationDrawable.getCurrent().equals(expected)); 362 } 363 364 /** 365 * Assert animation had been stopped. It will sleep duration + TOLERANCE milliseconds and 366 * then make sure current frame had not been changed. 367 * @param index - index of current frame. 368 * @param duration - duration of current frame. 369 */ 370 private void verifyStoppedAnimation(int index, long duration) throws InterruptedException { 371 Thread.sleep(duration + TOLERANCE); 372 assertSame(mAnimationDrawable.getFrame(index), mAnimationDrawable.getCurrent()); 373 } 374 } 375