Browse Source

fix(sync): instant input clearing, app switch auto-canvas reset, and bidirectional empty state synchronization

main
Ali Alavi 3 days ago
parent
commit
d7ad83b71f
  1. 36
      android/app/src/main/java/com/soniox/remotemic/MainActivity.kt
  2. 11
      mac/src/FocusedInputSync.swift

36
android/app/src/main/java/com/soniox/remotemic/MainActivity.kt

@ -89,7 +89,7 @@ class MainActivity : AppCompatActivity() {
setupUI()
checkPermissions()
AppLogger.log("Main", "اپلیکیشن همگام‌سازی صوتی سانی‌اوکس راه‌اندازی شد (نسخه بهینه‌شده فوق‌سریع v5.7)")
AppLogger.log("Main", "اپلیکیشن همگام‌سازی صوتی سانی‌اوکس راه‌اندازی شد (نسخه بهینه‌شده فوق‌سریع v5.8)")
// Initialize Collaborative WebSocket Client
streamDictationClient = StreamDictationClient(
@ -105,8 +105,8 @@ class MainActivity : AppCompatActivity() {
if (state.source != "android") {
val timeSinceLocalEdit = System.currentTimeMillis() - lastLocalUserEditTime
// If user is actively typing or recording right now on phone, do not override
if ((timeSinceLocalEdit < 1000L && binding.etTranscript.hasFocus()) || isCurrentlyRecording) {
// If user is actively typing right now on phone, do not override
if ((timeSinceLocalEdit < 600L && binding.etTranscript.hasFocus()) || isCurrentlyRecording) {
return@StreamDictationClient
}
@ -115,12 +115,8 @@ class MainActivity : AppCompatActivity() {
return@StreamDictationClient
}
// Ghost empty sync protection for opaque apps
if (state.text.isEmpty() && lastLocalText.isNotEmpty() && state.source == "mac") {
if (state.app.isNotEmpty() && state.app != "Desktop" && state.app != "App") {
binding.tvMacStatus.text = "متصل به ${state.app} 🖥️"
}
return@StreamDictationClient
if (state.app.isNotEmpty() && state.app != "Desktop" && state.app != "App") {
binding.tvMacStatus.text = "متصل به ${state.app} 🖥️"
}
if (state.text != lastLocalText) {
@ -128,21 +124,14 @@ class MainActivity : AppCompatActivity() {
lastLocalText = state.text
currentRevision = state.revision
val currentCursor = binding.etTranscript.selectionStart
binding.etTranscript.setText(state.text)
val targetCursor = if (binding.etTranscript.hasFocus() && currentCursor >= 0) {
currentCursor.coerceIn(0, state.text.length)
} else {
state.cursor.coerceIn(0, state.text.length)
}
val targetCursor = state.cursor.coerceIn(0, state.text.length)
binding.etTranscript.setSelection(targetCursor)
isApplyingRemoteUpdate = false
}
val wordCount = if (state.text.trim().isEmpty()) 0 else state.text.trim().split("\\s+".toRegex()).size
binding.tvCharCount.text = "$wordCount کلمه"
if (state.app.isNotEmpty() && state.app != "Desktop" && state.app != "App") {
binding.tvMacStatus.text = "متصل به ${state.app} 🖥️"
isApplyingRemoteUpdate = false
}
}
},
@ -297,13 +286,18 @@ class MainActivity : AppCompatActivity() {
isApplyingRemoteUpdate = true
binding.etTranscript.setText("")
lastLocalText = ""
binding.tvCharCount.text = "0 کلمه"
lastLocalUserEditTime = System.currentTimeMillis()
isApplyingRemoteUpdate = false
pendingSyncRunnable?.let { debounceHandler.removeCallbacks(it) }
pendingSyncRunnable = null
streamDictationClient?.sendPhoneEdit("", 0)
// Cleanly clear active box on Mac as well (Cmd+A -> Backspace)
streamDictationClient?.sendForceReplace("", 0)
lifecycleScope.launch {
sendDirectPaste("", 0)
}
binding.tvInstruction.text = getString(R.string.hold_to_speak)
binding.tvInstruction.setTextColor(ContextCompat.getColor(this, R.color.text_secondary))
}

11
mac/src/FocusedInputSync.swift

@ -173,12 +173,14 @@ public final class FocusedInputSync {
let appChanged = (appName != lastObservedApp)
if appChanged {
lastObservedApp = appName
lastObservedText = ""
lastObservedCursor = 0
}
guard let elem = elemOpt else {
if appChanged && !lastObservedText.isEmpty {
if appChanged {
localRevision += 1
return MacInputState(app: appName, text: lastObservedText, cursor: lastObservedCursor, selection: 0, revision: localRevision)
return MacInputState(app: appName, text: "", cursor: 0, selection: 0, revision: localRevision)
}
return nil
}
@ -214,11 +216,6 @@ public final class FocusedInputSync {
}
}
// Ghost empty suppression for non-native containers
if text.isEmpty && !lastObservedText.isEmpty && !isTextRole {
return nil
}
var cursor = text.count
var selLen = 0
var rangeObj: CFTypeRef?

Loading…
Cancel
Save