Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ configurations.configureEach {
exclude(module = "commons-logging")
}

val canonicalVersionCode = 416
val canonicalVersionName = "1.27.0"
val canonicalVersionCode = 419
val canonicalVersionName = "1.27.1"

val postFixSize = 10
val abiPostFix = mapOf(
Expand Down
69 changes: 38 additions & 31 deletions app/src/main/java/org/session/libsession/utilities/ViewUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.os.Build
import android.text.Layout
import android.text.StaticLayout
import android.text.TextDirectionHeuristics
import android.text.TextUtils
import android.util.TypedValue
import android.view.View
import android.view.View.TEXT_ALIGNMENT_CENTER
Expand All @@ -14,8 +15,6 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.core.text.TextDirectionHeuristicsCompat
import androidx.core.widget.TextViewCompat

@ColorInt
fun Context.getColorFromAttr(
Expand All @@ -27,51 +26,59 @@ fun Context.getColorFromAttr(
return typedValue.data
}

inline fun <reified LP: ViewGroup.LayoutParams> View.modifyLayoutParams(function: LP.() -> Unit) {
inline fun <reified LP : ViewGroup.LayoutParams> View.modifyLayoutParams(function: LP.() -> Unit) {
layoutParams = (layoutParams as LP).apply { function() }
}

fun TextView.needsCollapsing(
availableWidthPx: Int,
maxLines: Int
): Boolean {
if (availableWidthPx <= 0 || text.isNullOrEmpty()) return false
// Pick the width the TextView will actually respect before draw
val rawWidth = when {
measuredWidth > 0 -> measuredWidth
// if maxWidth is set, we check it
maxWidth in 1 until Int.MAX_VALUE -> minOf(availableWidthPx, maxWidth)
else -> availableWidthPx
}
val contentWidth = (rawWidth - paddingLeft - paddingRight).coerceAtLeast(0)
if (contentWidth <= 0 || text.isNullOrEmpty()) return false

// The exact text that will be drawn (all-caps, password dots …)
val textForLayout = transformationMethod?.getTransformation(text, this) ?: text

// Build a StaticLayout that mirrors this TextView’s wrap rules
val alignment = when (textAlignment) {
TEXT_ALIGNMENT_CENTER -> Layout.Alignment.ALIGN_CENTER
TEXT_ALIGNMENT_VIEW_END, TEXT_ALIGNMENT_TEXT_END -> Layout.Alignment.ALIGN_OPPOSITE
else -> Layout.Alignment.ALIGN_NORMAL
}
val direction = when (textDirection) {
View.TEXT_DIRECTION_FIRST_STRONG_RTL -> TextDirectionHeuristics.FIRSTSTRONG_RTL
View.TEXT_DIRECTION_RTL -> TextDirectionHeuristics.RTL
View.TEXT_DIRECTION_LTR -> TextDirectionHeuristics.LTR
else -> TextDirectionHeuristics.FIRSTSTRONG_LTR
}

val builder = StaticLayout.Builder
.obtain(textForLayout, 0, textForLayout.length, paint, availableWidthPx)
.obtain(textForLayout, 0, textForLayout.length, paint, contentWidth)
.setIncludePad(includeFontPadding)
.setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
.setBreakStrategy(breakStrategy) // API 23+
.setBreakStrategy(breakStrategy)
.setHyphenationFrequency(hyphenationFrequency)
.setMaxLines(Int.MAX_VALUE)

// Alignment (honours RTL if textAlignment is END/VIEW_END)
builder.setAlignment(
when (textAlignment) {
TEXT_ALIGNMENT_CENTER -> Layout.Alignment.ALIGN_CENTER
TEXT_ALIGNMENT_VIEW_END,
TEXT_ALIGNMENT_TEXT_END -> Layout.Alignment.ALIGN_OPPOSITE
else -> Layout.Alignment.ALIGN_NORMAL
}
)

// Direction heuristic
val dir = when (textDirection) {
View.TEXT_DIRECTION_FIRST_STRONG_RTL -> TextDirectionHeuristics.FIRSTSTRONG_RTL
View.TEXT_DIRECTION_RTL -> TextDirectionHeuristics.RTL
View.TEXT_DIRECTION_LTR -> TextDirectionHeuristics.LTR
else -> TextDirectionHeuristics.FIRSTSTRONG_LTR
}
builder.setTextDirection(dir)

builder.setEllipsize(ellipsize)
.setAlignment(alignment)
.setTextDirection(direction)
.setMaxLines(maxLines) // cap at maxLines
.setEllipsize(ellipsize ?: TextUtils.TruncateAt.END) // compute ellipsis

builder.setJustificationMode(justificationMode)

val layout = builder.build()
return layout.lineCount > maxLines

// Fewer than maxLines: definitely no truncation
if (layout.lineCount < maxLines) return false
// (Defensive) more than maxLines: truncated
if (layout.lineCount > maxLines) return true

// Exactly maxLines: truncated if last line is ellipsized or characters were cut
val last = (maxLines - 1).coerceAtMost(layout.lineCount - 1)
return layout.getEllipsisCount(last) > 0 || layout.getLineEnd(last) < textForLayout.length
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,9 +355,13 @@ class VisibleMessageContentView : ConstraintLayout {
}
}

val widthCap = binding.bodyTextView.maxWidth
.takeIf { it > 0 && it < Int.MAX_VALUE }
?: resources.getDimensionPixelSize(R.dimen.max_bubble_width)

// if the text was already manually expanded, we can skip this logic
if(!isTextExpanded && binding.bodyTextView.needsCollapsing(
availableWidthPx = context.resources.getDimensionPixelSize(R.dimen.max_bubble_width),
availableWidthPx = widthCap,
maxLines = MAX_COLLAPSED_LINE_COUNT)
){
// show the "Read mode" button
Expand Down