2021-08-19 06:16:14 +02:00
|
|
|
package org.thoughtcrime.securesms.calls
|
|
|
|
|
2021-09-07 08:12:37 +02:00
|
|
|
import android.Manifest
|
2021-09-06 09:06:03 +02:00
|
|
|
import android.content.BroadcastReceiver
|
|
|
|
import android.content.Context
|
2021-08-30 08:39:47 +02:00
|
|
|
import android.content.Intent
|
2021-09-06 09:06:03 +02:00
|
|
|
import android.content.IntentFilter
|
2021-11-15 08:02:55 +01:00
|
|
|
import android.graphics.BlendMode
|
|
|
|
import android.graphics.PorterDuff
|
2021-11-12 07:07:39 +01:00
|
|
|
import android.graphics.drawable.ColorDrawable
|
2021-10-29 07:41:01 +02:00
|
|
|
import android.media.AudioManager
|
2021-08-19 06:16:14 +02:00
|
|
|
import android.os.Bundle
|
2021-09-22 02:39:45 +02:00
|
|
|
import android.view.MenuItem
|
2021-11-12 07:07:39 +01:00
|
|
|
import android.view.View
|
2021-10-29 07:41:01 +02:00
|
|
|
import android.view.WindowManager
|
2021-10-28 08:06:14 +02:00
|
|
|
import androidx.activity.viewModels
|
2021-11-12 07:07:39 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
2021-11-10 01:57:03 +01:00
|
|
|
import androidx.core.view.isVisible
|
2021-09-06 01:15:48 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-11-12 02:21:05 +01:00
|
|
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
2021-10-28 08:06:14 +02:00
|
|
|
import dagger.hilt.android.AndroidEntryPoint
|
2021-11-12 02:21:05 +01:00
|
|
|
import kotlinx.android.synthetic.main.activity_webrtc.*
|
2021-11-10 01:57:03 +01:00
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import kotlinx.coroutines.flow.collect
|
2021-10-29 07:41:01 +02:00
|
|
|
import kotlinx.coroutines.launch
|
2021-08-19 06:16:14 +02:00
|
|
|
import network.loki.messenger.R
|
2021-11-12 02:21:05 +01:00
|
|
|
import org.session.libsession.avatars.ProfileContactPhoto
|
|
|
|
import org.session.libsession.messaging.contacts.Contact
|
2021-08-30 08:39:47 +02:00
|
|
|
import org.session.libsession.utilities.Address
|
2021-08-19 06:16:14 +02:00
|
|
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
2021-11-12 02:21:05 +01:00
|
|
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
|
|
|
import org.thoughtcrime.securesms.mms.GlideApp
|
2021-09-07 08:12:37 +02:00
|
|
|
import org.thoughtcrime.securesms.permissions.Permissions
|
2021-11-09 07:15:22 +01:00
|
|
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
2021-11-12 02:21:05 +01:00
|
|
|
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
|
2021-11-15 08:02:55 +01:00
|
|
|
import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
|
2021-10-28 08:06:14 +02:00
|
|
|
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
2021-11-12 02:21:05 +01:00
|
|
|
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.*
|
2021-11-15 08:02:55 +01:00
|
|
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager
|
|
|
|
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.*
|
2021-11-08 07:32:25 +01:00
|
|
|
import org.webrtc.IceCandidate
|
2021-10-21 23:52:57 +02:00
|
|
|
import java.util.*
|
2021-08-20 01:14:54 +02:00
|
|
|
|
2021-10-28 08:06:14 +02:00
|
|
|
@AndroidEntryPoint
|
2021-11-04 02:07:06 +01:00
|
|
|
class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
2021-08-20 01:14:54 +02:00
|
|
|
|
|
|
|
companion object {
|
2021-08-30 08:39:47 +02:00
|
|
|
const val ACTION_ANSWER = "answer"
|
2021-09-06 09:06:03 +02:00
|
|
|
const val ACTION_END = "end-call"
|
2021-08-30 08:39:47 +02:00
|
|
|
|
2021-11-04 02:07:06 +01:00
|
|
|
const val BUSY_SIGNAL_DELAY_FINISH = 5500L
|
2021-08-20 01:14:54 +02:00
|
|
|
}
|
|
|
|
|
2021-10-28 08:06:14 +02:00
|
|
|
private val viewModel by viewModels<CallViewModel>()
|
2021-11-12 02:21:05 +01:00
|
|
|
private val glide by lazy { GlideApp.with(this) }
|
2021-11-10 01:57:03 +01:00
|
|
|
private var uiJob: Job? = null
|
|
|
|
|
2021-09-22 02:39:45 +02:00
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
if (item.itemId == android.R.id.home) {
|
2021-11-05 06:35:40 +01:00
|
|
|
finish()
|
2021-09-22 02:39:45 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
return super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:07:39 +01:00
|
|
|
override fun onNewIntent(intent: Intent?) {
|
|
|
|
super.onNewIntent(intent)
|
|
|
|
if (intent?.action == ACTION_ANSWER) {
|
|
|
|
val answerIntent = WebRtcCallService.acceptCallIntent(this)
|
|
|
|
ContextCompat.startForegroundService(this,answerIntent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-19 06:16:14 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?, ready: Boolean) {
|
|
|
|
super.onCreate(savedInstanceState, ready)
|
2021-10-29 07:41:01 +02:00
|
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
|
|
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
2021-11-12 02:21:05 +01:00
|
|
|
setContentView(R.layout.activity_webrtc)
|
2021-10-29 07:41:01 +02:00
|
|
|
volumeControlStream = AudioManager.STREAM_VOICE_CALL
|
|
|
|
|
|
|
|
initializeResources()
|
2021-08-19 06:16:14 +02:00
|
|
|
|
2021-09-07 08:12:37 +02:00
|
|
|
Permissions.with(this)
|
2021-10-29 07:41:01 +02:00
|
|
|
.request(Manifest.permission.RECORD_AUDIO)
|
2021-09-07 08:12:37 +02:00
|
|
|
.onAllGranted {
|
|
|
|
setupStreams()
|
|
|
|
}
|
|
|
|
.execute()
|
|
|
|
|
2021-11-09 07:15:22 +01:00
|
|
|
if (intent.action == ACTION_ANSWER) {
|
|
|
|
val answerIntent = WebRtcCallService.acceptCallIntent(this)
|
2021-11-12 07:07:39 +01:00
|
|
|
ContextCompat.startForegroundService(this,answerIntent)
|
|
|
|
}
|
|
|
|
|
2021-11-15 08:02:55 +01:00
|
|
|
speakerPhoneButton.setOnClickListener {
|
|
|
|
val command = AudioManagerCommand.SetUserDevice( if (viewModel.isSpeaker) EARPIECE else SPEAKER_PHONE)
|
|
|
|
WebRtcCallService.sendAudioManagerCommand(this, command)
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:07:39 +01:00
|
|
|
acceptCallButton.setOnClickListener {
|
|
|
|
val answerIntent = WebRtcCallService.acceptCallIntent(this)
|
|
|
|
ContextCompat.startForegroundService(this,answerIntent)
|
|
|
|
}
|
|
|
|
|
|
|
|
declineCallButton.setOnClickListener {
|
|
|
|
val declineIntent = WebRtcCallService.hangupIntent(this)
|
|
|
|
startService(declineIntent)
|
2021-11-09 07:15:22 +01:00
|
|
|
}
|
2021-11-08 07:32:25 +01:00
|
|
|
|
2021-09-06 09:06:03 +02:00
|
|
|
registerReceiver(object: BroadcastReceiver() {
|
|
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
2021-11-05 06:35:40 +01:00
|
|
|
finish()
|
2021-09-06 09:06:03 +02:00
|
|
|
}
|
2021-11-08 07:32:25 +01:00
|
|
|
},IntentFilter(ACTION_END))
|
2021-11-10 01:57:03 +01:00
|
|
|
|
|
|
|
enableCameraButton.setOnClickListener {
|
2021-11-12 02:21:05 +01:00
|
|
|
Permissions.with(this)
|
|
|
|
.request(Manifest.permission.CAMERA)
|
|
|
|
.onAllGranted {
|
|
|
|
val intent = WebRtcCallService.cameraEnabled(this, !viewModel.videoEnabled)
|
|
|
|
startService(intent)
|
|
|
|
}
|
|
|
|
.execute()
|
2021-11-10 01:57:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switchCameraButton.setOnClickListener {
|
|
|
|
startService(WebRtcCallService.flipCamera(this))
|
|
|
|
}
|
|
|
|
|
|
|
|
endCallButton.setOnClickListener {
|
|
|
|
startService(WebRtcCallService.hangupIntent(this))
|
|
|
|
}
|
|
|
|
|
2021-09-06 01:15:48 +02:00
|
|
|
}
|
|
|
|
|
2021-10-29 07:41:01 +02:00
|
|
|
private fun initializeResources() {
|
2021-09-07 08:12:37 +02:00
|
|
|
|
2021-08-19 06:16:14 +02:00
|
|
|
}
|
|
|
|
|
2021-09-07 08:12:37 +02:00
|
|
|
private fun setupStreams() {
|
2021-08-19 06:16:14 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-11-10 01:57:03 +01:00
|
|
|
override fun onStart() {
|
|
|
|
super.onStart()
|
|
|
|
|
|
|
|
uiJob = lifecycleScope.launch {
|
|
|
|
|
2021-11-15 08:02:55 +01:00
|
|
|
launch {
|
|
|
|
viewModel.audioDeviceState.collect { state ->
|
|
|
|
val speakerEnabled = state.selectedDevice == SPEAKER_PHONE
|
|
|
|
speakerPhoneButton.setImageResource(
|
|
|
|
if (speakerEnabled) R.drawable.ic_baseline_volume_up_24
|
|
|
|
else R.drawable.ic_baseline_volume_mute_24
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 02:21:05 +01:00
|
|
|
launch {
|
|
|
|
viewModel.callState.collect { state ->
|
2021-11-12 07:07:39 +01:00
|
|
|
when (state) {
|
|
|
|
CALL_RINGING -> {
|
|
|
|
}
|
|
|
|
CALL_OUTGOING -> {
|
|
|
|
}
|
|
|
|
CALL_CONNECTED -> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
controlGroup.isVisible = state in listOf(CALL_CONNECTED, CALL_OUTGOING, CALL_INCOMING)
|
|
|
|
remote_loading_view.isVisible = state !in listOf(CALL_CONNECTED, CALL_RINGING)
|
|
|
|
incomingControlGroup.isVisible = state == CALL_RINGING
|
2021-11-10 01:57:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 02:21:05 +01:00
|
|
|
launch {
|
|
|
|
viewModel.recipient.collect { latestRecipient ->
|
|
|
|
if (latestRecipient.recipient != null) {
|
|
|
|
val signalProfilePicture = latestRecipient.recipient.contactPhoto
|
|
|
|
val avatar = (signalProfilePicture as? ProfileContactPhoto)?.avatarObject
|
|
|
|
if (signalProfilePicture != null && avatar != "0" && avatar != "") {
|
|
|
|
glide.clear(remote_recipient)
|
|
|
|
glide.load(signalProfilePicture).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).circleCrop().into(remote_recipient)
|
|
|
|
} else {
|
|
|
|
val publicKey = latestRecipient.recipient.address.serialize()
|
|
|
|
val displayName = getUserDisplayName(publicKey)
|
|
|
|
val sizeInPX = resources.getDimensionPixelSize(R.dimen.extra_large_profile_picture_size)
|
|
|
|
glide.clear(remote_recipient)
|
|
|
|
glide.load(AvatarPlaceholderGenerator.generate(this@WebRtcCallActivity, sizeInPX, publicKey, displayName))
|
|
|
|
.diskCacheStrategy(DiskCacheStrategy.ALL).circleCrop().into(remote_recipient)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
glide.clear(remote_recipient)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-10 01:57:03 +01:00
|
|
|
|
2021-11-12 02:21:05 +01:00
|
|
|
launch {
|
|
|
|
viewModel.localVideoEnabledState.collect { isEnabled ->
|
|
|
|
local_renderer.removeAllViews()
|
|
|
|
if (isEnabled) {
|
|
|
|
viewModel.localRenderer?.let { surfaceView ->
|
|
|
|
surfaceView.setZOrderOnTop(true)
|
|
|
|
local_renderer.addView(surfaceView)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
local_renderer.isVisible = isEnabled
|
|
|
|
enableCameraButton.setImageResource(
|
2021-11-15 08:02:55 +01:00
|
|
|
if (isEnabled) R.drawable.ic_baseline_videocam_off_24
|
|
|
|
else R.drawable.ic_baseline_videocam_24
|
2021-11-12 02:21:05 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
launch {
|
|
|
|
viewModel.remoteVideoEnabledState.collect { isEnabled ->
|
|
|
|
remote_renderer.removeAllViews()
|
|
|
|
if (isEnabled) {
|
|
|
|
viewModel.remoteRenderer?.let { remote_renderer.addView(it) }
|
|
|
|
}
|
|
|
|
remote_renderer.isVisible = isEnabled
|
|
|
|
remote_recipient.isVisible = !isEnabled
|
|
|
|
}
|
2021-11-10 01:57:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 07:07:39 +01:00
|
|
|
private fun getUserDisplayName(publicKey: String): String {
|
2021-11-12 02:21:05 +01:00
|
|
|
val contact = DatabaseComponent.get(this).sessionContactDatabase().getContactWithSessionID(publicKey)
|
|
|
|
return contact?.displayName(Contact.ContactContext.REGULAR) ?: publicKey
|
|
|
|
}
|
|
|
|
|
2021-11-10 01:57:03 +01:00
|
|
|
override fun onStop() {
|
|
|
|
super.onStop()
|
|
|
|
uiJob?.cancel()
|
|
|
|
}
|
2021-08-19 06:16:14 +02:00
|
|
|
}
|