blob: f262a1eb447dafcdd9259b6e7a9c49b4f5f19901 [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.ui.widget;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.annotation.StyleableRes;
import org.chromium.ui.R;
import org.chromium.ui.base.UiAndroidFeatureList;
/**
* A TextView with the added leading property. Leading is the distance between the baselines of
* successive lines of text (so the space between rules on ruled paper). This class performs the
* calculation to set up leading correctly and allows it to be set in XML. It overwrites
* android:lineSpacingExtra and android:lineSpacingMultiplier.
*/
public class TextViewWithLeading extends TextView {
/**
* Constructing TextViewWithLeading programmatically without an {@link AttributeSet} will
* render it functionally equivalent to a TextView - that is no leading will be applied. This
* method is provided for use from subclasses.
*/
protected TextViewWithLeading(Context context) {
super(context);
}
/** Constructor for use from XML with checkForLineSpacingAttributes assertion. */
public TextViewWithLeading(Context context, AttributeSet attrs) {
super(context, attrs);
checkForLineSpacingAttributes(attrs);
Float nullableLeading = getLeadingDimen(attrs);
// TODO(https://crbug.com:1499069): Remove feature/kill switch once certain this is safe.
if (UiAndroidFeatureList.sRequireLeadingInTextViewWithLeading.isEnabled()) {
assert nullableLeading != null : "Couldn't find leading for TextViewWithLeading";
applyLeading(nullableLeading);
} else if (nullableLeading != null) {
applyLeading(nullableLeading);
}
}
private @Nullable Float getLeadingDimen(AttributeSet attrs) {
// This result variable holds the return value so a single return can be used, allowing
// easier recycling of TypedArrays.
final Float result;
Context context = getContext();
TypedArray selfTypedArray =
context.obtainStyledAttributes(attrs, R.styleable.TextViewWithLeading, 0, 0);
@StyleableRes int leadingIndex = R.styleable.TextViewWithLeading_leading;
@StyleableRes int textAppIndex = R.styleable.TextViewWithLeading_android_textAppearance;
if (selfTypedArray.hasValue(leadingIndex)) {
// Found the attr directly inside the layout or style. This has a higher priority.
float leading = selfTypedArray.getDimension(leadingIndex, 0f);
result = leading;
} else if (selfTypedArray.hasValue(textAppIndex)) {
// Resolve the text appearance, hopefully the leading is there instead.
@StyleRes int textAppRes = selfTypedArray.getResourceId(textAppIndex, 0);
if (textAppRes == Resources.ID_NULL) {
result = null;
} else {
// Using R.styleable.TextViewWithLeading to specify the list of desired attributes,
// as this is what getLeadingFromTextAppearance will use.
TypedArray textAppTypedArray =
context.obtainStyledAttributes(textAppRes, R.styleable.TextViewWithLeading);
result = getLeadingFromTextAppearance(textAppTypedArray);
textAppTypedArray.recycle();
}
} else {
result = null;
}
selfTypedArray.recycle();
return result;
}
private @Nullable Float getLeadingFromTextAppearance(TypedArray textAppTypedArray) {
if (textAppTypedArray.hasValue(R.styleable.TextViewWithLeading_leading)) {
return textAppTypedArray.getDimension(R.styleable.TextViewWithLeading_leading, 0);
} else {
return null;
}
}
private void applyLeading(float newLeading) {
float oldLeading = getPaint().getFontMetrics(null);
setLineSpacing(newLeading - oldLeading, 1f);
}
private void checkForLineSpacingAttributes(AttributeSet attrs) {
// TODO(https://crbug.com:1499069): Fix namespaces, this check does not currently work.
assert attrs.getAttributeValue(null, "android:lineSpacingExtra") == null
&& attrs.getAttributeValue(null, "android:lineSpacingMultiplier") == null
: "Do not use android:lineSpacingExtra or android:lineSpacingMultiplier in"
+ " TextViewWithLeading.";
}
}