refactor: moving call code around to service and viewmodel interactions

This commit is contained in:
jubb
2021-10-28 17:06:14 +11:00
parent a3cfd7f03b
commit 71bb04cb34
5 changed files with 148 additions and 36 deletions

View File

@@ -37,11 +37,11 @@ dependencies {
implementation 'androidx.gridlayout:gridlayout:1.0.0'
implementation 'androidx.exifinterface:exifinterface:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.4.0'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
implementation 'androidx.activity:activity-ktx:1.2.2'
implementation 'androidx.fragment:fragment-ktx:1.3.2'
implementation "androidx.core:core-ktx:1.3.2"

View File

@@ -7,8 +7,10 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.activity_webrtc_tests.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
@@ -22,10 +24,11 @@ import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.webrtc.CallViewModel
import org.webrtc.*
import java.util.*
@AndroidEntryPoint
class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection.Observer,
SdpObserver, RTCStatsCollectorCallback {
@@ -45,25 +48,14 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
}
private val eglBase by lazy { EglBase.create() }
private val surfaceHelper by lazy { SurfaceTextureHelper.create(Thread.currentThread().name, eglBase.eglBaseContext) }
private val viewModel by viewModels<CallViewModel>()
private val surfaceHelper by lazy { SurfaceTextureHelper.create(Thread.currentThread().name, viewModel.eglBaseContext) }
private val audioSource by lazy { connectionFactory.createAudioSource(MediaConstraints()) }
private val videoCapturer by lazy { createCameraCapturer(Camera2Enumerator(this)) }
private val acceptedCallMessageHashes = mutableSetOf<Int>()
private val connectionFactory by lazy {
val decoderFactory = DefaultVideoDecoderFactory(eglBase.eglBaseContext)
val encoderFactory = DefaultVideoEncoderFactory(eglBase.eglBaseContext, true, true)
PeerConnectionFactory.builder()
.setVideoDecoderFactory(decoderFactory)
.setVideoEncoderFactory(encoderFactory)
.setOptions(PeerConnectionFactory.Options())
.createPeerConnectionFactory()
}
private val candidates: MutableList<IceCandidate> = mutableListOf()
private val iceDebouncer = Debouncer(2_000)
@@ -90,20 +82,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
private lateinit var callAddress: Address
private lateinit var callId: UUID
private val peerConnection by lazy {
// TODO: in a lokinet world, ice servers shouldn't be needed as .loki addresses should suffice to p2p
val stun = PeerConnection.IceServer.builder("stun:freyr.getsession.org:5349").setTlsCertPolicy(PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK).createIceServer()
val turn = PeerConnection.IceServer.builder("turn:freyr.getsession.org:5349").setUsername("webrtc").setPassword("webrtc").setTlsCertPolicy(PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK).createIceServer()
val iceServers = mutableListOf(turn, stun)
val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply {
this.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED
this.candidateNetworkPolicy = PeerConnection.CandidateNetworkPolicy.ALL
// this.iceTransportsType = PeerConnection.IceTransportsType.RELAY
}
rtcConfig.keyType = PeerConnection.KeyType.ECDSA
connectionFactory.createPeerConnection(rtcConfig, this)!!
}
override fun onBackPressed() {
endCall()
}

View File

@@ -11,8 +11,11 @@ import org.thoughtcrime.securesms.dependencies.CallComponent
import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
import org.thoughtcrime.securesms.webrtc.CallManager
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
import java.sql.CallableStatement
import java.util.*
import javax.inject.Inject
import kotlin.properties.Delegates
import kotlin.properties.Delegates.observable
@AndroidEntryPoint
class WebRtcCallService: Service(), SignalAudioManager.EventListener {
@@ -22,6 +25,10 @@ class WebRtcCallService: Service(), SignalAudioManager.EventListener {
SignalAudioManager(this, this, CallComponent.get(this).callManagerCompat())
}
private enum class CallState {
STATE_IDLE, STATE_DIALING, STATE_ANSWERING, STATE_REMOTE_RINGING, STATE_LOCAL_RINGING, STATE_CONNECTED
}
companion object {
private const val ACTION_UPDATE = "UPDATE"
private const val ACTION_STOP = "STOP"
@@ -74,6 +81,10 @@ class WebRtcCallService: Service(), SignalAudioManager.EventListener {
}
}
private var state: CallState by observable(CallState.STATE_IDLE) { _, previousValue, newValue ->
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() {

View File

@@ -3,16 +3,44 @@ package org.thoughtcrime.securesms.webrtc
import android.content.Context
import com.android.mms.transaction.MessageSender
import org.thoughtcrime.securesms.database.Storage
import org.webrtc.*
import java.util.concurrent.Executors
import javax.inject.Inject
class CallManager(private val context: Context,
private val storage: Storage) {
private val storage: Storage): PeerConnection.Observer {
private val serviceExecutor = Executors.newSingleThreadExecutor()
private val networkExecutor = Executors.newSingleThreadExecutor()
private val eglBase: EglBase = EglBase.create()
private val connectionFactory by lazy {
val decoderFactory = DefaultVideoDecoderFactory(eglBase.eglBaseContext)
val encoderFactory = DefaultVideoEncoderFactory(eglBase.eglBaseContext, true, true)
PeerConnectionFactory.builder()
.setVideoDecoderFactory(decoderFactory)
.setVideoEncoderFactory(encoderFactory)
.setOptions(PeerConnectionFactory.Options())
.createPeerConnectionFactory()!!
}
private var peerConnection: PeerConnection? = null
private fun getPeerConnection(): PeerConnection {
val stun = PeerConnection.IceServer.builder("stun:freyr.getsession.org:5349").setTlsCertPolicy(PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK).createIceServer()
val turn = PeerConnection.IceServer.builder("turn:freyr.getsession.org:5349").setUsername("webrtc").setPassword("webrtc").setTlsCertPolicy(PeerConnection.TlsCertPolicy.TLS_CERT_POLICY_INSECURE_NO_CHECK).createIceServer()
val iceServers = mutableListOf(turn, stun)
val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply {
this.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.ENABLED
this.candidateNetworkPolicy = PeerConnection.CandidateNetworkPolicy.ALL
// this.iceTransportsType = PeerConnection.IceTransportsType.RELAY
}
rtcConfig.keyType = PeerConnection.KeyType.ECDSA
return connectionFactory.createPeerConnection(rtcConfig, this)!!
}
fun networkChange(networkAvailable: Boolean) {
@@ -26,6 +54,11 @@ class CallManager(private val context: Context,
}
fun callEnded() {
peerConnection?.close()
peerConnection = null
}
fun setAudioEnabled(isEnabled: Boolean) {
}
@@ -34,4 +67,47 @@ class CallManager(private val context: Context,
}
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
TODO("Not yet implemented")
}
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
TODO("Not yet implemented")
}
override fun onIceConnectionReceivingChange(p0: Boolean) {
TODO("Not yet implemented")
}
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
TODO("Not yet implemented")
}
override fun onIceCandidate(p0: IceCandidate?) {
TODO("Not yet implemented")
}
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
TODO("Not yet implemented")
}
override fun onAddStream(p0: MediaStream?) {
TODO("Not yet implemented")
}
override fun onRemoveStream(p0: MediaStream?) {
TODO("Not yet implemented")
}
override fun onDataChannel(p0: DataChannel?) {
TODO("Not yet implemented")
}
override fun onRenegotiationNeeded() {
TODO("Not yet implemented")
}
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
TODO("Not yet implemented")
}
}

