@@ -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