feat: hanging up and bottom sheet behaviors should work now

This commit is contained in:
Harris 2021-09-06 17:06:03 +10:00
parent b3576336d9
commit 459dfa72c2
4 changed files with 126 additions and 61 deletions

View File

@ -1,6 +1,9 @@
package org.thoughtcrime.securesms.calls
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import kotlinx.android.synthetic.main.activity_webrtc_tests.*
@ -9,6 +12,7 @@ import kotlinx.coroutines.isActive
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.messaging.utilities.WebRtcUtils
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Debouncer
import org.session.libsignal.protos.SignalServiceProtos
@ -28,7 +32,7 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
private const val LOCAL_STREAM_ID = "local_track"
const val ACTION_ANSWER = "answer"
const val ACTION_UPDATE_ICE = "updateIce"
const val ACTION_END = "end-call"
const val EXTRA_SDP = "WebRtcTestsActivity_EXTRA_SDP"
const val EXTRA_ADDRESS = "WebRtcTestsActivity_EXTRA_ADDRESS"
@ -42,6 +46,8 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
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)
@ -127,13 +133,44 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
peerConnection.createOffer(this, MediaConstraints())
}
lifecycleScope.launchWhenResumed {
lifecycleScope.launchWhenCreated {
while (this.isActive) {
delay(5_000L)
peerConnection.getStats(this@WebRtcTestsActivity)
val answer = synchronized(WebRtcUtils.callCache) {
WebRtcUtils.callCache[callAddress]?.firstOrNull { it.type == SignalServiceProtos.CallMessage.Type.ANSWER }
}
if (answer != null) {
peerConnection.setRemoteDescription(
this@WebRtcTestsActivity,
SessionDescription(SessionDescription.Type.ANSWER, answer.sdps[0])
)
break
}
delay(2_000L)
}
}
registerReceiver(object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
endCall()
}
}, IntentFilter(ACTION_END))
lifecycleScope.launchWhenResumed {
while (this.isActive) {
delay(2_000L)
peerConnection.getStats(this@WebRtcTestsActivity)
synchronized(WebRtcUtils.callCache) {
val set = WebRtcUtils.callCache[callAddress] ?: mutableSetOf()
set.filter { it.hashCode() !in acceptedCallMessageHashes
&& it.type == SignalServiceProtos.CallMessage.Type.ICE_CANDIDATES }.forEach { callMessage ->
callMessage.iceCandidates().forEach { candidate ->
peerConnection.addIceCandidate(candidate)
}
acceptedCallMessageHashes.add(callMessage.hashCode())
}
}
}
}
}
override fun onStatsDelivered(statsReport: RTCStatsReport?) {
@ -143,11 +180,11 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
}
private fun endCall() {
peerConnection.close()
MessageSender.sendNonDurably(
CallMessage(SignalServiceProtos.CallMessage.Type.END_CALL,emptyList(),emptyList(), emptyList()),
CallMessage.endCall(),
callAddress
)
peerConnection.close()
finish()
}
@ -156,29 +193,6 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
endCall()
}
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): CameraVideoCapturer? {
val deviceNames = enumerator.deviceNames
@ -316,4 +330,11 @@ class WebRtcTestsActivity: PassphraseRequiredActionBarActivity(), PeerConnection
Log.d("Loki-RTC", "onSetFailure: $p0")
}
private fun CallMessage.iceCandidates(): List<IceCandidate> {
val candidateSize = sdpMids.size
return (0 until candidateSize).map { i ->
IceCandidate(sdpMids[i], sdpMLineIndexes[i], sdps[i])
}
}
}

View File

@ -20,6 +20,7 @@ import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.seed_reminder_stub.*
import kotlinx.android.synthetic.main.seed_reminder_stub.view.*
@ -42,6 +43,7 @@ import org.session.libsignal.utilities.toHexString
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.MuteDialog
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
import org.thoughtcrime.securesms.calls.WebRtcTestsActivity
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
@ -138,6 +140,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
lifecycleScope.launchWhenCreated {
// web rtc channel handling
for (message in WebRtcUtils.SIGNAL_QUEUE) {
val sender = Address.fromSerialized(message.sender!!)
synchronized(WebRtcUtils.callCache) {
val set = WebRtcUtils.callCache[sender] ?: mutableSetOf()
@ -156,37 +159,21 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
show(this@HomeActivity.supportFragmentManager,"call-sheet")
}
}
ICE_CANDIDATES -> {
// do nothing, already have candidates saved
}
END_CALL -> {
// do nothing, maybe clear the callCache or something
// dismiss the call sheet
supportFragmentManager.findFragmentByTag("call-sheet")?.let { callSheet ->
if (callSheet is BottomSheetDialogFragment) {
callSheet.dismiss()
}
}
// clear the callCache for this sender
synchronized(WebRtcUtils.callCache) {
WebRtcUtils.callCache[sender] = mutableSetOf()
}
sendBroadcast(Intent(WebRtcTestsActivity.ACTION_END))
}
else -> { /* do nothing */ }
}
// 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 {

