Skip to content

Commit

Permalink
Changing how password toggle end icon updates its checked state.
Browse files Browse the repository at this point in the history
Now it will automatically change to checked or not checked depending if the EditText's transformation method is changed.

PiperOrigin-RevId: 248185643
  • Loading branch information
leticiarossi committed May 16, 2019
1 parent 462150b commit 5f8f604
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 21 deletions.
Expand Up @@ -21,24 +21,45 @@
import com.google.android.material.textfield.TextInputLayout.OnEditTextAttachedListener;
import com.google.android.material.textfield.TextInputLayout.OnEndIconChangedListener;
import androidx.appcompat.content.res.AppCompatResources;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;

/**
* Default initialization of the password toggle end icon.
*/
/** Default initialization of the password toggle end icon. */
class PasswordToggleEndIconDelegate extends EndIconDelegate {

private final OnEditTextAttachedListener passwordToggleOnEditTextAttachedListener =
private final TextWatcher textWatcher =
new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Make sure the password toggle state always matches the EditText's transformation
// method.
endIconView.setChecked(!hasPasswordTransformation());
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}

@Override
public void afterTextChanged(Editable s) {}
};

private final OnEditTextAttachedListener onEditTextAttachedListener =
new OnEditTextAttachedListener() {
@Override
public void onEditTextAttached() {
textInputLayout.setEndIconVisible(hasPasswordTransformation());
EditText editText = textInputLayout.getEditText();
textInputLayout.setEndIconVisible(true);
endIconView.setChecked(!hasPasswordTransformation());
// Make sure there's always only one password toggle text watcher added
editText.removeTextChangedListener(textWatcher);
editText.addTextChangedListener(textWatcher);
}
};
private final OnEndIconChangedListener passwordToggleEndIconChangedListener =
private final OnEndIconChangedListener onEndIconChangedListener =
new OnEndIconChangedListener() {
@Override
public void onEndIconChanged(int previousIcon) {
Expand Down Expand Up @@ -73,17 +94,15 @@ public void onClick(View v) {
final int selection = editText.getSelectionEnd();
if (hasPasswordTransformation()) {
editText.setTransformationMethod(null);
endIconView.setChecked(true);
} else {
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
endIconView.setChecked(false);
}
// And restore the cursor position
editText.setSelection(selection);
}
});
textInputLayout.addOnEditTextAttachedListener(passwordToggleOnEditTextAttachedListener);
textInputLayout.addOnEndIconChangedListener(passwordToggleEndIconChangedListener);
textInputLayout.addOnEditTextAttachedListener(onEditTextAttachedListener);
textInputLayout.addOnEndIconChangedListener(onEndIconChangedListener);
}

private boolean hasPasswordTransformation() {
Expand Down
Expand Up @@ -2623,7 +2623,9 @@ public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode)
*
* @param shouldSkipAnimations true if the password toggle indicator icon should not animate
* changes
* @deprecated Use {@link #setEndIconOnClickListener(OnClickListener)} instead.
* @deprecated The password toggle will show as checked or unchecked depending on whether the
* {@link EditText}'s {@link android.text.method.TransformationMethod} is of type {@link
* android.text.method.PasswordTransformationMethod}
*/
@Deprecated
public void passwordVisibilityToggleRequested(boolean shouldSkipAnimations) {
Expand Down
Expand Up @@ -28,11 +28,10 @@
import com.google.android.material.internal.CheckableImageButton;
import com.google.android.material.testapp.R;
import com.google.android.material.textfield.TextInputLayout;
import android.text.method.PasswordTransformationMethod;
import android.text.method.TransformationMethod;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
import androidx.test.espresso.matcher.ViewMatchers;
Expand Down Expand Up @@ -180,22 +179,25 @@ public void perform(UiController uiController, View view) {
};
}

public static ViewAction setInputTypeToPasswordTransformationMethod() {
/** Sets the transformation method. */
public static ViewAction setTransformationMethod(
final TransformationMethod transformationMethod) {
return new ViewAction() {

@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(EditText.class);
return ViewMatchers.isAssignableFrom(TextInputLayout.class);
}

@Override
public String getDescription() {
return "Set the input type of the EditText to be of password type";
return "Sets the transformation method";
}

@Override
public void perform(UiController uiController, View view) {
EditText edittext = (EditText) view;
edittext.setTransformationMethod(PasswordTransformationMethod.getInstance());
TextInputLayout item = (TextInputLayout) view;
item.getEditText().setTransformationMethod(transformationMethod);
}
};
}
Expand Down
Expand Up @@ -16,6 +16,7 @@

