CTS for Drawable.hasFocusStateSpecified.

Test: cts-tradefed run singleCommand cts --skip-device-info
--skip-preconditions --abi armeabi-v7a -m CtsGraphicsTestCases
-t android.graphics.drawable.cts.DefaultFocusHighlightTest
Bug: 35096940
Change-Id: I8a95e3cb72b7b9a0cfbd2ea6eef3777226198a20
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DefaultFocusHighlightTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DefaultFocusHighlightTest.java
new file mode 100644
index 0000000..4849e71
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DefaultFocusHighlightTest.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2017 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.NinePatch;
+import android.graphics.Picture;
+import android.graphics.cts.R;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableContainer;
+import android.graphics.drawable.DrawableContainer.DrawableContainerState;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LevelListDrawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.PictureDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.shapes.RectShape;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.StateSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DefaultFocusHighlightTest {
+
+    // The target context.
+    private Context mContext;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+    }
+
+    private static final int A_COLOR = 0x920424;
+    private static final int[] NO_STATE_FOCUSED = new int[] { android.R.attr.state_enabled };
+    private static final int[] ONLY_STATE_FOCUSED = new int[] { android.R.attr.state_focused };
+    private static final int[] STATE_FOCUSED_WITH_POS =
+            new int[] { android.R.attr.state_focused, android.R.attr.state_hovered };
+    private static final int[] STATE_FOCUSED_WITH_NEG =
+            new int[] { android.R.attr.state_focused,  -android.R.attr.state_hovered };
+    private static final int[] STATE_FOCUSED_WITH_ENABLED =
+            new int[] { android.R.attr.state_focused, android.R.attr.state_enabled };
+
+    final static int[] FOCUSED_STATE =
+            new int[] { android.R.attr.state_focused, android.R.attr.state_enabled };
+
+    @UiThreadTest
+    @Test
+    public void testStateListDrawable() {
+        Drawable d;
+        // Empty state spec
+        d = DrawableFactory.createStateListDrawable(
+                new int[][] {}
+            );
+        d.setState(FOCUSED_STATE);
+        assertFalse(d.hasFocusStateSpecified());
+
+        // Wild card
+        d = DrawableFactory.createStateListDrawable(
+                new int[][] { StateSet.WILD_CARD }
+            );
+        d.setState(FOCUSED_STATE);
+        assertFalse(d.hasFocusStateSpecified());
+
+        // No state spec of state_focused=true
+        d = DrawableFactory.createStateListDrawable(
+                new int[][] { NO_STATE_FOCUSED }
+            );
+        d.setState(FOCUSED_STATE);
+        assertFalse(d.hasFocusStateSpecified());
+
+        // One state spec of only state_focused=true
+        d = DrawableFactory.createStateListDrawable(
+                new int[][] { ONLY_STATE_FOCUSED }
+            );
+        d.setState(FOCUSED_STATE);
+        assertTrue(d.hasFocusStateSpecified());
+
+        // One state spec of state_focused=true and something=true, but no state spec of
+        // state_focused=true and something=false (something is not enabled)
+        d = DrawableFactory.createStateListDrawable(
+                new int[][] { STATE_FOCUSED_WITH_POS }
+            );
+        d.setState(FOCUSED_STATE);
+        assertTrue(d.hasFocusStateSpecified());
+
+        // One state spec of state_focused=true and something=true, and one spec of
+        // state_focused=true and something=false (something is not enabled)
+        d = DrawableFactory.createStateListDrawable(
+            new int[][] { STATE_FOCUSED_WITH_POS, STATE_FOCUSED_WITH_NEG }
+        );
+        d.setState(FOCUSED_STATE);
+        assertTrue(d.hasFocusStateSpecified());
+
+        // One state spec of state_focused=true and enabled=true
+        d = DrawableFactory.createStateListDrawable(
+            new int[][] { STATE_FOCUSED_WITH_ENABLED }
+        );
+        d.setState(FOCUSED_STATE);
+        assertTrue(d.hasFocusStateSpecified());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testRippleDrawable() {
+        Drawable d = DrawableFactory.createRippleDrawable();
+        d.setState(FOCUSED_STATE);
+        assertTrue(d.hasFocusStateSpecified());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testPictureDrawable() {
+        Drawable d = DrawableFactory.createPictureDrawable(null);
+        d.setState(FOCUSED_STATE);
+        assertFalse(d.hasFocusStateSpecified());
+
+        d = DrawableFactory.createPictureDrawable(new Picture());
+        d.setState(FOCUSED_STATE);
+        assertFalse(d.hasFocusStateSpecified());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testColorStateListHandledDrawable() {
+        final Drawable[] drawables = new Drawable[] {
+            DrawableFactory.createShapeDrawable(),
+            DrawableFactory.createPaintDrawable(),
+            DrawableFactory.createBitmapDrawable(mContext),
+            DrawableFactory.createColorDrawable(),
+            DrawableFactory.createGradientDrawable(),
+            DrawableFactory.createNinePatchDrawable(mContext),
+        };
+        final ColorStateList[] stateLists = new ColorStateList[] {
+            // Empty state spec
+            new ColorStateList(
+                new int[][] {  },
+                new int[] {  }),
+            // Wild card
+            new ColorStateList(
+                new int[][] { StateSet.WILD_CARD },
+                new int[] { A_COLOR }),
+            // No state spec of state_focused=true
+            new ColorStateList(
+                new int[][] { NO_STATE_FOCUSED },
+                new int[] { A_COLOR }),
+            // One state spec of only state_focused=true
+            new ColorStateList(
+                new int[][] { ONLY_STATE_FOCUSED },
+                new int[] { A_COLOR }),
+            // One state spec of state_focused=true and something=true,
+            // but no state spec of state_focused=true and something=false
+            new ColorStateList(
+                new int[][] { STATE_FOCUSED_WITH_POS },
+                new int[] { A_COLOR }),
+            // One state spec of state_focused=true and something=true,
+            // and one spec of state_focused=true and something=false
+            new ColorStateList(
+                new int[][] { STATE_FOCUSED_WITH_POS, STATE_FOCUSED_WITH_NEG },
+                new int[] { A_COLOR, A_COLOR }),
+        };
+        final boolean[] expectedResults = new boolean[] {
+            // Empty state spec
+            false,
+            // Wild card
+            false,
+            // No state spec of state_focused=true
+            false,
+            // One state spec of only state_focused=true
+            true,
+            // One state spec of state_focused=true and something=true,
+            // but no state spec of state_focused=true and something=false
+            true,
+            // One state spec of state_focused=true and something=true,
+            // and one spec of state_focused=true and something=false
+            true
+        };
+        assertEquals(stateLists.length, expectedResults.length);
+        for (Drawable drawable : drawables) {
+            // No ColorStateList set
+            String drawableName = drawable.getClass().toString();
+            String errorMsg = "[" + drawableName + "] Testing no ColorStateList failed.";
+            drawable.setState(FOCUSED_STATE);
+            assertFalse(errorMsg, drawable.hasFocusStateSpecified());
+            // With ColorStateList set
+            for (int i = 0; i < stateLists.length; i++) {
+                ColorStateList stateList = stateLists[i];
+                boolean expectedResult = expectedResults[i];
+                drawable.setTintList(stateList);
+                errorMsg = "[" + drawableName + "] Testing ColorStateList No." + i + " failed.";
+
+                drawable.setState(FOCUSED_STATE);
+                if (expectedResult) {
+                    assertTrue(errorMsg, drawable.hasFocusStateSpecified());
+                } else {
+                    assertFalse(errorMsg, drawable.hasFocusStateSpecified());
+                }
+            }
+        }
+    }
+
+    @UiThreadTest
+    @Test
+    public void testDrawableContainer() {
+        MockDrawableContainer container;
+        DrawableContainerState containerState;
+
+        // Empty
+        container = new MockDrawableContainer();
+        containerState = (DrawableContainerState) new LevelListDrawable().getConstantState();
+        assertNotNull(containerState);
+        container.setConstantState(containerState);
+        container.setState(FOCUSED_STATE);
+        assertFalse(container.hasFocusStateSpecified());
+
+        // No drawable of state_focused=true
+        container = new MockDrawableContainer();
+        containerState = (DrawableContainerState) new LevelListDrawable().getConstantState();
+        assertNotNull(containerState);
+        container.setConstantState(containerState);
+        containerState.addChild(DrawableFactory.createPaintDrawable());
+        containerState.addChild(DrawableFactory.createBitmapDrawable(mContext));
+        containerState.addChild(DrawableFactory.createColorDrawable());
+        container.selectDrawable(0);
+        container.setState(FOCUSED_STATE);
+        assertFalse(container.hasFocusStateSpecified());
+        container.selectDrawable(1);
+        container.setState(FOCUSED_STATE);
+        assertFalse(container.hasFocusStateSpecified());
+        container.selectDrawable(2);
+        container.setState(FOCUSED_STATE);
+        assertFalse(container.hasFocusStateSpecified());
+
+        // Only drawables of state_focused=true
+        container = new MockDrawableContainer();
+        containerState = (DrawableContainerState) new LevelListDrawable().getConstantState();
+        assertNotNull(containerState);
+        container.setConstantState(containerState);
+        containerState.addChild(DrawableFactory.createRippleDrawable());
+        containerState.addChild(
+            DrawableFactory.createStateListDrawable(
+                new int[][] { STATE_FOCUSED_WITH_POS, STATE_FOCUSED_WITH_NEG }
+            )
+        );
+        container.selectDrawable(0);
+        container.setState(FOCUSED_STATE);
+        assertTrue(container.hasFocusStateSpecified());
+        container.selectDrawable(1);
+        container.setState(FOCUSED_STATE);
+        assertTrue(container.hasFocusStateSpecified());
+
+        // Both drawables of state_focused=true and state_focused=false
+        containerState.addChild(DrawableFactory.createColorDrawable());
+        container.selectDrawable(2);
+        container.setState(FOCUSED_STATE);
+        assertFalse(container.hasFocusStateSpecified());
+        container.selectDrawable(1);
+        container.setState(FOCUSED_STATE);
+        assertTrue(container.hasFocusStateSpecified());
+        container.selectDrawable(0);
+        container.setState(FOCUSED_STATE);
+        assertTrue(container.hasFocusStateSpecified());
+    }
+
+    static class DrawableFactory {
+        static ShapeDrawable createShapeDrawable() {
+            return new ShapeDrawable(new RectShape());
+        }
+        static PaintDrawable createPaintDrawable() {
+            PaintDrawable paintDrawable = new PaintDrawable();
+            paintDrawable.setCornerRadius(1.5f);
+            return paintDrawable;
+        }
+        static BitmapDrawable createBitmapDrawable(Context context) {
+            Bitmap bitmap = Bitmap.createBitmap(200, 300, Config.ARGB_8888);
+            BitmapDrawable bitmapDrawable = new BitmapDrawable(context.getResources(), bitmap);
+            return bitmapDrawable;
+        }
+        static ColorDrawable createColorDrawable() {
+            return new ColorDrawable(A_COLOR);
+        }
+        static GradientDrawable createGradientDrawable() {
+            GradientDrawable gradientDrawable = new GradientDrawable();
+            gradientDrawable.setColor(A_COLOR);
+            gradientDrawable.setCornerRadius(10f);
+            return gradientDrawable;
+        }
+        static NinePatchDrawable createNinePatchDrawable(Context context) {
+            Resources res = context.getResources();
+            Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.ninepatch_0);
+            NinePatch np = new NinePatch(bitmap, bitmap.getNinePatchChunk(), null);
+            NinePatchDrawable ninePatchDrawable = new NinePatchDrawable(res, np);
+            return ninePatchDrawable;
+        }
+        static RippleDrawable createRippleDrawable() {
+            RippleDrawable rippleDrawable =
+                    new RippleDrawable(ColorStateList.valueOf(A_COLOR), null, null);
+            return rippleDrawable;
+        }
+        static PictureDrawable createPictureDrawable(Picture picture) {
+            PictureDrawable pictureDrawable = new PictureDrawable(picture);
+            return pictureDrawable;
+        }
+        static StateListDrawable createStateListDrawable(int[][] stateList) {
+            StateListDrawable drawable = new StateListDrawable();
+            ColorDrawable colorDrawable = DrawableFactory.createColorDrawable();
+            for (int i = 0; i < stateList.length; i++) {
+                drawable.addState(stateList[i], colorDrawable);
+            }
+            return drawable;
+        }
+    }
+
+    // We're calling protected methods in DrawableContainer.
+    // So we have to extend it here to make it accessible.
+    private class MockDrawableContainer extends DrawableContainer {
+        @Override
+        protected void setConstantState(DrawableContainerState state) {
+            super.setConstantState(state);
+        }
+    }
+
+}