From 5a21f9c6686b7bcc0c82f58ae1cd8f2d03053339 Mon Sep 17 00:00:00 2001 From: Avently <7953703+avently@users.noreply.github.com> Date: Wed, 4 Sep 2024 00:58:06 +0900 Subject: [PATCH] changes --- .../common/views/call/CallView.android.kt | 29 ++-- .../views/chatlist/ChatListView.android.kt | 3 +- .../simplex/common/views/call/CallManager.kt | 2 +- .../chat/simplex/common/views/call/WebRTC.kt | 11 +- .../simplex/common/views/chat/ChatView.kt | 2 +- .../resources/assets/www/android/call.html | 20 +-- .../resources/assets/www/android/style.css | 11 ++ .../commonMain/resources/assets/www/call.js | 152 +++++++++-------- .../resources/assets/www/desktop/call.html | 28 ++-- .../resources/assets/www/desktop/style.css | 11 ++ .../resources/assets/www/desktop/ui.js | 6 +- .../common/views/call/CallView.desktop.kt | 17 +- .../views/chatlist/ChatListView.desktop.kt | 3 +- .../simplex-chat-webrtc/src/android/call.html | 20 +-- .../simplex-chat-webrtc/src/android/style.css | 11 ++ packages/simplex-chat-webrtc/src/call.ts | 153 +++++++++++------- .../simplex-chat-webrtc/src/desktop/call.html | 28 ++-- .../simplex-chat-webrtc/src/desktop/style.css | 11 ++ .../simplex-chat-webrtc/src/desktop/ui.ts | 6 +- 19 files changed, 316 insertions(+), 208 deletions(-) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt index 5088869a4e..8e04d85033 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt @@ -119,7 +119,7 @@ actual fun ActiveCallView() { val callRh = call.remoteHostId when (val r = apiMsg.resp) { is WCallResponse.Capabilities -> withBGApi { - val callType = CallType(call.localMedia, r.capabilities) + val callType = CallType(call.initialCallType, r.capabilities) chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType) updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { @@ -132,7 +132,7 @@ actual fun ActiveCallView() { activeCallWaitDeliveryReceipt(scope) } is WCallResponse.Offer -> withBGApi { - chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities) + chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.initialCallType, r.capabilities) updateActiveCall(call) { it.copy(callState = CallState.OfferSent, localCapabilities = r.capabilities) } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { // Starting is delayed to make Android <= 11 working good with Bluetooth @@ -185,16 +185,19 @@ actual fun ActiveCallView() { updateActiveCall(call) { it.copy(callState = CallState.Negotiated) } is WCallCommand.Media -> { updateActiveCall(call) { - when (cmd.media) { - CallMediaType.Video -> it.copy(videoEnabled = cmd.enable, localMedia = if (cmd.enable) CallMediaType.Video else CallMediaType.Audio) - CallMediaType.Audio -> it.copy(audioEnabled = cmd.enable) + val sources = it.localMediaSources + when (cmd.source) { + CallMediaSource.Mic -> it.copy(localMediaSources = sources.copy(mic = cmd.enable)) + CallMediaSource.Camera -> it.copy(localMediaSources = sources.copy(camera = cmd.enable)) + CallMediaSource.ScreenAudio -> it.copy(localMediaSources = sources.copy(screenAudio = cmd.enable)) + CallMediaSource.ScreenVideo -> it.copy(localMediaSources = sources.copy(screenVideo = cmd.enable)) } } } is WCallCommand.Camera -> { updateActiveCall(call) { it.copy(localCamera = cmd.camera) } - if (!call.audioEnabled) { - chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = false)) + if (!call.localMediaSources.mic) { + chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = false)) } } is WCallCommand.End -> { @@ -248,9 +251,9 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceM devices = remember { callAudioDeviceManager.devices }.value, currentDevice = remember { callAudioDeviceManager.currentDevice }, dismiss = { withBGApi { chatModel.callManager.endCall(call) } }, - toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) }, + toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Mic, enable = !call.localMediaSources.mic)) }, selectDevice = { callAudioDeviceManager.selectDevice(it.id) }, - toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) }, + toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Camera, enable = !call.localMediaSources.camera)) }, toggleSound = { val enableSpeaker = callAudioDeviceManager.currentDevice.value?.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE val preferredInternalDevice = callAudioDeviceManager.devices.value.firstOrNull { it.type == if (enableSpeaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE } @@ -365,7 +368,7 @@ private fun ActiveCallOverlayLayout( IconButton(onClick = dismiss, enabled = enabled) { Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp)) } - if (call.videoEnabled) { + if (call.localMediaSources.camera) { ControlButton(call, painterResource(MR.images.ic_flip_camera_android_filled), MR.strings.icon_descr_flip_camera, enabled, flipCamera) ControlButton(call, painterResource(MR.images.ic_videocam_filled), MR.strings.icon_descr_video_off, enabled, toggleVideo) } else { @@ -390,7 +393,7 @@ private fun ControlButton(call: Call, icon: Painter, iconText: StringResource, e @Composable private fun ToggleAudioButton(call: Call, enabled: Boolean = true, toggleAudio: () -> Unit) { - if (call.audioEnabled) { + if (call.localMediaSources.mic) { ControlButton(call, painterResource(MR.images.ic_mic), MR.strings.icon_descr_audio_off, enabled, toggleAudio) } else { ControlButton(call, painterResource(MR.images.ic_mic_off), MR.strings.icon_descr_audio_on, enabled, toggleAudio) @@ -762,7 +765,7 @@ fun PreviewActiveCallOverlayVideo() { userProfile = Profile.sampleData, contact = Contact.sampleData, callState = CallState.Negotiated, - localMedia = CallMediaType.Video, + initialCallType = CallMediaType.Video, peerMediaSources = CallMediaSources(), callUUID = "", connectionInfo = ConnectionInfo( @@ -792,7 +795,7 @@ fun PreviewActiveCallOverlayAudio() { userProfile = Profile.sampleData, contact = Contact.sampleData, callState = CallState.Negotiated, - localMedia = CallMediaType.Audio, + initialCallType = CallMediaType.Audio, peerMediaSources = CallMediaSources(), callUUID = "", connectionInfo = ConnectionInfo( diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt index cb72e5dde3..4616b67cdb 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.android.kt @@ -54,8 +54,7 @@ actual fun ActiveCallInteractiveArea(call: Call) { .align(Alignment.BottomCenter), contentAlignment = Alignment.Center ) { - val media = call.peerMediaSources ?: call.localMedia - if (media == CallMediaType.Video) { + if (call.supportsVideo()) { Icon(painterResource(MR.images.ic_videocam_filled), null, Modifier.size(27.dp).offset(x = 2.5.dp, y = 2.dp), tint = Color.White) } else { Icon(painterResource(MR.images.ic_call_filled), null, Modifier.size(27.dp).offset(x = -0.5.dp, y = 2.dp), tint = Color.White) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt index 7704509148..405094f72a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/CallManager.kt @@ -49,7 +49,7 @@ class CallManager(val chatModel: ChatModel) { contact = invitation.contact, callUUID = invitation.callUUID, callState = CallState.InvitationAccepted, - localMedia = invitation.callType.media, + initialCallType = invitation.callType.media, sharedKey = invitation.sharedKey, ) showCallView.value = true diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt index ef6b8b5da2..a798829910 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/call/WebRTC.kt @@ -2,6 +2,7 @@ package chat.simplex.common.views.call import chat.simplex.common.views.helpers.generalGetString import chat.simplex.common.model.* +import chat.simplex.common.platform.appPlatform import chat.simplex.res.MR import kotlinx.datetime.Instant import kotlinx.serialization.SerialName @@ -15,12 +16,11 @@ data class Call( val contact: Contact, val callUUID: String?, val callState: CallState, - val localMedia: CallMediaType, + val initialCallType: CallMediaType, + val localMediaSources: CallMediaSources = CallMediaSources(mic = true, camera = initialCallType == CallMediaType.Video && appPlatform.isAndroid), val localCapabilities: CallCapabilities? = null, val peerMediaSources: CallMediaSources = CallMediaSources(), val sharedKey: String? = null, - val audioEnabled: Boolean = true, - val videoEnabled: Boolean = localMedia == CallMediaType.Video, var localCamera: VideoCamera = VideoCamera.User, val connectionInfo: ConnectionInfo? = null, var connectedAt: Instant? = null, @@ -35,6 +35,9 @@ data class Call( else -> generalGetString(if (!localEncrypted) MR.strings.status_no_e2e_encryption else if (sharedKey == null) MR.strings.status_contact_has_no_e2e_encryption else MR.strings.status_e2e_encrypted) } + val localMedia: CallMediaType + get() = if (localMediaSources.camera) CallMediaType.Video else CallMediaType.Audio + val hasMedia: Boolean get() = callState == CallState.OfferSent || callState == CallState.Negotiated || callState == CallState.Connected fun supportsVideo(): Boolean = peerMediaSources.hasVideo() || localMedia == CallMediaType.Video @@ -84,7 +87,7 @@ sealed class WCallCommand { @Serializable @SerialName("offer") data class Offer(val offer: String, val iceCandidates: String, val media: CallMediaType, val aesKey: String? = null, val iceServers: List? = null, val relay: Boolean? = null): WCallCommand() @Serializable @SerialName("answer") data class Answer (val answer: String, val iceCandidates: String): WCallCommand() @Serializable @SerialName("ice") data class Ice(val iceCandidates: String): WCallCommand() - @Serializable @SerialName("media") data class Media(val media: CallMediaType, val enable: Boolean): WCallCommand() + @Serializable @SerialName("media") data class Media(val source: CallMediaSource, val enable: Boolean): WCallCommand() @Serializable @SerialName("camera") data class Camera(val camera: VideoCamera): WCallCommand() @Serializable @SerialName("description") data class Description(val state: String, val description: String): WCallCommand() @Serializable @SerialName("layout") data class Layout(val layout: LayoutType): WCallCommand() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index c8ad89609d..bd67a72af5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -545,7 +545,7 @@ fun startChatCall(remoteHostId: Long?, chatInfo: ChatInfo, media: CallMediaType) if (chatInfo is ChatInfo.Direct) { val contactInfo = chatModel.controller.apiContactInfo(remoteHostId, chatInfo.contact.contactId) val profile = contactInfo?.second ?: chatModel.currentUser.value?.profile?.toProfile() ?: return@withBGApi - chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callUUID = null, callState = CallState.WaitCapabilities, localMedia = media, userProfile = profile) + chatModel.activeCall.value = Call(remoteHostId = remoteHostId, contact = chatInfo.contact, callUUID = null, callState = CallState.WaitCapabilities, initialCallType = media, userProfile = profile) chatModel.showCallView.value = true chatModel.callCommand.add(WCallCommand.Capabilities(media)) } diff --git a/apps/multiplatform/common/src/commonMain/resources/assets/www/android/call.html b/apps/multiplatform/common/src/commonMain/resources/assets/www/android/call.html index c672ea32aa..51815e2995 100644 --- a/apps/multiplatform/common/src/commonMain/resources/assets/www/android/call.html +++ b/apps/multiplatform/common/src/commonMain/resources/assets/www/android/call.html @@ -6,6 +6,15 @@ + + - -