package androidx.appcompat.widget; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.RectF; import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextDirectionHeuristic; import android.text.TextDirectionHeuristics; import android.text.TextPaint; import android.text.method.TransformationMethod; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.appcompat.R; import androidx.core.view.ViewCompat; import c.d.b.a.a; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; public class AppCompatTextViewAutoSizeHelper { private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1; private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112; private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12; private static final String TAG = "ACTVAutoSizeHelper"; private static final RectF TEMP_RECTF = new RectF(); public static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1.0f; private static final int VERY_WIDE = 1048576; private static ConcurrentHashMap sTextViewFieldByNameCache = new ConcurrentHashMap<>(); private static ConcurrentHashMap sTextViewMethodByNameCache = new ConcurrentHashMap<>(); private float mAutoSizeMaxTextSizeInPx = -1.0f; private float mAutoSizeMinTextSizeInPx = -1.0f; private float mAutoSizeStepGranularityInPx = -1.0f; private int[] mAutoSizeTextSizesInPx = new int[0]; private int mAutoSizeTextType = 0; private final Context mContext; private boolean mHasPresetAutoSizeValues = false; private final Impl mImpl; private boolean mNeedsAutoSizeText = false; private TextPaint mTempTextPaint; @NonNull private final TextView mTextView; public static class Impl { public void computeAndSetTextDirection(StaticLayout.Builder builder, TextView textView) { } public boolean isHorizontallyScrollable(TextView textView) { return ((Boolean) AppCompatTextViewAutoSizeHelper.invokeAndReturnWithDefault(textView, "getHorizontallyScrolling", Boolean.FALSE)).booleanValue(); } } @RequiresApi(23) public static class Impl23 extends Impl { @Override // androidx.appcompat.widget.AppCompatTextViewAutoSizeHelper.Impl public void computeAndSetTextDirection(StaticLayout.Builder builder, TextView textView) { builder.setTextDirection((TextDirectionHeuristic) AppCompatTextViewAutoSizeHelper.invokeAndReturnWithDefault(textView, "getTextDirectionHeuristic", TextDirectionHeuristics.FIRSTSTRONG_LTR)); } } @RequiresApi(29) public static class Impl29 extends Impl23 { @Override // androidx.appcompat.widget.AppCompatTextViewAutoSizeHelper.Impl23, androidx.appcompat.widget.AppCompatTextViewAutoSizeHelper.Impl public void computeAndSetTextDirection(StaticLayout.Builder builder, TextView textView) { builder.setTextDirection(textView.getTextDirectionHeuristic()); } @Override // androidx.appcompat.widget.AppCompatTextViewAutoSizeHelper.Impl public boolean isHorizontallyScrollable(TextView textView) { return textView.isHorizontallyScrollable(); } } public AppCompatTextViewAutoSizeHelper(@NonNull TextView textView) { this.mTextView = textView; this.mContext = textView.getContext(); int i = Build.VERSION.SDK_INT; if (i >= 29) { this.mImpl = new Impl29(); } else if (i >= 23) { this.mImpl = new Impl23(); } else { this.mImpl = new Impl(); } } private static T accessAndReturnWithDefault(@NonNull Object obj, @NonNull String str, @NonNull T t) { try { Field textViewField = getTextViewField(str); return textViewField == null ? t : (T) textViewField.get(obj); } catch (IllegalAccessException e) { Log.w("ACTVAutoSizeHelper", "Failed to access TextView#" + str + " member", e); return t; } } private int[] cleanupAutoSizePresetSizes(int[] iArr) { int length = iArr.length; if (length == 0) { return iArr; } Arrays.sort(iArr); ArrayList arrayList = new ArrayList(); for (int i : iArr) { if (i > 0 && Collections.binarySearch(arrayList, Integer.valueOf(i)) < 0) { arrayList.add(Integer.valueOf(i)); } } if (length == arrayList.size()) { return iArr; } int size = arrayList.size(); int[] iArr2 = new int[size]; for (int i2 = 0; i2 < size; i2++) { iArr2[i2] = ((Integer) arrayList.get(i2)).intValue(); } return iArr2; } private void clearAutoSizeConfiguration() { this.mAutoSizeTextType = 0; this.mAutoSizeMinTextSizeInPx = -1.0f; this.mAutoSizeMaxTextSizeInPx = -1.0f; this.mAutoSizeStepGranularityInPx = -1.0f; this.mAutoSizeTextSizesInPx = new int[0]; this.mNeedsAutoSizeText = false; } @RequiresApi(23) private StaticLayout createStaticLayoutForMeasuring(CharSequence charSequence, Layout.Alignment alignment, int i, int i2) { StaticLayout.Builder obtain = StaticLayout.Builder.obtain(charSequence, 0, charSequence.length(), this.mTempTextPaint, i); StaticLayout.Builder hyphenationFrequency = obtain.setAlignment(alignment).setLineSpacing(this.mTextView.getLineSpacingExtra(), this.mTextView.getLineSpacingMultiplier()).setIncludePad(this.mTextView.getIncludeFontPadding()).setBreakStrategy(this.mTextView.getBreakStrategy()).setHyphenationFrequency(this.mTextView.getHyphenationFrequency()); if (i2 == -1) { i2 = Integer.MAX_VALUE; } hyphenationFrequency.setMaxLines(i2); try { this.mImpl.computeAndSetTextDirection(obtain, this.mTextView); } catch (ClassCastException unused) { Log.w("ACTVAutoSizeHelper", "Failed to obtain TextDirectionHeuristic, auto size may be incorrect"); } return obtain.build(); } private StaticLayout createStaticLayoutForMeasuringPre16(CharSequence charSequence, Layout.Alignment alignment, int i) { return new StaticLayout(charSequence, this.mTempTextPaint, i, alignment, ((Float) accessAndReturnWithDefault(this.mTextView, "mSpacingMult", Float.valueOf(1.0f))).floatValue(), ((Float) accessAndReturnWithDefault(this.mTextView, "mSpacingAdd", Float.valueOf(0.0f))).floatValue(), ((Boolean) accessAndReturnWithDefault(this.mTextView, "mIncludePad", Boolean.TRUE)).booleanValue()); } @RequiresApi(16) private StaticLayout createStaticLayoutForMeasuringPre23(CharSequence charSequence, Layout.Alignment alignment, int i) { return new StaticLayout(charSequence, this.mTempTextPaint, i, alignment, this.mTextView.getLineSpacingMultiplier(), this.mTextView.getLineSpacingExtra(), this.mTextView.getIncludeFontPadding()); } private int findLargestTextSizeWhichFits(RectF rectF) { int length = this.mAutoSizeTextSizesInPx.length; if (length != 0) { int i = length - 1; int i2 = 1; int i3 = 0; while (i2 <= i) { int i4 = (i2 + i) / 2; if (suggestedSizeFitsInSpace(this.mAutoSizeTextSizesInPx[i4], rectF)) { int i5 = i4 + 1; i3 = i2; i2 = i5; } else { i3 = i4 - 1; i = i3; } } return this.mAutoSizeTextSizesInPx[i3]; } throw new IllegalStateException("No available text sizes to choose from."); } @Nullable private static Field getTextViewField(@NonNull String str) { try { Field field = sTextViewFieldByNameCache.get(str); if (field == null && (field = TextView.class.getDeclaredField(str)) != null) { field.setAccessible(true); sTextViewFieldByNameCache.put(str, field); } return field; } catch (NoSuchFieldException e) { Log.w("ACTVAutoSizeHelper", "Failed to access TextView#" + str + " member", e); return null; } } @Nullable private static Method getTextViewMethod(@NonNull String str) { try { Method method = sTextViewMethodByNameCache.get(str); if (method == null && (method = TextView.class.getDeclaredMethod(str, new Class[0])) != null) { method.setAccessible(true); sTextViewMethodByNameCache.put(str, method); } return method; } catch (Exception e) { Log.w("ACTVAutoSizeHelper", "Failed to retrieve TextView#" + str + "() method", e); return null; } } public static T invokeAndReturnWithDefault(@NonNull Object obj, @NonNull String str, @NonNull T t) { try { return (T) getTextViewMethod(str).invoke(obj, new Object[0]); } catch (Exception e) { Log.w("ACTVAutoSizeHelper", "Failed to invoke TextView#" + str + "() method", e); return t; } } private void setRawTextSize(float f) { if (f != this.mTextView.getPaint().getTextSize()) { this.mTextView.getPaint().setTextSize(f); boolean isInLayout = this.mTextView.isInLayout(); if (this.mTextView.getLayout() != null) { this.mNeedsAutoSizeText = false; try { Method textViewMethod = getTextViewMethod("nullLayouts"); if (textViewMethod != null) { textViewMethod.invoke(this.mTextView, new Object[0]); } } catch (Exception e) { Log.w("ACTVAutoSizeHelper", "Failed to invoke TextView#nullLayouts() method", e); } if (!isInLayout) { this.mTextView.requestLayout(); } else { this.mTextView.forceLayout(); } this.mTextView.invalidate(); } } } private boolean setupAutoSizeText() { if (!supportsAutoSizeText() || this.mAutoSizeTextType != 1) { this.mNeedsAutoSizeText = false; } else { if (!this.mHasPresetAutoSizeValues || this.mAutoSizeTextSizesInPx.length == 0) { int floor = ((int) Math.floor((double) ((this.mAutoSizeMaxTextSizeInPx - this.mAutoSizeMinTextSizeInPx) / this.mAutoSizeStepGranularityInPx))) + 1; int[] iArr = new int[floor]; for (int i = 0; i < floor; i++) { iArr[i] = Math.round((((float) i) * this.mAutoSizeStepGranularityInPx) + this.mAutoSizeMinTextSizeInPx); } this.mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(iArr); } this.mNeedsAutoSizeText = true; } return this.mNeedsAutoSizeText; } private void setupAutoSizeUniformPresetSizes(TypedArray typedArray) { int length = typedArray.length(); int[] iArr = new int[length]; if (length > 0) { for (int i = 0; i < length; i++) { iArr[i] = typedArray.getDimensionPixelSize(i, -1); } this.mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(iArr); setupAutoSizeUniformPresetSizesConfiguration(); } } private boolean setupAutoSizeUniformPresetSizesConfiguration() { int[] iArr = this.mAutoSizeTextSizesInPx; int length = iArr.length; boolean z2 = length > 0; this.mHasPresetAutoSizeValues = z2; if (z2) { this.mAutoSizeTextType = 1; this.mAutoSizeMinTextSizeInPx = (float) iArr[0]; this.mAutoSizeMaxTextSizeInPx = (float) iArr[length - 1]; this.mAutoSizeStepGranularityInPx = -1.0f; } return z2; } private boolean suggestedSizeFitsInSpace(int i, RectF rectF) { CharSequence transformation; CharSequence text = this.mTextView.getText(); TransformationMethod transformationMethod = this.mTextView.getTransformationMethod(); if (!(transformationMethod == null || (transformation = transformationMethod.getTransformation(text, this.mTextView)) == null)) { text = transformation; } int maxLines = this.mTextView.getMaxLines(); initTempTextPaint(i); StaticLayout createLayout = createLayout(text, (Layout.Alignment) invokeAndReturnWithDefault(this.mTextView, "getLayoutAlignment", Layout.Alignment.ALIGN_NORMAL), Math.round(rectF.right), maxLines); return (maxLines == -1 || (createLayout.getLineCount() <= maxLines && createLayout.getLineEnd(createLayout.getLineCount() - 1) == text.length())) && ((float) createLayout.getHeight()) <= rectF.bottom; } private boolean supportsAutoSizeText() { return !(this.mTextView instanceof AppCompatEditText); } private void validateAndSetAutoSizeTextTypeUniformConfiguration(float f, float f2, float f3) throws IllegalArgumentException { if (f <= 0.0f) { throw new IllegalArgumentException("Minimum auto-size text size (" + f + "px) is less or equal to (0px)"); } else if (f2 <= f) { throw new IllegalArgumentException("Maximum auto-size text size (" + f2 + "px) is less or equal to minimum auto-size text size (" + f + "px)"); } else if (f3 > 0.0f) { this.mAutoSizeTextType = 1; this.mAutoSizeMinTextSizeInPx = f; this.mAutoSizeMaxTextSizeInPx = f2; this.mAutoSizeStepGranularityInPx = f3; this.mHasPresetAutoSizeValues = false; } else { throw new IllegalArgumentException("The auto-size step granularity (" + f3 + "px) is less or equal to (0px)"); } } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public void autoSizeText() { if (isAutoSizeEnabled()) { if (this.mNeedsAutoSizeText) { if (this.mTextView.getMeasuredHeight() > 0 && this.mTextView.getMeasuredWidth() > 0) { int measuredWidth = this.mImpl.isHorizontallyScrollable(this.mTextView) ? 1048576 : (this.mTextView.getMeasuredWidth() - this.mTextView.getTotalPaddingLeft()) - this.mTextView.getTotalPaddingRight(); int height = (this.mTextView.getHeight() - this.mTextView.getCompoundPaddingBottom()) - this.mTextView.getCompoundPaddingTop(); if (measuredWidth > 0 && height > 0) { RectF rectF = TEMP_RECTF; synchronized (rectF) { rectF.setEmpty(); rectF.right = (float) measuredWidth; rectF.bottom = (float) height; float findLargestTextSizeWhichFits = (float) findLargestTextSizeWhichFits(rectF); if (findLargestTextSizeWhichFits != this.mTextView.getTextSize()) { setTextSizeInternal(0, findLargestTextSizeWhichFits); } } } else { return; } } else { return; } } this.mNeedsAutoSizeText = true; } } @VisibleForTesting public StaticLayout createLayout(CharSequence charSequence, Layout.Alignment alignment, int i, int i2) { return Build.VERSION.SDK_INT >= 23 ? createStaticLayoutForMeasuring(charSequence, alignment, i, i2) : createStaticLayoutForMeasuringPre23(charSequence, alignment, i); } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public int getAutoSizeMaxTextSize() { return Math.round(this.mAutoSizeMaxTextSizeInPx); } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public int getAutoSizeMinTextSize() { return Math.round(this.mAutoSizeMinTextSizeInPx); } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public int getAutoSizeStepGranularity() { return Math.round(this.mAutoSizeStepGranularityInPx); } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public int[] getAutoSizeTextAvailableSizes() { return this.mAutoSizeTextSizesInPx; } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public int getAutoSizeTextType() { return this.mAutoSizeTextType; } @VisibleForTesting public void initTempTextPaint(int i) { TextPaint textPaint = this.mTempTextPaint; if (textPaint == null) { this.mTempTextPaint = new TextPaint(); } else { textPaint.reset(); } this.mTempTextPaint.set(this.mTextView.getPaint()); this.mTempTextPaint.setTextSize((float) i); } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public boolean isAutoSizeEnabled() { return supportsAutoSizeText() && this.mAutoSizeTextType != 0; } public void loadFromAttributes(@Nullable AttributeSet attributeSet, int i) { int resourceId; Context context = this.mContext; int[] iArr = R.styleable.AppCompatTextView; TypedArray obtainStyledAttributes = context.obtainStyledAttributes(attributeSet, iArr, i, 0); TextView textView = this.mTextView; ViewCompat.saveAttributeDataForStyleable(textView, textView.getContext(), iArr, attributeSet, obtainStyledAttributes, i, 0); int i2 = R.styleable.AppCompatTextView_autoSizeTextType; if (obtainStyledAttributes.hasValue(i2)) { this.mAutoSizeTextType = obtainStyledAttributes.getInt(i2, 0); } int i3 = R.styleable.AppCompatTextView_autoSizeStepGranularity; float dimension = obtainStyledAttributes.hasValue(i3) ? obtainStyledAttributes.getDimension(i3, -1.0f) : -1.0f; int i4 = R.styleable.AppCompatTextView_autoSizeMinTextSize; float dimension2 = obtainStyledAttributes.hasValue(i4) ? obtainStyledAttributes.getDimension(i4, -1.0f) : -1.0f; int i5 = R.styleable.AppCompatTextView_autoSizeMaxTextSize; float dimension3 = obtainStyledAttributes.hasValue(i5) ? obtainStyledAttributes.getDimension(i5, -1.0f) : -1.0f; int i6 = R.styleable.AppCompatTextView_autoSizePresetSizes; if (obtainStyledAttributes.hasValue(i6) && (resourceId = obtainStyledAttributes.getResourceId(i6, 0)) > 0) { TypedArray obtainTypedArray = obtainStyledAttributes.getResources().obtainTypedArray(resourceId); setupAutoSizeUniformPresetSizes(obtainTypedArray); obtainTypedArray.recycle(); } obtainStyledAttributes.recycle(); if (!supportsAutoSizeText()) { this.mAutoSizeTextType = 0; } else if (this.mAutoSizeTextType == 1) { if (!this.mHasPresetAutoSizeValues) { DisplayMetrics displayMetrics = this.mContext.getResources().getDisplayMetrics(); if (dimension2 == -1.0f) { dimension2 = TypedValue.applyDimension(2, 12.0f, displayMetrics); } if (dimension3 == -1.0f) { dimension3 = TypedValue.applyDimension(2, 112.0f, displayMetrics); } if (dimension == -1.0f) { dimension = 1.0f; } validateAndSetAutoSizeTextTypeUniformConfiguration(dimension2, dimension3, dimension); } setupAutoSizeText(); } } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public void setAutoSizeTextTypeUniformWithConfiguration(int i, int i2, int i3, int i4) throws IllegalArgumentException { if (supportsAutoSizeText()) { DisplayMetrics displayMetrics = this.mContext.getResources().getDisplayMetrics(); validateAndSetAutoSizeTextTypeUniformConfiguration(TypedValue.applyDimension(i4, (float) i, displayMetrics), TypedValue.applyDimension(i4, (float) i2, displayMetrics), TypedValue.applyDimension(i4, (float) i3, displayMetrics)); if (setupAutoSizeText()) { autoSizeText(); } } } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] iArr, int i) throws IllegalArgumentException { if (supportsAutoSizeText()) { int length = iArr.length; if (length > 0) { int[] iArr2 = new int[length]; if (i == 0) { iArr2 = Arrays.copyOf(iArr, length); } else { DisplayMetrics displayMetrics = this.mContext.getResources().getDisplayMetrics(); for (int i2 = 0; i2 < length; i2++) { iArr2[i2] = Math.round(TypedValue.applyDimension(i, (float) iArr[i2], displayMetrics)); } } this.mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(iArr2); if (!setupAutoSizeUniformPresetSizesConfiguration()) { StringBuilder L = a.L("None of the preset sizes is valid: "); L.append(Arrays.toString(iArr)); throw new IllegalArgumentException(L.toString()); } } else { this.mHasPresetAutoSizeValues = false; } if (setupAutoSizeText()) { autoSizeText(); } } } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public void setAutoSizeTextTypeWithDefaults(int i) { if (!supportsAutoSizeText()) { return; } if (i == 0) { clearAutoSizeConfiguration(); } else if (i == 1) { DisplayMetrics displayMetrics = this.mContext.getResources().getDisplayMetrics(); validateAndSetAutoSizeTextTypeUniformConfiguration(TypedValue.applyDimension(2, 12.0f, displayMetrics), TypedValue.applyDimension(2, 112.0f, displayMetrics), 1.0f); if (setupAutoSizeText()) { autoSizeText(); } } else { throw new IllegalArgumentException(a.k("Unknown auto-size text type: ", i)); } } @RestrictTo({RestrictTo.Scope.LIBRARY_GROUP_PREFIX}) public void setTextSizeInternal(int i, float f) { Context context = this.mContext; setRawTextSize(TypedValue.applyDimension(i, f, (context == null ? Resources.getSystem() : context.getResources()).getDisplayMetrics())); } }