Skip to content

Commit

Permalink
[CleanUp][TextField] Split icon tinting logic to a helper class
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 431477762
  • Loading branch information
drchen authored and hunterstich committed Mar 1, 2022
1 parent 3b2b56e commit 9789f2e
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 69 deletions.
95 changes: 95 additions & 0 deletions lib/java/com/google/android/material/textfield/IconTintHelper.java
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 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 com.google.android.material.textfield;

import android.content.res.ColorStateList;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.DrawableCompat;
import com.google.android.material.internal.CheckableImageButton;
import java.util.Arrays;

class IconTintHelper {
private IconTintHelper() {}

/**
* Applies the given icon tint according to the merged view state of the host text input layout
* and the icon view.
*/
static void applyIconTint(
@NonNull TextInputLayout textInputLayout,
@NonNull CheckableImageButton iconView,
ColorStateList iconTintList,
PorterDuff.Mode iconTintMode) {
Drawable icon = iconView.getDrawable();
if (icon != null) {
icon = DrawableCompat.wrap(icon).mutate();
if (iconTintList != null && iconTintList.isStateful()) {
// Make sure the right color for the current state is applied.
int color =
iconTintList.getColorForState(
mergeIconState(textInputLayout, iconView), iconTintList.getDefaultColor());
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
} else {
DrawableCompat.setTintList(icon, iconTintList);
}
if (iconTintMode != null) {
DrawableCompat.setTintMode(icon, iconTintMode);
}
}

if (iconView.getDrawable() != icon) {
iconView.setImageDrawable(icon);
}
}

/**
* Refresh the icon tint according to the new drawable state.
*/
static void refreshIconDrawableState(
@NonNull TextInputLayout textInputLayout,
@NonNull CheckableImageButton iconView,
ColorStateList colorStateList) {
Drawable icon = iconView.getDrawable();
if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) {
return;
}

int color =
colorStateList.getColorForState(
mergeIconState(textInputLayout, iconView), colorStateList.getDefaultColor());

icon = DrawableCompat.wrap(icon).mutate();
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
iconView.setImageDrawable(icon);
}

private static int[] mergeIconState(
@NonNull TextInputLayout textInputLayout,
@NonNull CheckableImageButton iconView) {
int[] textInputStates = textInputLayout.getDrawableState();
int[] iconStates = iconView.getDrawableState();

int index = textInputStates.length;
int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length);

System.arraycopy(iconStates, 0, states, index, iconStates.length);

return states;
}
}
87 changes: 18 additions & 69 deletions lib/java/com/google/android/material/textfield/TextInputLayout.java
Expand Up @@ -19,6 +19,8 @@
import com.google.android.material.R;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static com.google.android.material.textfield.IconTintHelper.applyIconTint;
import static com.google.android.material.textfield.IconTintHelper.refreshIconDrawableState;
import static com.google.android.material.textfield.IndicatorViewController.COUNTER_INDEX;
import static com.google.android.material.theme.overlay.MaterialThemeOverlay.wrap;

Expand Down Expand Up @@ -96,7 +98,6 @@
import com.google.android.material.shape.ShapeAppearanceModel;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.LinkedHashSet;

/**
Expand Down Expand Up @@ -2118,7 +2119,7 @@ public void setErrorIconDrawable(@DrawableRes int resId) {
public void setErrorIconDrawable(@Nullable Drawable errorIconDrawable) {
errorIconView.setImageDrawable(errorIconDrawable);
updateErrorIconVisibility();
applyIconTint(errorIconView, errorIconTintList, errorIconTintMode);
applyIconTint(this, errorIconView, errorIconTintList, errorIconTintMode);
}

/**
Expand All @@ -2141,7 +2142,7 @@ public Drawable getErrorIconDrawable() {
public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) {
if (this.errorIconTintList != errorIconTintList) {
this.errorIconTintList = errorIconTintList;
applyIconTint(errorIconView, this.errorIconTintList, errorIconTintMode);
applyIconTint(this, errorIconView, this.errorIconTintList, errorIconTintMode);
}
}

Expand All @@ -2155,7 +2156,7 @@ public void setErrorIconTintList(@Nullable ColorStateList errorIconTintList) {
public void setErrorIconTintMode(@Nullable PorterDuff.Mode errorIconTintMode) {
if (this.errorIconTintMode != errorIconTintMode) {
this.errorIconTintMode = errorIconTintMode;
applyIconTint(errorIconView, errorIconTintList, this.errorIconTintMode);
applyIconTint(this, errorIconView, errorIconTintList, this.errorIconTintMode);
}
}

Expand Down Expand Up @@ -3276,7 +3277,7 @@ public void setStartIconDrawable(@DrawableRes int resId) {
public void setStartIconDrawable(@Nullable Drawable startIconDrawable) {
startIconView.setImageDrawable(startIconDrawable);
if (startIconDrawable != null) {
applyIconTint(startIconView, startIconTintList, startIconTintMode);
applyIconTint(this, startIconView, startIconTintList, startIconTintMode);
setStartIconVisible(true);
refreshStartIconDrawableState();
} else {
Expand Down Expand Up @@ -3351,7 +3352,7 @@ public boolean isStartIconVisible() {
* has a color for a state that depends on a click (such as checked state).
*/
public void refreshStartIconDrawableState() {
refreshIconDrawableState(startIconView, startIconTintList);
refreshIconDrawableState(this, startIconView, startIconTintList);
}

