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