Angel
Angel

Reputation: 61

Creating custom keyboard for Android, how to hide it with a button inside keyboard?

I'm creating a custom keyboard for Android (especifically, for Wear OS) with Kotlin and Compose.

How to hide the keyboard when a button inside keyboard is pressed?

When typing in Wear OS, keyboard takes all screen, so that button is needed, as I see it. Thank you in advance!

I have tried this, but does not hide the keyboard:

val imeService = IMEService()
val inputConnection = LocalSoftwareKeyboardController.current?.let 
{
    imeService.currentInputConnection
}
inputConnection?.finishComposingText()
inputConnection?.sendKeyEvent(
    KeyEvent(
        KeyEvent.ACTION_DOWN,
        KeyEvent.KEYCODE_ENTER
    )
)
inputConnection?.performEditorAction(EditorInfo.IME_ACTION_GO)
inputConnection?.performEditorAction(EditorInfo.IME_ACTION_DONE)
inputConnection?.closeConnection()
imeService.hideKeyboard(view)

And how to commit text into the text field correctly?

Upvotes: 0

Views: 344

Answers (1)

Lanre
Lanre

Reputation: 967

it seems your attempt to use IMEService and LocalSoftwareKeyboardController together seems to be mixing concepts that don't directly interact in the way you've described. Instead, you should focus on using LocalSoftwareKeyboardController to control the visibility of the keyboard.

@Composable
fun CustomKeyboardWithButton() {
    val keyboardController = LocalSoftwareKeyboardController.current
    val focusRequester = remember { FocusRequester() }

    Column(
        modifier = Modifier
           .fillMaxSize()
           .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        // Your keyboard UI here
        Button(onClick = {
            // Assuming you have a way to get the current input connection
            // and it's stored in a variable named `inputConnection`
            inputConnection?.finishComposingText()
            inputConnection?.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER))
            inputConnection?.performEditorAction(EditorInfo.IME_ACTION_GO)
            inputConnection?.performEditorAction(EditorInfo.IME_ACTION_DONE)
            inputConnection?.closeConnection()
            keyboardController?.hide()
        }) {
            Text("Hide Keyboard")
        }
    }
}

Regarding committing text into the text field properly, you would typically handle this through the onValueChange parameter of the TextField composable. Here's a simple example:

@Composable
fun TextFieldWithCommitExample() {
    var text by remember { mutableStateOf("") }

    TextField(
        value = text,
        onValueChange = { newText ->
            text = newText
            // Commit the text to the underlying input connection here
            // This might involve using the InputConnection to send the text
        },
        modifier = Modifier.focusRequester(focusRequester)
    )
}

Update: As requested by @Angel. Here's a simple IMEService class:

import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputConnection;

public class MyWearOSInputMethodService extends InputMethodService {

    private KeyboardView mKeyboardView;
    private Keyboard mKeyboard;

    @Override
    public View onCreateInputView() {
        // Inflate the keyboard layout
        mKeyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard_view, null);
        mKeyboard = new Keyboard(this, R.xml.keyboard);

        // Set the keyboard view
        mKeyboardView.setKeyboard(mKeyboard);
        mKeyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {
            @Override
            public void onPress(int primaryCode) {
                // Handle key press
            }

            @Override
            public void onRelease(int primaryCode) {
                // Handle key release
            }

            @Override
            public void onKey(int primaryCode, int[] keyCodes) {
                // Handle key event
                InputConnection ic = getCurrentInputConnection();
                if (ic!= null) {
                    switch (primaryCode) {
                        case Keyboard.KEYCODE_DELETE:
                            ic.deleteSurroundingText(1);
                            break;
                        case Keyboard.KEYCODE_ENTER:
                            ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
                            break;
                        default:
                            char text = (char) primaryCode;
                            ic.commitText(String.valueOf(text), 1);
                            break;
                    }
                }
            }

            @Override
            public void onText(CharSequence text) {
                // Handle text input
            }

            @Override
            public void swipeLeft() {
                // Handle swipe left
            }

            @Override
            public void swipeRight() {
                // Handle swipe right
            }

            @Override
            public void swipeDown() {
                // Handle swipe down
            }

            @Override
            public void swipeUp() {
                // Handle swipe up
            }
        });

        return mKeyboardView;
    }

    @Override
    public void onStartInput(InputConnection ic, boolean restarting) {
        super.onStartInput(ic, restarting);
        // Handle input connection start
    }

    @Override
    public void onStartInputSession(InputConnection ic, boolean restarting) {
        super.onStartInputSession(ic, restarting);
        // Handle input session start
    }

    @Override
    public void onCurrentInputChanged() {
        super.onCurrentInputChanged();
        // Handle current input changed
    }

    @Override
    public void onInputContextChanged() {
        super.onInputContextChanged();
        // Handle input context changed
    }

    @Override
    public void onStartedInputSession() {
        super.onStartedInputSession();
        // Handle started input session
    }

    @Override
    public void onFinishedInputSession() {
        super.onFinishedInputSession();
        // Handle finished input session
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // Handle detached from window
    }
}

Let me know if it helps :D

Upvotes: 2

Related Questions