Skip to content

feat: device.io.keyboard.hide via app_process#36

Open
gmegidish wants to merge 1 commit into
mainfrom
feat/keyboard-hide
Open

feat: device.io.keyboard.hide via app_process#36
gmegidish wants to merge 1 commit into
mainfrom
feat/keyboard-hide

Conversation

@gmegidish

@gmegidish gmegidish commented Jun 30, 2026

Copy link
Copy Markdown
Member

Adds a device.io.keyboard.hide JSON-RPC method.

What

The soft keyboard is just an IME-type window. hide detects it (AccessibilityWindowInfo.TYPE_INPUT_METHOD) and, if present, injects BACK via uiAutomation to dismiss it — the same approach uiautomator2/Appium use. Returns {"dismissed": <bool>}; false means no IME was up.

Why not device.io.keyboard.show / InputMethodManager

InputMethodManager.showSoftInput/hideSoftInputFromWindow both need a View/window token, which this headless Instrumentation/app_process server doesn't have. toggleSoftInput is deprecated since API 31 and is a no-op for a process with no served view (targetSdk 35). There's no reliable headless way to summon the IME — Android only raises it for a focused editable view in the target app, which happens when automation taps the field. So only hide is offered.

Test plan

  • Focus a text field on device, call device.io.keyboard.hide → keyboard dismisses, returns dismissed: true
  • Call with no keyboard up → returns dismissed: false, no BACK navigation

Summary by cubic

Adds a device.io.keyboard.hide JSON-RPC method to dismiss the soft keyboard in headless mode. It detects an IME window (AccessibilityWindowInfo.TYPE_INPUT_METHOD) and injects a BACK key via UiAutomation, returning { "dismissed": <bool> } (no-op if none shown).

Written for commit d0b5f94. Summary will update on new commits.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Walkthrough

DeviceKitServer gains a new JSON-RPC method device.io.keyboard.hide. When dispatched, it calls a new hideKeyboard helper that checks for an IME-type accessibility window; if none is found it returns false, otherwise it injects BACK key down and up events via UiAutomation and returns true. The response contains a dismissed boolean. Two new imports (android.os.SystemClock, android.view.KeyEvent) support the key-event construction.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the new device.io.keyboard.hide method for the app_process server.
Description check ✅ Passed The description directly explains the new keyboard hide JSON-RPC method and its behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/keyboard-hide

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt`:
- Around line 137-140: The BACK key injection in hideKeyboard currently ignores
UiAutomation.injectInputEvent(...) return values and always reports success.
Update hideKeyboard in DeviceKitServer to check both BACK down/up injections,
and if either returns false, propagate failure by returning false or an error
instead of true so device.io.keyboard.hide only succeeds when dismissal actually
happens.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 292f80fc-87fb-4a0f-a73b-e6c788e60ed2

📥 Commits

Reviewing files that changed from the base of the PR and between 7639e58 and d0b5f94.

📒 Files selected for processing (1)
  • app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt

Comment on lines +137 to +140
val now = SystemClock.uptimeMillis()
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0), true)
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0), true)
return true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Android UiAutomation.injectInputEvent return false when an input event cannot be injected, and is that failure expected to be handled by callers?

💡 Result:

Yes, the method android.app.UiAutomation.injectInputEvent returns a boolean indicating whether the input event injection succeeded [1][2]. It explicitly returns false if the injection fails [3][4], which can occur due to factors such as remote exceptions [3][5] or other underlying system issues [6]. It is expected that callers handle this failure [7]. Because the return value provides a direct signal regarding the success or failure of the injection operation, robust implementations typically monitor this boolean to verify that the injected input reached the system [8][7]. If it returns false, it indicates that the event was not successfully injected, and callers should generally treat this as an actionable failure rather than assuming the operation was performed [7]. [1][3][2][7]

Citations:


Surface BACK injection failures in hideKeyboard. UiAutomation.injectInputEvent(...) can return false, but both calls are ignored and the method still returns true, so "device.io.keyboard.hide" may report dismissal when the IME is still visible. Return an error or false when either injection fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt` around lines
137 - 140, The BACK key injection in hideKeyboard currently ignores
UiAutomation.injectInputEvent(...) return values and always reports success.
Update hideKeyboard in DeviceKitServer to check both BACK down/up injections,
and if either returns false, propagate failure by returning false or an error
instead of true so device.io.keyboard.hide only succeeds when dismissal actually
happens.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt">

<violation number="1" location="app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt:138">
P2: `injectInputEvent` returns `false` when injection fails, but both calls ignore the result. The method unconditionally returns `true` once the IME window is detected, so callers may be told the keyboard was dismissed when it wasn't. Check the return values and propagate failure (return `false` or throw).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment on lines +138 to +140
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0), true)
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0), true)
return true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: injectInputEvent returns false when injection fails, but both calls ignore the result. The method unconditionally returns true once the IME window is detected, so callers may be told the keyboard was dismissed when it wasn't. Check the return values and propagate failure (return false or throw).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/com/mobilenext/devicekit/DeviceKitServer.kt, line 138:

<comment>`injectInputEvent` returns `false` when injection fails, but both calls ignore the result. The method unconditionally returns `true` once the IME window is detected, so callers may be told the keyboard was dismissed when it wasn't. Check the return values and propagate failure (return `false` or throw).</comment>

<file context>
@@ -120,6 +127,19 @@ class DeviceKitServer : Instrumentation() {
+        if (!imeShown) return false
+
+        val now = SystemClock.uptimeMillis()
+        uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0), true)
+        uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0), true)
+        return true
</file context>
Suggested change
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0), true)
uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0), true)
return true
val downOk = uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0), true)
val upOk = uiAutomation.injectInputEvent(KeyEvent(now, now, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK, 0), true)
return downOk && upOk

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