feat: added basic call functionality

This commit is contained in:
Harris 2021-08-30 16:39:47 +10:00
parent 5ea37254b9
commit 9e5e137919
14 changed files with 1775 additions and 240 deletions

View File

@ -53,7 +53,8 @@ dependencies {
implementation 'com.google.android.exoplayer:exoplayer-ui:2.9.1'
implementation 'org.conscrypt:conscrypt-android:2.0.0'
implementation 'org.signal:aesgcmprovider:0.0.3'
implementation 'org.whispersystems:webrtc-android:M77'
// implementation 'org.whispersystems:webrtc-android:M77'
implementation 'org.webrtc:google-webrtc:1.0.32006'
implementation "me.leolin:ShortcutBadger:1.1.16"
implementation 'se.emilsjolander:stickylistheaders:2.7.0'
implementation 'com.jpardogo.materialtabstrip:library:1.0.9'
@ -153,7 +154,7 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4'
}
def canonicalVersionCode = 220
def canonicalVersionCode = 222
def canonicalVersionName = "1.11.9"
def postFixSize = 10
@ -232,6 +233,7 @@ android {
buildTypes {
release {
debuggable true
minifyEnabled false
}
debug {

View File

@ -297,6 +297,7 @@
android:theme="@style/Theme.Session.DayNight.NoActionBar" />
<activity android:name="org.thoughtcrime.securesms.calls.WebRtcTestsActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:parentActivityName="org.thoughtcrime.securesms.home.HomeActivity"
android:theme="@style/Theme.Session.DayNight.FlatActionBar">
<meta-data

View File

@ -1,85 +1,136 @@
package org.thoughtcrime.securesms.calls
import android.content.Intent
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_webrtc_tests.*
import network.loki.messenger.R
import org.session.libsession.messaging.messages.control.CallMessage
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Debouncer
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.webrtc.*
import org.webrtc.RendererCommon.ScalingType
class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection.Observer,
SdpObserver, RendererCommon.RendererEvents {
SdpObserver {
companion object {
const val HD_VIDEO_WIDTH = 1280
const val HD_VIDEO_HEIGHT = 720
const val HD_VIDEO_WIDTH = 320
const val HD_VIDEO_HEIGHT = 240
const val CALL_ID = "call_id_session"
private const val LOCAL_TRACK_ID = "local_track"
private const val LOCAL_STREAM_ID = "local_track"
const val ACTION_ANSWER = "answer"
const val ACTION_UPDATE_ICE = "updateIce"
const val EXTRA_SDP = "WebRtcTestsActivity_EXTRA_SDP"
const val EXTRA_ADDRESS = "WebRtcTestsActivity_EXTRA_ADDRESS"
const val EXTRA_SDP_MLINE_INDEXES = "WebRtcTestsActivity_EXTRA_SDP_MLINE_INDEXES"
const val EXTRA_SDP_MIDS = "WebRtcTestsActivity_EXTRA_SDP_MIDS"
}
private class ProxyVideoSink : VideoSink {
private var target: VideoSink? = null
private val eglBase by lazy { EglBase.create() }
private val surfaceHelper by lazy { SurfaceTextureHelper.create(Thread.currentThread().name, eglBase.eglBaseContext) }
@Synchronized
override fun onFrame(frame: VideoFrame) {
if (target == null) {
Log.d("Loki-RTC", "Dropping frame in proxy because target is null.")
return
}
target!!.onFrame(frame)
}
private val connectionFactory by lazy {
@Synchronized
fun setTarget(target: VideoSink?) {
this.target = target
}
val decoderFactory = DefaultVideoDecoderFactory(eglBase.eglBaseContext)
val encoderFactory = DefaultVideoEncoderFactory(eglBase.eglBaseContext, true, true)
PeerConnectionFactory.builder()
.setVideoDecoderFactory(decoderFactory)
.setVideoEncoderFactory(encoderFactory)
.setOptions(PeerConnectionFactory.Options())
.createPeerConnectionFactory()
}
private val connectionFactory by lazy { PeerConnectionFactory.builder().createPeerConnectionFactory() }
private val remoteVideoSink = ProxyVideoSink()
private val localVideoSink = ProxyVideoSink()
private val candidates: MutableList<IceCandidate> = mutableListOf()
private val iceDebouncer = Debouncer(2_000)
private lateinit var callAddress: Address
private val peerConnection by lazy {
// TODO: in a lokinet world, ice servers shouldn't be needed as .loki addresses should suffice to p2p
val server = PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()
val server1 = PeerConnection.IceServer.builder("stun:stun1.l.google.com:19302").createIceServer()
val server2 = PeerConnection.IceServer.builder("stun:stun2.l.google.com:19302").createIceServer()
val server3 = PeerConnection.IceServer.builder("stun:stun3.l.google.com:19302").createIceServer()
val server4 = PeerConnection.IceServer.builder("stun:stun4.l.google.com:19302").createIceServer()
val rtcConfig = PeerConnection.RTCConfiguration(listOf(server, server1, server2, server3, server4))
rtcConfig.keyType = PeerConnection.KeyType.ECDSA
connectionFactory.createPeerConnection(rtcConfig, this)!!
}
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
super.onCreate(savedInstanceState, ready)
setContentView(R.layout.activity_webrtc_tests)
val server = PeerConnection.IceServer.builder("stun:stun.l.google.com:19302").createIceServer()
local_renderer.run {
setMirror(true)
setEnableHardwareScaler(true)
init(eglBase.eglBaseContext, null)
}
remote_renderer.run {
setMirror(true)
setEnableHardwareScaler(true)
init(eglBase.eglBaseContext, null)
}
val rtcConfig = PeerConnection.RTCConfiguration(listOf(server))
rtcConfig.keyType = PeerConnection.KeyType.ECDSA
rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
val peerConnection = connectionFactory.createPeerConnection(rtcConfig, this) ?: return
Log.d("Loki-RTC", "peer connecting?")
val stream = connectionFactory.createLocalMediaStream("stream")
val audioSource = connectionFactory.createAudioSource(MediaConstraints())
val audioTrack = connectionFactory.createAudioTrack("audio", audioSource)
val videoSource = connectionFactory.createVideoSource(false)
val videoTrack = connectionFactory.createVideoTrack("video", videoSource)
stream.addTrack(audioTrack)
stream.addTrack(videoTrack)
val remoteTrack = getRemoteVideoTrack(peerConnection) ?: return
videoTrack.addSink(localVideoSink)
remoteTrack.addSink(remoteVideoSink)
remoteTrack.setEnabled(true)
videoTrack.setEnabled(true)
val eglBase = EglBase.create()
local_renderer.init(eglBase.eglBaseContext, this)
local_renderer.setScalingType(ScalingType.SCALE_ASPECT_FILL)
remote_renderer.init(eglBase.eglBaseContext, this)
val videoCapturer = createCameraCapturer(Camera2Enumerator(this)) ?: kotlin.run { finish(); return }
val surfaceHelper = SurfaceTextureHelper.create("video-thread", eglBase.eglBaseContext)
surfaceHelper.startListening(localVideoSink)
videoCapturer.initialize(surfaceHelper, this, null)
videoCapturer.initialize(surfaceHelper, local_renderer.context, videoSource.capturerObserver)
videoCapturer.startCapture(HD_VIDEO_WIDTH, HD_VIDEO_HEIGHT, 10)
videoCapturer.startCapture(HD_VIDEO_WIDTH, HD_VIDEO_HEIGHT, 30)
peerConnection.createOffer(this, MediaConstraints())
val audioTrack = connectionFactory.createAudioTrack(LOCAL_TRACK_ID + "_audio", audioSource)
val videoTrack = connectionFactory.createVideoTrack(LOCAL_TRACK_ID, videoSource)
videoTrack.addSink(local_renderer)
val stream = connectionFactory.createLocalMediaStream(LOCAL_STREAM_ID)
stream.addTrack(videoTrack)
stream.addTrack(audioTrack)
peerConnection.addStream(stream)
// create either call or answer
if (intent.action == ACTION_ANSWER) {
callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return }
val offerSdp = intent.getStringArrayExtra(EXTRA_SDP)!![0]
peerConnection.setRemoteDescription(this, SessionDescription(SessionDescription.Type.OFFER, offerSdp))
peerConnection.createAnswer(this, MediaConstraints())
} else {
callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return }
peerConnection.createOffer(this, MediaConstraints())
}
}
private fun getRemoteVideoTrack(peerConnection: PeerConnection): VideoTrack? = peerConnection.transceivers.firstOrNull { it.receiver.track() is VideoTrack } as VideoTrack?
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent == null) return
callAddress = intent.getParcelableExtra(EXTRA_ADDRESS) ?: run { finish(); return }
when (intent.action) {
ACTION_ANSWER -> {
peerConnection.setRemoteDescription(this,
SessionDescription(SessionDescription.Type.ANSWER, intent.getStringArrayExtra(EXTRA_SDP)!![0])
)
}
ACTION_UPDATE_ICE -> {
val sdpIndexes = intent.getIntArrayExtra(EXTRA_SDP_MLINE_INDEXES)!!
val sdpMids = intent.getStringArrayExtra(EXTRA_SDP_MIDS)!!
val sdp = intent.getStringArrayExtra(EXTRA_SDP)!!
val amount = minOf(sdpIndexes.size, sdpMids.size)
(0 until amount).map { index ->
val candidate = IceCandidate(sdpMids[index], sdpIndexes[index], sdp[index])
peerConnection.addIceCandidate(candidate)
}
}
}
}
private fun createCameraCapturer(enumerator: CameraEnumerator): VideoCapturer? {
val deviceNames = enumerator.deviceNames
@ -96,8 +147,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
}
}
// Front facing camera not found, try something else
// Front facing camera not found, try something else
Log.d("Loki-RTC-vid", "Looking for other cameras.")
for (deviceName in deviceNames) {
@ -113,14 +162,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
return null
}
override fun onFirstFrameRendered() {
Log.d("Loki-RTC-vid", "first frame rendered")
}
override fun onFrameResolutionChanged(p0: Int, p1: Int, p2: Int) {
Log.d("Loki-RTC-vid", "frame resolution changed")
}
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
Log.d("Loki-RTC", "onSignalingChange: $p0")
}
@ -135,18 +176,48 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
Log.d("Loki-RTC", "onIceGatheringChange: $p0")
p0 ?: return
Log.d("Loki-RTC","sending IceCandidates of size: ${candidates.size}")
MessageSender.sendNonDurably(
CallMessage(SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES,
candidates.map { it.sdp },
candidates.map { it.sdpMLineIndex },
candidates.map { it.sdpMid }
),
callAddress
)
}
override fun onIceCandidate(p0: IceCandidate?) {
Log.d("Loki-RTC", "onIceCandidate: $p0")
override fun onIceCandidate(iceCandidate: IceCandidate?) {
Log.d("Loki-RTC", "onIceCandidate: $iceCandidate")
if (iceCandidate == null) return
// TODO: in a lokinet world, these might have to be filtered specifically to drop anything that is not .loki
peerConnection.addIceCandidate(iceCandidate)
candidates.add(iceCandidate)
iceDebouncer.publish {
MessageSender.sendNonDurably(
CallMessage(SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES,
candidates.map { it.sdp },
candidates.map { it.sdpMLineIndex },
candidates.map { it.sdpMid }
),
callAddress
)
}
}
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
Log.d("Loki-RTC", "onIceCandidatesRemoved: $p0")
peerConnection.removeIceCandidates(p0)
}
override fun onAddStream(p0: MediaStream?) {
Log.d("Loki-RTC", "onAddStream: $p0")
override fun onAddStream(remoteStream: MediaStream?) {
Log.d("Loki-RTC", "onAddStream: $remoteStream")
if (remoteStream == null) {
return
}
remoteStream.videoTracks.firstOrNull()?.addSink(remote_renderer)
}
override fun onRemoveStream(p0: MediaStream?) {
@ -165,8 +236,25 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
Log.d("Loki-RTC", "onAddTrack: $p0: $p1")
}
override fun onCreateSuccess(p0: SessionDescription) {
Log.d("Loki-RTC", "onCreateSuccess: ${p0.description}, ${p0.type}")
override fun onCreateSuccess(sdp: SessionDescription) {
Log.d("Loki-RTC", "onCreateSuccess: ${sdp.type}")
when (sdp.type) {
SessionDescription.Type.OFFER -> {
peerConnection.setLocalDescription(this, sdp)
MessageSender.sendNonDurably(
CallMessage(SignalServiceProtos.CallMessage.Type.OFFER, listOf(sdp.description), listOf(), listOf()),
callAddress
)
}
SessionDescription.Type.ANSWER -> {
peerConnection.setLocalDescription(this, sdp)
MessageSender.sendNonDurably(
CallMessage(SignalServiceProtos.CallMessage.Type.ANSWER, listOf(sdp.description), listOf(), listOf()),
callAddress
)
}
SessionDescription.Type.PRANSWER -> TODO("do the PR answer create success handling") // MessageSender.send()
}
}
override fun onSetSuccess() {

View File

@ -36,6 +36,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.*
import org.thoughtcrime.securesms.calls.WebRtcTestsActivity
import org.thoughtcrime.securesms.contacts.SelectContactsActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
@ -98,6 +99,11 @@ object ConversationMenuHelper {
inflater.inflate(R.menu.menu_conversation_notification_settings, menu)
}
// Call Tests
if (!isOpenGroup) {
inflater.inflate(R.menu.menu_conversation_call, menu)
}
// Search
val searchViewItem = menu.findItem(R.id.menu_search)
(context as ConversationActivityV2).searchViewItem = searchViewItem
@ -158,6 +164,7 @@ object ConversationMenuHelper {
R.id.menu_unmute_notifications -> { unmute(context, thread) }
R.id.menu_mute_notifications -> { mute(context, thread) }
R.id.menu_notification_settings -> { setNotifyType(context, thread) }
R.id.menu_call -> { call(context, thread) }
}
return true
}
@ -174,6 +181,13 @@ object ConversationMenuHelper {
searchViewModel.onSearchOpened()
}
private fun call(context: Context, thread: Recipient) {
val intent = Intent(context, WebRtcTestsActivity::class.java)
intent.putExtra(WebRtcTestsActivity.EXTRA_ADDRESS, thread.address)
val activity = context as AppCompatActivity
activity.startActivity(intent)
}
@SuppressLint("StaticFieldLeak")
private fun addShortcut(context: Context, thread: Recipient) {
object : AsyncTask<Void?, Void?, IconCompat?>() {

View File

@ -12,6 +12,7 @@ import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.Toast
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
@ -32,8 +33,10 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.session.libsession.messaging.jobs.JobQueue
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.utilities.WebRtcUtils
import org.session.libsession.utilities.*
import org.session.libsession.utilities.Util
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext
@ -53,6 +56,7 @@ import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.onboarding.SeedActivity
import org.thoughtcrime.securesms.onboarding.SeedReminderViewDelegate
import org.thoughtcrime.securesms.preferences.SettingsActivity
import org.thoughtcrime.securesms.util.*
import java.io.IOException
import java.util.*
@ -131,6 +135,36 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
}
this.broadcastReceiver = broadcastReceiver
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
lifecycleScope.launchWhenCreated {
// web rtc channel handling
for (message in WebRtcUtils.SIGNAL_QUEUE) {
val sender = Address.fromSerialized(message.sender!!)
val intent = Intent(this@HomeActivity, WebRtcTestsActivity::class.java)
val bundle = bundleOf(
WebRtcTestsActivity.EXTRA_ADDRESS to sender,
)
if (message.sdps.isNotEmpty() && message.sdpMids.isEmpty()) {
// offer message
Log.d("Loki-RTC", "answer receive")
val sdps = message.sdps
intent.action = WebRtcTestsActivity.ACTION_ANSWER
bundle.putStringArray(WebRtcTestsActivity.EXTRA_SDP, sdps.toTypedArray())
} else if (message.sdpMids.isNotEmpty()) {
// ice candidates message
Log.d("Loki-RTC", "update ice intent")
val sdpMLineIndexes = message.sdpMLineIndexes
val sdpMids = message.sdpMids
val sdps = message.sdps
intent.action = WebRtcTestsActivity.ACTION_UPDATE_ICE
bundle.putStringArray(WebRtcTestsActivity.EXTRA_SDP, sdps.toTypedArray())
bundle.putIntArray(WebRtcTestsActivity.EXTRA_SDP_MLINE_INDEXES, sdpMLineIndexes.toIntArray())
bundle.putStringArray(WebRtcTestsActivity.EXTRA_SDP_MIDS, sdpMids.toTypedArray())
}
intent.putExtras(bundle)
startActivity(intent)
}
}
lifecycleScope.launchWhenStarted {
launch(Dispatchers.IO) {
// Double check that the long poller is up
@ -400,8 +434,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
}
private fun openSettings() {
val intent = Intent(this, WebRtcTestsActivity::class.java)
// val intent = Intent(this, SettingsActivity::class.java)
val intent = Intent(this, SettingsActivity::class.java)
show(intent, isForResult = false)
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M20.01,15.38c-1.23,0 -2.42,-0.2 -3.53,-0.56 -0.35,-0.12 -0.74,-0.03 -1.01,0.24l-1.57,1.97c-2.83,-1.35 -5.48,-3.9 -6.89,-6.83l1.95,-1.66c0.27,-0.28 0.35,-0.67 0.24,-1.02 -0.37,-1.11 -0.56,-2.3 -0.56,-3.53 0,-0.54 -0.45,-0.99 -0.99,-0.99H4.19C3.65,3 3,3.24 3,3.99 3,13.28 10.73,21 20.01,21c0.71,0 0.99,-0.63 0.99,-1.18v-3.45c0,-0.54 -0.45,-0.99 -0.99,-0.99z"/>
</vector>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:title="@string/conversation_context__menu_call"
android:icon="@drawable/ic_baseline_call_24"
app:showAsAction="always"
android:id="@+id/menu_call"/>
</menu>

View File

@ -585,6 +585,7 @@
<string name="conversation_context__menu_ban_and_delete_all">Ban and delete all</string>
<string name="conversation_context__menu_resend_message">Resend message</string>
<string name="conversation_context__menu_reply_to_message">Reply to message</string>
<string name="conversation_context__menu_call">Call</string>
<!-- conversation_context_image -->
<string name="conversation_context_image__save_attachment">Save attachment</string>

View File

@ -0,0 +1,57 @@
package org.session.libsession.messaging.messages.control
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Log
class CallMessage(): ControlMessage() {
var type: SignalServiceProtos.CallMessage.Type? = null
var sdps: List<String> = listOf()
var sdpMLineIndexes: List<Int> = listOf()
var sdpMids: List<String> = listOf()
override val isSelfSendValid: Boolean = false
override fun isValid(): Boolean = super.isValid() && type != null && !sdps.isNullOrEmpty()
constructor(type: SignalServiceProtos.CallMessage.Type,
sdps: List<String>,
sdpMLineIndexes: List<Int>,
sdpMids: List<String>) : this() {
this.type = type
this.sdps = sdps
this.sdpMLineIndexes = sdpMLineIndexes
this.sdpMids = sdpMids
}
companion object {
const val TAG = "CallMessage"
fun fromProto(proto: SignalServiceProtos.Content): CallMessage? {
val callMessageProto = if (proto.hasCallMessage()) proto.callMessage else return null
val type = callMessageProto.type
val sdps = callMessageProto.sdpsList
val sdpMLineIndexes = callMessageProto.sdpMLineIndexesList
val sdpMids = callMessageProto.sdpMidsList
return CallMessage(type,sdps, sdpMLineIndexes, sdpMids)
}
}
override fun toProto(): SignalServiceProtos.Content? {
val nonNullType = type ?: run {
Log.w(TAG,"Couldn't construct call message request proto from: $this")
return null
}
val callMessage = SignalServiceProtos.CallMessage.newBuilder()
.setType(nonNullType)
.addAllSdps(sdps)
.addAllSdpMLineIndexes(sdpMLineIndexes)
.addAllSdpMids(sdpMids)
return SignalServiceProtos.Content.newBuilder()
.setCallMessage(
callMessage
)
.build()
}
}

View File

@ -95,6 +95,7 @@ object MessageReceiver {
ExpirationTimerUpdate.fromProto(proto) ?:
ConfigurationMessage.fromProto(proto) ?:
UnsendRequest.fromProto(proto) ?:
CallMessage.fromProto(proto) ?:
VisibleMessage.fromProto(proto) ?: throw Error.UnknownMessage
// Ignore self send if needed
if (!message.isSelfSendValid && sender == userPublicKey) throw Error.SelfSend

View File

@ -14,24 +14,20 @@ import org.session.libsession.messaging.sending_receiving.link_preview.LinkPrevi
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.utilities.WebRtcUtils
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupRecord
import org.session.libsession.utilities.*
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.SSKEnvironment
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.ProfileKeyUtil
import org.session.libsignal.crypto.ecc.DjbECPrivateKey
import org.session.libsignal.crypto.ecc.DjbECPublicKey
import org.session.libsignal.crypto.ecc.ECKeyPair
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.messages.SignalServiceGroup
import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.removing05PrefixIfNeeded
import org.session.libsignal.utilities.toHexString
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.guava.Optional
import org.session.libsignal.utilities.removing05PrefixIfNeeded
import org.session.libsignal.utilities.toHexString
import java.security.MessageDigest
import java.util.*
import kotlin.collections.ArrayList
@ -52,6 +48,7 @@ fun MessageReceiver.handle(message: Message, proto: SignalServiceProtos.Content,
is ConfigurationMessage -> handleConfigurationMessage(message)
is UnsendRequest -> handleUnsendRequest(message)
is VisibleMessage -> handleVisibleMessage(message, proto, openGroupID)
is CallMessage -> handleCallMessage(message)
}
}
@ -61,6 +58,11 @@ private fun MessageReceiver.handleReadReceipt(message: ReadReceipt) {
SSKEnvironment.shared.readReceiptManager.processReadReceipts(context, message.sender!!, message.timestamps!!, message.receivedTimestamp!!)
}
private fun MessageReceiver.handleCallMessage(message: CallMessage) {
// TODO: refactor this out to persistence, just to help debug the flow and send/receive in synchronous testing
WebRtcUtils.SIGNAL_QUEUE.offer(message)
}
private fun MessageReceiver.handleTypingIndicator(message: TypingIndicator) {
when (message.kind!!) {
TypingIndicator.Kind.STARTED -> showTypingIndicatorIfNeeded(message.sender!!)

View File

@ -0,0 +1,11 @@
package org.session.libsession.messaging.utilities
import kotlinx.coroutines.channels.Channel
import org.session.libsession.messaging.messages.control.CallMessage
object WebRtcUtils {
// TODO: move this to a better place that is persistent
val SIGNAL_QUEUE = Channel<CallMessage>(Channel.UNLIMITED)
}

View File

@ -168,12 +168,15 @@ message CallMessage {
OFFER = 1;
ANSWER = 2;
PROVISIONAL_ANSWER = 3;
ICE_CANDIDATES = 4;
END_CALL = 5;
}
// @required
required Type type = 1;
// @required
required string sdp = 2;
required Type type = 1;
repeated string sdps = 2;
repeated uint32 sdpMLineIndexes = 3;
repeated string sdpMids = 4;
}