package org.thoughtcrime.securesms.conversation.v2.utilities import android.graphics.Rect import android.text.Layout import android.text.StaticLayout import android.text.TextPaint import android.view.MotionEvent import android.widget.TextView import androidx.core.text.getSpans import androidx.core.text.toSpannable object TextUtilities { fun getIntrinsicHeight(text: CharSequence, paint: TextPaint, width: Int): Int { val builder = StaticLayout.Builder.obtain(text, 0, text.length, paint, width) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.0f, 1.0f) .setIncludePad(false) val layout = builder.build() return layout.height } fun getIntrinsicLayout(text: CharSequence, paint: TextPaint, width: Int): StaticLayout { val builder = StaticLayout.Builder.obtain(text, 0, text.length, paint, width) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0.0f, 1.0f) .setIncludePad(false) return builder.build() } fun TextView.getIntersectedModalSpans(event: MotionEvent): List { val xInt = event.rawX.toInt() val yInt = event.rawY.toInt() val hitRect = Rect(xInt, yInt, xInt, yInt) return getIntersectedModalSpans(hitRect) } fun TextView.getIntersectedModalSpans(hitRect: Rect): List { val textLayout = layout ?: return emptyList() val lineRect = Rect() val offset = intArrayOf(0, 0).also { getLocationOnScreen(it) } val textSpannable = text.toSpannable() return (0 until textLayout.lineCount).flatMap { line -> textLayout.getLineBounds(line, lineRect) lineRect.offset(offset[0] + totalPaddingLeft, offset[1] + totalPaddingTop) if (lineRect.contains(hitRect)) { // calculate the url span intersected with (if any) val off = textLayout.getOffsetForHorizontal(line, hitRect.left.toFloat()) // left and right will be the same textSpannable.getSpans(off, off).toList() } else { emptyList() } } } }