In the previous post, I explained how I set up a “Get Input” fragment for my apps with a voice or keyboard option and how to get the voice input. In this post I will explain how to get input from the keyboard.

I’ve seen some apps use the RemoteInput API, but this feels jarring to me since it seems to take you out of the app. Of course, they or I could be doing this wrong. The only other way I have found in the documentation is by using the EditText. However, you may not want the user to have to select the text field, so the method I have is more similar to the Play Store where selecting the keyboard button takes you directly to the keyboard.

We will first create a fragment with just a EditText view. Wear OS will open the keyboard when the text box is selected. To achieve this, we are going to open the fragment and programatically select the EditText view.

You will want to change the IME type of the view to whatever best suits your situation. In this example, we are using it as a search. Be sure to change it in the code and the layout, otherwise the types will not match and you will not get an input back.

Here is the layout for the KeyboardInputFragment:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone">

    <EditText
        android:id="@+id/search_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:imeOptions="actionSearch"
        android:inputType="text"
        android:maxLines="1"
        android:visibility="visible" />
</FrameLayout>

Notice the whole layout is set to Gone. The keyboard will take up the whole screen, so we do not need to show anything. Here is the code for the KeyboardInputFragment:

public class KeyboardInputFragment extends Fragment {

    private GetInputFragment getInputFragment;

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_keyboard_input, container, false);
        final KeyboardInputFragment keyboardInputFragment = this;
        final EditText editText = view.findViewById(R.id.search_input);

        editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
                boolean handled = false;
                if(actionId == EditorInfo.IME_ACTION_SEARCH) {
                    getInputFragment.sendResult(editText.getText().toString());
                    handled = true;
                    getActivity().getFragmentManager().beginTransaction().remove(keyboardInputFragment)
                            .commitAllowingStateLoss();
                }
                return handled;
            }
        });

        showSoftKeyboard(editText);
        return view;
    }

    public void setGetInputFragment(GetInputFragment getInputFragment) {
        this.getInputFragment = getInputFragment;
    }

    private void showSoftKeyboard(View view) {
        if(view.requestFocus()) {
            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            if(imm != null) {
                imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
                imm.toggleSoftInput(0, 0);
            } else {
                Cat.e("Couldn't open keyboard");
            }
        }
    }
}

TheĀ showSoftKeyboard function is what selects the EditText view to open the keyboard. TheĀ setGetInputFragment passes an instance of the previous fragment so that we can send the text back after we got something from the user. After we have sent the information back, we can close the fragment and the fragment from the previous post will have the necessary text to complete the action.

Hope this helps! You could probably do this in a separate activity and send the result back in an intent. I also thought extending a popup window might also work. Or am I using the Remote API wrong? I’d be interested in hearing some other solutions!