Hey, I'm currently trying to implement an android ...
# compose
l
Hey, I'm currently trying to implement an android on-screen keyboard, doing the UI with Jetpack compose if possible. I've gotten errors about missing ViewTreeLifecycleOwner, ViewTreeViewModelStoreOwner, and ViewTreeSavedStateRegistryOwner.... is it even possible to do this? given that the keyboard stuff seems to be handled via a View, could these even have a ViewTreeViewModelStoreOwner and stuff like that? It seems as though the Compose stuff is bound to running inside of an activity, which - as far as i can tell - doesn't translate to the Input Method API
a
You can create these dependencies and add them to the associated view where you're using compose
you should be able to scope them to your input method service
l
how'd i do that? My initial idea was to put something like this into the onCreateInputView:
Copy code
override fun onCreateInputView(): View? {
        lifecycleRegistry.currentState = Lifecycle.State.CREATED
        return ComposeView(applicationContext).apply {
            ViewTreeLifecycleOwner.set(this@apply, this@MyInputMethodService)
            ViewTreeViewModelStoreOwner.set(this@apply, this@MyInputMethodService)
            ViewTreeSavedStateRegistryOwner.set(this@apply, this@MyInputMethodService)

            setContent {
                Text(text = "heyho")
            }
        }
    }
but then,... how do i properly implement ViewTreeViewModelStoreOwner, and even more confusingly, ViewTreeSavedStateRegistryOwner? There seem to be pretty much 0 docs on this
a
l
it seems to be invoking
onCreateInputView()
before it calls
onCreate
on the service blob thinking upside down because of that, i'm getting:
java.lang.IllegalStateException: You can consumeRestoredStateForKey only after super.onCreate of corresponding component
😕
a
eh, move the LifecycleRegistry along to created early then 🤷‍♂️
it's been years since I wrote an IME service and I don't remember if it even participates in savedInstanceState anyway
you're probably just stubbing this out to make some other downstream composables happy
l
likely,.... i tried setting the LifecycleRegistry as well as the savedRegistryController via
by lazy
, so they're initialized as late as possible, but still the same exception.... any other recommendations as to where to look?
a
did you move the LifecycleRegistry to the created state?
by lazy is probably just going to make your life harder here
l
how do you mean exactly? like how'd i move that to the state? i don't really know anything about the savedState stuff in general ^^'
a
when you create a
LifecycleRegistry
yourself it only moves to lifecycle states when you tell it to: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:li[…]dx/lifecycle/LifecycleRegistry.java;l=119?q=LifecycleRegistry
the saved state stuff is expecting the lifecycle to be at least created, from the error message
l
i'm now setting the lifecycleRegistry.currentState value to
CREATED
as soon as i create the instance.... still the same error blob thinking fast
already, thanks a ton for spending ur time helping me, btw
👍 1
a
at this point I'd have to just source dive a bit further 🙂 there shouldn't be any problems wiring this up from a dependency standpoint, but you're right in that these pieces aren't the most ergonomic things in the world
l
Copy code
class MyInputMethodService : InputMethodService(), LifecycleOwner, ViewModelStoreOwner,
    SavedStateRegistryOwner {

    private val lifecycleRegistry: LifecycleRegistry by lazy {
        LifecycleRegistry(this).apply {
            currentState = Lifecycle.State.CREATED
        }
    }
    private val viewModelStore = ViewModelStore()
    private val savedStateRegistryController: SavedStateRegistryController by lazy {
        SavedStateRegistryController.create(this)
    }

    override fun getLifecycle(): Lifecycle = lifecycleRegistry
    override fun getSavedStateRegistry() = savedStateRegistryController.savedStateRegistry
    override fun getViewModelStore() = viewModelStore

    override fun onCreateInputView(): View? {
        lifecycleRegistry.currentState = Lifecycle.State.CREATED
        return ComposeView(applicationContext).apply {
            ViewTreeLifecycleOwner.set(this@apply, this@MyInputMethodService)
            ViewTreeViewModelStoreOwner.set(this@apply, this@MyInputMethodService)
            ViewTreeSavedStateRegistryOwner.set(this@apply, this@MyInputMethodService)

            setContent {
                Button(onClick = { sendKey() }) {
                    Text(text = "heyho")
                }
            }
        }
    }
// rest of the class
}
Is the minimum relevant bit of code here.... i also don't really get why it wouldn't work
a
set a breakpoint for the thrown exception and trace back from there
l
ProvideAndroidAmbients (being called somewherein the setContent stuff) seems to look at the saved state here,..... it's kinda hard to know what to look for there, honestly. It all get's called as soon as i try to open the soft keyboard, although i didn't see the actual onCreateInputView call there,....
h
@Leon K - Did you ever resolve this? I’m attempting to draw a ComposeView as an Overlay to display some data over top of another app. I’m hitting the same exceptions/issues.