package com.rizoma.trazo import android.inputmethodservice.InputMethodService import android.text.InputType import android.view.KeyEvent import android.view.View import android.view.inputmethod.EditorInfo import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeViewModelStoreOwner import androidx.savedstate.SavedStateRegistry import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.rizoma.trazo.ime.ImeHost import com.rizoma.trazo.ime.KeyboardController import com.rizoma.trazo.ui.KeyboardScreen import com.rizoma.trazo.ui.theme.TrazoTheme /** * Trazo IME. Hospeda un teclado Compose dentro del InputMethodService, para lo cual * el servicio actĂșa como Lifecycle/ViewModelStore/SavedState owner del ComposeView. */ class TrazoImeService : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner { private val lifecycleRegistry = LifecycleRegistry(this) override val lifecycle: Lifecycle get() = lifecycleRegistry private val store = ViewModelStore() override val viewModelStore: ViewModelStore get() = store private val savedStateController = SavedStateRegistryController.create(this) override val savedStateRegistry: SavedStateRegistry get() = savedStateController.savedStateRegistry private var editorInputType: Int = InputType.TYPE_CLASS_TEXT private val host = object : ImeHost { override fun commit(text: String) { currentInputConnection?.commitText(text, 1) } override fun backspace() { val ic = currentInputConnection ?: return val selected = ic.getSelectedText(0) if (selected.isNullOrEmpty()) ic.deleteSurroundingText(1, 0) else ic.commitText("", 1) } override fun newline() { val action = currentInputEditorInfo?.imeOptions ?: 0 val opt = action and EditorInfo.IME_MASK_ACTION if (opt != EditorInfo.IME_ACTION_NONE && opt != EditorInfo.IME_ACTION_UNSPECIFIED && (action and EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0 ) { currentInputConnection?.performEditorAction(opt) } else { currentInputConnection?.sendKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER)) currentInputConnection?.sendKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER)) } } override fun textBeforeCursor(n: Int): CharSequence = currentInputConnection?.getTextBeforeCursor(n, 0) ?: "" override fun cursorCapsMode(): Int = currentInputConnection?.getCursorCapsMode(editorInputType) ?: 0 } private val controller = KeyboardController(host) override fun onCreate() { super.onCreate() savedStateController.performRestore(null) lifecycleRegistry.currentState = Lifecycle.State.CREATED } override fun onCreateInputView(): View { val view = ComposeView(this).apply { setViewTreeLifecycleOwner(this@TrazoImeService) setViewTreeViewModelStoreOwner(this@TrazoImeService) setViewTreeSavedStateRegistryOwner(this@TrazoImeService) setContent { TrazoTheme { KeyboardScreen(controller) } } } return view } override fun onStartInputView(info: EditorInfo?, restarting: Boolean) { super.onStartInputView(info, restarting) editorInputType = info?.inputType ?: InputType.TYPE_CLASS_TEXT lifecycleRegistry.currentState = Lifecycle.State.RESUMED controller.syncAutoShift() } override fun onFinishInputView(finishingInput: Boolean) { super.onFinishInputView(finishingInput) lifecycleRegistry.currentState = Lifecycle.State.CREATED } override fun onDestroy() { lifecycleRegistry.currentState = Lifecycle.State.DESTROYED store.clear() super.onDestroy() } }