1 /* 2 * Copyright (C) 2016 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 org.xmlpull.v1.XmlPullParser; 20 import org.xmlpull.v1.XmlPullParserException; 21 22 import android.R.attr; 23 import android.content.Context; 24 import android.content.res.Resources; 25 import android.content.res.XmlResourceParser; 26 import android.graphics.Canvas; 27 import android.graphics.ColorFilter; 28 import android.graphics.cts.R; 29 import android.graphics.drawable.Animatable; 30 import android.graphics.drawable.Animatable2; 31 import android.graphics.drawable.AnimatedStateListDrawable; 32 import android.graphics.drawable.Drawable; 33 import android.graphics.drawable.DrawableContainer.DrawableContainerState; 34 import android.graphics.drawable.StateListDrawable; 35 import android.test.InstrumentationTestCase; 36 import android.util.StateSet; 37 import android.util.Xml; 38 39 import java.io.IOException; 40 import java.util.Arrays; 41 import java.util.HashSet; 42 43 import static org.mockito.Mockito.mock; 44 45 public class AnimatedStateListDrawableTest extends InstrumentationTestCase { 46 private static final int[] STATE_EMPTY = new int[] { }; 47 private static final int[] STATE_FOCUSED = new int[] { attr.state_focused }; 48 49 private Context mContext; 50 private Resources mResources; 51 52 @Override 53 protected void setUp() throws Exception { 54 super.setUp(); 55 56 mContext = getInstrumentation().getTargetContext(); 57 mResources = mContext.getResources(); 58 } 59 60 public void testStateListDrawable() { 61 new AnimatedStateListDrawable(); 62 63 // Check the values set in the constructor 64 assertNotNull(new AnimatedStateListDrawable().getConstantState()); 65 } 66 67 public void testAddState() { 68 AnimatedStateListDrawable asld = new AnimatedStateListDrawable(); 69 DrawableContainerState cs = (DrawableContainerState) asld.getConstantState(); 70 assertEquals(0, cs.getChildCount()); 71 72 try { 73 asld.addState(StateSet.WILD_CARD, null, R.id.focused); 74 fail("Expected IllegalArgumentException"); 75 } catch (IllegalArgumentException e) { 76 // Expected. 77 } 78 79 Drawable unfocused = mock(Drawable.class); 80 asld.addState(StateSet.WILD_CARD, unfocused, R.id.focused); 81 assertEquals(1, cs.getChildCount()); 82 83 Drawable focused = mock(Drawable.class); 84 asld.addState(STATE_FOCUSED, focused, R.id.unfocused); 85 assertEquals(2, cs.getChildCount()); 86 } 87 88 public void testAddTransition() { 89 AnimatedStateListDrawable asld = new AnimatedStateListDrawable(); 90 DrawableContainerState cs = (DrawableContainerState) asld.getConstantState(); 91 92 Drawable focused = mock(Drawable.class); 93 Drawable unfocused = mock(Drawable.class); 94 asld.addState(STATE_FOCUSED, focused, R.id.focused); 95 asld.addState(StateSet.WILD_CARD, unfocused, R.id.unfocused); 96 97 try { 98 asld.addTransition(R.id.focused, R.id.focused, null, false); 99 fail("Expected IllegalArgumentException"); 100 } catch (IllegalArgumentException e) { 101 // Expected. 102 } 103 104 MockTransition focusedToUnfocused = mock(MockTransition.class); 105 asld.addTransition(R.id.focused, R.id.unfocused, focusedToUnfocused, false); 106 assertEquals(3, cs.getChildCount()); 107 108 MockTransition unfocusedToFocused = mock(MockTransition.class); 109 asld.addTransition(R.id.unfocused, R.id.focused, unfocusedToFocused, false); 110 assertEquals(4, cs.getChildCount()); 111 112 MockTransition reversible = mock(MockTransition.class); 113 asld.addTransition(R.id.focused, R.id.unfocused, reversible, true); 114 assertEquals(5, cs.getChildCount()); 115 } 116 117 public void testIsStateful() { 118 assertTrue(new AnimatedStateListDrawable().isStateful()); 119 } 120 121 public void testOnStateChange() { 122 AnimatedStateListDrawable asld = new AnimatedStateListDrawable(); 123 124 Drawable focused = mock(Drawable.class); 125 Drawable unfocused = mock(Drawable.class); 126 asld.addState(STATE_FOCUSED, focused, R.id.focused); 127 asld.addState(StateSet.WILD_CARD, unfocused, R.id.unfocused); 128 129 MockTransition focusedToUnfocused = mock(MockTransition.class); 130 MockTransition unfocusedToFocused = mock(MockTransition.class); 131 asld.addTransition(R.id.focused, R.id.unfocused, focusedToUnfocused, false); 132 asld.addTransition(R.id.unfocused, R.id.focused, unfocusedToFocused, false); 133 134 asld.setState(STATE_EMPTY); 135 assertSame(unfocused, asld.getCurrent()); 136 137 asld.setState(STATE_FOCUSED); 138 assertSame(unfocusedToFocused, asld.getCurrent()); 139 140 asld.setState(STATE_FOCUSED); 141 assertSame(unfocusedToFocused, asld.getCurrent()); 142 } 143 144 public void testPreloadDensity() throws XmlPullParserException, IOException { 145 runPreloadDensityTestForDrawable( 146 R.drawable.animated_state_list_density, false); 147 } 148 149 public void testPreloadDensityConstantSize() throws XmlPullParserException, IOException { 150 runPreloadDensityTestForDrawable( 151 R.drawable.animated_state_list_density_constant_size, true); 152 } 153 154 private void runPreloadDensityTestForDrawable(int drawableResId, boolean isConstantSize) 155 throws XmlPullParserException, IOException { 156 final Resources res = mResources; 157 final int densityDpi = res.getConfiguration().densityDpi; 158 try { 159 runPreloadDensityTestForDrawableInner(res, densityDpi, drawableResId, isConstantSize); 160 } finally { 161 DrawableTestUtils.setResourcesDensity(res, densityDpi); 162 } 163 } 164 165 private void runPreloadDensityTestForDrawableInner(Resources res, int densityDpi, 166 int drawableResId, boolean isConstantSize) throws XmlPullParserException, IOException { 167 // Capture initial state at default density. 168 final XmlResourceParser parser = getResourceParser(drawableResId); 169 final AnimatedStateListDrawable asld = new AnimatedStateListDrawable(); 170 asld.inflate(res, parser, Xml.asAttributeSet(parser)); 171 172 final DrawableContainerState cs = (DrawableContainerState) asld.getConstantState(); 173 final int count = cs.getChildCount(); 174 final int[] origWidth = new int[count]; 175 int max = 0; 176 for (int i = 0; i < count; i++) { 177 final int width = cs.getChild(i).getIntrinsicWidth(); 178 max = Math.max(max, width); 179 origWidth[i] = width; 180 } 181 if (isConstantSize) { 182 Arrays.fill(origWidth, max); 183 } 184 185 // Set density to half of original. 186 DrawableTestUtils.setResourcesDensity(res, densityDpi / 2); 187 final StateListDrawable halfDrawable = 188 (StateListDrawable) cs.newDrawable(res); 189 for (int i = 0; i < count; i++) { 190 halfDrawable.selectDrawable(i); 191 assertEquals(Math.round(origWidth[i] / 2f), halfDrawable.getIntrinsicWidth()); 192 } 193 194 // Set density to double original. 195 DrawableTestUtils.setResourcesDensity(res, densityDpi * 2); 196 final StateListDrawable doubleDrawable = 197 (StateListDrawable) cs.newDrawable(res); 198 for (int i = 0; i < count; i++) { 199 doubleDrawable.selectDrawable(i); 200 assertEquals(origWidth[i] * 2, doubleDrawable.getIntrinsicWidth()); 201 } 202 203 // Restore original configuration and metrics. 204 DrawableTestUtils.setResourcesDensity(res, densityDpi); 205 final StateListDrawable origDrawable = 206 (StateListDrawable) cs.newDrawable(res); 207 for (int i = 0; i < count; i++) { 208 origDrawable.selectDrawable(i); 209 assertEquals(origWidth[i], origDrawable.getIntrinsicWidth()); 210 } 211 } 212 213 private XmlResourceParser getResourceParser(int resId) throws XmlPullParserException, 214 IOException { 215 XmlResourceParser parser = getInstrumentation().getTargetContext().getResources().getXml( 216 resId); 217 int type; 218 while ((type = parser.next()) != XmlPullParser.START_TAG 219 && type != XmlPullParser.END_DOCUMENT) { 220 // Empty loop 221 } 222 return parser; 223 } 224 225 public void testInflate() throws XmlPullParserException, IOException { 226 AnimatedStateListDrawable asld = (AnimatedStateListDrawable) mContext.getDrawable( 227 R.drawable.animated_state_list_density); 228 DrawableContainerState asldState = (DrawableContainerState) asld.getConstantState(); 229 assertTrue(asld.isVisible()); 230 assertFalse(asldState.isConstantSize()); 231 assertNull(asldState.getConstantPadding()); 232 assertEquals(4, asldState.getChildCount()); 233 } 234 235 public abstract class MockTransition extends MockDrawable implements Animatable, Animatable2 { 236 private HashSet<AnimationCallback> mCallbacks = new HashSet<>(); 237 238 @Override 239 public void registerAnimationCallback(AnimationCallback callback) { 240 mCallbacks.add(callback); 241 } 242 243 @Override 244 public boolean unregisterAnimationCallback(AnimationCallback callback) { 245 return mCallbacks.remove(callback); 246 } 247 248 @Override 249 public void clearAnimationCallbacks() { 250 mCallbacks.clear(); 251 } 252 } 253 254 public class MockDrawable extends Drawable { 255 @Override 256 public void draw(Canvas canvas) { 257 } 258 259 @Override 260 public int getOpacity() { 261 return 0; 262 } 263 264 @Override 265 public void setAlpha(int alpha) { 266 } 267 268 @Override 269 public void setColorFilter(ColorFilter cf) { 270 } 271 } 272 } 273