1 /* 2 * Copyright (C) 2008 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.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.content.Context; 26 import android.content.res.Resources; 27 import android.content.res.Resources.Theme; 28 import android.content.res.XmlResourceParser; 29 import android.graphics.Canvas; 30 import android.graphics.ColorFilter; 31 import android.graphics.Insets; 32 import android.graphics.PixelFormat; 33 import android.graphics.Rect; 34 import android.graphics.cts.R; 35 import android.graphics.drawable.Drawable; 36 import android.graphics.drawable.Drawable.ConstantState; 37 import android.graphics.drawable.InsetDrawable; 38 import android.util.AttributeSet; 39 import android.util.StateSet; 40 import android.util.Xml; 41 import android.view.InflateException; 42 43 import androidx.test.InstrumentationRegistry; 44 import androidx.test.filters.SmallTest; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 53 import java.io.IOException; 54 import java.util.Arrays; 55 56 @SmallTest 57 @RunWith(AndroidJUnit4.class) 58 public class InsetDrawableTest { 59 private Context mContext; 60 private Drawable mPassDrawable; 61 private InsetDrawable mInsetDrawable; 62 63 @Before 64 public void setup() { 65 mContext = InstrumentationRegistry.getTargetContext(); 66 mPassDrawable = mContext.getDrawable(R.drawable.pass); 67 mInsetDrawable = new InsetDrawable(mPassDrawable, 0); 68 } 69 70 @Test 71 public void testConstructor() { 72 new InsetDrawable(mPassDrawable, 1); 73 new InsetDrawable(mPassDrawable, 1, 1, 1, 1); 74 75 new InsetDrawable(null, -1); 76 new InsetDrawable(null, -1, -1, -1, -1); 77 78 new InsetDrawable(mPassDrawable, .1f); 79 new InsetDrawable(mPassDrawable, .1f, .1f, .1f, .1f); 80 } 81 82 @Test 83 public void testInflate() throws Throwable { 84 InsetDrawable insetDrawable = new InsetDrawable(null, 0); 85 86 Resources r = mContext.getResources(); 87 XmlPullParser parser = r.getXml(R.layout.framelayout_layout); 88 AttributeSet attrs = Xml.asAttributeSet(parser); 89 90 try { 91 insetDrawable.inflate(r, parser, attrs); 92 fail("There should be an InflateException thrown out."); 93 } catch (InflateException e) { 94 // expected, test success 95 } 96 } 97 98 99 @Test(expected=NullPointerException.class) 100 public void testInflateNull() throws Throwable { 101 InsetDrawable insetDrawable = new InsetDrawable(null, 0); 102 103 insetDrawable.inflate(null, null, null); 104 } 105 106 @Test 107 public void testInvalidateDrawable() { 108 mInsetDrawable.invalidateDrawable(mPassDrawable); 109 } 110 111 @Test 112 public void testScheduleDrawable() { 113 mInsetDrawable.scheduleDrawable(mPassDrawable, () -> {}, 10); 114 115 // input null as params 116 mInsetDrawable.scheduleDrawable(null, null, -1); 117 // expected, no Exception thrown out, test success 118 } 119 120 @Test 121 public void testUnscheduleDrawable() { 122 mInsetDrawable.unscheduleDrawable(mPassDrawable, () -> {}); 123 124 // input null as params 125 mInsetDrawable.unscheduleDrawable(null, null); 126 // expected, no Exception thrown out, test success 127 } 128 129 @Test 130 public void testDraw() { 131 Canvas c = new Canvas(); 132 mInsetDrawable.draw(c); 133 } 134 135 @Test(expected=NullPointerException.class) 136 public void testDrawNull() { 137 mInsetDrawable.draw(null); 138 } 139 140 @Test 141 public void testGetChangingConfigurations() { 142 mInsetDrawable.setChangingConfigurations(11); 143 assertEquals(11, mInsetDrawable.getChangingConfigurations()); 144 145 mInsetDrawable.setChangingConfigurations(-21); 146 assertEquals(-21, mInsetDrawable.getChangingConfigurations()); 147 } 148 149 @Test 150 public void testGetPadding_dimension() { 151 InsetDrawable insetDrawable = new InsetDrawable(mPassDrawable, 1, 2, 3, 4); 152 Rect r = new Rect(); 153 assertEquals(0, r.left); 154 assertEquals(0, r.top); 155 assertEquals(0, r.right); 156 assertEquals(0, r.bottom); 157 158 assertTrue(insetDrawable.getPadding(r)); 159 160 assertEquals(1, r.left); 161 assertEquals(2, r.top); 162 assertEquals(3, r.right); 163 assertEquals(4, r.bottom); 164 165 // padding is set to 0, then return value should be false 166 insetDrawable = new InsetDrawable(mPassDrawable, .0f); 167 168 r = new Rect(); 169 assertEquals(0, r.left); 170 assertEquals(0, r.top); 171 assertEquals(0, r.right); 172 assertEquals(0, r.bottom); 173 174 assertFalse(insetDrawable.getPadding(r)); 175 176 assertEquals(0, r.left); 177 assertEquals(0, r.top); 178 assertEquals(0, r.right); 179 assertEquals(0, r.bottom); 180 } 181 182 @Test 183 public void testGetPadding_fraction() { 184 InsetDrawable insetDrawable = new InsetDrawable(mPassDrawable, .1f, .2f, .3f, .4f); 185 insetDrawable.setBounds(0, 0, 10, 10); 186 Rect r = new Rect(); 187 assertEquals(0, r.left); 188 assertEquals(0, r.top); 189 assertEquals(0, r.right); 190 assertEquals(0, r.bottom); 191 192 assertTrue(insetDrawable.getPadding(r)); 193 194 assertEquals(1, r.left); 195 assertEquals(2, r.top); 196 assertEquals(3, r.right); 197 assertEquals(4, r.bottom); 198 199 // padding is set to 0, then return value should be false 200 insetDrawable = new InsetDrawable(mPassDrawable, 0); 201 202 r = new Rect(); 203 assertEquals(0, r.left); 204 assertEquals(0, r.top); 205 assertEquals(0, r.right); 206 assertEquals(0, r.bottom); 207 208 assertFalse(insetDrawable.getPadding(r)); 209 210 assertEquals(0, r.left); 211 assertEquals(0, r.top); 212 assertEquals(0, r.right); 213 assertEquals(0, r.bottom); 214 } 215 216 @Test(expected=NullPointerException.class) 217 public void testGetPaddingNull() { 218 InsetDrawable insetDrawable = new InsetDrawable(mPassDrawable, 1, 2, 3, 4); 219 insetDrawable.getPadding(null); 220 } 221 222 @Test 223 public void testSetVisible() { 224 assertFalse(mInsetDrawable.setVisible(true, true)); /* unchanged */ 225 assertTrue(mInsetDrawable.setVisible(false, true)); /* changed */ 226 assertFalse(mInsetDrawable.setVisible(false, true)); /* unchanged */ 227 } 228 229 @Test 230 public void testSetAlpha() { 231 mInsetDrawable.setAlpha(1); 232 mInsetDrawable.setAlpha(-1); 233 234 mInsetDrawable.setAlpha(0); 235 mInsetDrawable.setAlpha(Integer.MAX_VALUE); 236 mInsetDrawable.setAlpha(Integer.MIN_VALUE); 237 } 238 239 @Test 240 public void testSetColorFilter() { 241 ColorFilter cf = new ColorFilter(); 242 mInsetDrawable.setColorFilter(cf); 243 244 // input null as param 245 mInsetDrawable.setColorFilter(null); 246 // expected, no Exception thrown out, test success 247 } 248 249 @Test 250 public void testGetOpacity() { 251 mInsetDrawable.setAlpha(255); 252 assertEquals(PixelFormat.OPAQUE, mInsetDrawable.getOpacity()); 253 254 mInsetDrawable.setAlpha(100); 255 assertEquals(PixelFormat.TRANSLUCENT, mInsetDrawable.getOpacity()); 256 } 257 258 @Test 259 public void testIsStateful() { 260 assertFalse(mInsetDrawable.isStateful()); 261 } 262 263 @Test 264 public void testOnStateChange() { 265 MockInsetDrawable insetDrawable = new MockInsetDrawable(mPassDrawable, 10); 266 assertEquals("initial child state is empty", mPassDrawable.getState(), StateSet.WILD_CARD); 267 268 int[] state = new int[] {1, 2, 3}; 269 assertFalse("child did not change", insetDrawable.onStateChange(state)); 270 assertEquals("child state did not change", mPassDrawable.getState(), StateSet.WILD_CARD); 271 272 mPassDrawable = mContext.getDrawable(R.drawable.statelistdrawable); 273 insetDrawable = new MockInsetDrawable(mPassDrawable, 10); 274 assertEquals("initial child state is empty", mPassDrawable.getState(), StateSet.WILD_CARD); 275 insetDrawable.onStateChange(state); 276 assertTrue("child state changed", Arrays.equals(state, mPassDrawable.getState())); 277 278 // input null as param 279 insetDrawable.onStateChange(null); 280 // expected, no Exception thrown out, test success 281 } 282 283 @Test 284 public void testOnBoundsChange_dimension() { 285 MockInsetDrawable insetDrawable = new MockInsetDrawable(mPassDrawable, 5); 286 287 Rect bounds = mPassDrawable.getBounds(); 288 assertEquals(0, bounds.left); 289 assertEquals(0, bounds.top); 290 assertEquals(0, bounds.right); 291 assertEquals(0, bounds.bottom); 292 293 Rect r = new Rect(); 294 insetDrawable.onBoundsChange(r); 295 296 assertEquals(5, bounds.left); 297 assertEquals(5, bounds.top); 298 assertEquals(-5, bounds.right); 299 assertEquals(-5, bounds.bottom); 300 } 301 302 @Test 303 public void testOnBoundsChange_fraction() { 304 float inset = .1f; 305 int size = 40; 306 MockInsetDrawable insetDrawable = new MockInsetDrawable(mPassDrawable, 0.1f); 307 308 Rect bounds = mPassDrawable.getBounds(); 309 assertEquals(0, bounds.left); 310 assertEquals(0, bounds.top); 311 assertEquals(0, bounds.right); 312 assertEquals(0, bounds.bottom); 313 314 Rect r = new Rect(0, 0, size, size); 315 insetDrawable.onBoundsChange(r); 316 317 assertEquals((int) (size * inset), bounds.left); 318 assertEquals((int) (size * inset), bounds.top); 319 assertEquals(size - (int) (size * inset), bounds.right); 320 assertEquals(size - (int) (size * inset), bounds.bottom); 321 } 322 323 @Test 324 public void testIsBoundsAndIntrinsicSizeInverse() { 325 float inset = .1f; 326 327 // Test that intrinsic Width and Height calculation logic is inverse of the onBoundsChange. 328 MockInsetDrawable insetDrawable = new MockInsetDrawable(mPassDrawable, inset); 329 330 assertEquals((int)(mPassDrawable.getIntrinsicWidth() / (1 - 2 * inset)), 331 insetDrawable.getIntrinsicWidth()); 332 assertEquals((int)(mPassDrawable.getIntrinsicHeight() / (1 - 2 * inset)), 333 insetDrawable.getIntrinsicHeight()); 334 335 Rect r = new Rect(0, 0, insetDrawable.getIntrinsicWidth(), 336 insetDrawable.getIntrinsicHeight()); 337 insetDrawable.onBoundsChange(r); 338 r = mPassDrawable.getBounds(); 339 340 assertEquals((int)(insetDrawable.getIntrinsicWidth() * inset), r.left); 341 assertEquals((int)(insetDrawable.getIntrinsicHeight() * inset), r.top); 342 assertEquals(insetDrawable.getIntrinsicWidth() 343 - (int)((inset) * insetDrawable.getIntrinsicWidth()), r.right); 344 assertEquals(insetDrawable.getIntrinsicHeight() 345 - (int)((inset) * insetDrawable.getIntrinsicHeight()), r.bottom); 346 347 // Verify inverse!! 348 assertEquals(r.width(), mPassDrawable.getIntrinsicWidth(), 1); 349 assertEquals(r.height(), mPassDrawable.getIntrinsicHeight(), 1); 350 } 351 352 @Test(expected=NullPointerException.class) 353 public void testOnBoundsChangeNull() { 354 MockInsetDrawable insetDrawable = new MockInsetDrawable(mPassDrawable, 5); 355 356 insetDrawable.onBoundsChange(null); 357 } 358 359 @Test 360 public void testGetIntrinsicWidth() { 361 int expected = mPassDrawable.getIntrinsicWidth(); 362 assertEquals(expected, mInsetDrawable.getIntrinsicWidth()); 363 364 mPassDrawable = mContext.getDrawable(R.drawable.scenery); 365 mInsetDrawable = new InsetDrawable(mPassDrawable, 0); 366 367 expected = mPassDrawable.getIntrinsicWidth(); 368 assertEquals(expected, mInsetDrawable.getIntrinsicWidth()); 369 370 mPassDrawable = mContext.getDrawable(R.drawable.scenery); 371 mInsetDrawable = new InsetDrawable(mPassDrawable, 20); 372 373 expected = mPassDrawable.getIntrinsicWidth() + 40; 374 assertEquals(expected, mInsetDrawable.getIntrinsicWidth()); 375 376 mPassDrawable = mContext.getDrawable(R.drawable.inset_color); 377 expected = -1; 378 assertEquals(expected, mPassDrawable.getIntrinsicWidth()); 379 380 mPassDrawable = mContext.getDrawable(R.drawable.inset_color_fraction); 381 expected = (int)(mPassDrawable.getIntrinsicWidth() * (1.4f)); 382 assertEquals(expected, mPassDrawable.getIntrinsicWidth()); 383 } 384 385 @Test 386 public void testGetIntrinsicHeight() { 387 int expected = mPassDrawable.getIntrinsicHeight(); 388 assertEquals(expected, mInsetDrawable.getIntrinsicHeight()); 389 390 mPassDrawable = mContext.getDrawable(R.drawable.scenery); 391 mInsetDrawable = new InsetDrawable(mPassDrawable, 0); 392 393 expected = mPassDrawable.getIntrinsicHeight(); 394 assertEquals(expected, mInsetDrawable.getIntrinsicHeight()); 395 396 mPassDrawable = mContext.getDrawable(R.drawable.scenery); 397 mInsetDrawable = new InsetDrawable(mPassDrawable, 20); 398 399 expected = mPassDrawable.getIntrinsicHeight() + 40; 400 assertEquals(expected, mInsetDrawable.getIntrinsicHeight()); 401 402 mPassDrawable = mContext.getDrawable(R.drawable.inset_color); 403 expected = -1; 404 assertEquals(expected, mPassDrawable.getIntrinsicHeight()); 405 406 mPassDrawable = mContext.getDrawable(R.drawable.inset_color_fraction); 407 expected = (int)(mPassDrawable.getIntrinsicHeight() * (1.4f)); 408 assertEquals(expected, mPassDrawable.getIntrinsicHeight()); 409 } 410 411 @Test 412 public void testGetConstantState() { 413 ConstantState constantState = mInsetDrawable.getConstantState(); 414 assertNotNull(constantState); 415 } 416 417 @Test 418 public void testMutate() { 419 // Obtain the first instance, then mutate and modify a property held by 420 // constant state. If mutate() works correctly, the property should not 421 // be modified on the second or third instances. 422 Resources res = mContext.getResources(); 423 InsetDrawable first = (InsetDrawable) res.getDrawable(R.drawable.inset_mutate, null); 424 InsetDrawable pre = (InsetDrawable) res.getDrawable(R.drawable.inset_mutate, null); 425 426 first.mutate().setAlpha(128); 427 428 assertEquals("Modified first loaded instance", 128, first.getDrawable().getAlpha()); 429 assertEquals("Did not modify pre-mutate() instance", 255, pre.getDrawable().getAlpha()); 430 431 InsetDrawable post = (InsetDrawable) res.getDrawable(R.drawable.inset_mutate, null); 432 433 assertEquals("Did not modify post-mutate() instance", 255, post.getDrawable().getAlpha()); 434 } 435 436 437 @Test 438 public void testPreloadDensity() throws XmlPullParserException, IOException { 439 final Resources res = mContext.getResources(); 440 final int densityDpi = res.getConfiguration().densityDpi; 441 try { 442 DrawableTestUtils.setResourcesDensity(res, densityDpi); 443 verifyPreloadDensityInner(res, densityDpi); 444 } finally { 445 DrawableTestUtils.setResourcesDensity(res, densityDpi); 446 } 447 } 448 449 @Test 450 public void testPreloadDensity_tvdpi() throws XmlPullParserException, IOException { 451 final Resources res = mContext.getResources(); 452 final int densityDpi = res.getConfiguration().densityDpi; 453 try { 454 DrawableTestUtils.setResourcesDensity(res, 213); 455 verifyPreloadDensityInner(res, 213); 456 } finally { 457 DrawableTestUtils.setResourcesDensity(res, densityDpi); 458 } 459 } 460 461 @Test 462 public void testOpticalInsets() { 463 InsetDrawable drawable = new InsetDrawable(mPassDrawable, 1, 2, 3, 4); 464 assertEquals(Insets.of(1, 2, 3, 4), drawable.getOpticalInsets()); 465 } 466 467 private void verifyPreloadDensityInner(Resources res, int densityDpi) 468 throws XmlPullParserException, IOException { 469 // Capture initial state at default density. 470 final XmlResourceParser parser = DrawableTestUtils.getResourceParser( 471 res, R.drawable.inset_density); 472 final InsetDrawable preloadedDrawable = new InsetDrawable(null, 0); 473 preloadedDrawable.inflate(res, parser, Xml.asAttributeSet(parser)); 474 final ConstantState preloadedConstantState = preloadedDrawable.getConstantState(); 475 final int origInsetHoriz = preloadedDrawable.getIntrinsicWidth() 476 - preloadedDrawable.getDrawable().getIntrinsicWidth(); 477 478 // Set density to approximately half of original. Unlike offsets, which are 479 // truncated, dimensions are rounded to the nearest pixel. 480 DrawableTestUtils.setResourcesDensity(res, densityDpi / 2); 481 final InsetDrawable halfDrawable = 482 (InsetDrawable) preloadedConstantState.newDrawable(res); 483 // NOTE: densityDpi may not be an even number, so account for *actual* scaling in asserts 484 final float approxHalf = (float)(densityDpi / 2) / densityDpi; 485 assertEquals(Math.round(origInsetHoriz * approxHalf), halfDrawable.getIntrinsicWidth() 486 - halfDrawable.getDrawable().getIntrinsicWidth()); 487 488 // Set density to double original. 489 DrawableTestUtils.setResourcesDensity(res, densityDpi * 2); 490 final InsetDrawable doubleDrawable = 491 (InsetDrawable) preloadedConstantState.newDrawable(res); 492 assertEquals(origInsetHoriz * 2, doubleDrawable.getIntrinsicWidth() 493 - doubleDrawable.getDrawable().getIntrinsicWidth()); 494 495 // Restore original density. 496 DrawableTestUtils.setResourcesDensity(res, densityDpi); 497 final InsetDrawable origDrawable = 498 (InsetDrawable) preloadedConstantState.newDrawable(); 499 assertEquals(origInsetHoriz, origDrawable.getIntrinsicWidth() 500 - origDrawable.getDrawable().getIntrinsicWidth()); 501 502 // Ensure theme density is applied correctly. 503 final Theme t = res.newTheme(); 504 halfDrawable.applyTheme(t); 505 float approxDouble = 1 / approxHalf; 506 // Reproduce imprecise truncated scale down, and back up. 507 assertEquals(Math.round(approxDouble * Math.round(origInsetHoriz * approxHalf)), 508 halfDrawable.getIntrinsicWidth() - halfDrawable.getDrawable().getIntrinsicWidth()); 509 doubleDrawable.applyTheme(t); 510 assertEquals(origInsetHoriz, doubleDrawable.getIntrinsicWidth() 511 - doubleDrawable.getDrawable().getIntrinsicWidth()); 512 } 513 514 private class MockInsetDrawable extends InsetDrawable { 515 public MockInsetDrawable(Drawable drawable, float inset) { 516 super(drawable, inset); 517 } 518 519 public MockInsetDrawable(Drawable drawable, int inset) { 520 super(drawable, inset); 521 } 522 523 protected boolean onStateChange(int[] state) { 524 return super.onStateChange(state); 525 } 526 527 protected void onBoundsChange(Rect bounds) { 528 super.onBoundsChange(bounds); 529 } 530 } 531 } 532