1 package android.graphics.drawable.cts; 2 3 import static org.junit.Assert.assertEquals; 4 import static org.junit.Assert.assertFalse; 5 import static org.junit.Assert.assertNotNull; 6 import static org.junit.Assert.assertTrue; 7 import static org.junit.Assert.fail; 8 9 import android.content.Context; 10 import android.content.res.Resources; 11 import android.graphics.Bitmap; 12 import android.graphics.Bitmap.Config; 13 import android.graphics.Canvas; 14 import android.graphics.Color; 15 import android.graphics.ColorFilter; 16 import android.graphics.Path; 17 import android.graphics.Path.Direction; 18 import android.graphics.PixelFormat; 19 import android.graphics.Rect; 20 import android.graphics.Region; 21 import android.graphics.cts.R; 22 import android.graphics.drawable.AdaptiveIconDrawable; 23 import android.graphics.drawable.BitmapDrawable; 24 import android.graphics.drawable.ColorDrawable; 25 import android.graphics.drawable.Drawable; 26 import android.graphics.drawable.Drawable.ConstantState; 27 import android.graphics.drawable.StateListDrawable; 28 import android.support.test.InstrumentationRegistry; 29 import android.support.test.filters.SmallTest; 30 import android.support.test.runner.AndroidJUnit4; 31 import android.util.AttributeSet; 32 import android.util.Log; 33 import android.util.Xml; 34 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.xmlpull.v1.XmlPullParser; 38 39 import java.io.File; 40 import java.io.FileOutputStream; 41 import java.util.Arrays; 42 43 @SmallTest 44 @RunWith(AndroidJUnit4.class) 45 public class AdaptiveIconDrawableTest { 46 47 public static final String TAG = AdaptiveIconDrawableTest.class.getSimpleName(); 48 public static void L(String s, Object... parts) { 49 Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts)); 50 } 51 52 @Test 53 public void testConstructor() { 54 new AdaptiveIconDrawable(null, null); 55 } 56 57 @Test 58 public void testInflate() throws Throwable { 59 AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); 60 61 Resources r = InstrumentationRegistry.getTargetContext().getResources(); 62 XmlPullParser parser = r.getXml(R.layout.framelayout_layout); 63 AttributeSet attrs = Xml.asAttributeSet(parser); 64 65 // Should not throw inflate exception 66 dr.inflate(r, parser, attrs); 67 } 68 69 @Test(expected=NullPointerException.class) 70 public void testInflateNull() throws Throwable { 71 AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); 72 dr.inflate(null, null, null); 73 } 74 75 @Test 76 public void testDraw() { 77 Canvas c = new Canvas(); 78 AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); 79 dr.draw(c); 80 } 81 82 @Test(expected=NullPointerException.class) 83 public void testDrawNull() { 84 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 85 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 86 iconDrawable.setBounds(0, 0, 100, 100); 87 iconDrawable.draw(null); 88 } 89 90 @Test 91 public void testInvalidateDrawable() { 92 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 93 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 94 iconDrawable.invalidateDrawable( 95 InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass)); 96 } 97 98 @Test 99 public void testScheduleDrawable() { 100 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 101 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 102 iconDrawable.scheduleDrawable( 103 InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass), 104 () -> {}, 10); 105 106 // input null as params 107 iconDrawable.scheduleDrawable(null, null, -1); 108 // expected, no Exception thrown out, test success 109 } 110 111 @Test 112 public void testUnscheduleDrawable() { 113 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 114 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 115 iconDrawable.unscheduleDrawable( 116 InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass), () -> {}); 117 118 // input null as params 119 iconDrawable.unscheduleDrawable(null, null); 120 // expected, no Exception thrown out, test success 121 } 122 123 @Test 124 public void testGetChangingConfigurations() { 125 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 126 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 127 iconDrawable.setChangingConfigurations(11); 128 assertEquals(11, iconDrawable.getChangingConfigurations()); 129 130 iconDrawable.setChangingConfigurations(-21); 131 assertEquals(-21, iconDrawable.getChangingConfigurations()); 132 } 133 134 /** 135 * When setBound isn't called before draw method is called. 136 * Nothing is drawn. 137 */ 138 @Test 139 public void testDrawWithoutSetBounds() throws Exception { 140 Drawable backgroundDrawable = new ColorDrawable(Color.BLUE); 141 Drawable foregroundDrawable = new ColorDrawable(Color.RED); 142 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable); 143 Context context = InstrumentationRegistry.getTargetContext(); 144 File dir = context.getExternalFilesDir(null); 145 L("writing temp bitmaps to %s...", dir); 146 147 final Bitmap bm_test = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888); 148 final Bitmap bm_org = bm_test.copy(Config.ARGB_8888, false); 149 final Canvas can1 = new Canvas(bm_test); 150 151 // Even when setBounds is not called, should not crash 152 iconDrawable.draw(can1); 153 // Draws nothing! Hence same as original. 154 if (!equalBitmaps(bm_test, bm_org)) { 155 findBitmapDifferences(bm_test, bm_org); 156 fail("bm differs, check " + dir); 157 } 158 } 159 160 /** 161 * When setBound is called, translate accordingly. 162 */ 163 @Test 164 public void testDrawSetBounds() throws Exception { 165 int dpi = 4 ; 166 int top = 18 * dpi; 167 int left = 18 * dpi; 168 int right = 90 * dpi; 169 int bottom = 90 * dpi; 170 int width = right - left; 171 int height = bottom - top; 172 Context context = InstrumentationRegistry.getTargetContext(); 173 AdaptiveIconDrawable iconDrawable = (AdaptiveIconDrawable) context.getResources().getDrawable(android.R.drawable.sym_def_app_icon); 174 File dir = context.getExternalFilesDir(null); 175 L("writing temp bitmaps to %s...", dir); 176 final Bitmap bm_org = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 177 final Canvas can_org = new Canvas(bm_org); 178 iconDrawable.setBounds(0, 0, width, height); 179 iconDrawable.draw(can_org); 180 181 // Tested bitmap is drawn from the adaptive icon drawable. 182 final Bitmap bm_test = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 183 final Canvas can_test = new Canvas(bm_test); 184 185 iconDrawable.setBounds(left, top, right, bottom); 186 can_test.translate(-left, -top); 187 iconDrawable.draw(can_test); 188 can_test.translate(left, top); 189 190 191 bm_org.compress(Bitmap.CompressFormat.PNG, 100, 192 new FileOutputStream(new File(dir, "adaptive-bm-original.png"))); 193 bm_test.compress(Bitmap.CompressFormat.PNG, 100, 194 new FileOutputStream(new File(dir, "adaptive-bm-test.png"))); 195 Region region = new Region(new Rect(0, 0, width, height)); 196 197 Path circle = new Path(); 198 circle.addCircle(width / 2, height / 2, (right - left)/2 -10 /* room for anti-alias */, Direction.CW); 199 200 region.setPath(circle, region); 201 if (!equalBitmaps(bm_test, bm_org, region)) { 202 findBitmapDifferences(bm_test, bm_org); 203 fail("bm differs, check " + dir); 204 } 205 } 206 207 @Test 208 public void testSetVisible() { 209 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 210 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 211 assertFalse(iconDrawable.setVisible(true, true)); /* unchanged */ 212 assertTrue(iconDrawable.setVisible(false, true)); /* changed */ 213 assertFalse(iconDrawable.setVisible(false, true)); /* unchanged */ 214 } 215 216 @Test 217 public void testSetAlpha() { 218 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 219 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 220 iconDrawable.setBounds(0, 0, 100, 100); 221 222 Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 223 Canvas canvas = new Canvas(bitmap); 224 225 iconDrawable.draw(canvas); 226 assertEquals(255, Color.alpha(bitmap.getPixel(50, 50))); 227 228 iconDrawable.setAlpha(200); 229 bitmap.eraseColor(Color.TRANSPARENT); 230 iconDrawable.draw(canvas); 231 assertEquals(200, Color.alpha(bitmap.getPixel(50, 50))); 232 233 iconDrawable.setAlpha(100); 234 bitmap.eraseColor(Color.TRANSPARENT); 235 iconDrawable.draw(canvas); 236 assertEquals(100, Color.alpha(bitmap.getPixel(50, 50))); 237 } 238 239 @Test 240 public void testSetColorFilter() { 241 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 242 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 243 ColorFilter cf = new ColorFilter(); 244 iconDrawable.setColorFilter(cf); 245 246 // input null as param 247 iconDrawable.setColorFilter(null); 248 // expected, no Exception thrown out, test success 249 } 250 251 @Test 252 public void testGetOpacity() { 253 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 254 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 255 iconDrawable.setOpacity(PixelFormat.OPAQUE); 256 assertEquals(PixelFormat.OPAQUE, iconDrawable.getOpacity()); 257 258 iconDrawable.setOpacity(PixelFormat.TRANSPARENT); 259 assertEquals(PixelFormat.TRANSPARENT, iconDrawable.getOpacity()); 260 } 261 262 @Test 263 public void testIsStateful() { 264 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 265 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 266 assertFalse(iconDrawable.isStateful()); 267 268 iconDrawable = new AdaptiveIconDrawable(new StateListDrawable(), new ColorDrawable(Color.RED)); 269 assertTrue(iconDrawable.isStateful()); 270 } 271 272 273 @Test 274 public void testGetConstantState() { 275 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 276 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 277 ConstantState constantState = iconDrawable.getConstantState(); 278 assertNotNull(constantState); 279 } 280 281 @Test 282 public void testMutate() { 283 // Obtain the first instance, then mutate and modify a property held by 284 // constant state. If mutate() works correctly, the property should not 285 // be modified on the second or third instances. 286 Resources res = InstrumentationRegistry.getTargetContext().getResources(); 287 AdaptiveIconDrawable first = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); 288 AdaptiveIconDrawable pre = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); 289 290 first.mutate().setBounds(0, 0, 100, 100); 291 292 assertEquals("Modified first loaded instance", 100, first.getBounds().width()); 293 assertEquals("Did not modify pre-mutate() instance", 0, pre.getBounds().width()); 294 295 AdaptiveIconDrawable post = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); 296 297 assertEquals("Did not modify post-mutate() instance", 0, post.getBounds().width()); 298 } 299 300 @Test 301 public void testGetForegroundBackground() { 302 Context context = InstrumentationRegistry.getTargetContext(); 303 AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( 304 new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); 305 Drawable fgDrawable = iconDrawable.getForeground(); 306 Drawable bgDrawable = iconDrawable.getBackground(); 307 assertTrue("Foreground layer is color drawable.", fgDrawable instanceof ColorDrawable); 308 assertTrue("Backgroud layer is color drawable.", bgDrawable instanceof ColorDrawable); 309 310 AdaptiveIconDrawable iconDrawableInflated = 311 (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); 312 fgDrawable = iconDrawableInflated.getForeground(); 313 bgDrawable = iconDrawableInflated.getBackground(); 314 assertTrue("Foreground layer is color drawable.", fgDrawable instanceof BitmapDrawable); 315 assertTrue("Backgroud layer is color drawable.", bgDrawable instanceof BitmapDrawable); 316 } 317 318 @Test 319 public void testGetIntrinsicWidth() { 320 Context context = InstrumentationRegistry.getTargetContext(); 321 AdaptiveIconDrawable iconDrawableInflated = 322 (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); 323 int fgWidth = iconDrawableInflated.getForeground().getIntrinsicWidth(); 324 int bgWidth = iconDrawableInflated.getBackground().getIntrinsicWidth(); 325 float iconIntrinsicWidth = Math.max(fgWidth, bgWidth) / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); 326 assertEquals("Max intrinsic width of the layers should be icon's intrinsic width", 327 (int) iconIntrinsicWidth, iconDrawableInflated.getIntrinsicWidth()); 328 } 329 330 @Test 331 public void testGetIntrinsicHeight() { 332 Context context = InstrumentationRegistry.getTargetContext(); 333 AdaptiveIconDrawable iconDrawableInflated = 334 (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); 335 int fgWidth = iconDrawableInflated.getForeground().getIntrinsicHeight(); 336 int bgWidth = iconDrawableInflated.getBackground().getIntrinsicHeight(); 337 float iconIntrinsicHeight = Math.max(fgWidth, bgWidth) / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); 338 assertEquals("Max intrinsic height of the layers should be icon's intrinsic height", 339 (int) iconIntrinsicHeight, iconDrawableInflated.getIntrinsicHeight()); 340 } 341 342 // 343 // Utils 344 // 345 346 boolean equalBitmaps(Bitmap a, Bitmap b) { 347 return equalBitmaps(a, b, null); 348 } 349 350 boolean equalBitmaps(Bitmap a, Bitmap b, Region region) { 351 if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false; 352 353 final int w = a.getWidth(); 354 final int h = a.getHeight(); 355 int[] aPix = new int[w * h]; 356 int[] bPix = new int[w * h]; 357 358 if (region != null) { 359 for (int i = 0; i < w; i++) { 360 for (int j = 0; j < h; j++) { 361 int ra = (a.getPixel(i, j) >> 16) & 0xff; 362 int ga = (a.getPixel(i, j) >> 8) & 0xff; 363 int ba = a.getPixel(i, j) & 0xff; 364 int rb = (b.getPixel(i, j) >> 16) & 0xff; 365 int gb = (b.getPixel(i, j) >> 8) & 0xff; 366 int bb = b.getPixel(i, j) & 0xff; 367 if (region.contains(i, j) && a.getPixel(i, j) != b.getPixel(i, j) ) { 368 return false; 369 } 370 } 371 } 372 return true; 373 } else { 374 a.getPixels(aPix, 0, w, 0, 0, w, h); 375 b.getPixels(bPix, 0, w, 0, 0, w, h); 376 return Arrays.equals(aPix, bPix); 377 } 378 } 379 380 void findBitmapDifferences(Bitmap a, Bitmap b) { 381 if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) { 382 L("different sizes: %dx%d vs %dx%d", 383 a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight()); 384 return; 385 } 386 387 final int w = a.getWidth(); 388 final int h = a.getHeight(); 389 int[] aPix = new int[w * h]; 390 int[] bPix = new int[w * h]; 391 392 a.getPixels(aPix, 0, w, 0, 0, w, h); 393 b.getPixels(bPix, 0, w, 0, 0, w, h); 394 395 L("bitmap a (%dx%d)", w, h); 396 printBits(aPix, w, h); 397 L("bitmap b (%dx%d)", w, h); 398 printBits(bPix, w, h); 399 400 StringBuffer sb = new StringBuffer("Different pixels: "); 401 for (int i=0; i<w; i++) { 402 for (int j=0; j<h; j++) { 403 if (aPix[i+w*j] != bPix[i+w*j]) { 404 sb.append(" ").append(i).append(",").append(j).append("<") 405 .append(aPix[i+w*j]).append(",").append(bPix[i+w*j]).append(">"); 406 } 407 } 408 } 409 L(sb.toString()); 410 } 411 412 static void printBits(int[] a, int w, int h) { 413 final StringBuilder sb = new StringBuilder(); 414 for (int i=0; i<w; i++) { 415 for (int j=0; j<h; j++) { 416 sb.append(colorToChar(a[i+w*j])); 417 } 418 sb.append('\n'); 419 } 420 L(sb.toString()); 421 } 422 423 static char colorToChar(int color) { 424 int sum = ((color >> 16) & 0xff) 425 + ((color >> 8) & 0xff) 426 + ((color) & 0xff); 427 return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)]; 428 } 429 static final char[] GRADIENT = " .:;+=xX$#".toCharArray(); 430 } 431