fix: terminal input serialization
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 0.3.4
|
||||||
|
|
||||||
|
- 修复: 终端输入未正确串行化
|
||||||
|
|
||||||
## 0.3.3
|
## 0.3.3
|
||||||
|
|
||||||
- 新增: 语言选择
|
- 新增: 语言选择
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ android {
|
|||||||
applicationId = "io.github.miuzarte.scrcpyforandroid"
|
applicationId = "io.github.miuzarte.scrcpyforandroid"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 37
|
targetSdk = 37
|
||||||
versionCode = 29
|
versionCode = 30
|
||||||
versionName = "0.3.3"
|
versionName = "0.3.4"
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ import io.github.miuzarte.scrcpyforandroid.services.LocalInputService
|
|||||||
import io.github.miuzarte.scrcpyforandroid.storage.BundleSyncDelegate
|
import io.github.miuzarte.scrcpyforandroid.storage.BundleSyncDelegate
|
||||||
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
import io.github.miuzarte.scrcpyforandroid.storage.Storage.appSettings
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import top.yukonga.miuix.kmp.basic.SnackbarResult
|
import top.yukonga.miuix.kmp.basic.SnackbarResult
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
@@ -67,33 +67,43 @@ internal class TerminalViewModel : ViewModel() {
|
|||||||
private val _shellConnecting = MutableStateFlow(false)
|
private val _shellConnecting = MutableStateFlow(false)
|
||||||
val shellConnecting: StateFlow<Boolean> = _shellConnecting.asStateFlow()
|
val shellConnecting: StateFlow<Boolean> = _shellConnecting.asStateFlow()
|
||||||
|
|
||||||
private val shellWriteMutex = Mutex()
|
|
||||||
private var shellStream: AdbSocketStream? = null
|
private var shellStream: AdbSocketStream? = null
|
||||||
|
private var shellWriterJob: Job? = null
|
||||||
|
private var shellWriteChannel = Channel<ByteArray>(Channel.UNLIMITED)
|
||||||
|
|
||||||
val sessionHolder = arrayOfNulls<TerminalSession>(1)
|
val sessionHolder = arrayOfNulls<TerminalSession>(1)
|
||||||
|
|
||||||
fun writeBytesToShell(data: ByteArray, offset: Int, count: Int) {
|
private fun startShellWriter() {
|
||||||
val stream = shellStream
|
shellWriteChannel = Channel(Channel.UNLIMITED)
|
||||||
if (stream == null || !_shellReady.value || stream.closed) return
|
shellWriterJob = viewModelScope.launch(Dispatchers.IO) {
|
||||||
val payload = data.copyOfRange(offset, offset + count)
|
for (payload in shellWriteChannel) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
val stream = shellStream ?: break
|
||||||
val result = runCatching {
|
if (stream.closed) break
|
||||||
shellWriteMutex.withLock {
|
val result = runCatching {
|
||||||
stream.outputStream.write(payload)
|
stream.outputStream.write(payload)
|
||||||
stream.outputStream.flush()
|
stream.outputStream.flush()
|
||||||
}
|
}
|
||||||
}
|
if (result.isFailure) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
result.onFailure { error ->
|
result.exceptionOrNull()?.let { error ->
|
||||||
AppRuntime.snackbar(
|
AppRuntime.snackbar(
|
||||||
R.string.terminal_snack_input_failed,
|
R.string.terminal_snack_input_failed,
|
||||||
error.message ?: error.javaClass.simpleName,
|
error.message ?: error.javaClass.simpleName,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun writeBytesToShell(data: ByteArray, offset: Int, count: Int) {
|
||||||
|
if (!_shellReady.value) return
|
||||||
|
val payload = data.copyOfRange(offset, offset + count)
|
||||||
|
shellWriteChannel.trySend(payload)
|
||||||
|
}
|
||||||
|
|
||||||
fun writeClipboardToShell(context: Context) {
|
fun writeClipboardToShell(context: Context) {
|
||||||
val text = LocalInputService.getClipboardText(context)
|
val text = LocalInputService.getClipboardText(context)
|
||||||
if (!text.isNullOrBlank()) {
|
if (!text.isNullOrBlank()) {
|
||||||
@@ -192,6 +202,7 @@ internal class TerminalViewModel : ViewModel() {
|
|||||||
shellStream = stream
|
shellStream = stream
|
||||||
_shellReady.value = true
|
_shellReady.value = true
|
||||||
_shellConnecting.value = false
|
_shellConnecting.value = false
|
||||||
|
startShellWriter()
|
||||||
if (showKeyboardAfterConnect) requestFocus()
|
if (showKeyboardAfterConnect) requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +225,8 @@ internal class TerminalViewModel : ViewModel() {
|
|||||||
} finally {
|
} finally {
|
||||||
runCatching { stream.close() }
|
runCatching { stream.close() }
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
shellWriterJob?.cancel()
|
||||||
|
shellWriterJob = null
|
||||||
if (shellStream === stream) shellStream = null
|
if (shellStream === stream) shellStream = null
|
||||||
_shellReady.value = false
|
_shellReady.value = false
|
||||||
_shellConnecting.value = false
|
_shellConnecting.value = false
|
||||||
@@ -233,6 +246,8 @@ internal class TerminalViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun closeShell() {
|
fun closeShell() {
|
||||||
|
shellWriterJob?.cancel()
|
||||||
|
shellWriterJob = null
|
||||||
runCatching { shellStream?.close() }
|
runCatching { shellStream?.close() }
|
||||||
shellStream = null
|
shellStream = null
|
||||||
_shellReady.value = false
|
_shellReady.value = false
|
||||||
|
|||||||
Reference in New Issue
Block a user