Skip to content
This repository has been archived by the owner on Sep 27, 2024. It is now read-only.

[Android] Fix using emojis inside composing regions #883

Merged
merged 2 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618",
"hello: android.text.style.StyleSpan (0-5) fl=#33",
"hello: android.text.method.TextKeyListener (0-5) fl=#18",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289",
"hello: android.widget.Editor.SpanController (0-5) fl=#18",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"hello: android.text.style.StyleSpan (0-5) fl=#33",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289",
)
)
)
Expand All @@ -75,9 +75,9 @@ class InterceptInputConnectionIntegrationTest {

assertThat(
textView.text.dumpSpans(), equalTo(
baseEditedSpans + listOf(
"world: android.text.style.StyleSpan (0-5) fl=#33",
)
baseEditedSpans.toMutableList().apply {
add(1, "world: android.text.style.StyleSpan (0-5) fl=#33")
}
)
)
}
Expand All @@ -92,9 +92,9 @@ class InterceptInputConnectionIntegrationTest {

assertThat(
textView.text.dumpSpans(), equalTo(
baseEditedSpans + listOf(
"world: android.text.style.UnderlineSpan (0-5) fl=#33",
)
baseEditedSpans.toMutableList().apply {
add(1, "world: android.text.style.UnderlineSpan (0-5) fl=#33")
}
)
)
}
Expand All @@ -109,9 +109,9 @@ class InterceptInputConnectionIntegrationTest {

assertThat(
textView.text.dumpSpans(), equalTo(
baseEditedSpans + listOf(
"world: android.text.style.StrikethroughSpan (0-5) fl=#33",
)
baseEditedSpans.toMutableList().apply {
add(1, "world: android.text.style.StrikethroughSpan (0-5) fl=#33")
}
)
)
}
Expand All @@ -126,9 +126,9 @@ class InterceptInputConnectionIntegrationTest {

assertThat(
textView.text.dumpSpans(), equalTo(
baseEditedSpans + listOf(
"world: io.element.android.wysiwyg.view.spans.InlineCodeSpan (0-5) fl=#33",
)
baseEditedSpans.toMutableList().apply {
add(1, "world: io.element.android.wysiwyg.view.spans.InlineCodeSpan (0-5) fl=#33")
}
)
)
}
Expand All @@ -143,13 +143,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618",
"hello: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-5) fl=#34",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289",
"hello: android.text.method.TextKeyListener (0-5) fl=#18",
"hello: android.widget.Editor.SpanController (0-5) fl=#18",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"hello: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-5) fl=#34",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289",
)
)
)
Expand All @@ -169,13 +169,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans().joinToString(",\n"), equalTo(
"""
hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618,
hello: io.element.android.wysiwyg.view.spans.UnorderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289,
hello: android.text.method.TextKeyListener (0-5) fl=#18,
hello: android.widget.Editor.SpanController (0-5) fl=#18,
: android.text.Selection.START (5-5) fl=#546,
: android.text.Selection.END (5-5) fl=#34,
hello: io.element.android.wysiwyg.view.spans.UnorderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289
: android.text.Selection.END (5-5) fl=#34
""".trimIndent()
)
)
Expand All @@ -191,13 +191,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans().joinToString(",\n"), equalTo(
"""
hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618,
hello: io.element.android.wysiwyg.view.spans.UnorderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289,
hello: android.text.method.TextKeyListener (0-5) fl=#18,
hello: android.widget.Editor.SpanController (0-5) fl=#18,
: android.text.Selection.START (5-5) fl=#546,
: android.text.Selection.END (5-5) fl=#34,
hello: io.element.android.wysiwyg.view.spans.UnorderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289
: android.text.Selection.END (5-5) fl=#34
""".trimIndent()
)
)
Expand All @@ -217,13 +217,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans().joinToString(",\n"), equalTo(
"""
hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618,
hello: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289,
hello: android.text.method.TextKeyListener (0-5) fl=#18,
hello: android.widget.Editor.SpanController (0-5) fl=#18,
: android.text.Selection.START (5-5) fl=#546,
: android.text.Selection.END (5-5) fl=#34,
hello: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-5) fl=#34,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289
: android.text.Selection.END (5-5) fl=#34
""".trimIndent()
)
)
Expand All @@ -240,13 +240,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"😋😋: android.widget.TextView.ChangeWatcher (0-4) fl=#6553618",
"😋😋: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-4) fl=#34",
"😋😋: android.text.style.UnderlineSpan (0-4) fl=#289",
"😋😋: android.view.inputmethod.ComposingText (0-4) fl=#289",
"😋😋: android.text.method.TextKeyListener (0-4) fl=#18",
"😋😋: android.widget.Editor.SpanController (0-4) fl=#18",
": android.text.Selection.START (4-4) fl=#546",
": android.text.Selection.END (4-4) fl=#34",
"😋😋: io.element.android.wysiwyg.view.spans.OrderedListSpan (0-4) fl=#34",
"😋😋: android.text.style.UnderlineSpan (0-4) fl=#289",
"😋😋: android.view.inputmethod.ComposingText (0-4) fl=#289",
)
)
)
Expand All @@ -266,13 +266,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618",
"hello: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-5) fl=#33",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289",
"hello: android.text.method.TextKeyListener (0-5) fl=#18",
"hello: android.widget.Editor.SpanController (0-5) fl=#18",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"hello: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-5) fl=#33",
"hello: android.text.style.UnderlineSpan (0-5) fl=#289",
"hello: android.view.inputmethod.ComposingText (0-5) fl=#289"
": android.text.Selection.END (5-5) fl=#34"
)
)
)
Expand All @@ -290,11 +290,11 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"$NBSP: android.widget.TextView.ChangeWatcher (0-1) fl=#6553618",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17",
"$NBSP: android.text.method.TextKeyListener (0-1) fl=#18",
"$NBSP: android.widget.Editor.SpanController (0-1) fl=#18",
": android.text.Selection.START (0-0) fl=#546",
": android.text.Selection.END (0-0) fl=#34",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17"
": android.text.Selection.END (0-0) fl=#34"
)
)
)
Expand All @@ -313,12 +313,12 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"Test\n$NBSP: android.widget.TextView.ChangeWatcher (0-6) fl=#6553618",
"Test\n$NBSP: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-6) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
"Test\n$NBSP: android.text.method.TextKeyListener (0-6) fl=#18",
"Test\n$NBSP: android.widget.Editor.SpanController (0-6) fl=#18",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"Test\n$NBSP: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-6) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
)
)
)
Expand All @@ -331,12 +331,12 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"Test\n$NBSP: android.widget.TextView.ChangeWatcher (0-6) fl=#6553618",
"Test: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-4) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
"Test\n$NBSP: android.text.method.TextKeyListener (0-6) fl=#18",
"Test\n$NBSP: android.widget.Editor.SpanController (0-6) fl=#18",
": android.text.Selection.START (5-5) fl=#34",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"Test: io.element.android.wysiwyg.view.spans.CodeBlockSpan (0-4) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
)
)
)
Expand All @@ -356,13 +356,13 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans().joinToString(",\n"), equalTo(
"""
hello: android.widget.TextView.ChangeWatcher (0-5) fl=#6553618,
hello: io.element.android.wysiwyg.view.spans.QuoteSpan (0-5) fl=#33,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289,
hello: android.text.method.TextKeyListener (0-5) fl=#18,
hello: android.widget.Editor.SpanController (0-5) fl=#18,
: android.text.Selection.START (5-5) fl=#546,
: android.text.Selection.END (5-5) fl=#34,
hello: io.element.android.wysiwyg.view.spans.QuoteSpan (0-5) fl=#33,
hello: android.text.style.UnderlineSpan (0-5) fl=#289,
hello: android.view.inputmethod.ComposingText (0-5) fl=#289
: android.text.Selection.END (5-5) fl=#34
""".trimIndent()
)
)
Expand All @@ -380,11 +380,11 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"$NBSP: android.widget.TextView.ChangeWatcher (0-1) fl=#6553618",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17",
"$NBSP: android.text.method.TextKeyListener (0-1) fl=#18",
"$NBSP: android.widget.Editor.SpanController (0-1) fl=#18",
": android.text.Selection.START (0-0) fl=#546",
": android.text.Selection.END (0-0) fl=#34",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17"
": android.text.Selection.END (0-0) fl=#34"
)
)
)
Expand All @@ -403,12 +403,12 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"Test\n$NBSP: android.widget.TextView.ChangeWatcher (0-6) fl=#6553618",
"Test\n$NBSP: io.element.android.wysiwyg.view.spans.QuoteSpan (0-6) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
"Test\n$NBSP: android.text.method.TextKeyListener (0-6) fl=#18",
"Test\n$NBSP: android.widget.Editor.SpanController (0-6) fl=#18",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"Test\n$NBSP: io.element.android.wysiwyg.view.spans.QuoteSpan (0-6) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
)
)
)
Expand All @@ -421,12 +421,12 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"Test\n$NBSP: android.widget.TextView.ChangeWatcher (0-6) fl=#6553618",
"Test: io.element.android.wysiwyg.view.spans.QuoteSpan (0-4) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
"Test\n$NBSP: android.text.method.TextKeyListener (0-6) fl=#18",
"Test\n$NBSP: android.widget.Editor.SpanController (0-6) fl=#18",
": android.text.Selection.START (5-5) fl=#34",
": android.text.Selection.START (5-5) fl=#546",
": android.text.Selection.END (5-5) fl=#34",
"Test: io.element.android.wysiwyg.view.spans.QuoteSpan (0-4) fl=#33",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (5-6) fl=#17",
)
)
)
Expand All @@ -442,12 +442,12 @@ class InterceptInputConnectionIntegrationTest {
textView.text.dumpSpans(), equalTo(
listOf(
"$NBSP\n$NBSP: android.widget.TextView.ChangeWatcher (0-3) fl=#6553618",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17",
"$NBSP\n$NBSP: android.text.method.TextKeyListener (0-3) fl=#18",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (2-3) fl=#17",
"$NBSP\n$NBSP: android.widget.Editor.SpanController (0-3) fl=#18",
": android.text.Selection.START (2-2) fl=#546",
": android.text.Selection.END (2-2) fl=#34",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (0-1) fl=#17",
"$NBSP: io.element.android.wysiwyg.view.spans.ExtraCharacterSpan (2-3) fl=#17"
": android.text.Selection.END (2-2) fl=#34"
)
)
)
Expand Down Expand Up @@ -517,6 +517,28 @@ class InterceptInputConnectionIntegrationTest {
)
}