View File

@ -1,14 +1,21 @@
package org.thoughtcrime.securesms.webrtc
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.*
import kotlinx.android.synthetic.main.fragment_call_bottom_sheet.*
import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.nameTextView
import kotlinx.android.synthetic.main.fragment_user_details_bottom_sheet.profilePictureView
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.recipients.Recipient
import org.thoughtcrime.securesms.calls.WebRtcTestsActivity
import org.thoughtcrime.securesms.mms.GlideApp
class CallBottomSheet: BottomSheetDialogFragment() {
@ -19,6 +26,8 @@ class CallBottomSheet: BottomSheetDialogFragment() {
const val ARGUMENT_TYPE = "CallBottomSheet_TYPE"
}
private lateinit var address: Address
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -30,9 +39,8 @@ class CallBottomSheet: BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val address = arguments?.getParcelable<Address>(ARGUMENT_ADDRESS) ?: return dismiss()
address = arguments?.getParcelable(ARGUMENT_ADDRESS) ?: return dismiss()
val sdp = arguments?.getStringArray(ARGUMENT_SDP) ?: return dismiss()
val type = arguments?.getString(ARGUMENT_TYPE) ?: return dismiss()
val recipient = Recipient.from(requireContext(), address, false)
profilePictureView.publicKey = address.serialize()
profilePictureView.glide = GlideApp.with(this)
@ -41,5 +49,23 @@ class CallBottomSheet: BottomSheetDialogFragment() {
nameTextView.text = recipient.name ?: address.serialize()
acceptButton.setOnClickListener {
val intent = Intent(requireContext(), WebRtcTestsActivity::class.java)
val bundle = bundleOf(
WebRtcTestsActivity.EXTRA_ADDRESS to address,
)
intent.action = WebRtcTestsActivity.ACTION_ANSWER
bundle.putStringArray(WebRtcTestsActivity.EXTRA_SDP, sdp)
intent.putExtras(bundle)
startActivity(intent)
dismiss()
}
declineButton.setOnClickListener {
MessageSender.sendNonDurably(CallMessage.endCall(), address)
dismiss()
}
}
}

View File

@ -13,7 +13,8 @@ class CallMessage(): ControlMessage() {
override val ttl: Long = 5 * 60 * 1000
override fun isValid(): Boolean = super.isValid() && type != null && !sdps.isNullOrEmpty()
override fun isValid(): Boolean = super.isValid() && type != null
&& (!sdps.isNullOrEmpty() || type == SignalServiceProtos.CallMessage.Type.END_CALL)
constructor(type: SignalServiceProtos.CallMessage.Type,
sdps: List<String>,
@ -28,6 +29,8 @@ class CallMessage(): ControlMessage() {
companion object {
const val TAG = "CallMessage"
fun endCall() = CallMessage(SignalServiceProtos.CallMessage.Type.END_CALL, emptyList(), emptyList(), emptyList())
fun fromProto(proto: SignalServiceProtos.Content): CallMessage? {
val callMessageProto = if (proto.hasCallMessage()) proto.callMessage else return null
val type = callMessageProto.type
@ -56,4 +59,32 @@ class CallMessage(): ControlMessage() {
)
.build()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CallMessage
if (type != other.type) return false
if (sdps != other.sdps) return false
if (sdpMLineIndexes != other.sdpMLineIndexes) return false
if (sdpMids != other.sdpMids) return false
if (isSelfSendValid != other.isSelfSendValid) return false
if (ttl != other.ttl) return false
return true
}
override fun hashCode(): Int {
var result = type?.hashCode() ?: 0
result = 31 * result + sdps.hashCode()
result = 31 * result + sdpMLineIndexes.hashCode()
result = 31 * result + sdpMids.hashCode()
result = 31 * result + isSelfSendValid.hashCode()
result = 31 * result + ttl.hashCode()
return result
}
}