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