set text line height by span

commit_hash:5ba7d1fd6b2345c4b89f9602e87390770ec56c7b
This commit is contained in:
gulevsky
2025-01-28 09:47:13 +03:00
parent 3a0bda821e
commit 162cb78753
126 changed files with 234 additions and 126 deletions
+1
View File
@@ -11781,6 +11781,7 @@
"client/android/utils/src/main/java/com/yandex/div/internal/util/NestedHorizontalScrollCompanion.java":"divkit/public/client/android/utils/src/main/java/com/yandex/div/internal/util/NestedHorizontalScrollCompanion.java",
"client/android/utils/src/main/java/com/yandex/div/internal/util/PermissionUtils.java":"divkit/public/client/android/utils/src/main/java/com/yandex/div/internal/util/PermissionUtils.java",
"client/android/utils/src/main/java/com/yandex/div/internal/util/SingleThreadExecutor.kt":"divkit/public/client/android/utils/src/main/java/com/yandex/div/internal/util/SingleThreadExecutor.kt",
"client/android/utils/src/main/java/com/yandex/div/internal/util/TextViews.kt":"divkit/public/client/android/utils/src/main/java/com/yandex/div/internal/util/TextViews.kt",
"client/android/utils/src/main/java/com/yandex/div/internal/util/UiThreadHandler.kt":"divkit/public/client/android/utils/src/main/java/com/yandex/div/internal/util/UiThreadHandler.kt",
"client/android/version.gradle":"divkit/public/client/android/version.gradle",
"client/android/video-custom/README.md":"divkit/public/client/android/video-custom/README.md",
+1
View File
@@ -36,6 +36,7 @@
* Removed custom XML attributes of `AspectImageView`.
* Set cursor at the last position on focus action.
* Supported `layout_mode` `wrap_content` in `pager`.
* Fixed combination of line heights set for whole text and specific ranges.
# iOS Client:
* Breaking change. From now the default tooltip width is `match_parent` (previously was `wrap_content` which did not match the default `div-base` behavior).
@@ -85,7 +85,6 @@ internal class DivTextBinder @Inject constructor(
view.bindTextAlignment(div, oldDiv, expressionResolver)
view.bindFontSize(div, oldDiv, expressionResolver)
view.bindFontFeatureSettings(div, oldDiv, expressionResolver)
view.bindLineHeight(context, div, oldDiv, expressionResolver)
view.bindTextColor(div, oldDiv, expressionResolver)
view.bindUnderline(div, oldDiv, expressionResolver)
view.bindStrikethrough(div, oldDiv, expressionResolver)
@@ -311,37 +310,6 @@ internal class DivTextBinder @Inject constructor(
//endregion
//region Line Height
private fun DivLineHeightTextView.bindLineHeight(
context: BindingContext,
newDiv: DivText,
oldDiv: DivText?,
resolver: ExpressionResolver,
) {
if (newDiv.lineHeight.equalsToConstant(oldDiv?.lineHeight)
&& newDiv.fontSizeUnit.equalsToConstant(oldDiv?.fontSizeUnit)) {
return
}
applyLineHeight(newDiv.lineHeight?.evaluate(resolver), newDiv.fontSizeUnit.evaluate(resolver))
if (newDiv.lineHeight.isConstantOrNull() && newDiv.fontSizeUnit.isConstant()) {
return
}
val callback = { _: Any ->
applyLineHeight(newDiv.lineHeight?.evaluate(resolver), newDiv.fontSizeUnit.evaluate(resolver))
if (newDiv.ranges != null || newDiv.images != null) {
applyRichText(context, newDiv)
}
}
addSubscription(newDiv.lineHeight?.observe(resolver, callback))
addSubscription(newDiv.fontSizeUnit.observe(resolver, callback))
}
//endregion
//region Text Color
private fun DivLineHeightTextView.bindTextColor(
@@ -669,12 +637,14 @@ internal class DivTextBinder @Inject constructor(
oldDiv: DivText?,
) {
if (newDiv.ranges == null && newDiv.images == null) {
bindPlainText(newDiv, oldDiv, bindingContext.expressionResolver)
bindPlainText(bindingContext, newDiv, oldDiv)
} else {
bindRichText(bindingContext, newDiv)
}
}
//region Text Color
private fun DivLineHeightTextView.bindRichText(
bindingContext: BindingContext,
newDiv: DivText,
@@ -737,31 +707,41 @@ internal class DivTextBinder @Inject constructor(
* to bind [DivText.text] as quick as possible.
*/
private fun DivLineHeightTextView.bindPlainText(
bindingContext: BindingContext,
newDiv: DivText,
oldDiv: DivText?,
resolver: ExpressionResolver
oldDiv: DivText?
) {
if (newDiv.text.equalsToConstant(oldDiv?.text)) {
if (newDiv.text.equalsToConstant(oldDiv?.text)
&& newDiv.lineHeight.equalsToConstant(oldDiv?.lineHeight)
&& newDiv.fontSizeUnit.equalsToConstant(oldDiv?.fontSizeUnit)) {
return
}
applyPlainText(newDiv.text.evaluate(resolver))
applyHyphenation(newDiv.text.evaluate(resolver))
val resolver = bindingContext.expressionResolver
if (newDiv.text.isConstant() && newDiv.text.isConstant()) {
val text = newDiv.text.evaluate(resolver)
applyPlainText(bindingContext, newDiv)
applyHyphenation(text)
if (newDiv.text.isConstant()
&& newDiv.lineHeight.isConstantOrNull()
&& newDiv.fontSizeUnit.isConstantOrNull()) {
return
}
addSubscription(
newDiv.text.observe(resolver) { text ->
applyPlainText(text)
applyHyphenation(text)
}
)
val callback = { _: Any ->
val newText = newDiv.text.evaluate(resolver)
applyPlainText(bindingContext, newDiv)
applyHyphenation(newText)
}
addSubscription(newDiv.text.observe(resolver, callback))
addSubscription(newDiv.lineHeight?.observe(resolver, callback))
addSubscription(newDiv.fontSizeUnit.observe(resolver, callback))
}
private fun TextView.applyPlainText(text: String) {
this.text = text
private fun TextView.applyPlainText(bindingContext: BindingContext, divText: DivText) {
this.text = spannedTextBuilder.buildPlainText(bindingContext, this, divText)
}
private fun TextView.applyHyphenation(text: String) {
@@ -879,14 +859,11 @@ internal class DivTextBinder @Inject constructor(
return
}
spannedTextBuilder.buildText(
spannedTextBuilder.buildEllipsis(
bindingContext = bindingContext,
textView = this,
divText = newDiv,
text = ellipsis.text.evaluate(bindingContext.expressionResolver),
ranges = ellipsis.ranges,
images = ellipsis.images,
actions = ellipsis.actions
ellipsis
) { ellipsis ->
this.ellipsis = ellipsis
}
@@ -18,8 +18,12 @@ internal class FontSizeSpan(
override fun updateMeasureState(paint: TextPaint) {
if (lineHeight == 0) {
paint.textSize = fontSize.toFloat()
} else if (lineHeight < paint.textSize) {
val scale = fontSize.toFloat() / lineHeight
paint.textScaleX = scale
paint.textSize = lineHeight.toFloat()
} else {
val scale = fontSize / paint.textSize
val scale = fontSize.toFloat() / paint.textSize
paint.textScaleX = scale
}
}
@@ -4,7 +4,8 @@ import android.graphics.Paint
import android.text.Spanned
import android.text.style.LineHeightSpan
import androidx.annotation.Px
import kotlin.math.roundToInt
import androidx.core.text.getSpans
import kotlin.math.max
private const val NOT_SET = Int.MAX_VALUE
@@ -13,9 +14,8 @@ private const val NOT_SET = Int.MAX_VALUE
* Adds top offset to a first line of range text that span belongs to.
*/
internal class LineHeightWithTopOffsetSpan(
@field:Px private val topOffset: Int,
@field:Px private val lineHeight: Int,
@field:Px private val textLineHeight: Int = 0,
@Px private val topOffset: Int,
@Px private val lineHeight: Int,
private val topOffsetStart: Int,
private val topOffsetEnd: Int,
) : LineHeightSpan {
@@ -38,19 +38,19 @@ internal class LineHeightWithTopOffsetSpan(
val spanStart = spanned.getSpanStart(this)
val spanEnd = spanned.getSpanEnd(this)
if (start > spanEnd || spanStart > end) return
if (fontMetricsSaved) {
restoreFontMetrics(fm)
} else if (start >= spanStart) {
} else {
fontMetricsSaved = true
saveFontMetrics(fm)
}
if (start <= spanEnd && spanStart <= end) { // segment intersection
if (start >= spanStart && end <= spanEnd) {
applyLineHeight(fm)
} else if (lineHeight > textLineHeight) {
applyLineHeight(fm)
}
}
val maxLineHeight = spanned.getSpans<LineHeightWithTopOffsetSpan>(start, end)
.fold(lineHeight) { result, span -> max(result, span.lineHeight) }
applyLineHeight(maxLineHeight, fm)
if (topOffsetStart == spanStart && topOffsetStart in start..end) {
applyTopOffset(fm)
}
@@ -60,7 +60,7 @@ internal class LineHeightWithTopOffsetSpan(
}
}
private fun applyLineHeight(fm: Paint.FontMetricsInt) {
private fun applyLineHeight(lineHeight: Int, fm: Paint.FontMetricsInt) {
if (lineHeight <= 0) {
return
}
@@ -69,9 +69,14 @@ internal class LineHeightWithTopOffsetSpan(
val topAscent = fm.top - fm.ascent
val bottomDescent = fm.bottom - fm.descent
if (originLineHeight >= 0) {
val ratio: Float = targetLineHeight * 1.0f / originLineHeight
fm.descent = (fm.descent * ratio).roundToInt()
fm.ascent = fm.descent - targetLineHeight
val extraHeight = targetLineHeight - originLineHeight
if (extraHeight < 0) {
fm.ascent = (fm.ascent - extraHeight / 2).coerceAtMost(0)
fm.descent = (fm.ascent + targetLineHeight).coerceAtLeast(0)
} else {
fm.descent = (fm.descent + extraHeight / 2).coerceAtLeast(0)
fm.ascent = (fm.descent - targetLineHeight).coerceAtMost(0)
}
fm.top = fm.ascent + topAscent
fm.bottom = fm.descent + bottomDescent
}
@@ -53,6 +53,26 @@ internal data class SpanData(
)
}
fun isEmpty(): Boolean {
return alignmentVertical == null
&& baselineOffset == DEFAULT_BASELINE_OFFSET
&& fontFamily == null
&& fontFeatureSettings == null
&& fontSize == null
&& fontSizeUnit == DEFAULT_FONT_SIZE_UNIT
&& fontWeight == null
&& fontWeightValue == null
&& letterSpacing == null
&& lineHeight == null
&& strike == null
&& textColor == null
&& textShadow == null
&& topOffset == null
&& topOffsetStart == null
&& topOffsetEnd == null
&& underline == null
}
override fun compareTo(other: SpanData): Int {
return start - other.start
}
@@ -85,5 +105,29 @@ internal data class SpanData(
underline = null,
)
}
internal fun lineHeight(start: Int, end: Int, lineHeight: Int): SpanData {
return SpanData(
start = start,
end = end,
alignmentVertical = null,
baselineOffset = DEFAULT_BASELINE_OFFSET,
fontFamily = null,
fontFeatureSettings = null,
fontSize = null,
fontSizeUnit = DEFAULT_FONT_SIZE_UNIT,
fontWeight = null,
fontWeightValue = null,
letterSpacing = null,
lineHeight = lineHeight,
strike = null,
textColor = null,
textShadow = null,
topOffset = null,
topOffsetStart = null,
topOffsetEnd = null,
underline = null,
)
}
}
}
@@ -58,6 +58,23 @@ internal class SpannedTextBuilder @Inject constructor(
private val tempPaint = Paint()
private val debugFontMetrics = false
fun buildPlainText(
bindingContext: BindingContext,
textView: TextView,
divText: DivText
): Spanned {
return buildText(
bindingContext,
textView,
divText,
divText.text.evaluate(bindingContext.expressionResolver),
null,
null,
null,
null
)
}
fun buildText(
bindingContext: BindingContext,
textView: TextView,
@@ -68,6 +85,7 @@ internal class SpannedTextBuilder @Inject constructor(
bindingContext,
textView,
divText,
divText.text.evaluate(bindingContext.expressionResolver),
divText.ranges,
divText.images,
divText.actions,
@@ -94,7 +112,25 @@ internal class SpannedTextBuilder @Inject constructor(
textConsumer)
}
fun buildText(
fun buildEllipsis(
bindingContext: BindingContext,
textView: TextView,
divText: DivText,
ellipsis: DivText.Ellipsis,
textConsumer: TextConsumer? = null
): Spanned {
return buildText(bindingContext,
textView,
divText,
ellipsis.text.evaluate(bindingContext.expressionResolver),
ellipsis.ranges,
ellipsis.images,
ellipsis.actions,
textConsumer)
}
private fun buildText(
bindingContext: BindingContext,
textView: TextView,
divText: DivText,
@@ -118,7 +154,13 @@ internal class SpannedTextBuilder @Inject constructor(
spannedText.setSpan(LineMetricsSpan(), 0, spannedText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
if (spans.isEmpty() && sortedImages.isEmpty()) {
val hasAdditionalRanges = ranges?.any { range ->
range.actions != null
|| range.background != null
|| range.border != null
} ?: false
if (spans.isEmpty() && sortedImages.isEmpty() && !hasAdditionalRanges) {
textConsumer?.invoke(spannedText)
return spannedText
}
@@ -132,13 +174,13 @@ internal class SpannedTextBuilder @Inject constructor(
addSpan(textView, spannedText, textData, span)
}
ranges?.forEach { range ->
val start = range.start.evaluate(resolver).toIntSafely().coerceAtMost(textLength)
val end = range.end?.evaluate(resolver)?.toIntSafely()?.coerceAtMost(textLength) ?: textLength
range.actions?.let { actions ->
addActionSpan(bindingContext, textView, spannedText, start, end, actions)
if (hasAdditionalRanges) {
ranges?.forEach { range ->
val start = range.start.evaluate(resolver).toIntSafely().coerceAtMost(textLength)
val end = range.end?.evaluate(resolver)?.toIntSafely()?.coerceAtMost(textLength) ?: textLength
addActionSpan(bindingContext, textView, spannedText, start, end, range.actions)
addDecorationSpan(bindingContext, textView, spannedText, start, end, range.border, range.background)
}
addDecorationSpan(bindingContext, textView, spannedText, start, end, range.border, range.background)
}
actions?.let {
@@ -185,26 +227,36 @@ internal class SpannedTextBuilder @Inject constructor(
textData: TextData,
ranges: List<DivText.Range>?,
): List<SpanData> {
if (ranges.isNullOrEmpty()) return emptyList()
if (textData.lineHeight == null && ranges.isNullOrEmpty()) return emptyList()
val resolver = bindingContext.expressionResolver
val textLength = textData.textLength
val rangeCount = ranges?.size ?: 0
val boundSet = sortedSetOf<Int>()
val overlappingSpans = mutableListOf<SpanData>()
ranges.forEach { range ->
val overlappingSpans = ArrayList<SpanData>(rangeCount + 1)
ranges?.forEach { range ->
val start = range.start.evaluate(resolver).toIntSafely().coerceAtMost(textLength)
val end = range.end?.evaluate(resolver)?.toIntSafely()?.coerceAtMost(textLength) ?: textLength
if (start < end) {
boundSet += start
boundSet += end
overlappingSpans += createSpanData(context, bindingContext, textData, range, start, end)
val span = createSpanData(context, bindingContext, textData, range, start, end)
if (!span.isEmpty()) {
boundSet += start
boundSet += end
overlappingSpans += span
}
}
}
if (overlappingSpans.isEmpty()) return emptyList()
overlappingSpans.sort()
textData.lineHeight?.let { lineHeight ->
boundSet += 0
boundSet += textLength
overlappingSpans.add(0, SpanData.lineHeight(start = 0, end = textLength, lineHeight))
}
if (overlappingSpans.isEmpty()) return emptyList()
val bounds = boundSet.toList()
val sequentialSpans = mutableListOf<SpanData>()
val activeSpans = mutableListOf<SpanData>()
@@ -302,7 +354,12 @@ internal class SpannedTextBuilder @Inject constructor(
}
span.letterSpacing?.let { letterSpacing ->
spannedText.setSpan(LetterSpacingSpan(letterSpacing.toFloat()), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
spannedText.setSpan(
LetterSpacingSpan(letterSpacing.toFloat()),
start,
end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
span.strike?.let { strike ->
@@ -353,7 +410,6 @@ internal class SpannedTextBuilder @Inject constructor(
LineHeightWithTopOffsetSpan(
topOffset = span.topOffset ?: 0,
lineHeight = span.lineHeight ?: 0,
textLineHeight = textData.lineHeight ?: 0,
topOffsetStart = span.topOffsetStart ?: start,
topOffsetEnd = span.topOffsetEnd ?: end
),
@@ -374,8 +430,10 @@ internal class SpannedTextBuilder @Inject constructor(
spannedText: Spannable,
start: Int,
end: Int,
actions: List<DivAction>
actions: List<DivAction>?
) {
if (actions.isNullOrEmpty()) return
textView.movementMethod = LinkMovementMethod.getInstance()
spannedText.setSpan(
PerformActionSpan(bindingContext, actions),
@@ -503,7 +561,7 @@ internal class SpannedTextBuilder @Inject constructor(
return SpanData(
start = start,
end = end,
alignmentVertical = range.alignmentVertical?.evaluate(resolver) ?: DivTextAlignmentVertical.BASELINE,
alignmentVertical = range.alignmentVertical?.evaluate(resolver),
baselineOffset = range.baselineOffset.evaluate(resolver).unitToPx(displayMetrics, fontSizeUnit),
fontFamily = range.fontFamily?.evaluate(resolver),
fontFeatureSettings = range.fontFeatureSettings?.evaluate(resolver),
@@ -14,6 +14,7 @@ import com.yandex.div.core.view2.DivBinder
import com.yandex.div.core.view2.divs.widgets.ReleaseViewVisitor
import com.yandex.div.core.view2.state.DivStateSwitcher
import com.yandex.div.internal.Assert
import com.yandex.div.internal.util.textString
import com.yandex.div.json.expressions.Expression
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@@ -67,7 +68,7 @@ internal fun compareViews(first: View, second: View): Boolean {
when (first) {
is TextView -> {
Assert.assertEquals(first.textSize, (second as TextView).textSize)
Assert.assertEquals(first.text, second.text)
Assert.assertEquals(first.textString, second.textString)
Assert.assertEquals(first.maxLines, second.maxLines)
}
}
@@ -13,6 +13,7 @@ import com.yandex.div.core.images.DivImageLoader
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.data.Variable
import com.yandex.div.data.VariableMutationException
import com.yandex.div.internal.util.textString
import com.yandex.div.json.ParsingErrorLogger
import com.yandex.div2.DivData
import org.json.JSONObject
@@ -210,7 +211,7 @@ class VariableUpdatesTest {
}
private fun Div2View.assertVariableShown(value: String) {
Assert.assertEquals(value, (getChildAt(0) as TextView).text)
Assert.assertEquals(value, (getChildAt(0) as TextView).textString)
}
private fun setDataToDivView(name: String, divView: Div2View) {
@@ -6,6 +6,7 @@ import com.yandex.div.core.state.DivStatePath
import com.yandex.div.core.view2.DivTypefaceResolver
import com.yandex.div.core.view2.divs.widgets.DivSelectView
import com.yandex.div.core.view2.errors.ErrorCollectors
import com.yandex.div.internal.util.textString
import com.yandex.div.internal.widget.SelectView
import com.yandex.div.json.expressions.ExpressionResolver
import com.yandex.div2.Div
@@ -103,9 +104,9 @@ class DivSelectBinderTest : DivBinderTest() {
}
private inline fun SelectView.assertTextApplied(expectedText: String, body: () -> Unit) {
Assert.assertNotEquals(text, expectedText)
Assert.assertNotEquals(textString, expectedText)
body()
Assert.assertEquals(text, expectedText)
Assert.assertEquals(textString, expectedText)
}
companion object {
@@ -10,10 +10,10 @@ import com.yandex.div.core.view2.Div2View
import com.yandex.div.core.view2.divs.widgets.DivLinearLayout
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.internal.Assert
import com.yandex.div.internal.util.textString
import com.yandex.div.json.expressions.Expression
import com.yandex.div2.DivAction
import com.yandex.div2.DivData
import java.lang.AssertionError
import org.json.JSONObject
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,8 +53,8 @@ class LocalTriggersTest {
setVariableValue(ACTIVE_TRIGGER_VARIABLE_VALUE)
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, firstStateText.text)
Assert.assertEquals(INACTIVE_TRIGGER_TEXT, secondStateText.text)
assertTextShown(ACTIVE_TRIGGER_TEXT, firstStateText)
assertTextShown(INACTIVE_TRIGGER_TEXT, secondStateText)
}
@Test
@@ -64,8 +64,8 @@ class LocalTriggersTest {
setState(2)
setVariableValue(ACTIVE_TRIGGER_VARIABLE_VALUE)
Assert.assertEquals(INACTIVE_TRIGGER_TEXT, firstStateText.text)
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, secondStateText.text)
assertTextShown(INACTIVE_TRIGGER_TEXT, firstStateText)
assertTextShown(ACTIVE_TRIGGER_TEXT, secondStateText)
}
@Test
@@ -75,8 +75,8 @@ class LocalTriggersTest {
setVariableValue(ACTIVE_TRIGGER_VARIABLE_VALUE)
setState(2)
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, firstStateText.text)
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, secondStateText.text)
assertTextShown(ACTIVE_TRIGGER_TEXT, firstStateText)
assertTextShown(ACTIVE_TRIGGER_TEXT, secondStateText)
}
@Test
@@ -93,8 +93,8 @@ class LocalTriggersTest {
setVariableValue(ACTIVE_TRIGGER_VARIABLE_VALUE)
// Created with state 1, only triggers from state 1 should be triggered
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, firstStateText.text)
Assert.assertEquals(INACTIVE_TRIGGER_TEXT, secondStateText.text)
assertTextShown(ACTIVE_TRIGGER_TEXT, firstStateText)
assertTextShown(INACTIVE_TRIGGER_TEXT, secondStateText)
}
@Test
@@ -110,8 +110,8 @@ class LocalTriggersTest {
setVariableValue(ACTIVE_TRIGGER_VARIABLE_VALUE)
// Created with state 2, only triggers from state 2 should be triggered
Assert.assertEquals(INACTIVE_TRIGGER_TEXT, firstStateText.text)
Assert.assertEquals(ACTIVE_TRIGGER_TEXT, secondStateText.text)
assertTextShown(INACTIVE_TRIGGER_TEXT, firstStateText)
assertTextShown(ACTIVE_TRIGGER_TEXT, secondStateText)
}
private fun setState(stateNum: Int) {
@@ -132,6 +132,10 @@ class LocalTriggersTest {
url = Expression.constant(uri))
)
}
private fun assertTextShown(expected: String, view: TextView) {
Assert.assertEquals(expected, view.textString)
}
}
private val testJsonWithTwoStates = """
{
@@ -12,10 +12,10 @@ import com.yandex.div.core.view2.divs.widgets.DivLinearLayout
import com.yandex.div.core.view2.divs.widgets.DivStateLayout
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.internal.Assert
import com.yandex.div.internal.util.textString
import com.yandex.div.json.expressions.Expression
import com.yandex.div2.DivAction
import com.yandex.div2.DivData
import java.lang.AssertionError
import org.json.JSONObject
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,9 +53,7 @@ class LocalVariablesTest {
fun `elements with defined local variable can use card variables`() {
setDivView(testJsonWithTwoStates)
Assert.assertEquals(
CARD_INPUT_INITIAL_VALUE, inputWithCardVariable.text.toString()
)
assertTextShown(CARD_INPUT_INITIAL_VALUE, inputWithCardVariable)
}
@Test
@@ -63,7 +61,7 @@ class LocalVariablesTest {
setDivView(testJsonWithTwoStates)
setVariable(LOCAL_VARIABLE_NAME, LOCAL_INPUT_MODIFIED_VALUE, "0/label/state_1")
Assert.assertEquals(LOCAL_INPUT_MODIFIED_VALUE, inputWithLocalVariable.text.toString())
assertTextShown(LOCAL_INPUT_MODIFIED_VALUE, inputWithLocalVariable)
}
@Test
@@ -72,10 +70,7 @@ class LocalVariablesTest {
setVariable(LOCAL_VARIABLE_NAME, LOCAL_INPUT_MODIFIED_VALUE, "0/label/state_1")
setState(2)
Assert.assertEquals(
LOCAL_INPUT_INITIAL_VALUE_SECOND_STATE,
inputWithLocalVariable.text.toString()
)
assertTextShown(LOCAL_INPUT_INITIAL_VALUE_SECOND_STATE, inputWithLocalVariable)
}
@Test
@@ -86,7 +81,7 @@ class LocalVariablesTest {
setState(2)
setState(1)
Assert.assertEquals(LOCAL_INPUT_MODIFIED_VALUE, inputWithLocalVariable.text.toString())
assertTextShown(LOCAL_INPUT_MODIFIED_VALUE, inputWithLocalVariable)
}
@Test
@@ -94,7 +89,7 @@ class LocalVariablesTest {
setDivView(testJsonWithContainerWithVariablesAndChild)
val text = container.getChildAt(0) as TextView
Assert.assertEquals("container_variable, text_variable", text.text)
assertTextShown("container_variable, text_variable", text)
}
@Test
@@ -103,12 +98,12 @@ class LocalVariablesTest {
val textWithLocalVariable = container.getChildAt(0) as TextView
val textWithParentVariable = container.getChildAt(1) as TextView
Assert.assertEquals("text", textWithLocalVariable.text)
Assert.assertEquals("text", textWithParentVariable.text)
assertTextShown("text", textWithLocalVariable)
assertTextShown("text", textWithParentVariable)
setVariable("text", "changed text", "0/label/state_4")
Assert.assertEquals("text", textWithLocalVariable.text)
Assert.assertEquals("changed text", textWithParentVariable.text)
assertTextShown("text", textWithLocalVariable)
assertTextShown("changed text", textWithParentVariable)
}
private fun setState(stateNum: Int) {
@@ -128,6 +123,10 @@ class LocalVariablesTest {
val variable = variableController?.getMutableVariable(name) ?: return
variable.set(value)
}
private fun assertTextShown(expected: String, view: TextView) {
Assert.assertEquals(expected, view.textString)
}
}
private val testJsonWithTwoStates = """
{
@@ -10,6 +10,7 @@ import com.yandex.div.core.view2.Div2View
import com.yandex.div.core.view2.divs.widgets.DivLineHeightTextView
import com.yandex.div.core.view2.divs.widgets.DivLinearLayout
import com.yandex.div.data.DivParsingEnvironment
import com.yandex.div.internal.util.textString
import com.yandex.div2.DivData
import org.json.JSONObject
import org.junit.Assert
@@ -54,7 +55,7 @@ class SetVariableForLocalVariablesTest {
fun `variable with card variable shows and updates card variable`() {
globalText.performClick()
Assert.assertEquals("global string_var = 'new value'", globalText.text)
Assert.assertEquals("global string_var = 'new value'", globalText.textString)
assertOtherViewsNotChanged(globalText)
}
@@ -62,7 +63,7 @@ class SetVariableForLocalVariablesTest {
fun `variable with local variable shows and updates local variable`() {
localText.performClick()
Assert.assertEquals("local string_var = 'new value'", localText.text)
Assert.assertEquals("local string_var = 'new value'", localText.textString)
assertOtherViewsNotChanged(localText)
}
@@ -70,19 +71,19 @@ class SetVariableForLocalVariablesTest {
fun `variable with parent local variable shows and updates parent local variable`() {
parentLocalText.performClick()
Assert.assertEquals("parent local string_var = 'new value'", parentLocalText.text)
Assert.assertEquals("parent local string_var = 'new value'", parentLocalText.textString)
assertOtherViewsNotChanged(parentLocalText)
}
private fun assertOtherViewsNotChanged(view: View?) {
if (view != globalText) {
Assert.assertEquals("global string_var = 'global value'", globalText.text)
Assert.assertEquals("global string_var = 'global value'", globalText.textString)
}
if (view != localText) {
Assert.assertEquals("local string_var = 'local value'", localText.text)
Assert.assertEquals("local string_var = 'local value'", localText.textString)
}
if (view != parentLocalText) {
Assert.assertEquals("parent local string_var = 'local value'", parentLocalText.text)
Assert.assertEquals("parent local string_var = 'local value'", parentLocalText.textString)
}
}
}
@@ -7,9 +7,11 @@ import com.yandex.div.core.view2.divs.dpToPx
import com.yandex.div.rule.uiTestRule
import com.yandex.div.steps.superLineHeightTextView
import com.yandex.divkit.demo.DummyActivity
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
@Ignore
class SuperLineHeightTextViewTest {
private val activityTestRule = ActivityTestRule(DummyActivity::class.java)
@@ -9,6 +9,7 @@ import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.rule.ActivityTestRule
import com.yandex.div.internal.util.textString
import com.yandex.div.view.click
import com.yandex.div.view.tap
import com.yandex.divkit.demo.DummyActivity
@@ -65,7 +66,7 @@ class DivSelectSteps : DivTestAssetSteps() {
private fun prepareTimerIdlingResource(): IdlingResource =
step("Prepare timer idling resource") {
timerInteraction.stealView().asIdlingResource {
(this as TextView).text == TIMER_FINISHED_TEXT
(this as TextView).textString == TIMER_FINISHED_TEXT
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Some files were not shown because too many files have changed in this diff Show More