Skip to content

Commit

Permalink
[TextInputLayout] Fixed icons behaving unexpectedly when switching mo…
Browse files Browse the repository at this point in the history
…des + bug of wrong icon tint state when setting an icon programmatically.

Resolves #503
Resolves #1849

PiperOrigin-RevId: 427268349
  • Loading branch information
leticiarossi authored and raajkumars committed Feb 9, 2022
1 parent b0f3700 commit ae16efd
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 7 deletions.
Expand Up @@ -71,10 +71,9 @@ public void onFocusChange(View v, boolean hasFocus) {
public void onEditTextAttached(@NonNull TextInputLayout textInputLayout) {
EditText editText = textInputLayout.getEditText();
textInputLayout.setEndIconVisible(shouldBeVisible());
// Make sure there's always only one clear text text watcher added
textInputLayout.setEndIconCheckable(false);
editText.setOnFocusChangeListener(onFocusChangeListener);
endIconView.setOnFocusChangeListener(onFocusChangeListener);
// Make sure there's always only one clear text text watcher added
editText.removeTextChangedListener(clearTextEndIconTextWatcher);
editText.addTextChangedListener(clearTextEndIconTextWatcher);
}
Expand All @@ -91,6 +90,8 @@ public void onEndIconChanged(@NonNull TextInputLayout textInputLayout, int previ
@Override
public void run() {
editText.removeTextChangedListener(clearTextEndIconTextWatcher);
// Make sure icon view is visible.
animateIcon(/* show= */ true);
}
});
if (editText.getOnFocusChangeListener() == onFocusChangeListener) {
Expand All @@ -99,7 +100,6 @@ public void run() {
if (endIconView.getOnFocusChangeListener() == onFocusChangeListener) {
endIconView.setOnFocusChangeListener(null);
}

}
}
};
Expand All @@ -118,6 +118,7 @@ void initialize() {
customEndIcon == 0 ? R.drawable.mtrl_ic_cancel : customEndIcon);
textInputLayout.setEndIconContentDescription(
textInputLayout.getResources().getText(R.string.clear_text_end_icon_content_description));
textInputLayout.setEndIconCheckable(false);
textInputLayout.setEndIconOnClickListener(
new OnClickListener() {
@Override
Expand Down
Expand Up @@ -48,8 +48,6 @@ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override
public void onEditTextAttached(@NonNull TextInputLayout textInputLayout) {
EditText editText = textInputLayout.getEditText();
textInputLayout.setEndIconVisible(true);
textInputLayout.setEndIconCheckable(true);
endIconView.setChecked(!hasPasswordTransformation());
// Make sure there's always only one password toggle text watcher added
editText.removeTextChangedListener(textWatcher);
Expand Down Expand Up @@ -88,6 +86,8 @@ void initialize() {
customEndIcon == 0 ? R.drawable.design_password_eye : customEndIcon);
textInputLayout.setEndIconContentDescription(
textInputLayout.getResources().getText(R.string.password_toggle_content_description));
textInputLayout.setEndIconVisible(true);
textInputLayout.setEndIconCheckable(true);
textInputLayout.setEndIconOnClickListener(
new OnClickListener() {
@Override
Expand Down
Expand Up @@ -3455,6 +3455,9 @@ public void setStartIconTintMode(@Nullable PorterDuff.Mode startIconTintMode) {
* @attr ref com.google.android.material.R.styleable#TextInputLayout_endIconMode
*/
public void setEndIconMode(@EndIconMode int endIconMode) {
if (this.endIconMode == endIconMode) {
return;
}
int previousEndIconMode = this.endIconMode;
this.endIconMode = endIconMode;
dispatchOnEndIconChanged(previousEndIconMode);
Expand Down Expand Up @@ -4133,7 +4136,14 @@ private void applyIconTint(
Drawable icon = iconView.getDrawable();
if (icon != null) {
icon = DrawableCompat.wrap(icon).mutate();
DrawableCompat.setTintList(icon, iconTintList);
if (iconTintList != null && iconTintList.isStateful()) {
// Make sure the right color for the current state is applied.
int color =
iconTintList.getColorForState(mergeIconState(iconView), iconTintList.getDefaultColor());
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
} else {
DrawableCompat.setTintList(icon, iconTintList);
}
if (iconTintMode != null) {
DrawableCompat.setTintMode(icon, iconTintMode);
}
Expand Down
Expand Up @@ -58,6 +58,7 @@
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
Expand Down Expand Up @@ -219,7 +220,7 @@ public void testPasswordToggleIsCheckedIfTransformationMethodNotPassword() {
onView(withId(R.id.textinput_no_icon))
.perform(setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE));

// Assert password toggle is not checked.
// Assert password toggle is checked.
onView(withId(R.id.textinput_no_icon)).check(matches(endIconIsChecked()));
}

Expand Down Expand Up @@ -382,6 +383,7 @@ public void testSwitchEndIconFromPasswordToggleToClearText() {
assertEquals(TextInputLayout.END_ICON_CLEAR_TEXT, textInputLayoutPassword.getEndIconMode());
assertEquals(
clearTextContentDesc, textInputLayoutPassword.getEndIconContentDescription().toString());
assertFalse(textInputLayoutPassword.isEndIconCheckable());
// Assert the clear button is not displayed as there was no text
onView(withId(R.id.textinput_password))
.check(matches(doesNotShowEndIcon()));
Expand All @@ -396,6 +398,36 @@ public void testSwitchEndIconFromPasswordToggleToClearText() {
assertEquals(0, textInputLayoutPassword.getEditText().getText().length());
}

@Test
public void testSwitchEndIcon_clearTextToPasswordToggle_succeeds() {
final Activity activity = activityTestRule.getActivity();
final EditText editTextClearIcon = activity.findViewById(R.id.textinput_edittext_clear);
final TextInputLayout textFieldClearIcon = activity.findViewById(R.id.textinput_clear);
final TextInputLayout textFieldPasswordIcon = activity.findViewById(R.id.textinput_password);
String passwordToggleContentDesc =
textFieldPasswordIcon.getEndIconContentDescription().toString();

// Set end icon as the password toggle on text field that has the clear text icon set
onView(withId(R.id.textinput_clear))
.perform(setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE));

// Assert the end icon mode is the password toggle icon
assertEquals(TextInputLayout.END_ICON_PASSWORD_TOGGLE, textFieldClearIcon.getEndIconMode());
assertEquals(
passwordToggleContentDesc, textFieldClearIcon.getEndIconContentDescription().toString());
assertTrue(textFieldClearIcon.isEndIconCheckable());
// Assert icon is displayed
onView(withId(R.id.textinput_clear)).check(matches(showsEndIcon()));
// Assert icon is checked since edit text's transformation method isn't password
onView(withId(R.id.textinput_clear)).check(matches(endIconIsChecked()));
// Set some text on the edit text
onView(withId(R.id.textinput_edittext_clear)).perform(typeText(INPUT_TEXT));
// Assert icon disguises text once clicked
onView(withId(R.id.textinput_clear)).perform(clickIcon(true));
onView(withId(R.id.textinput_clear)).check(matches(not(endIconIsChecked())));
assertNotEquals(INPUT_TEXT, editTextClearIcon.getLayout().getText().toString());
}

@Test
public void testSetCustomEndIconProgrammatically() {
final Activity activity = activityTestRule.getActivity();
Expand Down

0 comments on commit ae16efd

Please sign in to comment.