Skip to content

Add a bounded recursive fallback to find the focused editable node - #144

Closed
guoxpeng wants to merge 1 commit into
Musheer360:masterfrom
guoxpeng:refactor/find-focused-editable
Closed

Add a bounded recursive fallback to find the focused editable node#144
guoxpeng wants to merge 1 commit into
Musheer360:masterfrom
guoxpeng:refactor/find-focused-editable

Conversation

@guoxpeng

Copy link
Copy Markdown
Contributor

Re-submission of the focused-editable node search from #139, scoped down to just the refinement the maintainer requested.

What changed

findFocusedEditableSource() now runs in two stages:

  1. findFocus(FOCUS_INPUT) first — unchanged, keeps precedence (its non-null result is returned as-is).
  2. Only when findFocus reports nothing, a bounded depth-first search walks the tree for the node that is both isEditable and isFocused.

This helps hosts that expose the editable input deeper in the tree than findFocus reaches, without disturbing any source the stage-1 already accepts.

Guardrails (from the #139 review)

  • Second stage only — upstream's findFocus result keeps precedence.
  • Budget + depth cap — 500 nodes / depth 32, behind the existing 300 ms throttle in handleAccessibilityEvent.
  • Full recycling on every path — every visited node is recycled on exit (via finally), including when an accessor throws mid-walk; only the returned match is kept for the caller.
  • No Log.e diagnostics, no proguard changes — this PR touches only AssistantService.kt plus two new files.

Structure

  • FocusedEditableFinder.kt — a FocusNode seam plus the bounded, recycling walk, so the logic is unit-testable with a fake tree.
  • AssistantService.kt — wires the two-stage fallback through AccessibilityFocusNode.
  • FocusedEditableFinderTest.kt — 6 tests: match-at-root, deep match, miss recycles everything, budget cap, depth cap, exception path.

./gradlew testDebugUnitTest and ./gradlew lintDebug pass locally.

findFocus(FOCUS_INPUT) stays the first choice and keeps precedence, but some
hosts expose the editable input deeper in the tree than findFocus reaches. When
findFocus reports nothing, walk the tree for the node that is both editable and
focused.

The walk is bounded (500-node budget, depth cap 32) so a deep tree cannot spike
the main thread, and recycles every visited node on every exit path — including
when an accessor throws mid-walk — except the returned match. Extracted behind a
FocusNode seam so it is unit-tested with a fake tree.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Musheer360

Musheer360 commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Thanks for the focused fallback implementation. I validated this PR head in a temporary worktree. ./gradlew test lint passes. Before merge, could you please confirm whether you have run a real-device smoke test with an accessibility host where findFocus(FOCUS_INPUT) returns null, including normal text entry and node recycling? If device access is unavailable, no problem. Please note that in the PR so we can record the remaining validation gap. Thanks.

Musheer360 added a commit that referenced this pull request Aug 20, 2026
Build & Release's preview-apk job uploads a debug-signed side-by-side
APK (com.musheer360.swiftslate.preview) but the artifact was buried
under Actions > run > Artifacts, requiring multiple clicks and a login.

This adds Comment Preview APK — a workflow_run trigger that runs on
the base repo after Build & Release succeeds. It resolves the PR number
robustly (pull_requests array for same-repo PRs, commits/sha/pulls,
and pulls?head=owner:branch for forks like #144), fetches the
SwiftSlate-preview-pr* artifact, and upserts a single
<!-- preview-apk --> comment with a one-click run link, size/commit,
and install notes via peter-evans/find-comment+create-or-update-comment
(pinned SHAs). workflow_dispatch with run_id is kept for manual recovery
and temporary testing.

Safe for forks: workflow_run runs on master context with
pull-requests:write, never executes fork code with write token.
Repository owner deleted a comment from github-actions Bot Aug 20, 2026
Repository owner deleted a comment from github-actions Bot Aug 20, 2026
Repository owner deleted a comment from github-actions Bot Aug 20, 2026
Repository owner deleted a comment from github-actions Bot Aug 20, 2026
Musheer360 added a commit that referenced this pull request Aug 20, 2026
Musheer360 added a commit that referenced this pull request Aug 20, 2026
flawless: harden PR #144 — bounded fallback (isPassword, MainThread, tests)
@Musheer360

Copy link
Copy Markdown
Owner

Superseded by #147 — flawless hardening includes this PR's fallback (rebase + isPassword defense-in-depth, MainThread docs, expanded 12 tests). #147 merged at 1a0c9ba. Closing as duplicate. Thanks @guoxpeng for original implementation.

@Musheer360 Musheer360 closed this Aug 20, 2026
@guoxpeng

Copy link
Copy Markdown
Contributor Author

Thanks, and thanks for folding this into #147. On the smoke test: I did exercise this on a real device against two hosts (WeChat and the system Notes app) — normal text entry worked fine in both. Since the fallback is intentionally log-free, I couldn't instrument which branch was hit or observe recycling directly, so "fallback taken + no node leak" is inferred from correct behavior rather than verified via logs. Happy to add targeted logging and re-test if you'd like harder evidence before relying on it.

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.

2 participants