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