Merge change 7169 into donut

* changes:
  Add new donut packages to CTS test progress tracker.
diff --git a/tests/res/drawable/animationdrawable.xml b/tests/res/drawable/animationdrawable.xml
new file mode 100644
index 0000000..6756966
--- /dev/null
+++ b/tests/res/drawable/animationdrawable.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+    <item android:drawable="@drawable/testimage" android:duration="3000"/>
+    <item android:drawable="@drawable/pass" android:duration="2000"/>
+    <item android:drawable="@drawable/scenery" android:duration="1000"/>
+</animation-list>
diff --git a/tests/res/drawable/opaque.9.png b/tests/res/drawable/opaque.9.png
new file mode 100644
index 0000000..a60f6d8
--- /dev/null
+++ b/tests/res/drawable/opaque.9.png
Binary files differ
diff --git a/tests/res/drawable/scaled1.png b/tests/res/drawable/scaled1.png
new file mode 100644
index 0000000..2dad421
--- /dev/null
+++ b/tests/res/drawable/scaled1.png
Binary files differ
diff --git a/tests/res/drawable/scaled2.png b/tests/res/drawable/scaled2.png
new file mode 100644
index 0000000..6d6a94f
--- /dev/null
+++ b/tests/res/drawable/scaled2.png
Binary files differ
diff --git a/tests/res/drawable/transparent_border.9.png b/tests/res/drawable/transparent_border.9.png
new file mode 100644
index 0000000..2614c06
--- /dev/null
+++ b/tests/res/drawable/transparent_border.9.png
Binary files differ
diff --git a/tests/res/drawable/transparent_right.9.png b/tests/res/drawable/transparent_right.9.png
new file mode 100644
index 0000000..a438312
--- /dev/null
+++ b/tests/res/drawable/transparent_right.9.png
Binary files differ
diff --git a/tests/res/xml/anim_list_correct.xml b/tests/res/xml/anim_list_correct.xml
new file mode 100644
index 0000000..660aa96
--- /dev/null
+++ b/tests/res/xml/anim_list_correct.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:visible="false"
+    android:variablePadding="true"
+    android:oneshot="true" >
+
+    <item android:drawable="@drawable/testimage"
+        android:duration="2000" />
+
+    <item android:duration="1000" >
+        <color android:color="#77ffffff" />
+    </item>
+</animation-list>
+
diff --git a/tests/res/xml/anim_list_missing_item_drawable.xml b/tests/res/xml/anim_list_missing_item_drawable.xml
new file mode 100644
index 0000000..99052f4
--- /dev/null
+++ b/tests/res/xml/anim_list_missing_item_drawable.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:duration="2000" />
+</animation-list>
+
diff --git a/tests/res/xml/anim_list_missing_item_duration.xml b/tests/res/xml/anim_list_missing_item_duration.xml
new file mode 100644
index 0000000..e3986c2
--- /dev/null
+++ b/tests/res/xml/anim_list_missing_item_duration.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:drawable="@drawable/testimage" />
+</animation-list>
+
diff --git a/tests/res/xml/anim_list_missing_list_attrs.xml b/tests/res/xml/anim_list_missing_list_attrs.xml
new file mode 100644
index 0000000..25d2dfe
--- /dev/null
+++ b/tests/res/xml/anim_list_missing_list_attrs.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item android:drawable="@drawable/testimage"
+        android:duration="2000" />
+</animation-list>
+
diff --git a/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
new file mode 100644
index 0000000..67223ab
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/NinePatchTest.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.cts;
+
+import com.android.cts.stub.R;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.NinePatch;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.test.AndroidTestCase;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(NinePatch.class)
+public class NinePatchTest extends AndroidTestCase {
+    private static int ALPHA_OPAQUE = 0xFF;
+
+    private NinePatch mNinePatch;
+    private Bitmap mBitmap;
+    private final String NAME = "TESTNAME";
+    private final int WIDTH = 80;
+    private final int HEIGTH = 120;
+    private final int[] COLOR = new int[WIDTH * HEIGTH];
+    private byte[] mChunk;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mBitmap = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.opaque);
+        mChunk = mBitmap.getNinePatchChunk();
+        assertNotNull(mChunk);
+        mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "NinePatch",
+        args = {android.graphics.Bitmap.class, byte[].class, java.lang.String.class}
+    )
+    public void testConstructor() {
+        mNinePatch = null;
+        try {
+            mNinePatch = new NinePatch(mBitmap, new byte[2], NAME);
+            fail("should throw exception");
+        } catch (Exception e) {
+        }
+        mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isNinePatchChunk",
+        args = {byte[].class}
+    )
+    public void testIsNinePatchChunk() {
+        assertTrue(NinePatch.isNinePatchChunk(mChunk));
+        Bitmap bitmap = Bitmap.createBitmap(COLOR, 10, 10, Bitmap.Config.ARGB_4444);
+        assertFalse(NinePatch.isNinePatchChunk(bitmap.getNinePatchChunk()));
+        assertFalse(NinePatch.isNinePatchChunk(null));
+
+    }
+
+    @TestTargets(value = {
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "draw",
+            args = {android.graphics.Canvas.class, android.graphics.RectF.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "draw",
+            args = {android.graphics.Canvas.class, android.graphics.Rect.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "draw",
+            args = {android.graphics.Canvas.class, android.graphics.Rect.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setPaint",
+            args = {android.graphics.Paint.class}
+        )
+    })
+    public void testDraw() {
+        Bitmap expected =
+            BitmapFactory.decodeResource(getContext().getResources(), R.drawable.scaled1);
+
+        Bitmap bitmap = Bitmap.createBitmap(expected.getWidth(), expected.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(bitmap);
+        RectF rectf = new RectF(0, 0, c.getWidth(), c.getHeight());
+        mNinePatch.draw(c, rectf);
+        checkBitmapWithAlpha(expected, bitmap, ALPHA_OPAQUE);
+
+        expected = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.scaled2);
+        bitmap = Bitmap.createBitmap(expected.getWidth(), expected.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        c = new Canvas(bitmap);
+        Rect rect = new Rect(0, 0, c.getWidth(), c.getHeight());
+        mNinePatch.draw(c, rect);
+        checkBitmapWithAlpha(expected, bitmap, ALPHA_OPAQUE);
+
+        bitmap = Bitmap.createBitmap(expected.getWidth(), expected.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        c = new Canvas(bitmap);
+        rect = new Rect(0, 0, c.getWidth(), c.getHeight());
+        final int alpha = 128;
+        Paint p = new Paint();
+        p.setAlpha(alpha);
+        mNinePatch.draw(c, rect, p);
+        checkBitmapWithAlpha(expected, bitmap, alpha);
+
+        bitmap = Bitmap.createBitmap(expected.getWidth(), expected.getHeight(),
+                Bitmap.Config.ARGB_8888);
+        c = new Canvas(bitmap);
+        rectf = new RectF(0, 0, c.getWidth(), c.getHeight());
+        mNinePatch.setPaint(p);
+        mNinePatch.draw(c, rectf);
+        checkBitmapWithAlpha(expected, bitmap, alpha);
+    }
+
+    private void checkBitmapWithAlpha(Bitmap expected, Bitmap bitmap, int alpha) {
+        assertEquals(expected.getWidth(), bitmap.getWidth());
+        assertEquals(expected.getHeight(), bitmap.getHeight());
+        int width = expected.getWidth();
+        int height = expected.getHeight();
+
+        for (int i = 0; i < width; i++) {
+            for (int j = 0; j < height; j++) {
+                int expectedPixel = expected.getPixel(i, j);
+                int actualPixel = bitmap.getPixel(i, j);
+                int expectedAlpha = Color.alpha(expectedPixel);
+                int actualAlpha = Color.alpha(actualPixel);
+                expectedPixel &= 0xFFFFFF;
+                actualPixel &= 0xFFFFFF;
+                assertEquals(expectedPixel, actualPixel);
+                assertEquals(expectedAlpha * alpha / ALPHA_OPAQUE, actualAlpha, 1);
+            }
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "hasAlpha",
+        args = {}
+    )
+    public void testHasAlpha() {
+        assertFalse(mNinePatch.hasAlpha());
+        assertEquals(mNinePatch.hasAlpha(), mBitmap.hasAlpha());
+
+        Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.transparent_border);
+        byte[] chunk = bitmap.getNinePatchChunk();
+        NinePatch ninePatch = new NinePatch(bitmap, chunk, NAME);
+        assertTrue(ninePatch.hasAlpha());
+        assertEquals(ninePatch.hasAlpha(), bitmap.hasAlpha());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getHeight",
+        args = {}
+    )
+    public void testGetHeight() {
+        assertEquals(5, mNinePatch.getHeight());
+        assertEquals(mNinePatch.getHeight(), mBitmap.getHeight());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getWidth",
+        args = {}
+    )
+    public void testGetWidth() {
+        assertEquals(5, mNinePatch.getHeight());
+        assertEquals(mNinePatch.getWidth(), mBitmap.getWidth());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getTransparentRegion",
+        args = {android.graphics.Rect.class}
+    )
+    public void testGetTransparentRegion() {
+        // no transparency in opaque bitmap
+        Rect location = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+        Region region = mNinePatch.getTransparentRegion(location);
+        assertNull(region);
+
+        // transparent border of 1px
+        mBitmap = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.transparent_border);
+        mChunk = mBitmap.getNinePatchChunk();
+        assertNotNull(mChunk);
+        mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+
+        location = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+        region = mNinePatch.getTransparentRegion(location);
+        assertNotNull(region);
+        Rect regionBounds = region.getBounds();
+        assertBounds(regionBounds, 0, 0, 5, 5);
+
+        location = new Rect(0, 0, mBitmap.getWidth() * 2, mBitmap.getHeight() * 2);
+        region = mNinePatch.getTransparentRegion(location);
+        assertNotNull(region);
+        regionBounds = region.getBounds();
+        assertBounds(regionBounds, 0, 0, 10, 10);
+
+        // transparent padding of 1px on the right side
+        mBitmap = BitmapFactory.decodeResource(getContext().getResources(),
+                R.drawable.transparent_right);
+        mChunk = mBitmap.getNinePatchChunk();
+        assertNotNull(mChunk);
+        mNinePatch = new NinePatch(mBitmap, mChunk, NAME);
+
+        location = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+        region = mNinePatch.getTransparentRegion(location);
+        assertNotNull(region);
+        regionBounds = region.getBounds();
+        assertBounds(regionBounds, 4, 0, 5, 5);
+
+        location = new Rect(0, 0, mBitmap.getWidth() * 2, mBitmap.getHeight() * 2);
+        region = mNinePatch.getTransparentRegion(location);
+        regionBounds = region.getBounds();
+        assertBounds(regionBounds, 9, 0, 10, 10);
+    }
+
+    private void assertBounds(Rect regionBounds, int left, int top, int right, int bottom) {
+        assertEquals(left, regionBounds.left);
+        assertEquals(top, regionBounds.top);
+        assertEquals(right, regionBounds.right);
+        assertEquals(bottom, regionBounds.bottom);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
new file mode 100644
index 0000000..9954b10
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.cts;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Picture;
+import android.graphics.Paint.Style;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+@TestTargetClass(Picture.class)
+public class PictureTest extends TestCase {
+
+    private static final int TEST_WIDTH = 4; // must be >= 2
+    private static final int TEST_HEIGHT = 3; // must >= 2
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "Picture",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "Picture",
+            args = {android.graphics.Picture.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "beginRecording",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "endRecording",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getHeight",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getWidth",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "writeToStream",
+            args = {java.io.OutputStream.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "createFromStream",
+            args = {java.io.InputStream.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "draw",
+            args = {android.graphics.Canvas.class}
+        )
+    })
+    public void testPicture() throws Exception {
+
+        Picture picture = new Picture();
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+        Canvas canvas = picture.beginRecording(TEST_WIDTH, TEST_HEIGHT);
+        assertNotNull(canvas);
+        drawPicture(canvas);
+        picture.endRecording();
+
+        Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        canvas = new Canvas(bitmap);
+        picture.draw(canvas);
+        checkSize(picture);
+        checkBitmap(bitmap);
+
+        picture.writeToStream(bout);
+        picture = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
+
+        // create a new Canvas with a new bitmap
+        bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        canvas = new Canvas(bitmap);
+        picture.draw(canvas);
+        checkSize(picture);
+        checkBitmap(bitmap);
+
+        Picture pic = new Picture(picture);
+        bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        canvas = new Canvas(bitmap);
+        pic.draw(canvas);
+        checkSize(pic);
+        checkBitmap(bitmap);
+    }
+
+    private void checkSize(Picture picture) {
+        assertEquals(TEST_WIDTH, picture.getWidth());
+        assertEquals(TEST_HEIGHT, picture.getHeight());
+    }
+
+    private void drawPicture(Canvas canvas) {
+        Paint paint = new Paint();
+        // GREEN rectangle covering the entire canvas
+        paint.setColor(Color.GREEN);
+        paint.setStyle(Style.FILL);
+        canvas.drawRect(0, 0, TEST_WIDTH, TEST_HEIGHT, paint);
+        // horizontal red line starting from (0,0); overwrites first line of the rectangle
+        paint.setColor(Color.RED);
+        canvas.drawLine(0, 0, TEST_WIDTH, 0, paint);
+        // overwrite (0,0) with a blue dot
+        paint.setColor(Color.BLUE);
+        canvas.drawPoint(0, 0, paint);
+    }
+
+    private void checkBitmap(Bitmap bitmap) {
+        // first pixel is BLUE, rest of the line is RED
+        assertEquals(Color.BLUE, bitmap.getPixel(0, 0));
+        for (int x = 1; x < TEST_WIDTH; x++) {
+            assertEquals(Color.RED, bitmap.getPixel(x, 0));
+        }
+        // remaining lines are all green
+        for (int y = 1; y < TEST_HEIGHT; y++) {
+            for (int x = 0; x < TEST_WIDTH; x++) {
+                assertEquals(Color.GREEN, bitmap.getPixel(x, y));
+            }
+        }
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
new file mode 100644
index 0000000..e86f051
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable.cts;
+
+import com.android.cts.stub.R;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer.DrawableContainerState;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Xml;
+import android.view.animation.cts.DelayedCheck;
+import android.widget.ImageView;
+import android.widget.cts.ImageViewStubActivity;
+
+import java.io.IOException;
+
+@TestTargetClass(AnimationDrawable.class)
+public class AnimationDrawableTest extends ActivityInstrumentationTestCase2<ImageViewStubActivity> {
+    private static final int FRAMES_COUNT        = 3;
+    private static final int FIRST_FRAME_INDEX   = 0;
+    private static final int SECOND_FRAME_INDEX  = 1;
+    private static final int THIRD_FRAME_INDEX   = 2;
+    private static final long TOLERANCE = 500;
+    private static final long FIRST_FRAME_DURATION   = 3000;
+    private static final long SECOND_FRAME_DURATION  = 2000;
+    private static final long THIRD_FRAME_DURATION   = 1000;
+
+    private AnimationDrawable mAnimationDrawable;
+    private Resources mResources;
+
+    public AnimationDrawableTest() {
+        super("com.android.cts.stub", ImageViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final Activity activity = getActivity();
+        mResources = activity.getResources();
+        try {
+            runTestOnUiThread(new Runnable() {
+                public void run() {
+                    ImageView imageView = (ImageView) activity.findViewById(R.id.imageview);
+                    imageView.setBackgroundResource(R.drawable.animationdrawable);
+                    mAnimationDrawable = (AnimationDrawable) imageView.getBackground();
+                }
+            });
+        } catch (Throwable t) {
+            throw new Exception(t);
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "AnimationDrawable",
+        args = {}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete")
+    public void testConstructor() {
+        mAnimationDrawable = new AnimationDrawable();
+        // Check the values set in the constructor
+        assertNotNull(mAnimationDrawable.getConstantState());
+        assertFalse(mAnimationDrawable.isRunning());
+        assertTrue(mAnimationDrawable.isOneShot());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "setVisible",
+        args = {boolean.class, boolean.class}
+    )
+    public void testSetVisible() throws Throwable {
+        assertTrue(mAnimationDrawable.isVisible());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.start();
+            }
+        });
+        assertTrue(mAnimationDrawable.isRunning());
+        assertSame(mAnimationDrawable.getFrame(FIRST_FRAME_INDEX),
+                mAnimationDrawable.getCurrent());
+
+        delayedCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                assertTrue(mAnimationDrawable.setVisible(false, false));
+            }
+        });
+        assertFalse(mAnimationDrawable.isVisible());
+        assertFalse(mAnimationDrawable.isRunning());
+        assertStoppedAnimation(SECOND_FRAME_INDEX, SECOND_FRAME_DURATION);
+
+        // restart animation
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                assertTrue(mAnimationDrawable.setVisible(true, true));
+            }
+        });
+        assertTrue(mAnimationDrawable.isVisible());
+        assertFalse(mAnimationDrawable.isRunning());
+        assertStoppedAnimation(FIRST_FRAME_INDEX, FIRST_FRAME_DURATION);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "start",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "stop",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isRunning",
+            args = {}
+        )
+    })
+    public void testStart() throws Throwable {
+        // animation should play repeat if do not stop it.
+        assertFalse(mAnimationDrawable.isOneShot());
+        assertFalse(mAnimationDrawable.isRunning());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.start();
+            }
+        });
+
+        assertTrue(mAnimationDrawable.isRunning());
+        assertSame(mAnimationDrawable.getFrame(FIRST_FRAME_INDEX),
+                mAnimationDrawable.getCurrent());
+        delayedCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                // This method has no effect if the animation is running.
+                mAnimationDrawable.start();
+            }
+        });
+        delayedCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.stop();
+            }
+        });
+        assertFalse(mAnimationDrawable.isRunning());
+        assertStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                // This method has no effect if the animation is not running.
+                mAnimationDrawable.stop();
+            }
+        });
+        assertFalse(mAnimationDrawable.isRunning());
+        assertStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.NOT_NECESSARY,
+        method = "run",
+        args = {}
+    )
+    public void testRun() {
+        // This method should not be called directly.
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "unscheduleSelf",
+        args = {java.lang.Runnable.class}
+    )
+    public void testUnscheduleSelf() throws Throwable {
+        assertFalse(mAnimationDrawable.isRunning());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.start();
+            }
+        });
+
+        assertTrue(mAnimationDrawable.isRunning());
+        delayedCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.unscheduleSelf(mAnimationDrawable);
+            }
+        });
+        assertFalse(mAnimationDrawable.isRunning());
+        assertStoppedAnimation(SECOND_FRAME_INDEX, SECOND_FRAME_DURATION);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getNumberOfFrames",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "addFrame",
+            args = {android.graphics.drawable.Drawable.class, int.class}
+        )
+    })
+    @ToBeFixed(bug = "1695243", explanation = "should add @throws clause into javadoc of "
+        + "AnimationDrawable#addFrame(Drawable, int) when param frame is null")
+    public void testGetNumberOfFrames() {
+        assertEquals(FRAMES_COUNT, mAnimationDrawable.getNumberOfFrames());
+
+        Drawable frame = mResources.getDrawable(R.drawable.failed);
+        mAnimationDrawable.addFrame(frame, 2000);
+        assertEquals(FRAMES_COUNT + 1, mAnimationDrawable.getNumberOfFrames());
+
+        // add same frame with same duration
+        mAnimationDrawable.addFrame(frame, 2000);
+        assertEquals(FRAMES_COUNT + 2, mAnimationDrawable.getNumberOfFrames());
+
+        try {
+            mAnimationDrawable.addFrame(null, 1000);
+            fail("Should throw NullPointerException if param frame is null.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getFrame",
+        args = {int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "should add @throws clause into javadoc of "
+            + "AnimationDrawable#getFrame(int) when param index is out of bounds")
+    public void testGetFrame() {
+        Drawable frame = mAnimationDrawable.getFrame(FIRST_FRAME_INDEX);
+        Drawable drawable = mResources.getDrawable(R.drawable.testimage);
+        assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth());
+        assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight());
+
+        frame = mAnimationDrawable.getFrame(SECOND_FRAME_INDEX);
+        drawable = mResources.getDrawable(R.drawable.pass);
+        assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth());
+        assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight());
+
+        frame = mAnimationDrawable.getFrame(THIRD_FRAME_INDEX);
+        drawable = mResources.getDrawable(R.drawable.scenery);
+        assertEquals(drawable.getIntrinsicWidth(), frame.getIntrinsicWidth());
+        assertEquals(drawable.getIntrinsicHeight(), frame.getIntrinsicHeight());
+
+        assertNull(mAnimationDrawable.getFrame(THIRD_FRAME_INDEX + 1));
+
+        try {
+            mAnimationDrawable.getFrame(-1);
+            fail("Should throw ArrayIndexOutOfBoundsException.");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            mAnimationDrawable.getFrame(10);
+            fail("Should throw ArrayIndexOutOfBoundsException.");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getDuration",
+        args = {int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "should add @throws clause into javadoc of "
+            + "AnimationDrawable#getDuration(int) when param index is out of bounds")
+    public void testGetDuration() {
+        assertEquals(FIRST_FRAME_DURATION, mAnimationDrawable.getDuration(FIRST_FRAME_INDEX));
+        assertEquals(SECOND_FRAME_DURATION, mAnimationDrawable.getDuration(SECOND_FRAME_INDEX));
+        assertEquals(THIRD_FRAME_DURATION, mAnimationDrawable.getDuration(THIRD_FRAME_INDEX));
+        assertEquals(0, mAnimationDrawable.getDuration(THIRD_FRAME_INDEX + 1));
+
+        try {
+            mAnimationDrawable.getDuration(-1);
+            fail("Should throw ArrayIndexOutOfBoundsException.");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            mAnimationDrawable.getDuration(10);
+            fail("Should throw ArrayIndexOutOfBoundsException.");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isOneShot",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setOneShot",
+            args = {boolean.class}
+        )
+    })
+    public void testAccessOneShot() throws Throwable {
+        // animation should play repeat if do not stop it.
+        assertFalse(mAnimationDrawable.isOneShot());
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.start();
+            }
+        });
+        delayedCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION);
+        delayedCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION);
+        // begin to repeat
+        delayedCheckDrawable(FIRST_FRAME_INDEX, THIRD_FRAME_DURATION);
+
+        runTestOnUiThread(new Runnable() {
+            public void run() {
+                mAnimationDrawable.stop();
+                mAnimationDrawable.setOneShot(true);
+                assertTrue(mAnimationDrawable.isOneShot());
+                mAnimationDrawable.start();
+            }
+        });
+        delayedCheckDrawable(SECOND_FRAME_INDEX, FIRST_FRAME_DURATION);
+        delayedCheckDrawable(THIRD_FRAME_INDEX, SECOND_FRAME_DURATION);
+        // do not repeat
+        assertStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "inflate",
+        args = {android.content.res.Resources.class, org.xmlpull.v1.XmlPullParser.class,
+                android.util.AttributeSet.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "Android API javadocs are incomplete")
+    public void testInflate() throws XmlPullParserException, IOException {
+        mAnimationDrawable = new AnimationDrawable();
+        DrawableContainerState drawableContainerState =
+            (DrawableContainerState) mAnimationDrawable.getConstantState();
+
+        XmlResourceParser parser = getResourceParser(R.xml.anim_list_correct);
+        mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+        // android:visible="false"
+        assertFalse(mAnimationDrawable.isVisible());
+        // android:oneShot="true"
+        assertTrue(mAnimationDrawable.isOneShot());
+        // android:variablePadding="true"
+        assertNull(drawableContainerState.getConstantPadding());
+        assertEquals(2, mAnimationDrawable.getNumberOfFrames());
+        assertEquals(2000, mAnimationDrawable.getDuration(0));
+        assertEquals(1000, mAnimationDrawable.getDuration(1));
+        assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
+
+        parser = getResourceParser(R.xml.anim_list_missing_list_attrs);
+        mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+        // use current the visibility
+        assertFalse(mAnimationDrawable.isVisible());
+        // default value of android:oneShot is false
+        assertFalse(mAnimationDrawable.isOneShot());
+        // default value of android:variablePadding is false
+        assertNotNull(drawableContainerState.getConstantPadding());
+        // add a new frame from xml
+        assertEquals(3, mAnimationDrawable.getNumberOfFrames());
+        assertEquals(2000, mAnimationDrawable.getDuration(0));
+        assertEquals(1000, mAnimationDrawable.getDuration(1));
+        assertEquals(2000, mAnimationDrawable.getDuration(2));
+        assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
+
+        parser = getResourceParser(R.xml.anim_list_missing_item_drawable);
+        try {
+            mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+            fail("Should throw XmlPullParserException if drawable of item is missing");
+        } catch (XmlPullParserException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "inflate",
+        args = {android.content.res.Resources.class, org.xmlpull.v1.XmlPullParser.class,
+                android.util.AttributeSet.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "should add @throws clause into javadoc of "
+            + "AnimationDrawable#inflate(Resources, XmlPullParser, AttributeSet) "
+            + "when param r, parser or attrs is null")
+    public void testInflateWithNullParameters() throws XmlPullParserException, IOException {
+        XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+        try {
+            mAnimationDrawable.inflate(null, parser, Xml.asAttributeSet(parser));
+            fail("Should throw NullPointerException if resource is null");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            mAnimationDrawable.inflate(mResources, null, Xml.asAttributeSet(parser));
+            fail("Should throw NullPointerException if parser is null");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            mAnimationDrawable.inflate(mResources, parser, null);
+            fail("Should throw NullPointerException if AttributeSet is null");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.SUFFICIENT,
+        method = "mutate",
+        args = {}
+    )
+    @ToBeFixed(bug = "", explanation = "mutate always throws out NullPointerException")
+    public void testMutate() {
+        AnimationDrawable d1 = (AnimationDrawable) mResources
+                .getDrawable(R.drawable.animationdrawable);
+        // multiple instances inflated from the same resource do not share the state
+        // simply call mutate to make sure it does not throw an exception
+        d1.mutate();
+    }
+
+    private XmlResourceParser getResourceParser(int resId) throws XmlPullParserException,
+            IOException {
+        XmlResourceParser parser = mResources.getXml(resId);
+        int type;
+        while ((type = parser.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            // Empty loop
+        }
+        return parser;
+    }
+
+    /**
+     * Delayed check specific frame should be current one in timeout.
+     * @param index - expected index of frame.
+     * @param timeout - timeout.
+     */
+    private void delayedCheckDrawable(final int index, long timeout) {
+        new DelayedCheck(timeout + TOLERANCE) {
+            Drawable expected = mAnimationDrawable.getFrame(index);
+            @Override
+            protected boolean check() {
+                return mAnimationDrawable.getCurrent().equals(expected);
+            }
+        }.run();
+    }
+
+    /**
+     * Assert animation had been stopped. It will sleep duration + TOLERANCE milliseconds and
+     * then make sure current frame had not been changed.
+     * @param index - index of current frame.
+     * @param duration - duration of current frame.
+     */
+    private void assertStoppedAnimation(int index, long duration) throws InterruptedException {
+        Thread.sleep(duration + TOLERANCE);
+        assertSame(mAnimationDrawable.getFrame(index), mAnimationDrawable.getCurrent());
+    }
+}