Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2016 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 package androidx.appcompat.widget;
     17 
     18 import static android.support.test.espresso.Espresso.onView;
     19 import static android.support.test.espresso.assertion.ViewAssertions.matches;
     20 import static android.support.test.espresso.matcher.ViewMatchers.withId;
     21 
     22 import static androidx.appcompat.testutils.AppCompatTintableViewActions.setBackgroundResource;
     23 import static androidx.appcompat.testutils.AppCompatTintableViewActions.setBackgroundTintList;
     24 import static androidx.appcompat.testutils.AppCompatTintableViewActions.setBackgroundTintMode;
     25 import static androidx.appcompat.testutils.TestUtilsActions.setBackgroundTintListViewCompat;
     26 import static androidx.appcompat.testutils.TestUtilsActions.setBackgroundTintModeViewCompat;
     27 import static androidx.appcompat.testutils.TestUtilsActions.setEnabled;
     28 import static androidx.appcompat.testutils.TestUtilsMatchers.isBackground;
     29 
     30 import static org.junit.Assert.assertNull;
     31 
     32 import android.content.res.ColorStateList;
     33 import android.content.res.Resources;
     34 import android.graphics.PorterDuff;
     35 import android.graphics.drawable.Drawable;
     36 import android.support.test.filters.SmallTest;
     37 import android.support.test.rule.ActivityTestRule;
     38 import android.support.test.runner.AndroidJUnit4;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 
     42 import androidx.annotation.ColorInt;
     43 import androidx.annotation.IdRes;
     44 import androidx.annotation.NonNull;
     45 import androidx.appcompat.test.R;
     46 import androidx.appcompat.testutils.AppCompatTintableViewActions;
     47 import androidx.appcompat.testutils.BaseTestActivity;
     48 import androidx.appcompat.testutils.TestUtils;
     49 import androidx.core.content.res.ResourcesCompat;
     50 import androidx.core.graphics.ColorUtils;
     51 
     52 import org.junit.Before;
     53 import org.junit.Rule;
     54 import org.junit.Test;
     55 import org.junit.runner.RunWith;
     56 
     57 /**
     58  * Base class for testing custom view extensions in appcompat-v7 that implement the
     59  * <code>TintableBackgroundView</code> interface. Extensions of this class run all tests
     60  * from here and add test cases specific to the functionality they add to the relevant
     61  * base view class (such as <code>AppCompatTextView</code>'s all-caps support).
     62  */
     63 @RunWith(AndroidJUnit4.class)
     64 public abstract class AppCompatBaseViewTest<A extends BaseTestActivity, T extends View> {
     65     @Rule
     66     public final ActivityTestRule<A> mActivityTestRule;
     67 
     68     protected ViewGroup mContainer;
     69 
     70     protected A mActivity;
     71     protected Resources mResources;
     72 
     73     public AppCompatBaseViewTest(Class clazz) {
     74         mActivityTestRule = new ActivityTestRule<A>(clazz);
     75     }
     76 
     77     @Before
     78     public void setUp() {
     79         mActivity = mActivityTestRule.getActivity();
     80         mContainer = mActivity.findViewById(R.id.container);
     81         mResources = mActivity.getResources();
     82     }
     83 
     84     /**
     85      * Subclasses should override this method to return true if by default the matching
     86      * view (such as, say, {@link AppCompatSpinner}) has background set it.
     87      */
     88     protected boolean hasBackgroundByDefault() {
     89         return false;
     90     }
     91 
     92     private void verifyBackgroundIsColoredAs(String description, @NonNull View view,
     93             @ColorInt int color, int allowedComponentVariance) {
     94         Drawable background = view.getBackground();
     95         TestUtils.assertAllPixelsOfColor(description,
     96                 background, view.getWidth(), view.getHeight(), true,
     97                 color, allowedComponentVariance, false);
     98     }
     99 
    100     /**
    101      * This method tests that background tinting is not applied when the
    102      * tintable view has no background.
    103      */
    104     @Test
    105     @SmallTest
    106     public void testBackgroundTintingWithNoBackground() {
    107         if (hasBackgroundByDefault()) {
    108             return;
    109         }
    110 
    111         final @IdRes int viewId = R.id.view_tinted_no_background;
    112         final T view = (T) mContainer.findViewById(viewId);
    113 
    114         // Note that all the asserts in this test check that the view background
    115         // is null. This is because the matching child in the activity doesn't define any
    116         // background on itself, and there is nothing to tint.
    117 
    118         assertNull("No background after XML loading", view.getBackground());
    119 
    120         // Disable the view and check that the background is still null.
    121         onView(withId(viewId)).perform(setEnabled(false));
    122         assertNull("No background after disabling", view.getBackground());
    123 
    124         // Enable the view and check that the background is still null.
    125         onView(withId(viewId)).perform(setEnabled(true));
    126         assertNull("No background after re-enabling", view.getBackground());
    127 
    128         // Load a new color state list, set it on the view and check that the background
    129         // is still null.
    130         final ColorStateList sandColor = ResourcesCompat.getColorStateList(
    131                 mResources, R.color.color_state_list_sand, null);
    132         onView(withId(viewId)).perform(
    133                 setBackgroundTintList(sandColor));
    134 
    135         // Disable the view and check that the background is still null.
    136         onView(withId(viewId)).perform(setEnabled(false));
    137         assertNull("No background after disabling", view.getBackground());
    138 
    139         // Enable the view and check that the background is still null.
    140         onView(withId(viewId)).perform(setEnabled(true));
    141         assertNull("No background after re-enabling", view.getBackground());
    142     }
    143 
    144     /**
    145      * This method tests that background tinting is not applied when the
    146      * tintable view has no background.
    147      */
    148     @Test
    149     @SmallTest
    150     public void testBackgroundTintingViewCompatWithNoBackground() {
    151         if (hasBackgroundByDefault()) {
    152             return;
    153         }
    154 
    155         final @IdRes int viewId = R.id.view_tinted_no_background;
    156         final T view = (T) mContainer.findViewById(viewId);
    157 
    158         // Note that all the asserts in this test check that the view background
    159         // is null. This is because the matching child in the activity doesn't define any
    160         // background on itself, and there is nothing to tint.
    161 
    162         assertNull("No background after XML loading", view.getBackground());
    163 
    164         // Disable the view and check that the background is still null.
    165         onView(withId(viewId)).perform(setEnabled(false));
    166         assertNull("No background after disabling", view.getBackground());
    167 
    168         // Enable the view and check that the background is still null.
    169         onView(withId(viewId)).perform(setEnabled(true));
    170         assertNull("No background after re-enabling", view.getBackground());
    171 
    172         // Load a new color state list, set it on the view and check that the background
    173         // is still null.
    174         final ColorStateList lilacColor = ResourcesCompat.getColorStateList(
    175                 mResources, R.color.color_state_list_lilac, null);
    176         onView(withId(viewId)).perform(setBackgroundTintListViewCompat(lilacColor));
    177 
    178         // Disable the view and check that the background is still null.
    179         onView(withId(viewId)).perform(setEnabled(false));
    180         assertNull("No background after disabling", view.getBackground());
    181 
    182         // Enable the view and check that the background is still null.
    183         onView(withId(viewId)).perform(setEnabled(true));
    184         assertNull("No background after re-enabling", view.getBackground());
    185     }
    186 
    187     /**
    188      * This method tests that background tinting is applied to tintable view
    189      * in enabled and disabled state across a number of <code>ColorStateList</code>s set as
    190      * background tint lists on the same background.
    191      */
    192     @Test
    193     @SmallTest
    194     public void testBackgroundTintingAcrossStateChange() {
    195         final @IdRes int viewId = R.id.view_tinted_background;
    196         final T view = (T) mContainer.findViewById(viewId);
    197 
    198         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
    199                 mResources, R.color.lilac_default, null);
    200         final @ColorInt int lilacDisabled = ResourcesCompat.getColor(
    201                 mResources, R.color.lilac_disabled, null);
    202         final @ColorInt int sandDefault = ResourcesCompat.getColor(
    203                 mResources, R.color.sand_default, null);
    204         final @ColorInt int sandDisabled = ResourcesCompat.getColor(
    205                 mResources, R.color.sand_disabled, null);
    206         final @ColorInt int oceanDefault = ResourcesCompat.getColor(
    207                 mResources, R.color.ocean_default, null);
    208         final @ColorInt int oceanDisabled = ResourcesCompat.getColor(
    209                 mResources, R.color.ocean_disabled, null);
    210 
    211         // Test the default state for tinting set up in the layout XML file.
    212         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state", view,
    213                 lilacDefault, 0);
    214 
    215         // Disable the view and check that the background has switched to the matching entry
    216         // in the default color state list.
    217         onView(withId(viewId)).perform(setEnabled(false));
    218         verifyBackgroundIsColoredAs("Default lilac tinting in disabled state", view,
    219                 lilacDisabled, 0);
    220 
    221         // Enable the view and check that the background has switched to the matching entry
    222         // in the default color state list.
    223         onView(withId(viewId)).perform(setEnabled(true));
    224         verifyBackgroundIsColoredAs("Default lilac tinting in re-enabled state", view,
    225                 lilacDefault, 0);
    226 
    227         // Load a new color state list, set it on the view and check that the background has
    228         // switched to the matching entry in newly set color state list.
    229         final ColorStateList sandColor = ResourcesCompat.getColorStateList(
    230                 mResources, R.color.color_state_list_sand, null);
    231         onView(withId(viewId)).perform(setBackgroundTintList(sandColor));
    232         verifyBackgroundIsColoredAs("New sand tinting in enabled state", view,
    233                 sandDefault, 0);
    234 
    235         // Disable the view and check that the background has switched to the matching entry
    236         // in the newly set color state list.
    237         onView(withId(viewId)).perform(setEnabled(false));
    238         verifyBackgroundIsColoredAs("New sand tinting in disabled state", view,
    239                 sandDisabled, 0);
    240 
    241         // Enable the view and check that the background has switched to the matching entry
    242         // in the newly set color state list.
    243         onView(withId(viewId)).perform(setEnabled(true));
    244         verifyBackgroundIsColoredAs("New sand tinting in re-enabled state", view,
    245                 sandDefault, 0);
    246 
    247         // Load another color state list, set it on the view and check that the background has
    248         // switched to the matching entry in newly set color state list.
    249         final ColorStateList oceanColor = ResourcesCompat.getColorStateList(
    250                 mResources, R.color.color_state_list_ocean, null);
    251         onView(withId(viewId)).perform(setBackgroundTintList(oceanColor));
    252         verifyBackgroundIsColoredAs("New ocean tinting in enabled state", view,
    253                 oceanDefault, 0);
    254 
    255         // Disable the view and check that the background has switched to the matching entry
    256         // in the newly set color state list.
    257         onView(withId(viewId)).perform(setEnabled(false));
    258         verifyBackgroundIsColoredAs("New ocean tinting in disabled state", view,
    259                 oceanDisabled, 0);
    260 
    261         // Enable the view and check that the background has switched to the matching entry
    262         // in the newly set color state list.
    263         onView(withId(viewId)).perform(setEnabled(true));
    264         verifyBackgroundIsColoredAs("New ocean tinting in re-enabled state", view,
    265                 oceanDefault, 0);
    266     }
    267 
    268     /**
    269      * This method tests that background tinting is applied to tintable view
    270      * in enabled and disabled state across a number of <code>ColorStateList</code>s set as
    271      * background tint lists on the same background.
    272      */
    273     @Test
    274     @SmallTest
    275     public void testBackgroundTintingViewCompatAcrossStateChange() {
    276         final @IdRes int viewId = R.id.view_tinted_background;
    277         final T view = (T) mContainer.findViewById(viewId);
    278 
    279         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
    280                 mResources, R.color.lilac_default, null);
    281         final @ColorInt int lilacDisabled = ResourcesCompat.getColor(
    282                 mResources, R.color.lilac_disabled, null);
    283         final @ColorInt int sandDefault = ResourcesCompat.getColor(
    284                 mResources, R.color.sand_default, null);
    285         final @ColorInt int sandDisabled = ResourcesCompat.getColor(
    286                 mResources, R.color.sand_disabled, null);
    287         final @ColorInt int oceanDefault = ResourcesCompat.getColor(
    288                 mResources, R.color.ocean_default, null);
    289         final @ColorInt int oceanDisabled = ResourcesCompat.getColor(
    290                 mResources, R.color.ocean_disabled, null);
    291 
    292         // Test the default state for tinting set up in the layout XML file.
    293         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state", view,
    294                 lilacDefault, 0);
    295 
    296         // Disable the view and check that the background has switched to the matching entry
    297         // in the default color state list.
    298         onView(withId(viewId)).perform(setEnabled(false));
    299         verifyBackgroundIsColoredAs("Default lilac tinting in disabled state", view,
    300                 lilacDisabled, 0);
    301 
    302         // Enable the view and check that the background has switched to the matching entry
    303         // in the default color state list.
    304         onView(withId(viewId)).perform(setEnabled(true));
    305         verifyBackgroundIsColoredAs("Default lilac tinting in re-enabled state", view,
    306                 lilacDefault, 0);
    307 
    308         // Load a new color state list, set it on the view and check that the background has
    309         // switched to the matching entry in newly set color state list.
    310         final ColorStateList sandColor = ResourcesCompat.getColorStateList(
    311                 mResources, R.color.color_state_list_sand, null);
    312         onView(withId(viewId)).perform(setBackgroundTintListViewCompat(sandColor));
    313         verifyBackgroundIsColoredAs("New sand tinting in enabled state", view,
    314                 sandDefault, 0);
    315 
    316         // Disable the view and check that the background has switched to the matching entry
    317         // in the newly set color state list.
    318         onView(withId(viewId)).perform(setEnabled(false));
    319         verifyBackgroundIsColoredAs("New sand tinting in disabled state", view,
    320                 sandDisabled, 0);
    321 
    322         // Enable the view and check that the background has switched to the matching entry
    323         // in the newly set color state list.
    324         onView(withId(viewId)).perform(setEnabled(true));
    325         verifyBackgroundIsColoredAs("New sand tinting in re-enabled state", view,
    326                 sandDefault, 0);
    327 
    328         // Load another color state list, set it on the view and check that the background has
    329         // switched to the matching entry in newly set color state list.
    330         final ColorStateList oceanColor = ResourcesCompat.getColorStateList(
    331                 mResources, R.color.color_state_list_ocean, null);
    332         onView(withId(viewId)).perform(
    333                 setBackgroundTintListViewCompat(oceanColor));
    334         verifyBackgroundIsColoredAs("New ocean tinting in enabled state", view,
    335                 oceanDefault, 0);
    336 
    337         // Disable the view and check that the background has switched to the matching entry
    338         // in the newly set color state list.
    339         onView(withId(viewId)).perform(setEnabled(false));
    340         verifyBackgroundIsColoredAs("New ocean tinting in disabled state", view,
    341                 oceanDisabled, 0);
    342 
    343         // Enable the view and check that the background has switched to the matching entry
    344         // in the newly set color state list.
    345         onView(withId(viewId)).perform(setEnabled(true));
    346         verifyBackgroundIsColoredAs("New ocean tinting in re-enabled state", view,
    347                 oceanDefault, 0);
    348     }
    349 
    350     /**
    351      * This method tests that background tinting applied to tintable view
    352      * in enabled and disabled state across the same background respects the currently set
    353      * background tinting mode.
    354      */
    355     @Test
    356     @SmallTest
    357     public void testBackgroundTintingAcrossModeChange() {
    358         final @IdRes int viewId = R.id.view_untinted_background;
    359         final T view = (T) mContainer.findViewById(viewId);
    360 
    361         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
    362                 mResources, R.color.emerald_translucent_default, null);
    363         final @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
    364                 mResources, R.color.emerald_translucent_disabled, null);
    365         // This is the fill color of R.drawable.test_background_green set on our view
    366         // that we'll be testing in this method
    367         final @ColorInt int backgroundColor = ResourcesCompat.getColor(
    368                 mResources, R.color.test_green, null);
    369 
    370         // Test the default state for tinting set up in the layout XML file.
    371         verifyBackgroundIsColoredAs("Default no tinting in enabled state", view,
    372                 backgroundColor, 0);
    373 
    374         // From this point on in this method we're allowing a margin of error in checking the
    375         // color of the view background. This is due to both translucent colors being used
    376         // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing
    377         // translucent color on top of solid fill color. This is where the allowed variance
    378         // value of 2 comes from - one for compositing and one for color translucency.
    379         final int allowedComponentVariance = 2;
    380 
    381         // Set src_in tint mode on our view
    382         onView(withId(viewId)).perform(setBackgroundTintMode(PorterDuff.Mode.SRC_IN));
    383 
    384         // Load a new color state list, set it on the view and check that the background has
    385         // switched to the matching entry in newly set color state list.
    386         final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
    387                 mResources, R.color.color_state_list_emerald_translucent, null);
    388         onView(withId(viewId)).perform(setBackgroundTintList(emeraldColor));
    389         verifyBackgroundIsColoredAs("New emerald tinting in enabled state under src_in", view,
    390                 emeraldDefault, allowedComponentVariance);
    391 
    392         // Disable the view and check that the background has switched to the matching entry
    393         // in the newly set color state list.
    394         onView(withId(viewId)).perform(setEnabled(false));
    395         verifyBackgroundIsColoredAs("New emerald tinting in disabled state under src_in", view,
    396                 emeraldDisabled, allowedComponentVariance);
    397 
    398         // Set src_over tint mode on our view. As the currently set tint list is using
    399         // translucent colors, we expect the actual background of the view to be different under
    400         // this new mode (unlike src_in and src_over that behave identically when the destination is
    401         // a fully filled rectangle and the source is an opaque color).
    402         onView(withId(viewId)).perform(setBackgroundTintMode(PorterDuff.Mode.SRC_OVER));
    403 
    404         // Enable the view and check that the background has switched to the matching entry
    405         // in the color state list.
    406         onView(withId(viewId)).perform(setEnabled(true));
    407         verifyBackgroundIsColoredAs("New emerald tinting in enabled state under src_over", view,
    408                 ColorUtils.compositeColors(emeraldDefault, backgroundColor),
    409                 allowedComponentVariance);
    410 
    411         // Disable the view and check that the background has switched to the matching entry
    412         // in the newly set color state list.
    413         onView(withId(viewId)).perform(setEnabled(false));
    414         verifyBackgroundIsColoredAs("New emerald tinting in disabled state under src_over",
    415                 view, ColorUtils.compositeColors(emeraldDisabled, backgroundColor),
    416                 allowedComponentVariance);
    417     }
    418 
    419     /**
    420      * This method tests that background tinting applied to tintable view
    421      * in enabled and disabled state across the same background respects the currently set
    422      * background tinting mode.
    423      */
    424     @Test
    425     @SmallTest
    426     public void testBackgroundTintingViewCompatAcrossModeChange() {
    427         final @IdRes int viewId = R.id.view_untinted_background;
    428         final T view = (T) mContainer.findViewById(viewId);
    429 
    430         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
    431                 mResources, R.color.emerald_translucent_default, null);
    432         final @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
    433                 mResources, R.color.emerald_translucent_disabled, null);
    434         // This is the fill color of R.drawable.test_background_green set on our view
    435         // that we'll be testing in this method
    436         final @ColorInt int backgroundColor = ResourcesCompat.getColor(
    437                 mResources, R.color.test_green, null);
    438 
    439         // Test the default state for tinting set up in the layout XML file.
    440         verifyBackgroundIsColoredAs("Default no tinting in enabled state", view,
    441                 backgroundColor, 0);
    442 
    443         // From this point on in this method we're allowing a margin of error in checking the
    444         // color of the view background. This is due to both translucent colors being used
    445         // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing
    446         // translucent color on top of solid fill color. This is where the allowed variance
    447         // value of 2 comes from - one for compositing and one for color translucency.
    448         final int allowedComponentVariance = 2;
    449 
    450         // Set src_in tint mode on our view
    451         onView(withId(viewId)).perform(setBackgroundTintModeViewCompat(PorterDuff.Mode.SRC_IN));
    452 
    453         // Load a new color state list, set it on the view and check that the background has
    454         // switched to the matching entry in newly set color state list.
    455         final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
    456                 mResources, R.color.color_state_list_emerald_translucent, null);
    457         onView(withId(viewId)).perform(setBackgroundTintListViewCompat(emeraldColor));
    458         verifyBackgroundIsColoredAs("New emerald tinting in enabled state under src_in", view,
    459                 emeraldDefault, allowedComponentVariance);
    460 
    461         // Disable the view and check that the background has switched to the matching entry
    462         // in the newly set color state list.
    463         onView(withId(viewId)).perform(setEnabled(false));
    464         verifyBackgroundIsColoredAs("New emerald tinting in disabled state under src_in", view,
    465                 emeraldDisabled, allowedComponentVariance);
    466 
    467         // Set src_over tint mode on our view. As the currently set tint list is using
    468         // translucent colors, we expect the actual background of the view to be different under
    469         // this new mode (unlike src_in and src_over that behave identically when the destination is
    470         // a fully filled rectangle and the source is an opaque color).
    471         onView(withId(viewId)).perform(setBackgroundTintModeViewCompat(PorterDuff.Mode.SRC_OVER));
    472 
    473         // Enable the view and check that the background has switched to the matching entry
    474         // in the color state list.
    475         onView(withId(viewId)).perform(setEnabled(true));
    476         verifyBackgroundIsColoredAs("New emerald tinting in enabled state under src_over", view,
    477                 ColorUtils.compositeColors(emeraldDefault, backgroundColor),
    478                 allowedComponentVariance);
    479 
    480         // Disable the view and check that the background has switched to the matching entry
    481         // in the newly set color state list.
    482         onView(withId(viewId)).perform(setEnabled(false));
    483         verifyBackgroundIsColoredAs("New emerald tinting in disabled state under src_over",
    484                 view, ColorUtils.compositeColors(emeraldDisabled, backgroundColor),
    485                 allowedComponentVariance);
    486     }
    487 
    488     /**
    489      * This method tests that opaque background tinting applied to tintable view
    490      * is applied correctly after changing the background itself of the view.
    491      */
    492     @Test
    493     @SmallTest
    494     public void testBackgroundOpaqueTintingAcrossBackgroundChange() {
    495         final @IdRes int viewId = R.id.view_tinted_no_background;
    496         final T view = (T) mContainer.findViewById(viewId);
    497 
    498         final @ColorInt int lilacDefault = ResourcesCompat.getColor(
    499                 mResources, R.color.lilac_default, null);
    500         final @ColorInt int lilacDisabled = ResourcesCompat.getColor(
    501                 mResources, R.color.lilac_disabled, null);
    502 
    503         if (!hasBackgroundByDefault()) {
    504             assertNull("No background after XML loading", view.getBackground());
    505         }
    506 
    507         // Set background on our view
    508         onView(withId(viewId)).perform(setBackgroundResource(R.drawable.test_background_green));
    509 
    510         // Test the default state for tinting set up in the layout XML file.
    511         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state on green background",
    512                 view, lilacDefault, 0);
    513 
    514         // Disable the view and check that the background has switched to the matching entry
    515         // in the default color state list.
    516         onView(withId(viewId)).perform(setEnabled(false));
    517         verifyBackgroundIsColoredAs("Default lilac tinting in disabled state on green background",
    518                 view, lilacDisabled, 0);
    519 
    520         // Enable the view and check that the background has switched to the matching entry
    521         // in the default color state list.
    522         onView(withId(viewId)).perform(setEnabled(true));
    523         verifyBackgroundIsColoredAs("Default lilac tinting in re-enabled state on green background",
    524                 view, lilacDefault, 0);
    525 
    526         // Set a different background on our view based on resource ID
    527         onView(withId(viewId)).perform(AppCompatTintableViewActions.setBackgroundResource(
    528                 R.drawable.test_background_red));
    529 
    530         // Test the default state for tinting set up in the layout XML file.
    531         verifyBackgroundIsColoredAs("Default lilac tinting in enabled state on red background",
    532                 view, lilacDefault, 0);
    533 
    534         // Disable the view and check that the background has switched to the matching entry
    535         // in the default color state list.
    536         onView(withId(viewId)).perform(setEnabled(false));
    537         verifyBackgroundIsColoredAs("Default lilac tinting in disabled state on red background",
    538                 view, lilacDisabled, 0);
    539 
    540         // Enable the view and check that the background has switched to the matching entry
    541         // in the default color state list.
    542         onView(withId(viewId)).perform(setEnabled(true));
    543         verifyBackgroundIsColoredAs("Default lilac tinting in re-enabled state on red background",
    544                 view, lilacDefault, 0);
    545     }
    546 
    547     /**
    548      * This method tests that translucent background tinting applied to tintable view
    549      * is applied correctly after changing the background itself of the view.
    550      */
    551     @Test
    552     @SmallTest
    553     public void testBackgroundTranslucentTintingAcrossBackgroundChange() {
    554         final @IdRes int viewId = R.id.view_untinted_no_background;
    555         final T view = (T) mContainer.findViewById(viewId);
    556 
    557         final @ColorInt int emeraldDefault = ResourcesCompat.getColor(
    558                 mResources, R.color.emerald_translucent_default, null);
    559         final @ColorInt int emeraldDisabled = ResourcesCompat.getColor(
    560                 mResources, R.color.emerald_translucent_disabled, null);
    561         // This is the fill color of R.drawable.test_background_green set on our view
    562         // that we'll be testing in this method
    563         final @ColorInt int backgroundColorGreen = ResourcesCompat.getColor(
    564                 mResources, R.color.test_green, null);
    565         final @ColorInt int backgroundColorRed = ResourcesCompat.getColor(
    566                 mResources, R.color.test_red, null);
    567 
    568         if (!hasBackgroundByDefault()) {
    569             assertNull("No background after XML loading", view.getBackground());
    570         }
    571 
    572         // Set src_over tint mode on our view. As the currently set tint list is using
    573         // translucent colors, we expect the actual background of the view to be different under
    574         // this new mode (unlike src_in and src_over that behave identically when the destination is
    575         // a fully filled rectangle and the source is an opaque color).
    576         onView(withId(viewId)).perform(setBackgroundTintMode(PorterDuff.Mode.SRC_OVER));
    577         // Load and set a translucent color state list as the background tint list
    578         final ColorStateList emeraldColor = ResourcesCompat.getColorStateList(
    579                 mResources, R.color.color_state_list_emerald_translucent, null);
    580         onView(withId(viewId)).perform(
    581                 setBackgroundTintList(emeraldColor));
    582 
    583         // Set background on our view
    584         onView(withId(viewId)).perform(setBackgroundResource(R.drawable.test_background_green));
    585 
    586         // From this point on in this method we're allowing a margin of error in checking the
    587         // color of the view background. This is due to both translucent colors being used
    588         // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing
    589         // translucent color on top of solid fill color. This is where the allowed variance
    590         // value of 2 comes from - one for compositing and one for color translucency.
    591         final int allowedComponentVariance = 2;
    592 
    593         // Test the default state for tinting set up with the just loaded tint list.
    594         verifyBackgroundIsColoredAs("Emerald tinting in enabled state on green background",
    595                 view, ColorUtils.compositeColors(emeraldDefault, backgroundColorGreen),
    596                 allowedComponentVariance);
    597 
    598         // Disable the view and check that the background has switched to the matching entry
    599         // in the default color state list.
    600         onView(withId(viewId)).perform(setEnabled(false));
    601         verifyBackgroundIsColoredAs("Emerald tinting in disabled state on green background",
    602                 view, ColorUtils.compositeColors(emeraldDisabled, backgroundColorGreen),
    603                 allowedComponentVariance);
    604 
    605         // Enable the view and check that the background has switched to the matching entry
    606         // in the default color state list.
    607         onView(withId(viewId)).perform(setEnabled(true));
    608         verifyBackgroundIsColoredAs("Emerald tinting in re-enabled state on green background",
    609                 view, ColorUtils.compositeColors(emeraldDefault, backgroundColorGreen),
    610                 allowedComponentVariance);
    611 
    612         // Set a different background on our view based on resource ID
    613         onView(withId(viewId)).perform(AppCompatTintableViewActions.setBackgroundResource(
    614                 R.drawable.test_background_red));
    615 
    616         // Test the default state for tinting the new background with the same color state list
    617         verifyBackgroundIsColoredAs("Emerald tinting in enabled state on red background",
    618                 view, ColorUtils.compositeColors(emeraldDefault, backgroundColorRed),
    619                 allowedComponentVariance);
    620 
    621         // Disable the view and check that the background has switched to the matching entry
    622         // in our current color state list.
    623         onView(withId(viewId)).perform(setEnabled(false));
    624         verifyBackgroundIsColoredAs("Emerald tinting in disabled state on red background",
    625                 view, ColorUtils.compositeColors(emeraldDisabled, backgroundColorRed),
    626                 allowedComponentVariance);
    627 
    628         // Enable the view and check that the background has switched to the matching entry
    629         // in our current color state list.
    630         onView(withId(viewId)).perform(setEnabled(true));
    631         verifyBackgroundIsColoredAs("Emerald tinting in re-enabled state on red background",
    632                 view, ColorUtils.compositeColors(emeraldDefault, backgroundColorRed),
    633                 allowedComponentVariance);
    634     }
    635 
    636     protected void testUntintedBackgroundTintingViewCompatAcrossStateChange(@IdRes int viewId) {
    637         final T view = (T) mContainer.findViewById(viewId);
    638 
    639         final @ColorInt int oceanDefault = ResourcesCompat.getColor(
    640                 mResources, R.color.ocean_default, null);
    641         final @ColorInt int oceanDisabled = ResourcesCompat.getColor(
    642                 mResources, R.color.ocean_disabled, null);
    643 
    644         final ColorStateList oceanColor = ResourcesCompat.getColorStateList(
    645                 mResources, R.color.color_state_list_ocean, null);
    646         onView(withId(viewId)).perform(setBackgroundTintListViewCompat(oceanColor));
    647 
    648         // Disable the view and check that the background has switched to the matching entry
    649         // in the newly set color state list.
    650         onView(withId(viewId)).perform(setEnabled(false))
    651                 .check(matches(isBackground(oceanDisabled, true)));
    652 
    653         // Enable the view and check that the background has switched to the matching entry
    654         // in the newly set color state list.
    655         onView(withId(viewId)).perform(setEnabled(true))
    656                 .check(matches(isBackground(oceanDefault, true)));
    657     }
    658 }
    659