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