Skip to content

Commit 7371aec

Browse files
authored
Merge pull request #813 from wordpress-mobile/try/fix-dynamic-layout-crash2
Fix DynamicLayout Crash - Round 2
2 parents 0554236 + f77e576 commit 7371aec

File tree

1 file changed

+58
-21
lines changed

1 file changed

+58
-21
lines changed

aztec/src/main/kotlin/org/wordpress/aztec/AztecText.kt

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)