| /* |
| * Copyright (C) 2008 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.widget.cts; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.Manifest; |
| import android.app.Activity; |
| import android.app.Instrumentation; |
| import android.content.Context; |
| import android.content.res.ColorStateList; |
| import android.graphics.BlendMode; |
| import android.graphics.Canvas; |
| import android.graphics.Color; |
| import android.graphics.PorterDuff; |
| import android.graphics.drawable.Drawable; |
| import android.os.Parcelable; |
| import android.util.AttributeSet; |
| import android.util.StateSet; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.BaseAdapter; |
| import android.widget.CheckedTextView; |
| import android.widget.ListAdapter; |
| import android.widget.ListView; |
| import android.widget.cts.util.TestUtils; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.annotation.UiThreadTest; |
| import androidx.test.filters.SmallTest; |
| import androidx.test.rule.ActivityTestRule; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.AdoptShellPermissionsRule; |
| import com.android.compatibility.common.util.WidgetTestUtils; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.Arrays; |
| |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class CheckedTextViewTest { |
| private Instrumentation mInstrumentation; |
| private Activity mActivity; |
| private ListView mListView; |
| private CheckedTextView mCheckedTextView; |
| |
| @Rule(order = 0) |
| public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( |
| androidx.test.platform.app.InstrumentationRegistry |
| .getInstrumentation().getUiAutomation(), |
| Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); |
| |
| @Rule(order = 1) |
| public ActivityTestRule<CheckedTextViewCtsActivity> mActivityRule = |
| new ActivityTestRule<>(CheckedTextViewCtsActivity.class); |
| |
| @Before |
| public void setup() { |
| mInstrumentation = InstrumentationRegistry.getInstrumentation(); |
| mActivity = mActivityRule.getActivity(); |
| mListView = (ListView) mActivity.findViewById(R.id.checkedtextview_listview); |
| mCheckedTextView = (CheckedTextView) mActivity.findViewById(R.id.checkedtextview_test); |
| } |
| |
| @Test |
| public void testConstructor() { |
| new CheckedTextView(mActivity); |
| new CheckedTextView(mActivity, null); |
| new CheckedTextView(mActivity, null, android.R.attr.checkedTextViewStyle); |
| new CheckedTextView(mActivity, null, 0, |
| android.R.style.Widget_DeviceDefault_CheckedTextView); |
| new CheckedTextView(mActivity, null, 0, |
| android.R.style.Widget_DeviceDefault_Light_CheckedTextView); |
| new CheckedTextView(mActivity, null, 0, |
| android.R.style.Widget_Material_CheckedTextView); |
| new CheckedTextView(mActivity, null, 0, |
| android.R.style.Widget_Material_Light_CheckedTextView); |
| } |
| |
| @Test(expected=NullPointerException.class) |
| public void testConstructorWithNullContext1() { |
| new CheckedTextView(null); |
| } |
| |
| @Test(expected=NullPointerException.class) |
| public void testConstructorWithNullContext2() { |
| new CheckedTextView(null, null); |
| } |
| |
| @Test(expected=NullPointerException.class) |
| public void testConstructorWithNullContext3() { |
| new CheckedTextView(null, null, -1); |
| } |
| |
| @Test |
| public void testChecked() throws Throwable { |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> { |
| mListView.setAdapter(new CheckedTextViewAdapter()); |
| |
| mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
| mListView.setItemChecked(1, true); |
| }); |
| |
| assertEquals(1, mListView.getCheckedItemPosition()); |
| assertTrue(mListView.isItemChecked(1)); |
| assertFalse(mListView.isItemChecked(0)); |
| |
| final ListAdapter adapter = mListView.getAdapter(); |
| final CheckedTextView view0 = (CheckedTextView) adapter.getView(0, null, null); |
| final CheckedTextView view1 = (CheckedTextView) adapter.getView(1, null, null); |
| final CheckedTextView view2 = (CheckedTextView) adapter.getView(2, null, null); |
| assertFalse(view0.isChecked()); |
| assertTrue(view1.isChecked()); |
| assertFalse(view2.isChecked()); |
| |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> { |
| mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); |
| mListView.setItemChecked(2, true); |
| }); |
| assertFalse(view0.isChecked()); |
| assertTrue(view1.isChecked()); |
| assertTrue(view2.isChecked()); |
| |
| mActivityRule.runOnUiThread(() -> { |
| view0.setChecked(true); |
| view1.setChecked(false); |
| view2.setChecked(false); |
| }); |
| assertTrue(view0.isChecked()); |
| assertFalse(view1.isChecked()); |
| assertFalse(view2.isChecked()); |
| } |
| |
| @UiThreadTest |
| @Test |
| public void testToggle() { |
| assertFalse(mCheckedTextView.isChecked()); |
| |
| mCheckedTextView.toggle(); |
| assertTrue(mCheckedTextView.isChecked()); |
| |
| mCheckedTextView.toggle(); |
| assertFalse(mCheckedTextView.isChecked()); |
| |
| mCheckedTextView.setChecked(true); |
| mCheckedTextView.toggle(); |
| assertFalse(mCheckedTextView.isChecked()); |
| } |
| |
| @Test |
| public void testDrawableStateChanged() { |
| MockCheckedTextView checkedTextView = new MockCheckedTextView(mActivity); |
| |
| assertFalse(checkedTextView.hasDrawableStateChanged()); |
| checkedTextView.refreshDrawableState(); |
| assertTrue(checkedTextView.hasDrawableStateChanged()); |
| } |
| |
| @Test |
| public void testSetPadding() throws Throwable { |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> { |
| mListView.setPadding(1, 2, 3, 4); |
| mListView.requestLayout(); |
| }); |
| |
| final int origTop = mListView.getPaddingTop(); |
| final int origBottom = mListView.getPaddingBottom(); |
| final int origLeft = mListView.getPaddingLeft(); |
| final int origRight = mListView.getPaddingRight(); |
| |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mListView, () -> { |
| mListView.setPadding(10, 20, 30, 40); |
| mListView.requestLayout(); |
| }); |
| |
| assertTrue(origTop < mListView.getPaddingTop()); |
| assertTrue(origBottom < mListView.getPaddingBottom()); |
| assertTrue(origLeft < mListView.getPaddingLeft()); |
| assertTrue(origRight < mListView.getPaddingRight()); |
| } |
| |
| private void cleanUpForceLayoutFlags(View view) { |
| if (view != null) { |
| view.layout(0, 0, 0, 0); |
| assertFalse(view.isLayoutRequested()); |
| } |
| } |
| |
| @UiThreadTest |
| @Test |
| public void testSetCheckMarkDrawableByDrawable() { |
| int basePaddingRight = 10; |
| |
| // set drawable when checkedTextView is GONE |
| mCheckedTextView.setVisibility(View.GONE); |
| final Drawable firstDrawable = mActivity.getDrawable(R.drawable.scenery); |
| firstDrawable.setVisible(true, false); |
| assertEquals(StateSet.WILD_CARD, firstDrawable.getState()); |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| |
| mCheckedTextView.setCheckMarkDrawable(firstDrawable); |
| assertEquals(firstDrawable.getIntrinsicWidth(), mCheckedTextView.getPaddingRight()); |
| assertFalse(firstDrawable.isVisible()); |
| assertTrue(Arrays.equals(mCheckedTextView.getDrawableState(), firstDrawable.getState())); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| |
| mCheckedTextView.setCheckMarkDrawable(null); |
| |
| // update drawable when checkedTextView is VISIBLE |
| mCheckedTextView.setVisibility(View.VISIBLE); |
| mCheckedTextView.setPadding(0, 0, basePaddingRight, 0); |
| final Drawable secondDrawable = mActivity.getDrawable(R.drawable.pass); |
| secondDrawable.setVisible(true, false); |
| assertEquals(StateSet.WILD_CARD, secondDrawable.getState()); |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| |
| mCheckedTextView.setCheckMarkDrawable(secondDrawable); |
| assertEquals(secondDrawable.getIntrinsicWidth() + basePaddingRight, |
| mCheckedTextView.getPaddingRight()); |
| assertTrue(secondDrawable.isVisible()); |
| assertTrue(Arrays.equals(mCheckedTextView.getDrawableState(), secondDrawable.getState())); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| mCheckedTextView.setCheckMarkDrawable(null); |
| assertEquals(basePaddingRight, mCheckedTextView.getPaddingRight()); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| } |
| |
| @UiThreadTest |
| @Test |
| public void testSetCheckMarkDrawableById() { |
| int basePaddingRight = 10; |
| |
| // set drawable |
| mCheckedTextView.setPadding(0, 0, basePaddingRight, 0); |
| Drawable firstDrawable = mActivity.getDrawable(R.drawable.scenery); |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| |
| mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery); |
| assertEquals(firstDrawable.getIntrinsicWidth() + basePaddingRight, |
| mCheckedTextView.getPaddingRight()); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| |
| // set the same drawable again |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery); |
| assertEquals(firstDrawable.getIntrinsicWidth() + basePaddingRight, |
| mCheckedTextView.getPaddingRight()); |
| assertFalse(mCheckedTextView.isLayoutRequested()); |
| |
| // update drawable |
| final Drawable secondDrawable = mActivity.getDrawable(R.drawable.pass); |
| mCheckedTextView.setCheckMarkDrawable(secondDrawable); |
| assertEquals(secondDrawable.getIntrinsicWidth() + basePaddingRight, |
| mCheckedTextView.getPaddingRight()); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| |
| mCheckedTextView.setCheckMarkDrawable(null); |
| |
| // resId is 0 |
| mCheckedTextView.setPadding(0, 0, basePaddingRight, 0); |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| |
| mCheckedTextView.setCheckMarkDrawable(0); |
| assertEquals(basePaddingRight, mCheckedTextView.getPaddingRight()); |
| assertFalse(mCheckedTextView.isLayoutRequested()); |
| } |
| |
| @UiThreadTest |
| @Test |
| public void testSetCheckMarkByMixedTypes() { |
| cleanUpForceLayoutFlags(mCheckedTextView); |
| |
| // Specifically test for b/22626247 (AOSP issue 180455). |
| mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery); |
| mCheckedTextView.setCheckMarkDrawable(null); |
| mCheckedTextView.setCheckMarkDrawable(R.drawable.scenery); |
| assertNotNull(mCheckedTextView.getCheckMarkDrawable()); |
| } |
| |
| @UiThreadTest |
| @Test |
| public void testAccessInstanceState() { |
| assertFalse(mCheckedTextView.isChecked()); |
| assertFalse(mCheckedTextView.getFreezesText()); |
| |
| final Parcelable state = mCheckedTextView.onSaveInstanceState(); |
| assertNotNull(state); |
| assertFalse(mCheckedTextView.getFreezesText()); |
| |
| mCheckedTextView.setChecked(true); |
| |
| mCheckedTextView.onRestoreInstanceState(state); |
| assertFalse(mCheckedTextView.isChecked()); |
| assertTrue(mCheckedTextView.isLayoutRequested()); |
| } |
| |
| @Test |
| public void testCheckMarkTinting() throws Throwable { |
| mActivityRule.runOnUiThread(() -> mCheckedTextView.setChecked(true)); |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_red)); |
| |
| Drawable checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Initial state is red", checkMark, |
| checkMark.getBounds().width(), checkMark.getBounds().height(), false, |
| Color.RED, 1, true); |
| |
| // With SRC_IN we're expecting the translucent tint color to "take over" the |
| // original red checkmark. |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, () -> { |
| mCheckedTextView.setCheckMarkTintMode(PorterDuff.Mode.SRC_IN); |
| mCheckedTextView.setCheckMarkTintList(ColorStateList.valueOf(0x8000FF00)); |
| }); |
| |
| assertEquals(PorterDuff.Mode.SRC_IN, mCheckedTextView.getCheckMarkTintMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| 0x8000FF00, 1, true); |
| |
| // With SRC_OVER we're expecting the translucent tint color to be drawn on top |
| // of the original red checkmark, creating a composite color fill as the result. |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkTintMode(PorterDuff.Mode.SRC_OVER)); |
| |
| assertEquals(PorterDuff.Mode.SRC_OVER, mCheckedTextView.getCheckMarkTintMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green over full red", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| TestUtils.compositeColors(0x8000FF00, Color.RED), 1, true); |
| |
| // Switch to a different color for the underlying checkmark and verify that the |
| // currently configured tinting (50% green overlay) is still respected |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_yellow)); |
| assertEquals(PorterDuff.Mode.SRC_OVER, mCheckedTextView.getCheckMarkTintMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green over full yellow", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| TestUtils.compositeColors(0x8000FF00, Color.YELLOW), 1, true); |
| } |
| |
| @Test |
| public void testCheckMarkTintBlendMode() throws Throwable { |
| mActivityRule.runOnUiThread(() -> mCheckedTextView.setChecked(true)); |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_red)); |
| |
| Drawable checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Initial state is red", checkMark, |
| checkMark.getBounds().width(), checkMark.getBounds().height(), false, |
| Color.RED, 1, true); |
| |
| // With SRC_IN we're expecting the translucent tint color to "take over" the |
| // original red checkmark. |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, () -> { |
| mCheckedTextView.setCheckMarkTintBlendMode(BlendMode.SRC_IN); |
| mCheckedTextView.setCheckMarkTintList(ColorStateList.valueOf(0x8000FF00)); |
| }); |
| |
| assertEquals(BlendMode.SRC_IN, mCheckedTextView.getCheckMarkTintBlendMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| 0x8000FF00, 1, true); |
| |
| // With SRC_OVER we're expecting the translucent tint color to be drawn on top |
| // of the original red checkmark, creating a composite color fill as the result. |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkTintBlendMode(BlendMode.SRC_OVER)); |
| |
| assertEquals(BlendMode.SRC_OVER, mCheckedTextView.getCheckMarkTintBlendMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green over full red", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| TestUtils.compositeColors(0x8000FF00, Color.RED), 1, true); |
| |
| // Switch to a different color for the underlying checkmark and verify that the |
| // currently configured tinting (50% green overlay) is still respected |
| WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mCheckedTextView, |
| () -> mCheckedTextView.setCheckMarkDrawable(R.drawable.icon_yellow)); |
| assertEquals(BlendMode.SRC_OVER, mCheckedTextView.getCheckMarkTintBlendMode()); |
| assertEquals(0x8000FF00, mCheckedTextView.getCheckMarkTintList().getDefaultColor()); |
| checkMark = mCheckedTextView.getCheckMarkDrawable(); |
| TestUtils.assertAllPixelsOfColor("Expected 50% green over full yellow", checkMark, |
| checkMark.getIntrinsicWidth(), checkMark.getIntrinsicHeight(), false, |
| TestUtils.compositeColors(0x8000FF00, Color.YELLOW), 1, true); |
| } |
| |
| private static final class MockCheckedTextView extends CheckedTextView { |
| private boolean mHasDrawableStateChanged = false; |
| |
| public MockCheckedTextView(Context context) { |
| super(context); |
| } |
| |
| public MockCheckedTextView(Context context, AttributeSet attrs) { |
| super(context, attrs, 0); |
| } |
| |
| public MockCheckedTextView(Context context, AttributeSet attrs, int defStyle) { |
| super(context, attrs, defStyle); |
| } |
| |
| @Override |
| protected void drawableStateChanged() { |
| super.drawableStateChanged(); |
| mHasDrawableStateChanged = true; |
| } |
| |
| @Override |
| protected int[] onCreateDrawableState(int extraSpace) { |
| return super.onCreateDrawableState(extraSpace); |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| super.onDraw(canvas); |
| } |
| |
| public boolean hasDrawableStateChanged() { |
| return mHasDrawableStateChanged; |
| } |
| } |
| |
| private class CheckedTextViewAdapter extends BaseAdapter { |
| private CheckedTextView[] mCheckedTextViews = new CheckedTextView[]{ |
| new MockCheckedTextView(mActivity), |
| new MockCheckedTextView(mActivity), |
| new MockCheckedTextView(mActivity), |
| }; |
| |
| public int getCount() { |
| return mCheckedTextViews.length; |
| } |
| |
| public Object getItem(int position) { |
| return position; |
| } |
| |
| public long getItemId(int position) { |
| return position; |
| } |
| |
| public View getView(int position, View convertView, ViewGroup parent) { |
| return mCheckedTextViews[position]; |
| } |
| } |
| } |