v1.2.4: native EditText input bridge, proper IME support

This commit is contained in:
茂之钳
2026-05-24 11:34:18 +00:00
parent 7c09aae659
commit b446478265
@@ -4,13 +4,17 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.ui.text.input.TextFieldValue
import android.text.TextWatcher
import android.widget.EditText
import android.view.KeyEvent as AndroidKeyEvent
import android.view.inputmethod.EditorInfo
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CheckCircle
import androidx.compose.material.icons.filled.Close
@@ -425,6 +429,7 @@ fun SshTerminalScreen(
var showForwardDialog by remember { mutableStateOf(false) }
val context = androidx.compose.ui.platform.LocalContext.current
val currentFontSize = remember { mutableStateOf(SshConfigStorage.loadFontSize(context)) }
val editTextRef = remember { mutableStateOf<EditText?>(null) }
val terminalFontSize = fontMap[currentFontSize.value] ?: 12
val terminalFontSizeSp = terminalFontSize.sp
@@ -469,12 +474,25 @@ fun SshTerminalScreen(
}
}
val focusModifier = remember { FocusRequester() }
// 连接后自动获取焦点
// 连接后自动让 EditText 获取焦点(弹出软键盘)
LaunchedEffect(state.connected) {
if (state.connected) {
focusModifier.requestFocus()
// 延迟一点确保 EditText 已创建
kotlinx.coroutines.delay(200)
editTextRef.value?.let { et ->
et.requestFocus()
val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
imm?.showSoftInput(et, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
}
}
}
// 点击终端区域让 EditText 重新获取焦点
val requestEditTextFocus: () -> Unit = {
editTextRef.value?.let { et ->
et.requestFocus()
val imm = context.getSystemService(android.content.Context.INPUT_METHOD_SERVICE) as? android.view.inputmethod.InputMethodManager
imm?.showSoftInput(et, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT)
}
}
@@ -483,30 +501,65 @@ fun SshTerminalScreen(
.fillMaxSize()
.background(bg)
.onKeyEvent { handleKeyEvent(it) }
.focusRequester(focusModifier)
.clickable { focusModifier.requestFocus() }
) {
// 透明输入框 — 极小的 1x30 dp,仅用来弹出软键盘
BasicTextField(
value = TextFieldValue(currentInput),
onValueChange = { newVal ->
currentInput = newVal.text
if (newVal.text.endsWith('\n')) {
val cmd = newVal.text.dropLast(1).trim()
if (cmd.isNotEmpty()) {
coroutineScope.launch {
manager.sendCommand(cmd)
// 原生 EditText 输入桥——任何输入法都能正确识别
AndroidView(
factory = { ctx ->
EditText(ctx).apply {
setTextColor(0x00000000) // 透明文字
setBackgroundColor(0x00000000) // 透明背景
textSize = 1f // 极小的字号
val lp = android.view.ViewGroup.LayoutParams(1, 30)
layoutParams = lp
// IME 选项:Done 动作(回车即发送)
imeOptions = EditorInfo.IME_ACTION_SEND or EditorInfo.IME_FLAG_NO_EXTRACT_UI
setSingleLine()
// 文本变化监听
addTextChangedListener(object : android.text.TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun afterTextChanged(s: android.text.Editable?) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val text = s?.toString() ?: ""
// 检测回车
val nlIdx = text.indexOf('\n')
if (nlIdx >= 0) {
val cmd = text.substring(0, nlIdx)
if (cmd.isNotEmpty()) {
coroutineScope.launch {
manager.sendCommand(cmd)
}
}
currentInput = ""
post { setText("") }
} else {
currentInput = text
}
}
})
// 硬件键盘回车
setOnKeyListener { _, keyCode, event ->
if (event.action == android.view.KeyEvent.ACTION_DOWN && keyCode == AndroidKeyEvent.KEYCODE_ENTER) {
val cmd = currentInput.trim()
if (cmd.isNotEmpty()) {
coroutineScope.launch {
manager.sendCommand(cmd)
}
}
currentInput = ""
post { setText("") }
true
} else {
false
}
}
currentInput = ""
// IDE 编辑器标记:用于焦点请求
tag = "terminal_input"
editTextRef.value = this
}
},
modifier = Modifier
.width(1.dp)
.height(30.dp)
.focusRequester(focusModifier),
singleLine = true,
textStyle = TextStyle(color = Color.Transparent, fontSize = 1.sp)
)
Column(
@@ -515,6 +568,7 @@ fun SshTerminalScreen(
.systemBarsPadding()
.imePadding()
.navigationBarsPadding()
.clickable { requestEditTextFocus() }
) {
// ── 终端顶栏 ──
Surface(