Skip to content

Delete swipe: exact visual lines via requestTextBoundsInfo (Android 14+), opt-in - #716

Open
Zokii0 wants to merge 2 commits into
LeanBitLab:mainfrom
Zokii0:delete-swipe-textboundsinfo
Open

Zokii0 wants to merge 2 commits into
LeanBitLab:mainfrom
Zokii0:delete-swipe-textboundsinfo

Conversation

@Zokii0

@Zokii0 Zokii0 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Delete swipe: visual lines via requestTextBoundsInfo (Android 14+), opt-in, falls back to the current estimate

Based on main at version 4.2.9, with the previous estimate (about 40 characters, split at whitespace) restored as the fallback. Independent of PR #702: no DPAD_UP, no key events, no getExtractedText(),
no batch edit, no parallel "shadow" selection.
Offered as an alternative for the maintainer to look at, not a request.

Fallback: previous estimate restored

4.2.9 replaced the estimate in getLineBoundariesBackwards with findSegmentBreaksBackwards (sentence/clause punctuation only),
which selects the whole paragraph on the first vertical step when the text has no . ! ? … : ;. This change puts the
pre-4.2.9 getLineBoundariesBackwards back (identical to the earlier version), removes findSegmentBreaksBackwards, and
removes the two tests that were added for the punctuation behaviour in WordDeleteSwipeTest.kt
(long line with newline exceeding 50 chars remains intact, multi sentence paragraph without newline splits at sentence boundaries).
The file then matches the earlier version again. If you prefer to keep the 4.2.9 estimate, drop those parts of the patch;
the requestTextBoundsInfo part doesn't depend on them.

Idea

Instead of probing the editor by moving its caret, ask it where its lines break. Android 14 added
InputConnection.requestTextBoundsInfo(RectF, Executor, Consumer): the editor answers asynchronously with
the bounds of its characters. The swipe turns that into line starts and uses them in place of
getLineBoundariesBackwards() (the 40-character estimate). Everything else (char/word offsets, right-swipe deselect,
setSelection, release) is unchanged.

How it differs from the concerns raised on #702

Concern Here
Focus loss from DPAD_UP No key events are sent at all.
Synchronous IPC on the UI thread The request is asynchronous (callback on main thread). Nothing blocks.
Batch edit quirks Nothing is dispatched inside a batch edit; no temporary carets.
Shadow selection / desync The selection is only ever set with the same setSelection calls as today. No second selection record.
Stray changes Only the files listed below, no unrelated strings.

Behaviour

  • New setting "Exact lines when swiping up (experimental)" (delete_swipe_exact_lines, default off), next to the other delete swipe options. It is only shown on Android 14+ (the setting does nothing below that).
  • Only used when: setting on, Android 14+, multi-line field, and the editor has sent a CursorAnchorInfo (the keyboard already calls requestCursorUpdates(MONITOR | IMMEDIATE); LatinIME now keeps the latest one).
  • Asked once per swipe, on the first vertical movement, for the area from 40 lines above the cursor line down to the cursor line.
  • While waiting for the answer, vertical movement is held back and applied afterwards (horizontal movement continues normally). Timeout 150 ms.
  • Any failure (unsupported, failed, timeout, no anchor info, exception) means the estimate is used for that swipe, exactly as before. An app that doesn't answer is not asked again until the keyboard restarts.
  • Lines further up than what was requested (or a first line that begins mid-range) continue with the estimated boundaries.
  • Lines are detected from character bounds (new line after a line feed, or when a character's vertical centre leaves the current line's range), not from the editor's own line breaks, so no assumptions about SegmentFinder offsets.

Files

  • KeyboardActionListenerImpl.kt: request/hold/apply logic, conversion of TextBoundsInfo; previous estimate restored.
  • latin/utils/VisualLines.kt (new): pure functions lineStarts and buildBoundaries (no Android classes).
  • RichInputConnection.kt: requestTextBoundsInfo() wrapper (@RequiresApi(34)), lastCursorAnchorInfo.
  • LatinIME.kt: onUpdateCursorAnchorInfo stores the latest info.
  • Settings.kt, Defaults.kt, SettingsValues.kt, GestureTypingScreen.kt, res/values/strings.xml: the setting (English only).
  • VisualLinesTest.kt (new): 8 unit tests for the pure functions.
  • WordDeleteSwipeTest.kt: the two tests for the punctuation estimate removed.

leantype-textboundsinfo.patch is the same change (restore of the previous estimate + requestTextBoundsInfo) as one unified diff against 4.2.9; app/ holds the full changed files.

Limits

  • Android 14+ only, and only if the editor implements onRequestTextBoundsInfo. Native EditText/TextView should; I don't know about WebView/Chrome, Termux, Compose fields or custom editors. Where it isn't supported the behaviour is the current estimate.
  • First vertical step can lag by one round trip to the editor (usually a few ms, up to the 150 ms timeout in the worst case).
  • The cursor line comes from the last CursorAnchorInfo; if the editor scrolled without sending a new one, the requested area can be off and the result falls back / continues with the estimate.
  • Vertical comparison ignores the transformation matrix (assumes unrotated text, left-to-right line flow).
  • Selection semantics stay as on main: each step goes to the start of a line (not "same column on the line above").

Not verified

Not compiled and not run (no Android toolchain available to me). The TextBoundsInfo / TextBoundsInfoResult calls
(getCharacterBounds, getCharacterFlags, FLAG_CHARACTER_LINEFEED, requestTextBoundsInfo signature) are written from my
knowledge of the API and need a compile check. The pure logic was only checked by hand against the unit tests' expectations.

Suggested manual test (setting on, Android 14+): native EditText with soft-wrapped text, up/up/down/down, up then left/right,
word-by-word mode, release deletes exactly the highlighted text, single-line field, top of the text, an app that doesn't
support the request (must feel like before), and the setting off (must behave like before).

@Zokii0

Zokii0 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor Author

.

@Zokii0

Zokii0 commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Now you can test it, as back then was failed build, but now is all good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant