I'm currently creating a keyboard that needs to be able to get Data from a View Model that I've created.
The problem arises to the fact that the Keyboard is not able to be recomposed when any type of state is changed.
Is there a way to be able to implement recomposes/updates as any state is changed for the Abstract Compose View?
For reference this is the code I'm using for the InputMethodService
class IMEService:
InputMethodService(),
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner {
private val _lifecycleRegistry: LifecycleRegistry by lazy { LifecycleRegistry(this) }
private val _store by lazy { ViewModelStore() }
override fun getLifecycle(): Lifecycle = _lifecycleRegistry
override fun getViewModelStore(): ViewModelStore = _store
override val savedStateRegistry: SavedStateRegistry = SavedStateRegistryController.create(this).savedStateRegistry
private fun handleLifecycleEvent(event: Lifecycle.Event) =
_lifecycleRegistry.handleLifecycleEvent(event)
@CallSuper
override fun onCreate() {
super.onCreate()
// You must call performAttach() before calling performRestore(Bundle)
savedStateRegistry.performAttach(lifecycle)
savedStateRegistry.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
@CallSuper
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
@CallSuper
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
lateinit var viewModel: RepoViewModel
override fun onCreateInputView(): View {
val app = (application as Nitroless)
val view = ComposeKeyboardView(this)
viewModel = ViewModelProvider(this, RepoViewModelFactory(app.repository, app.communityReposRepository)).get(RepoViewModel::class.java)
this.attachToDecorView(
window?.window?.decorView
)
return view
}
fun attachToDecorView(decorView: View?) {
if (decorView == null) return
ViewTreeLifecycleOwner.set(decorView, this)
ViewTreeViewModelStoreOwner.set(decorView, this)
decorView.setViewTreeSavedStateRegistryOwner(this)
}
}
As you can see I've added the View Model as a Provider onCreateInputView() function. The problem is not that I'm not able to access it, the problem is I can't seem to update the ComposeKeyboardView.
Here's my ComposeKeyboardView -
class ComposeKeyboardView(context: Context): AbstractComposeView(context) {
@Composable
override fun Content() {
val viewModel: RepoViewModel = (context as IMEService).viewModel
KeyboardScreen()
}
}
I found a solution, I was looking at the Docs for InputMethodService and found setInputView(View: View?), this way I can force refresh the compose view onto the keyboard.
So I just do
(context as IMEService).setInputView(ComposeKeyboardVie(context))
I also found a way to use Hilt inside the IMEService class, by using @AndroidEntryPoint
annotation for the Service and using @Inject
annotation to inject the View Model.
@AndroidEntryPoint
class IMEService:
InputMethodService(),
LifecycleOwner,
ViewModelStoreOwner,
SavedStateRegistryOwner {
private val _lifecycleRegistry: LifecycleRegistry by lazy { LifecycleRegistry(this) }
private val _store by lazy { ViewModelStore() }
override fun getLifecycle(): Lifecycle = _lifecycleRegistry
override fun getViewModelStore(): ViewModelStore = _store
override val savedStateRegistry: SavedStateRegistry = SavedStateRegistryController.create(this).savedStateRegistry
private fun handleLifecycleEvent(event: Lifecycle.Event) =
_lifecycleRegistry.handleLifecycleEvent(event)
@CallSuper
override fun onCreate() {
super.onCreate()
// You must call performAttach() before calling performRestore(Bundle)
savedStateRegistry.performAttach(lifecycle)
savedStateRegistry.performRestore(null)
handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}
@CallSuper
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
stopSelf()
}
@CallSuper
override fun onDestroy() {
super.onDestroy()
handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
}
@Inject
lateinit var repoViewModel: RepoViewModel
override fun onCreateInputView(): View {
val view = ComposeKeyboardView(this)
this.attachToDecorView(
window?.window?.decorView
)
return view
}
fun attachToDecorView(decorView: View?) {
if (decorView == null) return
ViewTreeLifecycleOwner.set(decorView, this)
ViewTreeViewModelStoreOwner.set(decorView, this)
decorView.setViewTreeSavedStateRegistryOwner(this)
}
}
And inside the App Module I just added
@Singleton
@Provides
fun providesRepoViewModel(repoDatabaseDao: RepoDatabaseDao, communityReposRepository: CommunityReposRepository) = RepoViewModel(repository = RepoRepository(repoDatabaseDao), communityReposRepository = communityReposRepository)