@@ -102,7 +102,6 @@ import org.wordpress.aztec.watchers.DeleteMediaElementWatcherAPI25AndHigher
102102import org.wordpress.aztec.watchers.DeleteMediaElementWatcherPreAPI25
103103import org.wordpress.aztec.watchers.EndOfBufferMarkerAdder
104104import org.wordpress.aztec.watchers.EndOfParagraphMarkerAdder
105- import org.wordpress.aztec.watchers.EnterPressedWatcher
106105import org.wordpress.aztec.watchers.FullWidthImageElementWatcher
107106import org.wordpress.aztec.watchers.InlineTextWatcher
108107import org.wordpress.aztec.watchers.ParagraphBleedAdjuster
@@ -320,7 +319,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
320319 }
321320
322321 interface OnAztecKeyListener {
323- fun onEnterKey () : Boolean
322+ fun onEnterKey (text : Spannable , firedAfterTextChanged : Boolean , selStart : Int , selEnd : Int ) : Boolean
324323 fun onBackspaceKey () : Boolean
325324 }
326325
@@ -462,29 +461,27 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
462461 // https://android-review.googlesource.com/c/platform/frameworks/base/+/634929
463462 val dynamicLayoutCrashPreventer = InputFilter { source, start, end, dest, dstart, dend ->
464463 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
464+ if (! bypassCrashPreventerInputFilter && dend < dest.length && source != Constants .NEWLINE_STRING ) {
465+
470466 // if there are any images right after the destination position, hack the text
471- val spans = dest.getSpans(dstart , dend+ 1 , AztecImageSpan ::class .java)
467+ val spans = dest.getSpans(dend , dend+ 1 , AztecImageSpan ::class .java)
472468 if (spans.isNotEmpty()) {
473- // prevent this filter from running twice when `text.insert()` gets called a few lines below
469+
470+ // prevent this filter from running recursively
474471 disableCrashPreventerInputFilter()
475472 // disable MediaDeleted listener before operating on content
476473 disableMediaDeletedListener()
477474
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 ))
475+ // create a new Spannable to perform the text change here
476+ var newText = SpannableStringBuilder (dest.subSequence(0 , dstart))
477+ .append(source.subSequence(start, end))
478+ .append(dest.subSequence(dend, dest.length))
479+
480+ // force a history update to ensure the change is recorded
481+ history.beforeTextChanged(this @AztecText)
482482
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
483+ // use HTML from the new text to set the state of the editText directly
484+ fromHtml(toFormattedHtml(newText), false )
488485
489486 // re-enable MediaDeleted listener
490487 enableMediaDeletedListener()
@@ -523,7 +520,7 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
523520 if (event.action == KeyEvent .ACTION_DOWN && event.keyCode == KeyEvent .KEYCODE_ENTER ) {
524521 // Check if the external listener has consumed the enter pressed event
525522 // In that case stop the execution
526- if (onAztecKeyListener?.onEnterKey() == true ) {
523+ if (onAztecKeyListener?.onEnterKey(text, false , 0 , 0 ) == true ) {
527524 return true
528525 }
529526 }
@@ -559,11 +556,6 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
559556 }
560557
561558 private fun install () {
562- // Keep the enter pressed watcher at the beginning of the watchers list.
563- // We want to intercept Enter.key as soon as possible, and before other listeners start modifying the text.
564- // Also note that this Watchers, when the AztecKeyListener is set, keep hold a copy of the content in the editor.
565- EnterPressedWatcher .install(this )
566-
567559 ParagraphBleedAdjuster .install(this )
568560 ParagraphCollapseAdjuster .install(this )
569561
@@ -1239,8 +1231,15 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12391231 }
12401232
12411233 // returns regular or "calypso" html depending on the mode
1234+ // default behavior returns HTML from this text
12421235 fun toHtml (withCursorTag : Boolean = false): String {
1243- val html = toPlainHtml(withCursorTag)
1236+ return toHtml(text, withCursorTag)
1237+ }
1238+
1239+ // general function accepts any Spannable and converts it to regular or "calypso" html
1240+ // depending on the mode
1241+ fun toHtml (content : Spannable , withCursorTag : Boolean = false): String {
1242+ val html = toPlainHtml(content, withCursorTag)
12441243
12451244 if (isInCalypsoMode) {
12461245 // calypso format is a mix of newline characters and html
@@ -1252,23 +1251,29 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12521251 }
12531252
12541253 // platform agnostic HTML
1254+ // default behavior returns HTML from this text
12551255 fun toPlainHtml (withCursorTag : Boolean = false): String {
1256+ return toPlainHtml(text, withCursorTag)
1257+ }
1258+
1259+ // general function accepts any Spannable and converts it to platform agnostic HTML
1260+ fun toPlainHtml (content : Spannable , withCursorTag : Boolean = false): String {
12561261 return if (Looper .myLooper() != Looper .getMainLooper()) {
12571262 runBlocking {
12581263 withContext(Dispatchers .Main ) {
1259- parseHtml(withCursorTag)
1264+ parseHtml(content, withCursorTag)
12601265 }
12611266 }
12621267 } else {
1263- parseHtml(withCursorTag)
1268+ parseHtml(content, withCursorTag)
12641269 }
12651270 }
12661271
1267- private fun parseHtml (withCursorTag : Boolean ): String {
1272+ private fun parseHtml (content : Spannable , withCursorTag : Boolean ): String {
12681273 val parser = AztecParser (plugins)
12691274 val output: SpannableStringBuilder
12701275 try {
1271- output = SpannableStringBuilder (text )
1276+ output = SpannableStringBuilder (content )
12721277 } catch (e: Exception ) {
12731278 // FIXME: Remove this log once we've data to replicate the issue, and fix it in some way.
12741279 AppLog .e(AppLog .T .EDITOR , " There was an error creating SpannableStringBuilder. See #452 and #582 for details." )
@@ -1292,8 +1297,14 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
12921297 return EndOfBufferMarkerAdder .removeEndOfTextMarker(parser.toHtml(output, withCursorTag))
12931298 }
12941299
1300+ // default behavior returns formatted HTML from this text
12951301 fun toFormattedHtml (): String {
1296- return Format .addSourceEditorFormatting(toHtml(), isInCalypsoMode)
1302+ return toFormattedHtml(text)
1303+ }
1304+
1305+ // general function accepts any Spannable and converts it to formatted HTML
1306+ fun toFormattedHtml (content : Spannable ): String {
1307+ return Format .addSourceEditorFormatting(toHtml(content), isInCalypsoMode)
12971308 }
12981309
12991310 private fun switchToAztecStyle (editable : Editable , start : Int , end : Int ) {
@@ -1548,8 +1559,11 @@ open class AztecText : AppCompatEditText, TextWatcher, UnknownHtmlSpan.OnUnknown
15481559 (max == length || (length == 1 && text.toString() == Constants .END_OF_BUFFER_MARKER_STRING ))) {
15491560 setText(Constants .REPLACEMENT_MARKER_STRING )
15501561 } else {
1562+ // prevent changes here from triggering the crash preventer
1563+ disableCrashPreventerInputFilter()
15511564 editable.delete(min, max)
15521565 editable.insert(min, Constants .REPLACEMENT_MARKER_STRING )
1566+ enableCrashPreventerInputFilter()
15531567 }
15541568
15551569 // don't let the pasted text be included in any existing style
0 commit comments