Ensure proper text size is used when displaying and editing text stories.

This commit is contained in:
Alex Hart
2022-03-16 15:57:59 -03:00
committed by Cody Henthorne
parent 4abb169568
commit 40020728de
7 changed files with 58 additions and 173 deletions

View File

@@ -1,156 +0,0 @@
package org.thoughtcrime.securesms.mediasend.v2.text
import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.TypedValue
import androidx.core.content.res.use
import androidx.core.view.doOnNextLayout
import org.signal.core.util.DimensionUnit
import org.signal.core.util.EditTextUtil
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.emoji.EmojiEditText
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class AutoSizeEmojiEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : EmojiEditText(context, attrs) {
private val maxTextSize = DimensionUnit.DP.toPixels(32f)
private val minTextSize = DimensionUnit.DP.toPixels(6f)
private var lowerBounds = minTextSize
private var upperBounds = maxTextSize
private val sizeSet: MutableSet<Float> = mutableSetOf()
private var beforeText: String? = null
private var beforeCursorPosition = 0
private val watcher: TextWatcher = object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) = Unit
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
beforeText = s.toString()
beforeCursorPosition = start
}
override fun afterTextChanged(s: Editable) {
if (lineCount == 0) {
doOnNextLayout {
checkCountAndAddListener()
}
} else {
checkCountAndAddListener()
}
}
}
init {
EditTextUtil.addGraphemeClusterLimitFilter(this, 700)
if (attrs != null) {
context.obtainStyledAttributes(attrs, R.styleable.AutoSizeEmojiEditText).use { typedArray ->
if (typedArray.getBoolean(R.styleable.AutoSizeEmojiEditText_aseet_EnforceLineCount, true)) {
addTextChangedListener(watcher)
}
}
} else {
addTextChangedListener(watcher)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (isInEditMode) return
try {
val operation = getNextAutoSizeOperation()
val newSize = when (operation) {
AutoSizeOperation.INCREASE -> {
lowerBounds = textSize
val midpoint = abs(lowerBounds - upperBounds) / 2f + lowerBounds
min(maxTextSize, midpoint)
}
AutoSizeOperation.DECREASE -> {
upperBounds = textSize
val midpoint = abs(lowerBounds - upperBounds) / 2f + lowerBounds
max(minTextSize, midpoint)
}
AutoSizeOperation.NONE -> return
}
if (abs(upperBounds - lowerBounds) < 1f) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerBounds)
return
} else if (sizeSet.add(newSize) || operation == AutoSizeOperation.INCREASE) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, newSize)
measure(widthMeasureSpec, heightMeasureSpec)
} else {
return
}
} finally {
upperBounds = maxTextSize
lowerBounds = minTextSize
sizeSet.clear()
}
}
private fun getNextAutoSizeOperation(): AutoSizeOperation {
if (lineCount == 0) {
return AutoSizeOperation.NONE
}
val availableHeight = measuredHeight - paddingTop - paddingBottom
if (availableHeight <= 0) {
return AutoSizeOperation.NONE
}
val pixelsRequired = lineHeight * lineCount
return if (pixelsRequired > availableHeight) {
if (textSize > minTextSize) {
AutoSizeOperation.DECREASE
} else {
AutoSizeOperation.NONE
}
} else if (pixelsRequired < availableHeight) {
if (textSize < maxTextSize) {
AutoSizeOperation.INCREASE
} else {
AutoSizeOperation.NONE
}
} else {
AutoSizeOperation.NONE
}
}
private fun checkCountAndAddListener(): Boolean {
removeTextChangedListener(watcher)
if (lineCount > 12) {
setText(beforeText)
setSelection(beforeCursorPosition)
addTextChangedListener(watcher)
return true
}
if (getNextAutoSizeOperation() != AutoSizeOperation.NONE) {
requestLayout()
}
addTextChangedListener(watcher)
return false
}
private enum class AutoSizeOperation {
INCREASE,
DECREASE,
NONE
}
}

View File

@@ -104,6 +104,8 @@ class TextStoryPostTextEntryFragment : KeyboardEntryDialogFragment(
}
private fun initializeInput() {
TextStoryTextWatcher.install(input)
input.filters = input.filters + bufferFilter
input.doOnTextChanged { _, _, _, _ ->
presentHint()

View File

@@ -0,0 +1,46 @@
package org.thoughtcrime.securesms.mediasend.v2.text
import android.text.Editable
import android.text.TextWatcher
import android.util.TypedValue
import android.widget.EditText
import android.widget.TextView
import org.signal.core.util.BreakIteratorCompat
import org.signal.core.util.DimensionUnit
import org.signal.core.util.EditTextUtil
class TextStoryTextWatcher private constructor(private val textView: TextView) : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
ensureProperTextSize(textView)
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
override fun afterTextChanged(s: Editable) = Unit
companion object {
fun ensureProperTextSize(textView: TextView) {
val breakIteratorCompat = BreakIteratorCompat.getInstance()
breakIteratorCompat.setText(textView.text)
val length = breakIteratorCompat.countBreaks()
val expectedTextSize = when {
length < 50 -> 36f
length < 200 -> 24f
else -> 18f
}
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, DimensionUnit.DP.toPixels(expectedTextSize))
}
fun install(textView: TextView) {
val watcher = TextStoryTextWatcher(textView)
if (textView is EditText) {
EditTextUtil.addGraphemeClusterLimitFilter(textView, 700)
}
textView.addTextChangedListener(watcher)
}
}
}

View File

@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModel
import org.thoughtcrime.securesms.mediasend.v2.text.TextAlignment
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryPostCreationState
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryScale
import org.thoughtcrime.securesms.mediasend.v2.text.TextStoryTextWatcher
import org.thoughtcrime.securesms.util.ViewUtil
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture
import org.thoughtcrime.securesms.util.visible
@@ -59,6 +60,7 @@ class StoryTextPostView @JvmOverloads constructor(
}
}
TextStoryTextWatcher.install(textView)
textView.doAfterTextChanged {
textAlignment?.apply {
adjustTextTranslationX(this)