Fix/android voice call (#16180)

* fix(android): handle missing audio permission in voice calls

- Avoid stop errors when voice capture was never started
- Dispatch voice call error dialogs on the main thread
- Explain how to enable Audio capture on the Screen share page

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(android): restore audio after failed voice call switches

* fix: translations

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: translatin

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix(i18n): match UI label capitalization in translated messages

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
fufesou
2026-09-14 12:24:33 +08:00
committed by GitHub
parent 34c340be4e
commit cdcb4d4b4c
57 changed files with 111 additions and 30 deletions

View File

@@ -51,6 +51,7 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
private var minBufferSize = 0
private var audioRecordStat = false
private var audioThread: Thread? = null
private var playbackCapturePending = false
@RequiresApi(Build.VERSION_CODES.M)
fun createAudioRecorder(inVoiceCall: Boolean, mediaProjection: MediaProjection?): Boolean {
@@ -193,6 +194,17 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
return audioRecorder?.audioSource == MediaRecorder.AudioSource.VOICE_COMMUNICATION
}
fun getVoiceCallStartError(): String {
return if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.RECORD_AUDIO
) != PackageManager.PERMISSION_GRANTED) {
"To start a voice call, enable \"Audio capture\" on the \"Screen share\" page."
} else {
"Failed to start voice call."
}
}
fun onVoiceCallStarted(mediaProjection: MediaProjection?): Boolean {
if (!isSupportVoiceCall()) {
return false
@@ -220,6 +232,7 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
if (it.getAudioSource() == MediaRecorder.AudioSource.VOICE_COMMUNICATION) {
return true
}
playbackCapturePending = true
}
audioRecordStat = false
audioThread?.join()
@@ -234,11 +247,10 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
@RequiresApi(Build.VERSION_CODES.M)
fun switchOutVoiceCall(mediaProjection: MediaProjection?): Boolean {
audioRecorder?.let {
if (it.getAudioSource() != MediaRecorder.AudioSource.VOICE_COMMUNICATION) {
return true
}
if (!isVoiceCallActive() && !playbackCapturePending) {
return true
}
playbackCapturePending = true
audioRecordStat = false
audioThread?.join()
@@ -246,7 +258,11 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
Log.e(logTag, "createAudioRecorder fail")
return false
}
return startAudioRecorder()
val started = startAudioRecorder()
if (started) {
playbackCapturePending = false
}
return started
}
fun tryReleaseAudio() {
@@ -256,6 +272,7 @@ class AudioRecordHandle(private var context: Context, private var isVideoStart:
audioRecordStat = false
audioThread?.join()
audioThread = null
playbackCapturePending = false
}
fun destroy() {

View File

@@ -969,7 +969,7 @@ class MainActivity : FlutterActivity() {
flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to start voice call."))
"text" to audioRecordHandle.getVoiceCallStartError()))
} else {
Log.d(logTag, "onVoiceCallStarted success")
}

View File

@@ -153,19 +153,13 @@ class MainService : Service() {
} else {
if (!switchOutVoiceCall()) {
Log.e(logTag, "switchOutVoiceCall fail")
MainActivity.flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to switch out voice call."))
showVoiceCallError("Failed to switch out voice call.")
}
}
} else {
if (!switchToVoiceCall()) {
Log.e(logTag, "switchToVoiceCall fail")
MainActivity.flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to "Failed to switch to voice call."))
showVoiceCallError(audioRecordHandle.getVoiceCallStartError())
}
}
} catch (e: JSONException) {
@@ -507,6 +501,15 @@ class MainService : Service() {
}
}
private fun showVoiceCallError(message: String) {
Handler(Looper.getMainLooper()).post {
MainActivity.flutterMethodChannel?.invokeMethod("msgbox", mapOf(
"type" to "custom-nook-nocancel-hasclose-error",
"title" to "Voice call",
"text" to message))
}
}
@Synchronized
private fun startMicrophoneCapture(startAudio: () -> Boolean): Boolean {
if (!setMicrophoneForegroundService(true)) {