Работающие звонки

This commit is contained in:
2026-03-27 03:12:04 +05:00
parent 9cca071bd8
commit b663450db5
10 changed files with 343 additions and 53 deletions

View File

@@ -6,7 +6,6 @@ import android.util.Log
import com.rosetta.messenger.data.MessageRepository
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.IdentityHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -137,8 +136,8 @@ object CallManager {
// E2EE (XChaCha20 — compatible with Desktop)
private var sharedKeyBytes: ByteArray? = null
private val senderEncryptors = IdentityHashMap<RtpSender, XChaCha20E2EE.Encryptor>()
private val receiverDecryptors = IdentityHashMap<RtpReceiver, XChaCha20E2EE.Decryptor>()
private val senderEncryptors = LinkedHashMap<String, XChaCha20E2EE.Encryptor>()
private val receiverDecryptors = LinkedHashMap<String, XChaCha20E2EE.Decryptor>()
private var pendingAudioSenderForE2ee: RtpSender? = null
private var lastRemoteOfferFingerprint: String = ""
private var lastLocalOfferFingerprint: String = ""
@@ -1063,11 +1062,22 @@ object CallManager {
breadcrumb("STATE[$marker] ${buildStateSnapshot()}")
}
private fun senderMapKey(sender: RtpSender): String {
val id = runCatching { sender.id() }.getOrNull().orEmpty()
return if (id.isNotBlank()) "sid:$id" else "sender@${System.identityHashCode(sender)}"
}
private fun receiverMapKey(receiver: RtpReceiver): String {
val id = runCatching { receiver.id() }.getOrNull().orEmpty()
return if (id.isNotBlank()) "rid:$id" else "recv@${System.identityHashCode(receiver)}"
}
private fun attachSenderE2EE(sender: RtpSender?) {
if (!e2eeAvailable) return
val key = sharedKeyBytes ?: return
if (sender == null) return
val existing = senderEncryptors[sender]
val mapKey = senderMapKey(sender)
val existing = senderEncryptors[mapKey]
if (existing != null) {
runCatching { sender.setFrameEncryptor(existing) }
return
@@ -1086,7 +1096,7 @@ object CallManager {
breadcrumb("4. calling sender.setFrameEncryptor…")
sender.setFrameEncryptor(enc)
breadcrumb("5. setFrameEncryptor OK!")
senderEncryptors[sender] = enc
senderEncryptors[mapKey] = enc
pendingAudioSenderForE2ee = null
} catch (e: Throwable) {
saveCrashReport("attachSenderE2EE failed", e)
@@ -1104,7 +1114,8 @@ object CallManager {
if (!e2eeAvailable) return
val key = sharedKeyBytes ?: return
if (receiver == null) return
val existing = receiverDecryptors[receiver]
val mapKey = receiverMapKey(receiver)
val existing = receiverDecryptors[mapKey]
if (existing != null) {
runCatching { receiver.setFrameDecryptor(existing) }
return
@@ -1123,7 +1134,7 @@ object CallManager {
breadcrumb("9. calling receiver.setFrameDecryptor…")
receiver.setFrameDecryptor(dec)
breadcrumb("10. setFrameDecryptor OK!")
receiverDecryptors[receiver] = dec
receiverDecryptors[mapKey] = dec
} catch (e: Throwable) {
saveCrashReport("attachReceiverE2EE failed", e)
Log.e(TAG, "E2EE: receiver decryptor failed", e)

View File

@@ -1,5 +1,6 @@
package com.rosetta.messenger.ui.crashlogs
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -8,13 +9,16 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.BugReport
import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -263,6 +267,8 @@ private fun CrashDetailScreen(
onDelete: () -> Unit
) {
var showDeleteDialog by remember { mutableStateOf(false) }
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
Scaffold(
topBar = {
@@ -274,6 +280,14 @@ private fun CrashDetailScreen(
}
},
actions = {
IconButton(
onClick = {
clipboardManager.setText(AnnotatedString(crashReport.content))
Toast.makeText(context, "Full log copied", Toast.LENGTH_SHORT).show()
}
) {
Icon(Icons.Default.ContentCopy, contentDescription = "Copy Full Log")
}
IconButton(onClick = { /* TODO: Share */ }) {
Icon(Icons.Default.Share, contentDescription = "Share")
}