@@ -462,29 +462,27 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
462462 // https://android-review.googlesource.com/c/platform/frameworks/base/+/634929
463463 val dynamicLayoutCrashPreventer = InputFilter { source, start, end, dest, dstart, dend ->
464464 var temp : CharSequence? = null
465- if (! bypassCrashPreventerInputFilter
466- && dstart == dend && dest.length > dend+ 1
467- && source != Constants .NEWLINE_STRING ) {
468- // dstart == dend means this is an insertion
469- // avoid handling anything if it's a newline
465+ if (! bypassCrashPreventerInputFilter && dend < dest.length && source != Constants .NEWLINE_STRING ) {
466+
470467 // if there are any images right after the destination position, hack the text
471- val spans = dest.getSpans(dstart , dend+ 1 , AztecImageSpan ::class .java)
468+ val spans = dest.getSpans(dend , dend+ 1 , AztecImageSpan ::class .java)
472469 if (spans.isNotEmpty()) {
473- // prevent this filter from running twice when `text.insert()` gets called a few lines below
470+
471+ // prevent this filter from running recursively
474472 disableCrashPreventerInputFilter()
475473 // disable MediaDeleted listener before operating on content
476474 disableMediaDeletedListener()
477475
478- // take the source (that is, what is being inserted), and append the Image to it. We will delete
479- // the original Image later so to not have a duplicate.
480- // use Spannable to copy / keep the current spans
481- temp = SpannableStringBuilder (source).append(dest.subSequence(dend, dend+ 1 ))
476+ // create a new Spannable to perform the text change here
477+ var newText = SpannableStringBuilder (dest.subSequence(0 , dstart))
478+ .append(source.subSequence(start, end))
479+ .append(dest.subSequence(dend, dest.length))
480+
481+ // force a history update to ensure the change is recorded
482+ history.beforeTextChanged(this @AztecText)
482483
483- // delete the original AztecImageSpan
484- text.delete(dend, dend+ 1 )
485- // now insert both the new insertion _and_ the original AztecImageSpan
486- text.insert(dend, temp)
487- temp = " " // discard the original source parameter as an ouput from this InputFilter
484+ // use HTML from the new text to set the state of the editText directly
485+ fromHtml(toFormattedHtml(newText), false )
488486
489487 // re-enable MediaDeleted listener
490488 enableMediaDeletedListener()
@@ -1239,6 +1237,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12391237 }
12401238
12411239 // returns regular or "calypso" html depending on the mode
1240+ // default behavior returns HTML from this text
12421241 fun toHtml (withCursorTag : Boolean = false): String {
12431242 val html = toPlainHtml(withCursorTag)
12441243
@@ -1251,24 +1250,53 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12511250 }
12521251 }
12531252
1253+ // general function accepts any Spannable and converts it to regular or "calypso" html
1254+ // depending on the mode
1255+ fun toHtml (content : Spannable , withCursorTag : Boolean = false): String {
1256+ val html = toPlainHtml(content, withCursorTag)
1257+
1258+ if (isInCalypsoMode) {
1259+ // calypso format is a mix of newline characters and html
1260+ // paragraphs and line breaks are added on server, from newline characters
1261+ return Format .addSourceEditorFormatting(html, true )
1262+ } else {
1263+ return html
1264+ }
1265+ }
1266+
12541267 // platform agnostic HTML
1268+ // default behavior returns HTML from this text
12551269 fun toPlainHtml (withCursorTag : Boolean = false): String {
12561270 return if (Looper .myLooper() != Looper .getMainLooper()) {
12571271 runBlocking {
12581272 withContext(Dispatchers .Main ) {
1259- parseHtml(withCursorTag)
1273+ parseHtml(text, withCursorTag)
1274+ }
1275+ }
1276+ } else {
1277+ parseHtml(text, withCursorTag)
1278+ }
1279+ }
1280+
1281+ // general function accepts any Spannable and converts it to platform agnostic HTML
1282+ fun toPlainHtml (content : Spannable , withCursorTag : Boolean = false): String {
1283+ return if (Looper .myLooper() != Looper .getMainLooper()) {
1284+ runBlocking {
1285+ withContext(Dispatchers .Main ) {
1286+ parseHtml(content, withCursorTag)
12601287 }
12611288 }
12621289 } else {
1263- parseHtml(withCursorTag)
1290+ parseHtml(content, withCursorTag)
12641291 }
12651292 }
12661293
1267- private fun parseHtml (withCursorTag : Boolean ): String {
1294+ private fun parseHtml (content : Spannable , withCursorTag : Boolean ): String {
12681295 val parser = AztecParser (plugins)
12691296 val output: SpannableStringBuilder
12701297 try {
1271- output = SpannableStringBuilder (text)
1298+ // output = SpannableStringBuilder(text)
1299+ output = SpannableStringBuilder (content)
12721300 } catch (e: Exception ) {
12731301 // FIXME: Remove this log once we've data to replicate the issue, and fix it in some way.
12741302 AppLog .e(AppLog .T .EDITOR , " There was an error creating SpannableStringBuilder. See #452 and #582 for details." )
@@ -1292,8 +1320,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12921320 return EndOfBufferMarkerAdder .removeEndOfTextMarker(parser.toHtml(output, withCursorTag))
12931321 }
12941322
1323+ // default behavior returns formatted HTML from this text
12951324 fun toFormattedHtml (): String {
1296- return Format .addSourceEditorFormatting(toHtml(), isInCalypsoMode)
1325+ return toFormattedHtml(text)
1326+ }
1327+
1328+ // general function accepts any Spannable and converts it to formatted HTML
1329+ fun toFormattedHtml (content : Spannable ): String {
1330+ return Format .addSourceEditorFormatting(toHtml(content), isInCalypsoMode)
12971331 }
12981332
12991333 private fun switchToAztecStyle (editable : Editable , start : Int , end : Int ) {
@@ -1548,8 +1582,11 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
15481582 (max == length || (length == 1 && text.toString() == Constants .END_OF_BUFFER_MARKER_STRING ))) {
15491583 setText(Constants .REPLACEMENT_MARKER_STRING )
15501584 } else {
1585+ // prevent changes here from triggering the crash preventer
1586+ disableCrashPreventerInputFilter()
15511587 editable.delete(min, max)
15521588 editable.insert(min, Constants .REPLACEMENT_MARKER_STRING )
1589+ enableCrashPreventerInputFilter()
15531590 }
15541591
15551592 // don't let the pasted text be included in any existing style
0 commit comments