Skip to content

Commit 3ab5d98

Browse files
committed
fix(native_views): wire return_key_type on Android, and dismiss keyboard
1 parent 6b27f0c commit 3ab5d98

1 file changed

Lines changed: 67 additions & 1 deletion

File tree

src/pythonnative/native_views/android.py

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,73 @@ def onTextChanged(self, s: Any, start: int, before: int, count: int) -> None:
682682
et.addTextChangedListener(watcher)
683683
else:
684684
_pn_text_input_callbacks[key] = None
685-
if "on_submit" in props and props["on_submit"] is not None:
685+
if "return_key_type" in props and props["return_key_type"] is not None:
686+
# Map the cross-platform ``return_key_type`` to Android's
687+
# ``EditorInfo.IME_ACTION_*`` so the soft keyboard renders the
688+
# right action key (Done / Go / Search / Send / Next), which
689+
# is what triggers the ``OnEditorActionListener`` below. iOS
690+
# has a richer set (Google / Yahoo / Join / Route) with no
691+
# direct AOSP equivalents — fall back to ``IME_ACTION_DONE``
692+
# for those so the keyboard at least dismisses cleanly.
693+
try:
694+
EditorInfo = jclass("android.view.inputmethod.EditorInfo")
695+
rkt_mapping = {
696+
"default": EditorInfo.IME_ACTION_UNSPECIFIED,
697+
"go": EditorInfo.IME_ACTION_GO,
698+
"google": EditorInfo.IME_ACTION_DONE,
699+
"join": EditorInfo.IME_ACTION_DONE,
700+
"next": EditorInfo.IME_ACTION_NEXT,
701+
"route": EditorInfo.IME_ACTION_DONE,
702+
"search": EditorInfo.IME_ACTION_SEARCH,
703+
"send": EditorInfo.IME_ACTION_SEND,
704+
"yahoo": EditorInfo.IME_ACTION_DONE,
705+
"done": EditorInfo.IME_ACTION_DONE,
706+
}
707+
action = rkt_mapping.get(props["return_key_type"], EditorInfo.IME_ACTION_DONE)
708+
et.setImeOptions(action)
709+
except Exception:
710+
pass
711+
if not props.get("multiline"):
712+
# Always install an editor-action listener on single-line
713+
# inputs so pressing the IME action key (Done / Go / etc.)
714+
# *or* the Enter key on a single-line ``EditText`` dismisses
715+
# the soft keyboard. Without this the keyboard stays up after
716+
# ``inputText`` + ``pressKey: Enter`` in Maestro and on smaller
717+
# screens hides the rest of the layout — and matches React
718+
# Native's default Android behavior. ``on_submit`` (if any) is
719+
# fired before dismissal so the callback sees the final text.
720+
try:
721+
on_submit_cb = props.get("on_submit")
722+
EditorListener = jclass("android.widget.TextView$OnEditorActionListener")
723+
Context = jclass("android.content.Context")
724+
725+
class SubmitProxy(dynamic_proxy(EditorListener)):
726+
def __init__(self, callback: Optional[Callable[[str], None]]) -> None:
727+
super().__init__()
728+
self.callback = callback
729+
730+
def onEditorAction(self, view: Any, action_id: int, event: Any) -> bool:
731+
if self.callback is not None:
732+
try:
733+
self.callback(str(view.getText()))
734+
except Exception:
735+
pass
736+
try:
737+
view.clearFocus()
738+
ctx = view.getContext()
739+
imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE)
740+
imm.hideSoftInputFromWindow(view.getWindowToken(), 0)
741+
except Exception:
742+
pass
743+
return True
744+
745+
et.setOnEditorActionListener(SubmitProxy(on_submit_cb))
746+
except Exception:
747+
pass
748+
elif "on_submit" in props and props["on_submit"] is not None:
749+
# Multi-line inputs: only install the listener when an explicit
750+
# ``on_submit`` is provided. Enter inserts a newline by default
751+
# on multi-line ``EditText`` and we don't want to override that.
686752
try:
687753
cb = props["on_submit"]
688754
EditorListener = jclass("android.widget.TextView$OnEditorActionListener")

0 commit comments

Comments
 (0)