Code clean up and building a common parent class for WebRTC calls

This commit is contained in:
Vitor Pamplona
2026-04-06 12:02:08 -04:00
parent 2e3de795b6
commit 5a36d0b7ae
10 changed files with 48 additions and 73 deletions
@@ -229,14 +229,6 @@ class CallController(
} }
// ---- Call initiation (caller side) ---- // ---- Call initiation (caller side) ----
fun initiateCall(
peerPubKey: String,
callType: CallType,
) {
initiateCallInternal(setOf(peerPubKey), callType)
}
fun initiateGroupCall( fun initiateGroupCall(
peerPubKeys: Set<String>, peerPubKeys: Set<String>,
callType: CallType, callType: CallType,
@@ -49,28 +49,17 @@ fun ChatroomScreen(
nav: INav, nav: INav,
) { ) {
val context = LocalContext.current val context = LocalContext.current
val isGroupChat = roomId.users.size > 1
val isCallSupported = roomId.users.size <= 5 val isCallSupported = roomId.users.size <= 5
val startVoiceCall = val startVoiceCall =
rememberCallWithPermission(context) { rememberCallWithPermission(context) {
ActiveCallHolder.set(accountViewModel.callManager, accountViewModel.callController, accountViewModel) ActiveCallHolder.set(accountViewModel.callManager, accountViewModel.callController, accountViewModel)
if (isGroupChat) { accountViewModel.callController?.initiateGroupCall(roomId.users.toSet(), CallType.VOICE)
accountViewModel.callController?.initiateGroupCall(roomId.users.toSet(), CallType.VOICE)
} else {
val peerPubKey = roomId.users.firstOrNull() ?: return@rememberCallWithPermission
accountViewModel.callController?.initiateCall(peerPubKey, CallType.VOICE)
}
CallActivity.launch(context) CallActivity.launch(context)
} }
val startVideoCall = val startVideoCall =
rememberCallWithPermission(context, isVideo = true) { rememberCallWithPermission(context, isVideo = true) {
ActiveCallHolder.set(accountViewModel.callManager, accountViewModel.callController, accountViewModel) ActiveCallHolder.set(accountViewModel.callManager, accountViewModel.callController, accountViewModel)
if (isGroupChat) { accountViewModel.callController?.initiateGroupCall(roomId.users.toSet(), CallType.VIDEO)
accountViewModel.callController?.initiateGroupCall(roomId.users.toSet(), CallType.VIDEO)
} else {
val peerPubKey = roomId.users.firstOrNull() ?: return@rememberCallWithPermission
accountViewModel.callController?.initiateCall(peerPubKey, CallType.VIDEO)
}
CallActivity.launch(context) CallActivity.launch(context)
} }
@@ -27,11 +27,6 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -52,7 +47,6 @@ fun ChatroomHeader(
room: ChatroomKey, room: ChatroomKey,
modifier: Modifier = StdPadding, modifier: Modifier = StdPadding,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onCallClick: ((String) -> Unit)? = null,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
if (room.users.size == 1) { if (room.users.size == 1) {
@@ -63,7 +57,6 @@ fun ChatroomHeader(
modifier = modifier, modifier = modifier,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onClick = onClick, onClick = onClick,
onCallClick = onCallClick?.let { callback -> { callback(baseUser.pubkeyHex) } },
) )
} }
} }
@@ -83,7 +76,6 @@ fun UserChatroomHeader(
modifier: Modifier = StdPadding, modifier: Modifier = StdPadding,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
onClick: () -> Unit, onClick: () -> Unit,
onCallClick: (() -> Unit)? = null,
) { ) {
Column( Column(
Modifier Modifier
@@ -108,20 +100,6 @@ fun UserChatroomHeader(
) { ) {
UsernameDisplay(baseUser, accountViewModel = accountViewModel) UsernameDisplay(baseUser, accountViewModel = accountViewModel)
} }
if (onCallClick != null) {
IconButton(
onClick = onCallClick,
modifier = Modifier.size(40.dp),
) {
Icon(
imageVector = Icons.Default.Call,
contentDescription = "Voice call",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp),
)
}
}
} }
} }
} }
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
@@ -29,7 +28,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@Immutable @Immutable
@@ -40,9 +38,7 @@ class CallAnswerEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun sdpAnswer() = content fun sdpAnswer() = content
/** All pubkeys referenced by `p` tags in this event. */ /** All pubkeys referenced by `p` tags in this event. */
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
@@ -29,7 +28,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@Immutable @Immutable
@@ -40,9 +38,7 @@ class CallHangupEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun reason() = content.ifEmpty { null } fun reason() = content.ifEmpty { null }
/** All pubkeys referenced by `p` tags in this event. */ /** All pubkeys referenced by `p` tags in this event. */
@@ -21,13 +21,11 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@Immutable @Immutable
@@ -38,9 +36,7 @@ class CallIceCandidateEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun candidateJson() = content fun candidateJson() = content
fun candidateSdp(): String = CANDIDATE_REGEX.find(content)?.groupValues?.get(1) ?: "" fun candidateSdp(): String = CANDIDATE_REGEX.find(content)?.groupValues?.get(1) ?: ""
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
@@ -29,7 +28,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallType import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallType
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallTypeTag import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallTypeTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@@ -43,9 +41,7 @@ class CallOfferEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun callType() = tags.firstNotNullOfOrNull(CallTypeTag::parse) fun callType() = tags.firstNotNullOfOrNull(CallTypeTag::parse)
fun sdpOffer() = content fun sdpOffer() = content
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
@@ -29,7 +28,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@Immutable @Immutable
@@ -40,9 +38,7 @@ class CallRejectEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun reason() = content.ifEmpty { null } fun reason() = content.ifEmpty { null }
/** All pubkeys referenced by `p` tags in this event. */ /** All pubkeys referenced by `p` tags in this event. */
@@ -21,7 +21,6 @@
package com.vitorpamplona.quartz.nipACWebRtcCalls.events package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
@@ -29,7 +28,6 @@ import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTag import com.vitorpamplona.quartz.nip01Core.tags.people.pTag
import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds import com.vitorpamplona.quartz.nip01Core.tags.people.pTagIds
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.callId
@Immutable @Immutable
@@ -40,9 +38,7 @@ class CallRenegotiateEvent(
tags: Array<Array<String>>, tags: Array<Array<String>>,
content: String, content: String,
sig: HexKey, sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) { ) : WebRTCEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
fun sdpOffer() = content fun sdpOffer() = content
/** All pubkeys referenced by `p` tags in this event. */ /** All pubkeys referenced by `p` tags in this event. */
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.quartz.nipACWebRtcCalls.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Kind
import com.vitorpamplona.quartz.nipACWebRtcCalls.tags.CallIdTag
@Immutable
abstract class WebRTCEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
kind: Kind,
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, kind, tags, content, sig) {
fun callId() = tags.firstNotNullOfOrNull(CallIdTag::parse)
}