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)

View file

@ -18,7 +18,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<org.thoughtcrime.securesms.mediasend.v2.text.AutoSizeEmojiEditText
<org.thoughtcrime.securesms.components.emoji.EmojiEditText
android:id="@+id/input"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -29,17 +29,14 @@
android:gravity="center"
android:hint="@string/TextStoryPostTextEntryFragment__add_text"
android:inputType="textMultiLine"
android:maxLength="700"
android:maxLines="12"
android:paddingHorizontal="12dp"
android:paddingTop="15dp"
android:paddingBottom="13dp"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
android:textColor="@color/core_white"
android:textSize="32dp"
android:textStyle="bold"
android:textSize="36dp"
tools:ignore="SpUsage"
tools:text="Miles" />
tools:text="THIS IS SOME TEXT THAT I AM ENTERING" />
</FrameLayout>
<org.thoughtcrime.securesms.mediasend.v2.text.TextAlignmentButton

View file

@ -12,30 +12,28 @@
android:layout_height="match_parent"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Signal.Story.Text" />
<org.thoughtcrime.securesms.mediasend.v2.text.AutoSizeEmojiEditText
android:focusable="false"
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/text_story_post_text"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:background="@drawable/rounded_rectangle_secondary_18"
android:focusable="false"
android:gravity="center"
android:inputType="textNoSuggestions"
android:paddingHorizontal="12dp"
android:paddingTop="15dp"
android:paddingBottom="13dp"
android:text="@string/TextStoryPostCreationFragment__tap_to_add_text"
android:textAppearance="@style/TextAppearance.Signal.Body1.Bold"
android:textColor="@color/core_white"
android:textStyle="bold"
android:textSize="36dp"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:aseet_EnforceLineCount="false"
tools:ignore="SpUsage"
tools:text="THIS IS SOME TEXT THAT I AM ENTERING" />
tools:text="TEST" />
<org.thoughtcrime.securesms.stories.StoryLinkPreviewView
android:id="@+id/text_story_post_link_preview"

View file

@ -23,10 +23,6 @@
<attr name="contact_filter_toolbar_icon_tint" format="color" />
<declare-styleable name="AutoSizeEmojiEditText">
<attr name="aseet_EnforceLineCount" format="boolean" />
</declare-styleable>
<declare-styleable name="SegmentedProgressBar">
<attr name="totalSegments" format="integer" />
<attr name="timePerSegment" format="integer" />