fix(android): crash indexoutofboundsexecption set span#583
Open
eszlamczyk wants to merge 3 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes an Android crash on Samsung / One UI devices when long-pressing selectable EnrichedMarkdownText by preventing the movement method from mutating Selection spans during touch/long-press gestures (avoiding (-1, -1) selection offsets being replayed by One UI).
Changes:
- Reworks
LinkLongPressMovementMethodto extendArrowKeyMovementMethodand dispatch link taps based on touch offsets (ACTION_DOWN/ACTION_UP), without touchingSelection. - Preserves long-press scheduling/cancellation behavior while letting the platform selection machinery operate normally.
- Ports the same movement-method change to the duplicated implementation in
packages/android-enriched-markdown/ui.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/react-native-enriched-markdown/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt | Switches to ArrowKeyMovementMethod, removes Selection mutations, and implements offset-based link click dispatch with spoiler tap handling. |
| packages/android-enriched-markdown/ui/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLongPressMovementMethod.kt | Ports offset-based link click dispatch on top of ArrowKeyMovementMethod for the UI package copy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
eszlamczyk
marked this pull request as ready for review
July 23, 2026 12:36
hryhoriiK97
approved these changes
Jul 24, 2026
hryhoriiK97
left a comment
Collaborator
There was a problem hiding this comment.
Looks good! I left a few minor comments.
| * relying on LinkMovementMethod's selection-based implementation, and | ||
| * [ArrowKeyMovementMethod] keeps text selection behavior identical to a plain | ||
| * selectable TextView. | ||
| */ |
Collaborator
There was a problem hiding this comment.
The crash forensics are already well documented in the PR description and commit. Let's replace with something like this:
/**
* Movement method that adds link tap / long-press handling on top of
* [ArrowKeyMovementMethod], the method [setTextIsSelectable] installs.
*
* Must never mutate the buffer's Selection spans — the platform Editor
* manages them during long-press gestures, and removing or overwriting
* them mid-gesture crashes on some OEM skins.
* See: https://github.com/software-mansion/react-native-enriched-markdown/issues/580
*/
| * relying on LinkMovementMethod's selection-based implementation, and | ||
| * [ArrowKeyMovementMethod] keeps text selection behavior identical to a plain | ||
| * selectable TextView. | ||
| */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What/Why?
Fixes #580
Long-pressing an
EnrichedMarkdownText(commonmark flavor,selectabledefault) crashes onSamsung / One UI devices with:
Root cause.
The component enables
setTextIsSelectable(true)but installed aLinkMovementMethod-based movement method, and the two fight over the buffer'sSelectionspans.LinkLongPressMovementMethodremoved the Selection on everyACTION_DOWN(and on UP/CANCEL, plusLinkMovementMethod's own removal for any non-link touch), while the platformEditor- which processes each touch event before the movement method - was concurrently running its long-press word-selection machinery. Instrumentation on a stock emulator showedgetSelectionStart()/getSelectionEnd()returning(-1, -1)for the entire first half of every long-press gesture. AOSP tolerates that state; Samsung's One UI Editor caches the offsets mid-gesture and replays them after select-word viasemSetSelection→Selection.setSelection(spannable, -1, -1)→ crash. Replaying the cached offsets the way One UIdoes reproduces the byte-identical exception on a stock emulator.
Fix.
LinkLongPressMovementMethodnow extendsArrowKeyMovementMethod(the methodsetTextIsSelectableinstalls natively) and never mutates Selection spans. Link taps are dispatched from touch offsets: the pressedLinkSpanis recorded onACTION_DOWNandonClickfires onACTION_UPwhen the gesture stayed on the same span. Long-press scheduling, touch-slop cancellation,isLinkTouchActive(RNGH interop), spoiler taps, and the empty-space fall-through are unchanged. The same fix is ported to the copy inpackages/android-enriched-markdown/ui.Note
keyboard/d-pad traversal between links (a
LinkMovementMethodfeature) is not carried over; screen-reader link access is unaffected (handled byaccessibilityHelper).Testing
On a Samsung device (real crash, One UI) - NOT TESTED
yarn react-native-example android, or upload the APK to a Remote Test Lab session - pick an Android 13 / One UI device).IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0(visible in logcat). After this PR: the word is selected normally with handles and the selection menu.Without a Samsung device (any emulator - verifies the root cause and the fix)
The crash is Samsung-only, but the state that causes it is created by our code and is observable
anywhere. Add temporary logging around
super.onTouchEventinEnrichedMarkdownText.onTouchEvent:adb logcat -s SelDebug.ACTION_DOWNflips the offsets to-1..-1and they stay there for everyACTION_MOVEuntil the long-press timeout selects the word — this is the invalid stateSamsung's Editor caches and later feeds into
semSetSelection(-1, -1).After this PR: the offsets never become -1 at any point in the gesture; they keep their
previous value until the long-press sets the word range.
selectionStart/EndaftersuperonACTION_DOWN, and on a laterACTION_MOVEwhere thebuffer has a valid selection, call
Selection.setSelection(text as Spannable, cachedStart, cachedEnd). Before this PR a singlelong-press throws the byte-identical
setSpan (-1 ... -1)exception on a stock emulator;after this PR the replay never fires because the cached offsets are never -1.
PR Checklist