100 lines
3.3 KiB
Kotlin
100 lines
3.3 KiB
Kotlin
package calls.presentation
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
import androidx.lifecycle.viewModelScope
|
|
import calls.data.remote.WebRtcManager
|
|
import chats.data.remote.signalr.ChatEvent
|
|
import chats.data.remote.signalr.ChatHubClient
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import kotlinx.coroutines.flow.*
|
|
import kotlinx.coroutines.launch
|
|
import org.webrtc.*
|
|
import javax.inject.Inject
|
|
|
|
enum class CallStatus { IDLE, INCOMING, OUTGOING, CONNECTED, ENDED }
|
|
|
|
data class CallState(
|
|
val status: CallStatus = CallStatus.IDLE,
|
|
val chatId: String? = null,
|
|
val callerName: String = "",
|
|
val callerAvatar: String? = null,
|
|
val isMuted: Boolean = false,
|
|
val isSpeakerOn: Boolean = false,
|
|
val localVideoTrack: VideoTrack? = null,
|
|
val remoteVideoTrack: VideoTrack? = null
|
|
)
|
|
|
|
@HiltViewModel
|
|
class CallViewModel @Inject constructor(
|
|
private val webRtcManager: WebRtcManager,
|
|
private val signalrClient: ChatHubClient
|
|
) : ViewModel() {
|
|
|
|
private val _state = MutableStateFlow(CallState())
|
|
val state: StateFlow<CallState> = _state.asStateFlow()
|
|
|
|
init {
|
|
observeSignaling()
|
|
}
|
|
|
|
private fun observeSignaling() {
|
|
signalrClient.events
|
|
.onEach { event ->
|
|
when (event) {
|
|
is ChatEvent.CallIncoming -> onIncomingCall(event)
|
|
is ChatEvent.CallAnswered -> onCallAnswered(event)
|
|
is ChatEvent.IceCandidateReceived -> onIceCandidate(event)
|
|
is ChatEvent.CallEnded -> onCallEnded()
|
|
else -> Unit
|
|
}
|
|
}
|
|
.launchIn(viewModelScope)
|
|
}
|
|
|
|
private fun onIncomingCall(event: ChatEvent.CallIncoming) {
|
|
_state.update { it.copy(
|
|
status = CallStatus.INCOMING,
|
|
chatId = event.chatId,
|
|
callerName = "User ${event.from}" // TODO: Load actual user info
|
|
) }
|
|
// Set remote description from offer
|
|
webRtcManager.setRemoteDescription(event.offer, SessionDescription.Type.OFFER, object : SdpObserver {
|
|
override fun onCreateSuccess(p0: SessionDescription?) {}
|
|
override fun onSetSuccess() {}
|
|
override fun onCreateFailure(p0: String?) {}
|
|
override fun onSetFailure(p0: String?) {}
|
|
})
|
|
}
|
|
|
|
private fun onCallAnswered(event: ChatEvent.CallAnswered) {
|
|
_state.update { it.copy(status = CallStatus.CONNECTED) }
|
|
webRtcManager.setRemoteDescription(event.answer, SessionDescription.Type.ANSWER, object : SdpObserver {
|
|
override fun onCreateSuccess(p0: SessionDescription?) {}
|
|
override fun onSetSuccess() {}
|
|
override fun onCreateFailure(p0: String?) {}
|
|
override fun onSetFailure(p0: String?) {}
|
|
})
|
|
}
|
|
|
|
private fun onIceCandidate(event: ChatEvent.IceCandidateReceived) {
|
|
// Parse candidate JSON and add to peer connection
|
|
// webRtcManager.addIceCandidate(...)
|
|
}
|
|
|
|
private fun onCallEnded() {
|
|
_state.update { it.copy(status = CallStatus.ENDED) }
|
|
webRtcManager.close()
|
|
}
|
|
|
|
fun acceptCall() {
|
|
val chatId = _state.value.chatId ?: return
|
|
// Create answer and send via SignalR
|
|
}
|
|
|
|
fun endCall() {
|
|
val chatId = _state.value.chatId ?: return
|
|
// Send call_end via SignalR
|
|
onCallEnded()
|
|
}
|
|
}
|