| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.support.v7.widget; |
| |
| import static android.support.test.espresso.Espresso.onView; |
| import static android.support.test.espresso.matcher.ViewMatchers.withId; |
| import static android.support.v7.testutils.TestUtilsActions.setEnabled; |
| |
| import android.content.res.ColorStateList; |
| import android.content.res.Resources; |
| import android.graphics.PorterDuff; |
| import android.graphics.drawable.Drawable; |
| import android.support.annotation.ColorInt; |
| import android.support.annotation.IdRes; |
| import android.support.annotation.NonNull; |
| import android.support.test.filters.SmallTest; |
| import android.support.v4.content.res.ResourcesCompat; |
| import android.support.v4.graphics.ColorUtils; |
| import android.support.v7.appcompat.test.R; |
| import android.support.v7.testutils.AppCompatTintableViewActions; |
| import android.support.v7.testutils.BaseTestActivity; |
| import android.support.v7.testutils.TestUtils; |
| import android.widget.ImageView; |
| |
| import org.junit.Test; |
| |
| /** |
| * In addition to all tinting-related tests done by the base class, this class provides |
| * testing for tinting image resources on appcompat-v7 view classes that extend directly |
| * or indirectly the core {@link ImageView} class. |
| */ |
| public abstract class AppCompatBaseImageViewTest<T extends ImageView> |
| extends AppCompatBaseViewTest<BaseTestActivity, T> { |
| public AppCompatBaseImageViewTest(Class clazz) { |
| super(clazz); |
| } |
| |
| private void verifyImageSourceIsColoredAs(String description, @NonNull ImageView imageView, |
| @ColorInt int color, int allowedComponentVariance) { |
| Drawable imageSource = imageView.getDrawable(); |
| TestUtils.assertAllPixelsOfColor(description, |
| imageSource, imageSource.getIntrinsicWidth(), imageSource.getIntrinsicHeight(), |
| true, color, allowedComponentVariance, false); |
| } |
| |
| /** |
| * This method tests that image tinting is applied to tintable image view |
| * in enabled and disabled state across a number of <code>ColorStateList</code>s set as |
| * image source tint lists on the same image source. |
| */ |
| @Test |
| @SmallTest |
| public void testImageTintingAcrossStateChange() { |
| final @IdRes int viewId = R.id.view_tinted_source; |
| final Resources res = getActivity().getResources(); |
| final T view = (T) mContainer.findViewById(viewId); |
| |
| @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null); |
| @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null); |
| @ColorInt int sandDefault = ResourcesCompat.getColor(res, R.color.sand_default, null); |
| @ColorInt int sandDisabled = ResourcesCompat.getColor(res, R.color.sand_disabled, null); |
| @ColorInt int oceanDefault = ResourcesCompat.getColor(res, R.color.ocean_default, null); |
| @ColorInt int oceanDisabled = ResourcesCompat.getColor(res, R.color.ocean_disabled, null); |
| |
| // Test the default state for tinting set up in the layout XML file. |
| verifyImageSourceIsColoredAs("Default lilac tinting in enabled state", view, |
| lilacDefault, 0); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in disabled state", view, |
| lilacDisabled, 0); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in re-enabled state", view, |
| lilacDefault, 0); |
| |
| // Load a new color state list, set it on the view and check that the image has |
| // switched to the matching entry in newly set color state list. |
| final ColorStateList sandColor = ResourcesCompat.getColorStateList( |
| res, R.color.color_state_list_sand, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintList(sandColor)); |
| verifyImageSourceIsColoredAs("New sand tinting in enabled state", view, |
| sandDefault, 0); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("New sand tinting in disabled state", view, |
| sandDisabled, 0); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("New sand tinting in re-enabled state", view, |
| sandDefault, 0); |
| |
| // Load another color state list, set it on the view and check that the image has |
| // switched to the matching entry in newly set color state list. |
| final ColorStateList oceanColor = ResourcesCompat.getColorStateList( |
| res, R.color.color_state_list_ocean, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintList(oceanColor)); |
| verifyImageSourceIsColoredAs("New ocean tinting in enabled state", view, |
| oceanDefault, 0); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("New ocean tinting in disabled state", view, |
| oceanDisabled, 0); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("New ocean tinting in re-enabled state", view, |
| oceanDefault, 0); |
| } |
| |
| /** |
| * This method tests that image tinting is applied to tintable image view |
| * in enabled and disabled state across the same image source respects the currently set |
| * image source tinting mode. |
| */ |
| @Test |
| @SmallTest |
| public void testImageTintingAcrossModeChange() { |
| final @IdRes int viewId = R.id.view_untinted_source; |
| final Resources res = getActivity().getResources(); |
| final T view = (T) mContainer.findViewById(viewId); |
| |
| @ColorInt int emeraldDefault = ResourcesCompat.getColor( |
| res, R.color.emerald_translucent_default, null); |
| @ColorInt int emeraldDisabled = ResourcesCompat.getColor( |
| res, R.color.emerald_translucent_disabled, null); |
| // This is the fill color of R.drawable.test_drawable_blue set on our view |
| // that we'll be testing in this method |
| @ColorInt int sourceColor = ResourcesCompat.getColor( |
| res, R.color.test_blue, null); |
| |
| // Test the default state for tinting set up in the layout XML file. |
| verifyImageSourceIsColoredAs("Default no tinting in enabled state", view, |
| sourceColor, 0); |
| |
| // From this point on in this method we're allowing a margin of error in checking the |
| // color of the image source. This is due to both translucent colors being used |
| // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing |
| // translucent color on top of solid fill color. This is where the allowed variance |
| // value of 2 comes from - one for compositing and one for color translucency. |
| final int allowedComponentVariance = 2; |
| |
| // Set src_in tint mode on our view |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintMode(PorterDuff.Mode.SRC_IN)); |
| |
| // Load a new color state list, set it on the view and check that the image has |
| // switched to the matching entry in newly set color state list. |
| final ColorStateList emeraldColor = ResourcesCompat.getColorStateList( |
| res, R.color.color_state_list_emerald_translucent, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintList(emeraldColor)); |
| verifyImageSourceIsColoredAs("New emerald tinting in enabled state under src_in", view, |
| emeraldDefault, allowedComponentVariance); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("New emerald tinting in disabled state under src_in", view, |
| emeraldDisabled, allowedComponentVariance); |
| |
| // Set src_over tint mode on our view. As the currently set tint list is using |
| // translucent colors, we expect the actual image source of the view to be different under |
| // this new mode (unlike src_in and src_over that behave identically when the destination is |
| // a fully filled rectangle and the source is an opaque color). |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintMode(PorterDuff.Mode.SRC_OVER)); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("New emerald tinting in enabled state under src_over", view, |
| ColorUtils.compositeColors(emeraldDefault, sourceColor), |
| allowedComponentVariance); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the newly set color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("New emerald tinting in disabled state under src_over", |
| view, ColorUtils.compositeColors(emeraldDisabled, sourceColor), |
| allowedComponentVariance); |
| } |
| |
| /** |
| * This method tests that opaque tinting applied to tintable image source |
| * is applied correctly after changing the image source itself. |
| */ |
| @Test |
| @SmallTest |
| public void testImageOpaqueTintingAcrossImageChange() { |
| final @IdRes int viewId = R.id.view_tinted_no_source; |
| final Resources res = getActivity().getResources(); |
| final T view = (T) mContainer.findViewById(viewId); |
| |
| @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null); |
| @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null); |
| |
| // Set image source on our view |
| onView(withId(viewId)).perform(AppCompatTintableViewActions.setImageResource( |
| R.drawable.test_drawable_green)); |
| |
| // Test the default state for tinting set up in the layout XML file. |
| verifyImageSourceIsColoredAs("Default lilac tinting in enabled state on green source", |
| view, lilacDefault, 0); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in disabled state on green source", |
| view, lilacDisabled, 0); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in re-enabled state on green source", |
| view, lilacDefault, 0); |
| |
| // Set a different image source on our view based on resource ID |
| onView(withId(viewId)).perform(AppCompatTintableViewActions.setImageResource( |
| R.drawable.test_drawable_red)); |
| |
| // Test the default state for tinting set up in the layout XML file. |
| verifyImageSourceIsColoredAs("Default lilac tinting in enabled state on red source", |
| view, lilacDefault, 0); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in disabled state on red source", |
| view, lilacDisabled, 0); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("Default lilac tinting in re-enabled state on red source", |
| view, lilacDefault, 0); |
| } |
| |
| /** |
| * This method tests that translucent tinting applied to tintable image source |
| * is applied correctly after changing the image source itself. |
| */ |
| @Test |
| @SmallTest |
| public void testImageTranslucentTintingAcrossImageChange() { |
| final @IdRes int viewId = R.id.view_untinted_no_source; |
| final Resources res = getActivity().getResources(); |
| final T view = (T) mContainer.findViewById(viewId); |
| |
| @ColorInt int emeraldDefault = ResourcesCompat.getColor( |
| res, R.color.emerald_translucent_default, null); |
| @ColorInt int emeraldDisabled = ResourcesCompat.getColor( |
| res, R.color.emerald_translucent_disabled, null); |
| // This is the fill color of R.drawable.test_drawable_green that will be set on our view |
| // that we'll be testing in this method |
| @ColorInt int colorGreen = ResourcesCompat.getColor( |
| res, R.color.test_green, null); |
| // This is the fill color of R.drawable.test_drawable_red that will be set on our view |
| // that we'll be testing in this method |
| @ColorInt int colorRed = ResourcesCompat.getColor( |
| res, R.color.test_red, null); |
| |
| // Set src_over tint mode on our view. As the currently set tint list is using |
| // translucent colors, we expect the actual image source of the view to be different under |
| // this new mode (unlike src_in and src_over that behave identically when the destination is |
| // a fully filled rectangle and the source is an opaque color). |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintMode(PorterDuff.Mode.SRC_OVER)); |
| // Load and set a translucent color state list as the image source tint list |
| final ColorStateList emeraldColor = ResourcesCompat.getColorStateList( |
| res, R.color.color_state_list_emerald_translucent, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintList(emeraldColor)); |
| |
| // Set image source on our view |
| onView(withId(viewId)).perform(AppCompatTintableViewActions.setImageResource( |
| R.drawable.test_drawable_green)); |
| |
| // From this point on in this method we're allowing a margin of error in checking the |
| // color of the image source. This is due to both translucent colors being used |
| // in the color state list and off-by-one discrepancies of SRC_OVER when it's compositing |
| // translucent color on top of solid fill color. This is where the allowed variance |
| // value of 2 comes from - one for compositing and one for color translucency. |
| final int allowedComponentVariance = 2; |
| |
| // Test the default state for tinting set up with the just loaded tint list. |
| verifyImageSourceIsColoredAs("Emerald tinting in enabled state on green source", |
| view, ColorUtils.compositeColors(emeraldDefault, colorGreen), |
| allowedComponentVariance); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("Emerald tinting in disabled state on green source", |
| view, ColorUtils.compositeColors(emeraldDisabled, colorGreen), |
| allowedComponentVariance); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in the default color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("Emerald tinting in re-enabled state on green source", |
| view, ColorUtils.compositeColors(emeraldDefault, colorGreen), |
| allowedComponentVariance); |
| |
| // Set a different image source on our view based on resource ID |
| onView(withId(viewId)).perform(AppCompatTintableViewActions.setImageResource( |
| R.drawable.test_drawable_red)); |
| |
| // Test the default state for tinting the new image with the same color state list |
| verifyImageSourceIsColoredAs("Emerald tinting in enabled state on red source", |
| view, ColorUtils.compositeColors(emeraldDefault, colorRed), |
| allowedComponentVariance); |
| |
| // Disable the view and check that the image has switched to the matching entry |
| // in our current color state list. |
| onView(withId(viewId)).perform(setEnabled(false)); |
| verifyImageSourceIsColoredAs("Emerald tinting in disabled state on red source", |
| view, ColorUtils.compositeColors(emeraldDisabled, colorRed), |
| allowedComponentVariance); |
| |
| // Enable the view and check that the image has switched to the matching entry |
| // in our current color state list. |
| onView(withId(viewId)).perform(setEnabled(true)); |
| verifyImageSourceIsColoredAs("Emerald tinting in re-enabled state on red source", |
| view, ColorUtils.compositeColors(emeraldDefault, colorRed), |
| allowedComponentVariance); |
| } |
| |
| /** |
| * This method tests that background tinting applied on a tintable image view does not |
| * affect the tinting of the image source. |
| */ |
| @Test |
| @SmallTest |
| public void testImageTintingAcrossBackgroundTintingChange() { |
| final @IdRes int viewId = R.id.view_untinted_source; |
| final Resources res = getActivity().getResources(); |
| final T view = (T) mContainer.findViewById(viewId); |
| |
| @ColorInt int lilacDefault = ResourcesCompat.getColor(res, R.color.lilac_default, null); |
| @ColorInt int lilacDisabled = ResourcesCompat.getColor(res, R.color.lilac_disabled, null); |
| // This is the fill color of R.drawable.test_drawable_blue set on our view |
| // that we'll be testing in this method |
| @ColorInt int sourceColor = ResourcesCompat.getColor( |
| res, R.color.test_blue, null); |
| @ColorInt int newSourceColor = ResourcesCompat.getColor( |
| res, R.color.test_red, null); |
| |
| // Test the default state for tinting set up in the layout XML file. |
| verifyImageSourceIsColoredAs("Default no tinting in enabled state", view, |
| sourceColor, 0); |
| |
| // Change background tinting of our image |
| final ColorStateList lilacColor = ResourcesCompat.getColorStateList( |
| mResources, R.color.color_state_list_lilac, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setBackgroundResource( |
| R.drawable.test_background_green)); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setBackgroundTintMode(PorterDuff.Mode.SRC_IN)); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setBackgroundTintList(lilacColor)); |
| |
| // Verify that the image still has the original color (untinted) |
| verifyImageSourceIsColoredAs("No image tinting after change in background tinting", view, |
| sourceColor, 0); |
| |
| // Now set a different image source |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageResource(R.drawable.test_drawable_red)); |
| // And verify that the image has the new color (untinted) |
| verifyImageSourceIsColoredAs("No image tinting after change of image source", view, |
| newSourceColor, 0); |
| |
| // Change the background tinting again |
| final ColorStateList sandColor = ResourcesCompat.getColorStateList( |
| mResources, R.color.color_state_list_sand, null); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setBackgroundTintList(sandColor)); |
| // And verify that the image still has the same new color (untinted) |
| verifyImageSourceIsColoredAs("No image tinting after change in background tinting", view, |
| newSourceColor, 0); |
| |
| // Now set up image tinting on our view. We're using a color state list with fully |
| // opaque colors, and we expect the matching entry in that list to be applied on the |
| // image source (ignoring the background tinting) |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintMode(PorterDuff.Mode.SRC_IN)); |
| onView(withId(viewId)).perform( |
| AppCompatTintableViewActions.setImageSourceTintList(lilacColor)); |
| verifyImageSourceIsColoredAs("New lilac image tinting", view, |
| lilacDefault, 0); |
| } |
| } |