/**
Expand Down Expand Up @@ -3431,7 +3432,7 @@ public CharSequence getStartIconContentDescription() {
public void setStartIconTintList(@Nullable ColorStateList startIconTintList) {
if (this.startIconTintList != startIconTintList) {
this.startIconTintList = startIconTintList;
applyIconTint(startIconView, this.startIconTintList, startIconTintMode);
applyIconTint(this, startIconView, this.startIconTintList, startIconTintMode);
}
}

Expand All @@ -3446,7 +3447,7 @@ public void setStartIconTintList(@Nullable ColorStateList startIconTintList) {
public void setStartIconTintMode(@Nullable PorterDuff.Mode startIconTintMode) {
if (this.startIconTintMode != startIconTintMode) {
this.startIconTintMode = startIconTintMode;
applyIconTint(startIconView, startIconTintList, this.startIconTintMode);
applyIconTint(this, startIconView, startIconTintList, this.startIconTintMode);
}
}

Expand Down Expand Up @@ -3476,7 +3477,7 @@ public void setEndIconMode(@EndIconMode int endIconMode) {
+ " is not supported by the end icon mode "
+ endIconMode);
}
applyIconTint(endIconView, endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
}

/**
Expand Down Expand Up @@ -3544,7 +3545,7 @@ public void setErrorIconOnLongClickListener(
* has a color for a state that depends on a click (such as checked state).
*/
public void refreshErrorIconDrawableState() {
refreshIconDrawableState(errorIconView, errorIconTintList);
refreshIconDrawableState(this, errorIconView, errorIconTintList);
}

/**
Expand Down Expand Up @@ -3584,7 +3585,7 @@ public void setEndIconActivated(boolean endIconActivated) {
* has a color for a state that depends on a click (such as checked state).
*/
public void refreshEndIconDrawableState() {
refreshIconDrawableState(endIconView, endIconTintList);
refreshIconDrawableState(this, endIconView, endIconTintList);
}

/**
Expand Down Expand Up @@ -3637,7 +3638,7 @@ public void setEndIconDrawable(@DrawableRes int resId) {
public void setEndIconDrawable(@Nullable Drawable endIconDrawable) {
endIconView.setImageDrawable(endIconDrawable);
if (endIconDrawable != null) {
applyIconTint(endIconView, endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
refreshEndIconDrawableState();
}
}
Expand Down Expand Up @@ -3707,7 +3708,7 @@ public CharSequence getEndIconContentDescription() {
public void setEndIconTintList(@Nullable ColorStateList endIconTintList) {
if (this.endIconTintList != endIconTintList) {
this.endIconTintList = endIconTintList;
applyIconTint(endIconView, this.endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, this.endIconTintList, endIconTintMode);
}
}

Expand All @@ -3722,7 +3723,7 @@ public void setEndIconTintList(@Nullable ColorStateList endIconTintList) {
public void setEndIconTintMode(@Nullable PorterDuff.Mode endIconTintMode) {
if (this.endIconTintMode != endIconTintMode) {
this.endIconTintMode = endIconTintMode;
applyIconTint(endIconView, endIconTintList, this.endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, this.endIconTintMode);
}
}

Expand Down Expand Up @@ -3923,7 +3924,7 @@ public void setPasswordVisibilityToggleEnabled(final boolean enabled) {
@Deprecated
public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintList) {
endIconTintList = tintList;
applyIconTint(endIconView, endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
}

/**
Expand All @@ -3938,7 +3939,7 @@ public void setPasswordVisibilityToggleTintList(@Nullable ColorStateList tintLis
@Deprecated
public void setPasswordVisibilityToggleTintMode(@Nullable PorterDuff.Mode mode) {
endIconTintMode = mode;
applyIconTint(endIconView, endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
}

/**
Expand Down Expand Up @@ -4030,7 +4031,7 @@ private void tintEndIconOnError(boolean tintEndIconOnError) {
endIconDrawable, indicatorViewController.getErrorViewCurrentTextColor());
endIconView.setImageDrawable(endIconDrawable);
} else {
applyIconTint(endIconView, endIconTintList, endIconTintMode);
applyIconTint(this, endIconView, endIconTintList, endIconTintMode);
}
}

Expand Down Expand Up @@ -4138,31 +4139,6 @@ private CheckableImageButton getEndIconToUpdateDummyDrawable() {
}
}

private void applyIconTint(
@NonNull CheckableImageButton iconView,
ColorStateList iconTintList,
PorterDuff.Mode iconTintMode) {
Drawable icon = iconView.getDrawable();
if (icon != null) {
icon = DrawableCompat.wrap(icon).mutate();
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);
}
}

if (iconView.getDrawable() != icon) {
iconView.setImageDrawable(icon);
}
}

private static void setIconOnClickListener(
@NonNull CheckableImageButton iconView,
@Nullable OnClickListener onClickListener,
Expand Down Expand Up @@ -4484,33 +4460,6 @@ private boolean isErrorIconVisible() {
return errorIconView.getVisibility() == VISIBLE;
}

private void refreshIconDrawableState(
CheckableImageButton iconView, ColorStateList colorStateList) {
Drawable icon = iconView.getDrawable();
if (iconView.getDrawable() == null || colorStateList == null || !colorStateList.isStateful()) {
return;
}

int color =
colorStateList.getColorForState(mergeIconState(iconView), colorStateList.getDefaultColor());

icon = DrawableCompat.wrap(icon).mutate();
DrawableCompat.setTintList(icon, ColorStateList.valueOf(color));
iconView.setImageDrawable(icon);
}

private int[] mergeIconState(CheckableImageButton iconView) {
int[] textInputStates = this.getDrawableState();
int[] iconStates = iconView.getDrawableState();

int index = textInputStates.length;
int[] states = Arrays.copyOf(textInputStates, textInputStates.length + iconStates.length);

System.arraycopy(iconStates, 0, states, index, iconStates.length);

return states;
}

private void expandHint(boolean animate) {
if (animator != null && animator.isRunning()) {
animator.cancel();
Expand Down

0 comments on commit 9789f2e

Please sign in to comment.