@Test
fun testAddingAndRemovingEmojiInMiddleOfComposition() {
// Set initial text
simulateInput(
EditorInputAction.ReplaceAllHtml("Test")
)
textView.setSelection(2)

// Insert emoji at index 2. This would normally cause a crash.
inputConnection.commitText("\uD83D\uDE00", 1)
assertThat(viewModel.getContentAsMessageHtml(), equalTo("Te\uD83D\uDE00st"))
// Since an emoji of length 2 was added, the selection is now at index 4
assertThat(textView.selectionStart, equalTo(4))
assertThat(textView.selectionEnd, equalTo(4))

// Remove the emoji. This would also cause some issues with some keyboards.
inputConnection.deleteSurroundingText(1, 0)
assertThat(viewModel.getContentAsMessageHtml(), equalTo("Test"))
assertThat(textView.selectionStart, equalTo(2))
assertThat(textView.selectionEnd, equalTo(2))
}

private fun simulateInput(editorInputAction: EditorInputAction) =
viewModel.processInput(editorInputAction)?.let { (text, selection) ->
textView.setText(text)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,8 @@ internal class InterceptInputConnection(

private fun replaceAll(charSequence: CharSequence) {
editable.removeFormattingSpans()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary now we are clearing the editable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, I'll double check just in case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is needed, it seems like clear() only removes the text and selection:

image

Here I started a list and just wrote "Let". The list item span was added 3 extra times, one for each new character.

editable.replace(0, editable.length, charSequence)
editable.clear()
editable.append(charSequence)
}

private fun editorIndex(composerIndex: Int, editable: Editable): Int {
Expand Down
Loading