package com.google.android.material.testutils;

import com.google.android.material.internal.CheckableImageButton;
import com.google.android.material.testapp.R;
import com.google.android.material.textfield.TextInputLayout;
import android.text.TextUtils;
Expand Down Expand Up @@ -102,4 +103,40 @@ protected boolean matchesSafely(View item) {
}
};
}

/** Returns a matcher that matches TextInputLayouts with checked end icons. */
public static Matcher<View> endIconIsChecked() {
return new TypeSafeMatcher<View>(TextInputLayout.class) {
@Override
public void describeTo(Description description) {
description.appendText("TextInputLayout's end icon is checked.");
}

@Override
protected boolean matchesSafely(View item) {
// Reach in and find the start icon since we don't have a public API
// to get a reference to it
CheckableImageButton endIcon = item.findViewById(R.id.text_input_end_icon);
return endIcon.isChecked();
}
};
}

/** Returns a matcher that matches TextInputLayouts with non checked end icons. */
public static Matcher<View> endIconIsNotChecked() {
return new TypeSafeMatcher<View>(TextInputLayout.class) {
@Override
public void describeTo(Description description) {
description.appendText("TextInputLayout's end icon is not checked.");
}

@Override
protected boolean matchesSafely(View item) {
// Reach in and find the start icon since we don't have a public API
// to get a reference to it
CheckableImageButton endIcon = item.findViewById(R.id.text_input_end_icon);
return !endIcon.isChecked();
}
};
}
}
Expand Up @@ -21,14 +21,16 @@
import static com.google.android.material.testutils.TextInputLayoutActions.setCustomEndIconContent;
import static com.google.android.material.testutils.TextInputLayoutActions.setEndIconMode;
import static com.google.android.material.testutils.TextInputLayoutActions.setEndIconOnClickListener;
import static com.google.android.material.testutils.TextInputLayoutActions.setInputTypeToPasswordTransformationMethod;
import static com.google.android.material.testutils.TextInputLayoutActions.setStartIcon;
import static com.google.android.material.testutils.TextInputLayoutActions.setStartIconContentDescription;
import static com.google.android.material.testutils.TextInputLayoutActions.setStartIconOnClickListener;
import static com.google.android.material.testutils.TextInputLayoutActions.setStartIconTintList;
import static com.google.android.material.testutils.TextInputLayoutActions.setTransformationMethod;
import static com.google.android.material.testutils.TextInputLayoutMatchers.doesNotShowEndIcon;
import static com.google.android.material.testutils.TextInputLayoutMatchers.doesNotShowStartIcon;
import static com.google.android.material.testutils.TextInputLayoutMatchers.endIconHasContentDescription;
import static com.google.android.material.testutils.TextInputLayoutMatchers.endIconIsChecked;
import static com.google.android.material.testutils.TextInputLayoutMatchers.endIconIsNotChecked;
import static com.google.android.material.testutils.TextInputLayoutMatchers.showsEndIcon;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.espresso.Espresso.onView;
Expand Down Expand Up @@ -84,8 +86,8 @@ public class TextInputLayoutIconsTest {
@Test
public void testSetPasswordToggleProgrammatically() {
// Set edit text input type to be password
onView(withId(R.id.textinput_edittext_no_icon))
.perform(setInputTypeToPasswordTransformationMethod());
onView(withId(R.id.textinput_no_icon))
.perform(setTransformationMethod(PasswordTransformationMethod.getInstance()));
// Set end icon as the password toggle
onView(withId(R.id.textinput_no_icon))
.perform(setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE));
Expand Down Expand Up @@ -184,6 +186,28 @@ public void testPasswordToggleIsHiddenAfterReenable() {
onView(withId(R.id.textinput_password)).perform(clickIcon(true));
}

@Test
public void testPasswordToggleChangesWithTransformationMethod() {
// Assert password toggle is not checked.
onView(withId(R.id.textinput_password)).check(matches(endIconIsNotChecked()));

// Change the edit text transformation method to null.
onView(withId(R.id.textinput_password)).perform(setTransformationMethod(null));

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

@Test
public void testPasswordToggleIsCheckedIfTransformationMethodNotPassword() {
// Set end icon as the password toggle
onView(withId(R.id.textinput_no_icon))
.perform(setEndIconMode(TextInputLayout.END_ICON_PASSWORD_TOGGLE));

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

@Test
public void testPasswordToggleHasDefaultContentDescription() {
// Check that the TextInputLayout says that it has a content description and that the
Expand Down

0 comments on commit 5f8f604

Please sign in to comment.