1 /* 2 * Copyright (C) 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 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.assertNotEquals; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.app.Activity; 27 import android.content.ContentResolver; 28 import android.content.res.Resources; 29 import android.graphics.Bitmap; 30 import android.graphics.Canvas; 31 import android.graphics.Color; 32 import android.graphics.ColorFilter; 33 import android.graphics.ImageDecoder; 34 import android.graphics.LightingColorFilter; 35 import android.graphics.Paint; 36 import android.graphics.PixelFormat; 37 import android.graphics.Rect; 38 import android.graphics.cts.R; 39 import android.graphics.drawable.AnimatedImageDrawable; 40 import android.graphics.drawable.Drawable; 41 import android.net.Uri; 42 import android.support.test.InstrumentationRegistry; 43 import android.support.test.rule.ActivityTestRule; 44 import android.support.test.runner.AndroidJUnit4; 45 import android.view.View; 46 import android.widget.ImageView; 47 48 import com.android.compatibility.common.util.BitmapUtils; 49 50 import org.junit.Before; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 import org.xmlpull.v1.XmlPullParser; 55 import org.xmlpull.v1.XmlPullParserException; 56 57 import java.io.IOException; 58 import java.util.function.BiFunction; 59 60 @RunWith(AndroidJUnit4.class) 61 public class AnimatedImageDrawableTest { 62 private Resources mRes; 63 private ContentResolver mContentResolver; 64 65 private static final int RES_ID = R.drawable.animated; 66 private static final int WIDTH = 278; 67 private static final int HEIGHT = 183; 68 private static final int NUM_FRAMES = 4; 69 private static final int FRAME_DURATION = 250; // in milliseconds 70 private static final int DURATION = NUM_FRAMES * FRAME_DURATION; 71 private static final int LAYOUT = R.layout.animated_image_layout; 72 private static final int IMAGE_ID = R.id.animated_image; 73 @Rule 74 public ActivityTestRule<DrawableStubActivity> mActivityRule = 75 new ActivityTestRule<DrawableStubActivity>(DrawableStubActivity.class); 76 private Activity mActivity; 77 78 private Uri getAsResourceUri(int resId) { 79 return new Uri.Builder() 80 .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 81 .authority(mRes.getResourcePackageName(resId)) 82 .appendPath(mRes.getResourceTypeName(resId)) 83 .appendPath(mRes.getResourceEntryName(resId)) 84 .build(); 85 } 86 87 @Before 88 public void setup() { 89 mRes = InstrumentationRegistry.getTargetContext().getResources(); 90 mContentResolver = InstrumentationRegistry.getTargetContext().getContentResolver(); 91 mActivity = mActivityRule.getActivity(); 92 } 93 94 @Test 95 public void testEmptyConstructor() { 96 new AnimatedImageDrawable(); 97 } 98 99 @Test 100 public void testMutate() { 101 AnimatedImageDrawable aid1 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated); 102 AnimatedImageDrawable aid2 = (AnimatedImageDrawable) mRes.getDrawable(R.drawable.animated); 103 104 final int originalAlpha = aid1.getAlpha(); 105 assertEquals(255, originalAlpha); 106 assertEquals(255, aid2.getAlpha()); 107 108 try { 109 aid1.mutate(); 110 aid1.setAlpha(100); 111 assertEquals(originalAlpha, aid2.getAlpha()); 112 } finally { 113 mRes.getDrawable(R.drawable.animated).setAlpha(originalAlpha); 114 } 115 } 116 117 private AnimatedImageDrawable createFromImageDecoder(int resId) { 118 Uri uri = null; 119 try { 120 uri = getAsResourceUri(resId); 121 ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri); 122 Drawable drawable = ImageDecoder.decodeDrawable(source); 123 assertTrue(drawable instanceof AnimatedImageDrawable); 124 return (AnimatedImageDrawable) drawable; 125 } catch (IOException e) { 126 fail("failed to create image from " + uri); 127 return null; 128 } 129 } 130 131 @Test 132 public void testDecodeAnimatedImageDrawable() { 133 Drawable drawable = createFromImageDecoder(RES_ID); 134 assertEquals(WIDTH, drawable.getIntrinsicWidth()); 135 assertEquals(HEIGHT, drawable.getIntrinsicHeight()); 136 } 137 138 private static class Callback extends Animatable2Callback { 139 private final Drawable mDrawable; 140 141 public Callback(Drawable d) { 142 mDrawable = d; 143 } 144 145 @Override 146 public void onAnimationStart(Drawable drawable) { 147 assertNotNull(drawable); 148 assertEquals(mDrawable, drawable); 149 super.onAnimationStart(drawable); 150 } 151 152 @Override 153 public void onAnimationEnd(Drawable drawable) { 154 assertNotNull(drawable); 155 assertEquals(mDrawable, drawable); 156 super.onAnimationEnd(drawable); 157 } 158 }; 159 160 @Test(expected=IllegalStateException.class) 161 public void testRegisterWithoutLooper() { 162 AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated); 163 164 // registerAnimationCallback must be run on a thread with a Looper, 165 // which the test thread does not have. 166 Callback cb = new Callback(drawable); 167 drawable.registerAnimationCallback(cb); 168 } 169 170 @Test 171 public void testRegisterCallback() throws Throwable { 172 AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated); 173 174 mActivityRule.runOnUiThread(() -> { 175 // Register a callback. 176 Callback cb = new Callback(drawable); 177 drawable.registerAnimationCallback(cb); 178 assertTrue(drawable.unregisterAnimationCallback(cb)); 179 180 // Now that it has been removed, it cannot be removed again. 181 assertFalse(drawable.unregisterAnimationCallback(cb)); 182 }); 183 } 184 185 @Test 186 public void testClearCallbacks() throws Throwable { 187 AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated); 188 189 Callback[] callbacks = new Callback[] { 190 new Callback(drawable), 191 new Callback(drawable), 192 new Callback(drawable), 193 new Callback(drawable), 194 new Callback(drawable), 195 new Callback(drawable), 196 new Callback(drawable), 197 new Callback(drawable), 198 }; 199 200 mActivityRule.runOnUiThread(() -> { 201 for (Callback cb : callbacks) { 202 drawable.registerAnimationCallback(cb); 203 } 204 }); 205 206 drawable.clearAnimationCallbacks(); 207 208 for (Callback cb : callbacks) { 209 // It has already been removed. 210 assertFalse(drawable.unregisterAnimationCallback(cb)); 211 } 212 } 213 214 /** 215 * Helper for attaching drawable to the view system. 216 * 217 * Necessary for the drawable to animate. 218 * 219 * Must be called from UI thread. 220 */ 221 private void setContentView(AnimatedImageDrawable drawable) { 222 mActivity.setContentView(LAYOUT); 223 ImageView imageView = (ImageView) mActivity.findViewById(IMAGE_ID); 224 imageView.setImageDrawable(drawable); 225 } 226 227 @Test 228 public void testUnregisterCallback() throws Throwable { 229 AnimatedImageDrawable drawable = createFromImageDecoder(R.drawable.animated); 230 231 Callback cb = new Callback(drawable); 232 mActivityRule.runOnUiThread(() -> { 233 setContentView(drawable); 234 235 drawable.registerAnimationCallback(cb); 236 assertTrue(drawable.unregisterAnimationCallback(cb)); 237 drawable.setRepeatCount(0); 238 drawable.start(); 239 }); 240 241 cb.waitForStart(); 242 cb.assertStarted(false); 243 244 cb.waitForEnd(DURATION * 2); 245 cb.assertEnded(false); 246 } 247 248 @Test 249 public void testLifeCycle() throws Throwable { 250 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 251 252 // Only run the animation one time. 253 drawable.setRepeatCount(0); 254 255 Callback cb = new Callback(drawable); 256 mActivityRule.runOnUiThread(() -> { 257 setContentView(drawable); 258 259 drawable.registerAnimationCallback(cb); 260 }); 261 262 assertFalse(drawable.isRunning()); 263 cb.assertStarted(false); 264 cb.assertEnded(false); 265 266 mActivityRule.runOnUiThread(() -> { 267 drawable.start(); 268 assertTrue(drawable.isRunning()); 269 }); 270 cb.waitForStart(); 271 cb.assertStarted(true); 272 273 // Extra time, to wait for the message to post. 274 cb.waitForEnd(DURATION * 3); 275 cb.assertEnded(true); 276 assertFalse(drawable.isRunning()); 277 } 278 279 @Test 280 public void testLifeCycleSoftware() throws Throwable { 281 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 282 283 Bitmap bm = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), 284 Bitmap.Config.ARGB_8888); 285 Canvas canvas = new Canvas(bm); 286 287 Callback cb = new Callback(drawable); 288 mActivityRule.runOnUiThread(() -> { 289 drawable.registerAnimationCallback(cb); 290 drawable.draw(canvas); 291 }); 292 293 assertFalse(drawable.isRunning()); 294 cb.assertStarted(false); 295 cb.assertEnded(false); 296 297 mActivityRule.runOnUiThread(() -> { 298 drawable.start(); 299 assertTrue(drawable.isRunning()); 300 drawable.draw(canvas); 301 }); 302 cb.waitForStart(); 303 cb.assertStarted(true); 304 305 // Only run the animation one time. 306 drawable.setRepeatCount(0); 307 308 // The drawable will prevent skipping frames, so we actually have to 309 // draw each frame. (Start with 1, since we already drew frame 0.) 310 for (int i = 1; i < NUM_FRAMES; i++) { 311 cb.waitForEnd(FRAME_DURATION); 312 cb.assertEnded(false); 313 mActivityRule.runOnUiThread(() -> { 314 assertTrue(drawable.isRunning()); 315 drawable.draw(canvas); 316 }); 317 } 318 319 cb.waitForEnd(FRAME_DURATION); 320 assertFalse(drawable.isRunning()); 321 cb.assertEnded(true); 322 } 323 324 @Test 325 public void testAddCallbackAfterStart() throws Throwable { 326 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 327 Callback cb = new Callback(drawable); 328 mActivityRule.runOnUiThread(() -> { 329 setContentView(drawable); 330 331 drawable.setRepeatCount(0); 332 drawable.start(); 333 drawable.registerAnimationCallback(cb); 334 }); 335 336 // Add extra duration to wait for the message posted by the end of the 337 // animation. This should help fix flakiness. 338 cb.waitForEnd(DURATION * 3); 339 cb.assertEnded(true); 340 } 341 342 @Test 343 public void testStop() throws Throwable { 344 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 345 Callback cb = new Callback(drawable); 346 mActivityRule.runOnUiThread(() -> { 347 setContentView(drawable); 348 349 drawable.registerAnimationCallback(cb); 350 351 drawable.start(); 352 assertTrue(drawable.isRunning()); 353 354 drawable.stop(); 355 assertFalse(drawable.isRunning()); 356 }); 357 358 // This duration may be overkill, but we need to wait for the message 359 // to post. Increasing it should help with flakiness on bots. 360 cb.waitForEnd(DURATION * 3); 361 cb.assertStarted(true); 362 cb.assertEnded(true); 363 } 364 365 @Test 366 public void testRepeatCounts() throws Throwable { 367 for (int repeatCount : new int[] { 3, 5, 7, 16 }) { 368 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 369 assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount()); 370 371 Callback cb = new Callback(drawable); 372 mActivityRule.runOnUiThread(() -> { 373 setContentView(drawable); 374 375 drawable.registerAnimationCallback(cb); 376 drawable.setRepeatCount(repeatCount); 377 assertEquals(repeatCount, drawable.getRepeatCount()); 378 drawable.start(); 379 }); 380 381 // The animation runs repeatCount + 1 total times. 382 cb.waitForEnd(DURATION * repeatCount); 383 cb.assertEnded(false); 384 385 cb.waitForEnd(DURATION * 2); 386 cb.assertEnded(true); 387 388 drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE); 389 assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, drawable.getRepeatCount()); 390 } 391 } 392 393 @Test 394 public void testRepeatCountInfinite() throws Throwable { 395 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 396 Callback cb = new Callback(drawable); 397 mActivityRule.runOnUiThread(() -> { 398 setContentView(drawable); 399 400 drawable.registerAnimationCallback(cb); 401 drawable.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE); 402 drawable.start(); 403 }); 404 405 // There is no way to truly test infinite, but let it run for a long 406 // time and verify that it's still running. 407 cb.waitForEnd(DURATION * 30); 408 cb.assertEnded(false); 409 assertTrue(drawable.isRunning()); 410 } 411 412 @Test 413 public void testGetOpacity() { 414 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 415 assertEquals(PixelFormat.TRANSLUCENT, drawable.getOpacity()); 416 } 417 418 @Test 419 public void testColorFilter() { 420 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 421 422 ColorFilter filter = new LightingColorFilter(0, Color.RED); 423 drawable.setColorFilter(filter); 424 assertEquals(filter, drawable.getColorFilter()); 425 426 Bitmap actual = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 427 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 428 { 429 Canvas canvas = new Canvas(actual); 430 drawable.draw(canvas); 431 } 432 433 for (int i = 0; i < actual.getWidth(); ++i) { 434 for (int j = 0; j < actual.getHeight(); ++j) { 435 int color = actual.getPixel(i, j); 436 // The LightingColorFilter does not affect the transparent pixels, 437 // so all pixels should either remain transparent or turn red. 438 if (color != Color.RED && color != Color.TRANSPARENT) { 439 fail("pixel at " + i + ", " + j + " does not match expected. " 440 + "expected: " + Color.RED + " OR " + Color.TRANSPARENT 441 + " actual: " + color); 442 } 443 } 444 } 445 } 446 447 @Test 448 public void testPostProcess() { 449 // Compare post processing a Rect in the middle of the (not-animating) 450 // image with drawing manually. They should be exactly the same. 451 BiFunction<Integer, Integer, Rect> rectCreator = (width, height) -> { 452 int quarterWidth = width / 4; 453 int quarterHeight = height / 4; 454 return new Rect(quarterWidth, quarterHeight, 455 3 * quarterWidth, 3 * quarterHeight); 456 }; 457 458 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 459 Bitmap expected = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 460 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 461 462 Paint paint = new Paint(); 463 paint.setColor(Color.RED); 464 465 { 466 Rect r = rectCreator.apply(drawable.getIntrinsicWidth(), 467 drawable.getIntrinsicHeight()); 468 Canvas canvas = new Canvas(expected); 469 drawable.draw(canvas); 470 471 for (int i = r.left; i < r.right; ++i) { 472 for (int j = r.top; j < r.bottom; ++j) { 473 assertNotEquals(Color.RED, expected.getPixel(i, j)); 474 } 475 } 476 477 canvas.drawRect(r, paint); 478 479 for (int i = r.left; i < r.right; ++i) { 480 for (int j = r.top; j < r.bottom; ++j) { 481 assertEquals(Color.RED, expected.getPixel(i, j)); 482 } 483 } 484 } 485 486 487 AnimatedImageDrawable testDrawable = null; 488 Uri uri = null; 489 try { 490 uri = getAsResourceUri(RES_ID); 491 ImageDecoder.Source source = ImageDecoder.createSource(mContentResolver, uri); 492 Drawable dr = ImageDecoder.decodeDrawable(source, (decoder, info, src) -> { 493 decoder.setPostProcessor((canvas) -> { 494 canvas.drawRect(rectCreator.apply(canvas.getWidth(), 495 canvas.getHeight()), paint); 496 return PixelFormat.TRANSLUCENT; 497 }); 498 }); 499 assertTrue(dr instanceof AnimatedImageDrawable); 500 testDrawable = (AnimatedImageDrawable) dr; 501 } catch (IOException e) { 502 fail("failed to create image from " + uri); 503 } 504 505 Bitmap actual = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 506 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 507 508 { 509 Canvas canvas = new Canvas(actual); 510 testDrawable.draw(canvas); 511 } 512 513 BitmapUtils.compareBitmaps(expected, actual); 514 } 515 516 @Test 517 public void testCreateFromXml() throws XmlPullParserException, IOException { 518 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_tag); 519 Drawable drawable = Drawable.createFromXml(mRes, parser); 520 assertNotNull(drawable); 521 assertTrue(drawable instanceof AnimatedImageDrawable); 522 } 523 524 @Test 525 public void testCreateFromXmlClass() throws XmlPullParserException, IOException { 526 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable); 527 Drawable drawable = Drawable.createFromXml(mRes, parser); 528 assertNotNull(drawable); 529 assertTrue(drawable instanceof AnimatedImageDrawable); 530 } 531 532 @Test 533 public void testCreateFromXmlClassAttribute() throws XmlPullParserException, IOException { 534 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_class); 535 Drawable drawable = Drawable.createFromXml(mRes, parser); 536 assertNotNull(drawable); 537 assertTrue(drawable instanceof AnimatedImageDrawable); 538 } 539 540 @Test(expected=XmlPullParserException.class) 541 public void testMissingSrcInflate() throws XmlPullParserException, IOException { 542 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_nosrc); 543 Drawable drawable = Drawable.createFromXml(mRes, parser); 544 } 545 546 @Test 547 public void testAutoMirrored() { 548 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 549 assertFalse(drawable.isAutoMirrored()); 550 551 drawable.setAutoMirrored(true); 552 assertTrue(drawable.isAutoMirrored()); 553 554 drawable.setAutoMirrored(false); 555 assertFalse(drawable.isAutoMirrored()); 556 } 557 558 @Test 559 public void testAutoMirroredFromXml() throws XmlPullParserException, IOException { 560 AnimatedImageDrawable drawable = parseXml(R.drawable.animatedimagedrawable_tag); 561 assertFalse(drawable.isAutoMirrored()); 562 563 drawable = parseXml(R.drawable.animatedimagedrawable_automirrored); 564 assertTrue(drawable.isAutoMirrored()); 565 } 566 567 private AnimatedImageDrawable parseXml(int resId) throws XmlPullParserException, IOException { 568 XmlPullParser parser = mRes.getXml(resId); 569 Drawable drawable = Drawable.createFromXml(mRes, parser); 570 assertNotNull(drawable); 571 assertTrue(drawable instanceof AnimatedImageDrawable); 572 return (AnimatedImageDrawable) drawable; 573 } 574 575 @Test 576 public void testAutoStartFromXml() throws XmlPullParserException, IOException { 577 AnimatedImageDrawable drawable = parseXml(R.drawable.animatedimagedrawable_tag); 578 assertFalse(drawable.isRunning()); 579 580 drawable = parseXml(R.drawable.animatedimagedrawable_autostart_false); 581 assertFalse(drawable.isRunning()); 582 583 drawable = parseXml(R.drawable.animatedimagedrawable_autostart); 584 assertTrue(drawable.isRunning()); 585 } 586 587 private void drawAndCompare(Bitmap expected, Drawable drawable) { 588 Bitmap test = Bitmap.createBitmap(drawable.getIntrinsicWidth(), 589 drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); 590 Canvas canvas = new Canvas(test); 591 drawable.draw(canvas); 592 BitmapUtils.compareBitmaps(expected, test); 593 } 594 595 @Test 596 public void testAutoMirroredDrawing() { 597 AnimatedImageDrawable drawable = createFromImageDecoder(RES_ID); 598 assertFalse(drawable.isAutoMirrored()); 599 600 final int width = drawable.getIntrinsicWidth(); 601 final int height = drawable.getIntrinsicHeight(); 602 Bitmap normal = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 603 { 604 Canvas canvas = new Canvas(normal); 605 drawable.draw(canvas); 606 } 607 608 Bitmap flipped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 609 { 610 Canvas canvas = new Canvas(flipped); 611 canvas.translate(width, 0); 612 canvas.scale(-1, 1); 613 drawable.draw(canvas); 614 } 615 616 for (int i = 0; i < width; ++i) { 617 for (int j = 0; j < height; ++j) { 618 assertEquals(normal.getPixel(i, j), flipped.getPixel(width - 1 - i, j)); 619 } 620 } 621 622 drawable.setAutoMirrored(true); 623 drawAndCompare(normal, drawable); 624 625 drawable.setLayoutDirection(View.LAYOUT_DIRECTION_RTL); 626 drawAndCompare(flipped, drawable); 627 628 drawable.setAutoMirrored(false); 629 drawAndCompare(normal, drawable); 630 } 631 632 @Test 633 public void testRepeatCountFromXml() throws XmlPullParserException, IOException { 634 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count); 635 Drawable drawable = Drawable.createFromXml(mRes, parser); 636 assertNotNull(drawable); 637 assertTrue(drawable instanceof AnimatedImageDrawable); 638 639 AnimatedImageDrawable aid = (AnimatedImageDrawable) drawable; 640 assertEquals(17, aid.getRepeatCount()); 641 } 642 643 @Test 644 public void testInfiniteRepeatCountFromXml() throws XmlPullParserException, IOException { 645 // This image has an encoded repeat count of 1. Verify that. 646 Drawable drawable = mRes.getDrawable(R.drawable.animated_one_loop); 647 assertNotNull(drawable); 648 assertTrue(drawable instanceof AnimatedImageDrawable); 649 AnimatedImageDrawable aid = (AnimatedImageDrawable) drawable; 650 assertEquals(1, aid.getRepeatCount()); 651 652 // This layout uses the same image and overrides the repeat count to infinity. 653 XmlPullParser parser = mRes.getXml(R.drawable.animatedimagedrawable_loop_count_infinite); 654 drawable = Drawable.createFromXml(mRes, parser); 655 assertNotNull(drawable); 656 assertTrue(drawable instanceof AnimatedImageDrawable); 657 658 aid = (AnimatedImageDrawable) drawable; 659 assertEquals(AnimatedImageDrawable.REPEAT_INFINITE, aid.getRepeatCount()); 660 } 661 } 662