Home | History | Annotate | Download | only in cts
      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