| 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 static org.junit.Assert.fail; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.graphics.Bitmap; |
| import android.graphics.Bitmap.Config; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.ColorFilter; |
| import android.graphics.Path; |
| import android.graphics.Path.Direction; |
| import android.graphics.PixelFormat; |
| import android.graphics.Rect; |
| import android.graphics.Region; |
| import android.graphics.cts.R; |
| import android.graphics.drawable.AdaptiveIconDrawable; |
| import android.graphics.drawable.BitmapDrawable; |
| import android.graphics.drawable.ColorDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.graphics.drawable.Drawable.ConstantState; |
| import android.graphics.drawable.StateListDrawable; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.util.Xml; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.filters.SmallTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.xmlpull.v1.XmlPullParser; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.util.Arrays; |
| |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class AdaptiveIconDrawableTest { |
| |
| public static final String TAG = AdaptiveIconDrawableTest.class.getSimpleName(); |
| public static final boolean DEBUG = false; |
| |
| public static void L(String s, Object... parts) { |
| Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts)); |
| } |
| |
| @Test |
| public void testConstructor() { |
| new AdaptiveIconDrawable(null, null); |
| new AdaptiveIconDrawable(null, null, null); |
| } |
| |
| @Test |
| public void testInflate() throws Throwable { |
| AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); |
| |
| Resources r = InstrumentationRegistry.getTargetContext().getResources(); |
| XmlPullParser parser = r.getXml(R.layout.framelayout_layout); |
| AttributeSet attrs = Xml.asAttributeSet(parser); |
| |
| // Should not throw inflate exception |
| dr.inflate(r, parser, attrs); |
| } |
| |
| @Test(expected=NullPointerException.class) |
| public void testInflateNull() throws Throwable { |
| AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); |
| dr.inflate(null, null, null); |
| } |
| |
| @Test |
| public void testDraw() { |
| Canvas c = new Canvas(); |
| AdaptiveIconDrawable dr = new AdaptiveIconDrawable(null, null); |
| dr.draw(c); |
| } |
| |
| @Test(expected=NullPointerException.class) |
| public void testDrawNull() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| iconDrawable.setBounds(0, 0, 100, 100); |
| iconDrawable.draw(null); |
| } |
| |
| @Test |
| public void testInvalidateDrawable() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| iconDrawable.invalidateDrawable( |
| InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass)); |
| } |
| |
| @Test |
| public void testScheduleDrawable() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| iconDrawable.scheduleDrawable( |
| InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass), |
| () -> {}, 10); |
| |
| // input null as params |
| iconDrawable.scheduleDrawable(null, null, -1); |
| // expected, no Exception thrown out, test success |
| } |
| |
| @Test |
| public void testUnscheduleDrawable() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| iconDrawable.unscheduleDrawable( |
| InstrumentationRegistry.getTargetContext().getDrawable(R.drawable.pass), () -> {}); |
| |
| // input null as params |
| iconDrawable.unscheduleDrawable(null, null); |
| // expected, no Exception thrown out, test success |
| } |
| |
| @Test |
| public void testGetChangingConfigurations() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| iconDrawable.setChangingConfigurations(11); |
| assertEquals(11, iconDrawable.getChangingConfigurations()); |
| |
| iconDrawable.setChangingConfigurations(-21); |
| assertEquals(-21, iconDrawable.getChangingConfigurations()); |
| } |
| |
| @Test |
| public void testGetAlpha() { |
| ColorDrawable drawable = new ColorDrawable(Color.RED); |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(null, drawable); |
| iconDrawable.setAlpha(128); |
| assertEquals(128, iconDrawable.getAlpha()); |
| } |
| |
| @Test |
| public void testGetMonochrome() { |
| ColorDrawable drawable = new ColorDrawable(Color.RED); |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(null, null, drawable); |
| assertEquals(drawable, iconDrawable.getMonochrome()); |
| } |
| |
| @Test |
| public void testMonochromeInflated() { |
| Resources r = InstrumentationRegistry.getTargetContext().getResources(); |
| AdaptiveIconDrawable iconDrawable = (AdaptiveIconDrawable) r.getDrawable( |
| R.drawable.adaptiveicondrawable); |
| assertNotNull(iconDrawable.getMonochrome()); |
| } |
| |
| /** |
| * When setBound isn't called before draw method is called. |
| * Nothing is drawn. |
| */ |
| @Test |
| public void testDrawWithoutSetBounds() throws Exception { |
| Drawable backgroundDrawable = new ColorDrawable(Color.BLUE); |
| Drawable foregroundDrawable = new ColorDrawable(Color.RED); |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable); |
| Context context = InstrumentationRegistry.getTargetContext(); |
| File dir = context.getExternalFilesDir(null); |
| L("writing temp bitmaps to %s...", dir); |
| |
| final Bitmap bm_test = Bitmap.createBitmap(150, 150, Bitmap.Config.ARGB_8888); |
| final Bitmap bm_org = bm_test.copy(Config.ARGB_8888, false); |
| final Canvas can1 = new Canvas(bm_test); |
| |
| // Even when setBounds is not called, should not crash |
| iconDrawable.draw(can1); |
| // Draws nothing! Hence same as original. |
| if (!equalBitmaps(bm_test, bm_org)) { |
| findBitmapDifferences(bm_test, bm_org); |
| fail("bm differs, check " + dir); |
| } |
| } |
| |
| /** |
| * When setBound is called, translate accordingly. |
| */ |
| @Test |
| public void testDrawSetBounds() throws Exception { |
| int dpi = 4 ; |
| int top = 18 * dpi; |
| int left = 18 * dpi; |
| int right = 90 * dpi; |
| int bottom = 90 * dpi; |
| int width = right - left; |
| int height = bottom - top; |
| Context context = InstrumentationRegistry.getTargetContext(); |
| AdaptiveIconDrawable iconDrawable = (AdaptiveIconDrawable) context.getResources().getDrawable(android.R.drawable.sym_def_app_icon); |
| File dir = context.getExternalFilesDir(null); |
| L("writing temp bitmaps to %s...", dir); |
| final Bitmap bm_org = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); |
| final Canvas can_org = new Canvas(bm_org); |
| iconDrawable.setBounds(0, 0, width, height); |
| iconDrawable.draw(can_org); |
| |
| // Tested bitmap is drawn from the adaptive icon drawable. |
| final Bitmap bm_test = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); |
| final Canvas can_test = new Canvas(bm_test); |
| |
| iconDrawable.setBounds(left, top, right, bottom); |
| can_test.translate(-left, -top); |
| iconDrawable.draw(can_test); |
| can_test.translate(left, top); |
| |
| if (DEBUG) { |
| bm_org.compress(Bitmap.CompressFormat.PNG, 100, |
| new FileOutputStream(new File(dir, "adaptive-bm-original.png"))); |
| bm_test.compress(Bitmap.CompressFormat.PNG, 100, |
| new FileOutputStream(new File(dir, "adaptive-bm-test.png"))); |
| } |
| Region region = new Region(new Rect(0, 0, width, height)); |
| |
| Path circle = new Path(); |
| circle.addCircle(width / 2, height / 2, (right - left)/2 -10 /* room for anti-alias */, Direction.CW); |
| |
| region.setPath(circle, region); |
| if (!equalBitmaps(bm_test, bm_org, region)) { |
| findBitmapDifferences(bm_test, bm_org); |
| fail("bm differs, check " + dir); |
| } |
| } |
| |
| @Test |
| public void testSetVisible() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| assertFalse(iconDrawable.setVisible(true, true)); /* unchanged */ |
| assertTrue(iconDrawable.setVisible(false, true)); /* changed */ |
| assertFalse(iconDrawable.setVisible(false, true)); /* unchanged */ |
| } |
| |
| @Test |
| public void testSetAlpha() { |
| Bitmap b = Bitmap.createBitmap(1, 1, Config.ARGB_8888); |
| Drawable bg = new BitmapDrawable(b); |
| Drawable fg = new BitmapDrawable(b); |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(bg, fg); |
| iconDrawable.setBounds(0, 0, 100, 100); |
| |
| Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); |
| Canvas canvas = new Canvas(bitmap); |
| |
| iconDrawable.draw(canvas); |
| assertEquals(255, Color.alpha(bitmap.getPixel(50, 50))); |
| |
| iconDrawable.setAlpha(200); |
| bitmap.eraseColor(Color.TRANSPARENT); |
| iconDrawable.draw(canvas); |
| assertEquals(200, Color.alpha(bitmap.getPixel(50, 50))); |
| assertEquals(200, bg.getAlpha()); |
| assertEquals(200, fg.getAlpha()); |
| |
| iconDrawable.setAlpha(100); |
| bitmap.eraseColor(Color.TRANSPARENT); |
| iconDrawable.draw(canvas); |
| assertEquals(100, Color.alpha(bitmap.getPixel(50, 50))); |
| assertEquals(100, bg.getAlpha()); |
| assertEquals(100, fg.getAlpha()); |
| } |
| |
| @Test |
| public void testSetColorFilter() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| ColorFilter cf = new ColorFilter(); |
| iconDrawable.setColorFilter(cf); |
| |
| // input null as param |
| iconDrawable.setColorFilter(null); |
| // expected, no Exception thrown out, test success |
| } |
| |
| @Test |
| public void testGetOpacity() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| // Drawable#getOpacity is deprecated, AdaptiveIconDrawable |
| // should return PixelFormat.TRANSLUCENT always |
| iconDrawable.setOpacity(PixelFormat.OPAQUE); |
| assertEquals(PixelFormat.TRANSLUCENT, iconDrawable.getOpacity()); |
| |
| iconDrawable.setOpacity(PixelFormat.TRANSPARENT); |
| assertEquals(PixelFormat.TRANSLUCENT, iconDrawable.getOpacity()); |
| } |
| |
| @Test |
| public void testIsStateful() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| assertFalse(iconDrawable.isStateful()); |
| |
| iconDrawable = new AdaptiveIconDrawable(new StateListDrawable(), new ColorDrawable(Color.RED)); |
| assertTrue(iconDrawable.isStateful()); |
| } |
| |
| |
| @Test |
| public void testGetConstantState() { |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| ConstantState constantState = iconDrawable.getConstantState(); |
| assertNotNull(constantState); |
| } |
| |
| @Test |
| public void testMutate() { |
| // Obtain the first instance, then mutate and modify a property held by |
| // constant state. If mutate() works correctly, the property should not |
| // be modified on the second or third instances. |
| Resources res = InstrumentationRegistry.getTargetContext().getResources(); |
| AdaptiveIconDrawable first = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); |
| AdaptiveIconDrawable pre = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); |
| |
| first.mutate().setBounds(0, 0, 100, 100); |
| |
| assertEquals("Modified first loaded instance", 100, first.getBounds().width()); |
| assertEquals("Did not modify pre-mutate() instance", 0, pre.getBounds().width()); |
| |
| AdaptiveIconDrawable post = (AdaptiveIconDrawable) res.getDrawable(R.drawable.adaptive_icon_drawable, null); |
| |
| assertEquals("Did not modify post-mutate() instance", 0, post.getBounds().width()); |
| } |
| |
| @Test |
| public void testGetForegroundBackground() { |
| Context context = InstrumentationRegistry.getTargetContext(); |
| AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable( |
| new ColorDrawable(Color.RED), new ColorDrawable(Color.BLUE)); |
| Drawable fgDrawable = iconDrawable.getForeground(); |
| Drawable bgDrawable = iconDrawable.getBackground(); |
| assertTrue("Foreground layer is color drawable.", fgDrawable instanceof ColorDrawable); |
| assertTrue("Backgroud layer is color drawable.", bgDrawable instanceof ColorDrawable); |
| |
| AdaptiveIconDrawable iconDrawableInflated = |
| (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); |
| fgDrawable = iconDrawableInflated.getForeground(); |
| bgDrawable = iconDrawableInflated.getBackground(); |
| assertTrue("Foreground layer is color drawable.", fgDrawable instanceof BitmapDrawable); |
| assertTrue("Backgroud layer is color drawable.", bgDrawable instanceof BitmapDrawable); |
| } |
| |
| @Test |
| public void testGetIntrinsicWidth() { |
| Context context = InstrumentationRegistry.getTargetContext(); |
| AdaptiveIconDrawable iconDrawableInflated = |
| (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); |
| int fgWidth = iconDrawableInflated.getForeground().getIntrinsicWidth(); |
| int bgWidth = iconDrawableInflated.getBackground().getIntrinsicWidth(); |
| float iconIntrinsicWidth = Math.max(fgWidth, bgWidth) / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); |
| assertEquals("Max intrinsic width of the layers should be icon's intrinsic width", |
| (int) iconIntrinsicWidth, iconDrawableInflated.getIntrinsicWidth()); |
| } |
| |
| @Test |
| public void testGetIntrinsicHeight() { |
| Context context = InstrumentationRegistry.getTargetContext(); |
| AdaptiveIconDrawable iconDrawableInflated = |
| (AdaptiveIconDrawable) context.getDrawable(R.drawable.adaptive_icon_drawable); |
| int fgWidth = iconDrawableInflated.getForeground().getIntrinsicHeight(); |
| int bgWidth = iconDrawableInflated.getBackground().getIntrinsicHeight(); |
| float iconIntrinsicHeight = Math.max(fgWidth, bgWidth) / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction()); |
| assertEquals("Max intrinsic height of the layers should be icon's intrinsic height", |
| (int) iconIntrinsicHeight, iconDrawableInflated.getIntrinsicHeight()); |
| } |
| |
| // |
| // Utils |
| // |
| |
| boolean equalBitmaps(Bitmap a, Bitmap b) { |
| return equalBitmaps(a, b, null); |
| } |
| |
| boolean equalBitmaps(Bitmap a, Bitmap b, Region region) { |
| if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false; |
| |
| final int w = a.getWidth(); |
| final int h = a.getHeight(); |
| int[] aPix = new int[w * h]; |
| int[] bPix = new int[w * h]; |
| |
| if (region != null) { |
| for (int i = 0; i < w; i++) { |
| for (int j = 0; j < h; j++) { |
| int ra = (a.getPixel(i, j) >> 16) & 0xff; |
| int ga = (a.getPixel(i, j) >> 8) & 0xff; |
| int ba = a.getPixel(i, j) & 0xff; |
| int rb = (b.getPixel(i, j) >> 16) & 0xff; |
| int gb = (b.getPixel(i, j) >> 8) & 0xff; |
| int bb = b.getPixel(i, j) & 0xff; |
| if (region.contains(i, j) && a.getPixel(i, j) != b.getPixel(i, j) ) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } else { |
| a.getPixels(aPix, 0, w, 0, 0, w, h); |
| b.getPixels(bPix, 0, w, 0, 0, w, h); |
| return Arrays.equals(aPix, bPix); |
| } |
| } |
| |
| void findBitmapDifferences(Bitmap a, Bitmap b) { |
| if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) { |
| L("different sizes: %dx%d vs %dx%d", |
| a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight()); |
| return; |
| } |
| |
| final int w = a.getWidth(); |
| final int h = a.getHeight(); |
| int[] aPix = new int[w * h]; |
| int[] bPix = new int[w * h]; |
| |
| a.getPixels(aPix, 0, w, 0, 0, w, h); |
| b.getPixels(bPix, 0, w, 0, 0, w, h); |
| |
| L("bitmap a (%dx%d)", w, h); |
| printBits(aPix, w, h); |
| L("bitmap b (%dx%d)", w, h); |
| printBits(bPix, w, h); |
| |
| StringBuffer sb = new StringBuffer("Different pixels: "); |
| for (int i=0; i<w; i++) { |
| for (int j=0; j<h; j++) { |
| if (aPix[i+w*j] != bPix[i+w*j]) { |
| sb.append(" ").append(i).append(",").append(j).append("<") |
| .append(aPix[i+w*j]).append(",").append(bPix[i+w*j]).append(">"); |
| } |
| } |
| } |
| L(sb.toString()); |
| } |
| |
| static void printBits(int[] a, int w, int h) { |
| final StringBuilder sb = new StringBuilder(); |
| for (int i=0; i<w; i++) { |
| for (int j=0; j<h; j++) { |
| sb.append(colorToChar(a[i+w*j])); |
| } |
| sb.append('\n'); |
| } |
| L(sb.toString()); |
| } |
| |
| static char colorToChar(int color) { |
| int sum = ((color >> 16) & 0xff) |
| + ((color >> 8) & 0xff) |
| + ((color) & 0xff); |
| return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)]; |
| } |
| static final char[] GRADIENT = " .:;+=xX$#".toCharArray(); |
| } |