From b8aa5419269f9c8d37a21144d1fbc48282a5e44c Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Tue, 30 Mar 2021 17:07:54 +1000 Subject: [PATCH 1/5] Add logic to address Gboard's unusual invocation of TextWatcher methods Note: This is probably not enough, since this only addresses when the caret is at the end of the word, but Gboard may exhibit the unusual behavior also when the caret is in the middle or start of the word. --- .../ReactNativeAztec/EnterPressedWatcher.kt | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt index cc68aac2e17fd4..097ec027fdee6b 100644 --- a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt +++ b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -24,6 +24,9 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) private var selEnd: Int = 0 var done = false + private var gboardReplacement: CharSequence? = null + + override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { val aztecText = aztecTextRef.get() if (aztecText?.getAztecKeyListener() != null && !aztecText.isTextChangedListenerDisabled()) { @@ -32,6 +35,13 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) this.start = start this.selStart = aztecText.selectionStart this.selEnd = aztecText.selectionEnd + + if (selStart == selEnd && 0 < count && count < after) { + // possible gboard replacement detected + gboardReplacement = text.subSequence(start, start + count) + } else { + gboardReplacement = null + } } } @@ -40,14 +50,25 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) val aztecKeyListener = aztecText?.getAztecKeyListener() if (aztecText != null && !aztecText.isTextChangedListenerDisabled() && aztecKeyListener != null) { val newTextCopy = SpannableStringBuilder(text) + + var gboardOffset = this.start + // If gboard replacement is happening, we offset the start position by the length + // of the gboard replacement + if (gboardReplacement != null) { + val gboardRestored = newTextCopy.subSequence(start, start + before) + if (gboardRestored.toString() == gboardReplacement.toString()) { + gboardOffset += gboardRestored.length + } + } + // if new text length is longer than original text by 1 if (textBefore?.length == newTextCopy.length - 1) { // now check that the inserted character is actually a NEWLINE - if (newTextCopy[this.start] == Constants.NEWLINE) { + if (newTextCopy[gboardOffset] == Constants.NEWLINE) { done = false aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) aztecKeyListener.onEnterKey( - newTextCopy.replace(this.start, this.start + 1, ""), + newTextCopy.replace(gboardOffset, gboardOffset + 1, ""), true, selStart, selEnd @@ -61,9 +82,12 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { if (!done) { done = true - if (enterDeleter.shouldDeleteEnter()) - text.replace(start, start + 1, "") + if (enterDeleter.shouldDeleteEnter()) { + var gboardOffset = start + gboardReplacement?.let { replacement -> gboardOffset += replacement.length } + text.replace(gboardOffset, gboardOffset + 1, "") } + } aztecTextRef.get()?.editableText?.removeSpan(it) } } From 0516ff13a1e9ed11a92b9151307c2cbc064626b3 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 31 Mar 2021 16:48:52 +1000 Subject: [PATCH 2/5] Revert "Add logic to address Gboard's unusual invocation of TextWatcher methods" This reverts commit 558174309df6dc790178c4c2ae87ef57b703f3ed. --- .../ReactNativeAztec/EnterPressedWatcher.kt | 32 +++---------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt index 097ec027fdee6b..cc68aac2e17fd4 100644 --- a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt +++ b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -24,9 +24,6 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) private var selEnd: Int = 0 var done = false - private var gboardReplacement: CharSequence? = null - - override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { val aztecText = aztecTextRef.get() if (aztecText?.getAztecKeyListener() != null && !aztecText.isTextChangedListenerDisabled()) { @@ -35,13 +32,6 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) this.start = start this.selStart = aztecText.selectionStart this.selEnd = aztecText.selectionEnd - - if (selStart == selEnd && 0 < count && count < after) { - // possible gboard replacement detected - gboardReplacement = text.subSequence(start, start + count) - } else { - gboardReplacement = null - } } } @@ -50,25 +40,14 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) val aztecKeyListener = aztecText?.getAztecKeyListener() if (aztecText != null && !aztecText.isTextChangedListenerDisabled() && aztecKeyListener != null) { val newTextCopy = SpannableStringBuilder(text) - - var gboardOffset = this.start - // If gboard replacement is happening, we offset the start position by the length - // of the gboard replacement - if (gboardReplacement != null) { - val gboardRestored = newTextCopy.subSequence(start, start + before) - if (gboardRestored.toString() == gboardReplacement.toString()) { - gboardOffset += gboardRestored.length - } - } - // if new text length is longer than original text by 1 if (textBefore?.length == newTextCopy.length - 1) { // now check that the inserted character is actually a NEWLINE - if (newTextCopy[gboardOffset] == Constants.NEWLINE) { + if (newTextCopy[this.start] == Constants.NEWLINE) { done = false aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) aztecKeyListener.onEnterKey( - newTextCopy.replace(gboardOffset, gboardOffset + 1, ""), + newTextCopy.replace(this.start, this.start + 1, ""), true, selStart, selEnd @@ -82,12 +61,9 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { if (!done) { done = true - if (enterDeleter.shouldDeleteEnter()) { - var gboardOffset = start - gboardReplacement?.let { replacement -> gboardOffset += replacement.length } - text.replace(gboardOffset, gboardOffset + 1, "") + if (enterDeleter.shouldDeleteEnter()) + text.replace(start, start + 1, "") } - } aztecTextRef.get()?.editableText?.removeSpan(it) } } From 9b49e3fdad6e017b5e5d2e6dd5ed6ffce417f4ad Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 31 Mar 2021 15:39:06 +1000 Subject: [PATCH 3/5] Search for newline in newTextCopy --- .../ReactNativeAztec/EnterPressedWatcher.kt | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt index cc68aac2e17fd4..37870b222eb9af 100644 --- a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt +++ b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -43,11 +43,13 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) // if new text length is longer than original text by 1 if (textBefore?.length == newTextCopy.length - 1) { // now check that the inserted character is actually a NEWLINE - if (newTextCopy[this.start] == Constants.NEWLINE) { + val enterPosition = newTextCopy.lastIndexOf(Constants.NEWLINE) + if (-1 < enterPosition) { done = false - aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) + aztecText.editableText.setSpan(EnterPressedUnderway(), enterPosition, + enterPosition, Spanned.SPAN_USER) aztecKeyListener.onEnterKey( - newTextCopy.replace(this.start, this.start + 1, ""), + newTextCopy.replace(enterPosition, enterPosition + 1, ""), true, selStart, selEnd @@ -58,12 +60,15 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) } override fun afterTextChanged(text: Editable) { - aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { + aztecTextRef.get()?.editableText?.getSpans(0, text.length, + EnterPressedUnderway::class.java)?.forEach { it -> if (!done) { done = true - if (enterDeleter.shouldDeleteEnter()) - text.replace(start, start + 1, "") + if (enterDeleter.shouldDeleteEnter()) { + val enterStart = text.getSpanStart(it) + text.replace(enterStart, enterStart + 1, "") } + } aztecTextRef.get()?.editableText?.removeSpan(it) } } From 1638c1cf47718f13e57723f585cd7f9e83890cb5 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 7 Apr 2021 18:26:47 +1000 Subject: [PATCH 4/5] Revert "Search for newline in newTextCopy" This reverts commit 93bbd1f84118a422ea90f471f4c5918057c6d587. --- .../ReactNativeAztec/EnterPressedWatcher.kt | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt index 37870b222eb9af..cc68aac2e17fd4 100644 --- a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt +++ b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -43,13 +43,11 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) // if new text length is longer than original text by 1 if (textBefore?.length == newTextCopy.length - 1) { // now check that the inserted character is actually a NEWLINE - val enterPosition = newTextCopy.lastIndexOf(Constants.NEWLINE) - if (-1 < enterPosition) { + if (newTextCopy[this.start] == Constants.NEWLINE) { done = false - aztecText.editableText.setSpan(EnterPressedUnderway(), enterPosition, - enterPosition, Spanned.SPAN_USER) + aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) aztecKeyListener.onEnterKey( - newTextCopy.replace(enterPosition, enterPosition + 1, ""), + newTextCopy.replace(this.start, this.start + 1, ""), true, selStart, selEnd @@ -60,15 +58,12 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) } override fun afterTextChanged(text: Editable) { - aztecTextRef.get()?.editableText?.getSpans(0, text.length, - EnterPressedUnderway::class.java)?.forEach { it -> + aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { if (!done) { done = true - if (enterDeleter.shouldDeleteEnter()) { - val enterStart = text.getSpanStart(it) - text.replace(enterStart, enterStart + 1, "") + if (enterDeleter.shouldDeleteEnter()) + text.replace(start, start + 1, "") } - } aztecTextRef.get()?.editableText?.removeSpan(it) } } From f64da2b69c789ec6b62532ba43c814dc5c2bb9c0 Mon Sep 17 00:00:00 2001 From: Matthew Kevins Date: Wed, 7 Apr 2021 18:27:11 +1000 Subject: [PATCH 5/5] Revert "Revert "Add logic to address Gboard's unusual invocation of TextWatcher methods"" This reverts commit 5da76c7f3b9393ca03c864f20764d2555e329f71. --- .../ReactNativeAztec/EnterPressedWatcher.kt | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt index cc68aac2e17fd4..097ec027fdee6b 100644 --- a/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt +++ b/packages/react-native-aztec/android/src/main/kotlin/org/wordpress/mobile/ReactNativeAztec/EnterPressedWatcher.kt @@ -24,6 +24,9 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) private var selEnd: Int = 0 var done = false + private var gboardReplacement: CharSequence? = null + + override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) { val aztecText = aztecTextRef.get() if (aztecText?.getAztecKeyListener() != null && !aztecText.isTextChangedListenerDisabled()) { @@ -32,6 +35,13 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) this.start = start this.selStart = aztecText.selectionStart this.selEnd = aztecText.selectionEnd + + if (selStart == selEnd && 0 < count && count < after) { + // possible gboard replacement detected + gboardReplacement = text.subSequence(start, start + count) + } else { + gboardReplacement = null + } } } @@ -40,14 +50,25 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) val aztecKeyListener = aztecText?.getAztecKeyListener() if (aztecText != null && !aztecText.isTextChangedListenerDisabled() && aztecKeyListener != null) { val newTextCopy = SpannableStringBuilder(text) + + var gboardOffset = this.start + // If gboard replacement is happening, we offset the start position by the length + // of the gboard replacement + if (gboardReplacement != null) { + val gboardRestored = newTextCopy.subSequence(start, start + before) + if (gboardRestored.toString() == gboardReplacement.toString()) { + gboardOffset += gboardRestored.length + } + } + // if new text length is longer than original text by 1 if (textBefore?.length == newTextCopy.length - 1) { // now check that the inserted character is actually a NEWLINE - if (newTextCopy[this.start] == Constants.NEWLINE) { + if (newTextCopy[gboardOffset] == Constants.NEWLINE) { done = false aztecText.editableText.setSpan(EnterPressedUnderway(), 0, 0, Spanned.SPAN_USER) aztecKeyListener.onEnterKey( - newTextCopy.replace(this.start, this.start + 1, ""), + newTextCopy.replace(gboardOffset, gboardOffset + 1, ""), true, selStart, selEnd @@ -61,9 +82,12 @@ class EnterPressedWatcher(aztecText: AztecText, var enterDeleter: EnterDeleter) aztecTextRef.get()?.editableText?.getSpans(0, 0, EnterPressedUnderway::class.java)?.forEach { if (!done) { done = true - if (enterDeleter.shouldDeleteEnter()) - text.replace(start, start + 1, "") + if (enterDeleter.shouldDeleteEnter()) { + var gboardOffset = start + gboardReplacement?.let { replacement -> gboardOffset += replacement.length } + text.replace(gboardOffset, gboardOffset + 1, "") } + } aztecTextRef.get()?.editableText?.removeSpan(it) } }