package org.tm.archive.components import android.text.Annotation import android.text.Editable import android.text.Spannable import android.text.Spanned import android.text.TextUtils import android.text.TextWatcher import org.signal.core.util.StringUtil import org.tm.archive.conversation.MessageStyler import org.tm.archive.conversation.MessageStyler.isSupportedStyle /** * Formatting should only grow when appending until a white space character is entered/pasted. * * This watcher observes changes to the text and will shrink supported style ranges as necessary * to provide the desired behavior. */ class ComposeTextStyleWatcher : TextWatcher { private val markerAnnotation = Annotation("text-formatting", "marker") private var textSnapshotPriorToChange: CharSequence? = null override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { if (s is Spannable) { s.removeSpan(markerAnnotation) } textSnapshotPriorToChange = s.subSequence(start, start + count) } override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (s is Spannable) { s.removeSpan(markerAnnotation) if (count > 0) { s.setSpan(markerAnnotation, start, start + count, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) } } } override fun afterTextChanged(s: Editable) { val editStart = s.getSpanStart(markerAnnotation) val editEnd = s.getSpanEnd(markerAnnotation) s.removeSpan(markerAnnotation) try { if (editStart < 0 || editEnd < 0 || editStart >= editEnd || (editStart == 0 && editEnd == s.length)) { textSnapshotPriorToChange = null return } val change = s.subSequence(editStart, editEnd) if (change.isEmpty() || textSnapshotPriorToChange == null || (editEnd - editStart == 1 && !StringUtil.isVisuallyEmpty(change[0])) || TextUtils.equals(textSnapshotPriorToChange, change) || editEnd - editStart > 1 ) { textSnapshotPriorToChange = null return } textSnapshotPriorToChange = null var newEnd = editStart for (i in change.indices) { if (StringUtil.isVisuallyEmpty(change[i])) { newEnd = editStart + i break } } s.getSpans(editStart, editEnd, Object::class.java) .filter { it.isSupportedStyle() } .forEach { style -> val styleStart = s.getSpanStart(style) val styleEnd = s.getSpanEnd(style) if (styleEnd == editEnd && styleStart < styleEnd) { s.removeSpan(style) s.setSpan(style, styleStart, newEnd, MessageStyler.SPAN_FLAGS) } else if (styleStart >= styleEnd) { s.removeSpan(style) } } } finally { s.getSpans(editStart, editEnd, Object::class.java) .filter { it.isSupportedStyle() } .forEach { style -> val styleStart = s.getSpanStart(style) val styleEnd = s.getSpanEnd(style) if (styleEnd == styleStart || styleStart > styleEnd) { s.removeSpan(style) } } } } }