blob: c8398ed896611b3015c7fe2657f54b6957902101 [file] [log] [blame]
/*
* 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);
}
}