|
|
@ -89,7 +89,7 @@ class MainActivity : AppCompatActivity() { |
|
|
setupUI() |
|
|
setupUI() |
|
|
checkPermissions() |
|
|
checkPermissions() |
|
|
|
|
|
|
|
|
AppLogger.log("Main", "اپلیکیشن همگامسازی صوتی سانیاوکس راهاندازی شد (نسخه بهینهشده فوقسریع v5.7)") |
|
|
|
|
|
|
|
|
AppLogger.log("Main", "اپلیکیشن همگامسازی صوتی سانیاوکس راهاندازی شد (نسخه بهینهشده فوقسریع v5.8)") |
|
|
|
|
|
|
|
|
// Initialize Collaborative WebSocket Client |
|
|
// Initialize Collaborative WebSocket Client |
|
|
streamDictationClient = StreamDictationClient( |
|
|
streamDictationClient = StreamDictationClient( |
|
|
@ -105,8 +105,8 @@ class MainActivity : AppCompatActivity() { |
|
|
if (state.source != "android") { |
|
|
if (state.source != "android") { |
|
|
val timeSinceLocalEdit = System.currentTimeMillis() - lastLocalUserEditTime |
|
|
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 |
|
|
return@StreamDictationClient |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -115,34 +115,23 @@ class MainActivity : AppCompatActivity() { |
|
|
return@StreamDictationClient |
|
|
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") { |
|
|
if (state.app.isNotEmpty() && state.app != "Desktop" && state.app != "App") { |
|
|
binding.tvMacStatus.text = "متصل به ${state.app} 🖥️" |
|
|
binding.tvMacStatus.text = "متصل به ${state.app} 🖥️" |
|
|
} |
|
|
} |
|
|
return@StreamDictationClient |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (state.text != lastLocalText) { |
|
|
if (state.text != lastLocalText) { |
|
|
isApplyingRemoteUpdate = true |
|
|
isApplyingRemoteUpdate = true |
|
|
lastLocalText = state.text |
|
|
lastLocalText = state.text |
|
|
currentRevision = state.revision |
|
|
currentRevision = state.revision |
|
|
|
|
|
|
|
|
val currentCursor = binding.etTranscript.selectionStart |
|
|
|
|
|
binding.etTranscript.setText(state.text) |
|
|
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) |
|
|
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 |
|
|
isApplyingRemoteUpdate = true |
|
|
binding.etTranscript.setText("") |
|
|
binding.etTranscript.setText("") |
|
|
lastLocalText = "" |
|
|
lastLocalText = "" |
|
|
|
|
|
binding.tvCharCount.text = "0 کلمه" |
|
|
lastLocalUserEditTime = System.currentTimeMillis() |
|
|
lastLocalUserEditTime = System.currentTimeMillis() |
|
|
isApplyingRemoteUpdate = false |
|
|
isApplyingRemoteUpdate = false |
|
|
|
|
|
|
|
|
pendingSyncRunnable?.let { debounceHandler.removeCallbacks(it) } |
|
|
pendingSyncRunnable?.let { debounceHandler.removeCallbacks(it) } |
|
|
pendingSyncRunnable = null |
|
|
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.text = getString(R.string.hold_to_speak) |
|
|
binding.tvInstruction.setTextColor(ContextCompat.getColor(this, R.color.text_secondary)) |
|
|
binding.tvInstruction.setTextColor(ContextCompat.getColor(this, R.color.text_secondary)) |
|
|
} |
|
|
} |
|
|
|