View File

@@ -6,12 +6,13 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.webrtc.*
import javax.inject.Inject
@HiltViewModel
class CallViewModel @Inject constructor(
private val callManager: CallManager
): ViewModel() {
): ViewModel(), PeerConnection.Observer {
sealed class StateEvent {
data class AudioEnabled(val isEnabled: Boolean): StateEvent()
@@ -21,6 +22,8 @@ class CallViewModel @Inject constructor(
private val audioEnabledState = MutableStateFlow(StateEvent.AudioEnabled(true))
private val videoEnabledState = MutableStateFlow(StateEvent.VideoEnabled(false))
private val peerConnection = callManager.getPeerConnection(this)
// set up listeners for establishing connection toggling video / audio
init {
audioEnabledState.onEach { (enabled) -> callManager.setAudioEnabled(enabled) }
@@ -29,4 +32,48 @@ class CallViewModel @Inject constructor(
.launchIn(viewModelScope)
}
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
}
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
}
override fun onIceConnectionReceivingChange(p0: Boolean) {
}
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
}
override fun onIceCandidate(p0: IceCandidate?) {
}
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
}
override fun onAddStream(p0: MediaStream?) {
}
override fun onRemoveStream(p0: MediaStream?) {
}
override fun onDataChannel(p0: DataChannel?) {
}
override fun onRenegotiationNeeded() {
}
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
}
}