Trying to make a custom Keyboard using Jetpack Compose. Cannot figure out how call currentInputConnection
or other methods from Composable.
@Composable
fun CustomKeyboard() {
var inputVal by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(8.dp)
) {
Spacer(modifier = Modifier.height(50.dp))
Text("Last key pressed: $inputVal")
Row(modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically)
{
MyButton(mText = "A") { inputVal = it}
MyButton(mText = "B") { inputVal = it}
MyButton(mText = "C") { inputVal = it}
}
}
}
@Composable
private fun MyButton(
mText: String,
onPressed: (String) -> Unit
) {
OutlinedButton(
onClick = {
onPressed(mText)
},
modifier = Modifier.padding(4.dp)
) {
Text(text = mText, fontSize = 30.sp, color = Color.White)
}
}
And the InputMethodService class here...
class ComposeKeyboardView(context: Context) : AbstractComposeView(context) {
@Composable
override fun Content() {
CustomKeyboard()
}
}
class IMEService : InputMethodService(), LifecycleOwner, ViewModelStoreOwner,
SavedStateRegistryOwner {
override fun onCreateInputView(): View {
val view = ComposeKeyboardView(this)
window!!.window!!.decorView.let { decorView ->
ViewTreeLifecycleOwner.set(decorView, this)
ViewTreeViewModelStoreOwner.set(decorView, this)
ViewTreeSavedStateRegistryOwner.set(decorView, this)
}
view.let {
ViewTreeLifecycleOwner.set(it, this)
ViewTreeViewModelStoreOwner.set(it, this)
ViewTreeSavedStateRegistryOwner.set(it, this)
}
return view
}
fun doSomethingWith(mData: String) {
currentInputConnection?.commitText(mData, 1)
}
//Lifecylce Methods
private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)
override fun getLifecycle(): Lifecycle {
return lifecycleRegistry
}
private fun handleLifecycleEvent(event: Lifecycle.Event) =
lifecycleRegistry.handleLifecycleEvent(event)
override fun onCreate() {
super.onCreate()
savedStateRegistry.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
//ViewModelStore Methods
private val store = ViewModelStore()
override fun getViewModelStore(): ViewModelStore = store
//SaveStateRegestry Methods
private val savedStateRegistry = SavedStateRegistryController.create(this)
override fun getSavedStateRegistry(): SavedStateRegistry = savedStateRegistry.savedStateRegistry
}
If you just need to call a single function, i.e. doSomethingWith(mData: String)
, or a few of them, then you can pass them into your composables and call them when you want to. This approach would be more loosely coupled and easier to @Preview
the CustomKeyboard
composable.
@Composable
fun CustomKeyboard(onKeyPressed: (String) -> Unit) {
//...
MyButton(mText = "A") {
inputVal = it
onKeyPressed(it)
}
// ...
}
class ComposeKeyboardView(
context: Context,
private val onKeyPressed: (String) -> Unit,
) : AbstractComposeView(context) {
@Composable
override fun Content() {
CustomKeyboard(onKeyPressed)
}
}
class IMEService : InputMethodService() {
override fun onCreateInputView(): View {
val view = ComposeKeyboardView(this, onKeyPressed = this::doSomethingWith)
// ...
return view
}
private fun doSomethingWith(mData: String) {
currentInputConnection?.commitText(mData, 1)
}
}
If you plan to add many more functions to the IMEService
that you will have to also call, then you can just pass the IMEService
(or some interface that IMEService
implements) into your composables and then call its members normally. Using an interface over an actual class would make it possible to @Preview
the CustomKeyboard
composable.
@Composable
fun CustomKeyboard(imeService: IMEService) {
//...
MyButton(mText = "A") {
inputVal = it
imeService.doSomethingWith(it)
}
// ...
}
class ComposeKeyboardView(private val imeService: IMEService) : AbstractComposeView(imeService) {
@Composable
override fun Content() {
CustomKeyboard(imeService)
}
}
class IMEService : InputMethodService() {
override fun onCreateInputView(): View {
val view = ComposeKeyboardView(this)
// ...
return view
}
fun doSomethingWith(mData: String) {
currentInputConnection?.commitText(mData, 1)
}
}