set text line height by span
commit_hash:5ba7d1fd6b2345c4b89f9602e87390770ec56c7b
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 240 KiB After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 127 KiB |