mirror of
https://github.com/oxen-io/session-android.git
synced 2025-08-25 19:57:33 +00:00
feat: hooking up calls and fixing broken dependencies and compile errors
This commit is contained in:
@@ -37,10 +37,12 @@ dependencies {
|
|||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'androidx.exifinterface:exifinterface:1.2.0'
|
implementation 'androidx.exifinterface:exifinterface:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-common-java8:2.3.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-process:2.3.1'
|
||||||
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
|
||||||
implementation 'androidx.activity:activity-ktx:1.2.2'
|
implementation 'androidx.activity:activity-ktx:1.2.2'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.3.2'
|
implementation 'androidx.fragment:fragment-ktx:1.3.2'
|
||||||
implementation "androidx.core:core-ktx:1.3.2"
|
implementation "androidx.core:core-ktx:1.3.2"
|
||||||
@@ -153,7 +155,7 @@ dependencies {
|
|||||||
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
testImplementation 'org.robolectric:shadows-multidex:4.4'
|
||||||
}
|
}
|
||||||
|
|
||||||
def canonicalVersionCode = 231
|
def canonicalVersionCode = 232
|
||||||
def canonicalVersionName = "1.11.12"
|
def canonicalVersionName = "1.11.12"
|
||||||
|
|
||||||
def postFixSize = 10
|
def postFixSize = 10
|
||||||
|
@@ -54,7 +54,6 @@ import org.thoughtcrime.securesms.crypto.KeyPairUtilities;
|
|||||||
import org.thoughtcrime.securesms.database.JobDatabase;
|
import org.thoughtcrime.securesms.database.JobDatabase;
|
||||||
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
import org.thoughtcrime.securesms.database.LokiAPIDatabase;
|
||||||
import org.thoughtcrime.securesms.database.Storage;
|
import org.thoughtcrime.securesms.database.Storage;
|
||||||
import org.thoughtcrime.securesms.dependencies.CallComponent;
|
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||||
import org.thoughtcrime.securesms.dependencies.DatabaseModule;
|
import org.thoughtcrime.securesms.dependencies.DatabaseModule;
|
||||||
import org.thoughtcrime.securesms.groups.OpenGroupManager;
|
import org.thoughtcrime.securesms.groups.OpenGroupManager;
|
||||||
@@ -82,6 +81,7 @@ import org.thoughtcrime.securesms.sskenvironment.TypingStatusRepository;
|
|||||||
import org.thoughtcrime.securesms.util.Broadcaster;
|
import org.thoughtcrime.securesms.util.Broadcaster;
|
||||||
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
import org.thoughtcrime.securesms.util.UiModeUtilities;
|
||||||
import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper;
|
import org.thoughtcrime.securesms.util.dynamiclanguage.LocaleParseHelper;
|
||||||
|
import org.thoughtcrime.securesms.webrtc.CallMessageProcessor;
|
||||||
import org.webrtc.PeerConnectionFactory;
|
import org.webrtc.PeerConnectionFactory;
|
||||||
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
import org.webrtc.PeerConnectionFactory.InitializationOptions;
|
||||||
import org.webrtc.voiceengine.WebRtcAudioManager;
|
import org.webrtc.voiceengine.WebRtcAudioManager;
|
||||||
@@ -134,6 +134,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
@Inject Storage storage;
|
@Inject Storage storage;
|
||||||
@Inject MessageDataProvider messageDataProvider;
|
@Inject MessageDataProvider messageDataProvider;
|
||||||
@Inject JobDatabase jobDatabase;
|
@Inject JobDatabase jobDatabase;
|
||||||
|
CallMessageProcessor callMessageProcessor;
|
||||||
|
|
||||||
private volatile boolean isAppVisible;
|
private volatile boolean isAppVisible;
|
||||||
|
|
||||||
@@ -160,6 +161,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
DatabaseModule.init(this);
|
DatabaseModule.init(this);
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
callMessageProcessor = new CallMessageProcessor(this, ProcessLifecycleOwner.get().getLifecycle());
|
||||||
Log.i(TAG, "onCreate()");
|
Log.i(TAG, "onCreate()");
|
||||||
startKovenant();
|
startKovenant();
|
||||||
initializeSecurityProvider();
|
initializeSecurityProvider();
|
||||||
|
@@ -8,28 +8,17 @@ import android.content.IntentFilter
|
|||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.Window
|
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.android.synthetic.main.activity_webrtc_tests.*
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import network.loki.messenger.R
|
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.Address
|
||||||
import org.session.libsignal.protos.SignalServiceProtos
|
|
||||||
import org.session.libsignal.utilities.Log
|
|
||||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||||
import org.thoughtcrime.securesms.permissions.Permissions
|
import org.thoughtcrime.securesms.permissions.Permissions
|
||||||
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
import org.thoughtcrime.securesms.webrtc.CallViewModel
|
||||||
import org.webrtc.*
|
import org.webrtc.IceCandidate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -67,7 +56,6 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
super.onCreate(savedInstanceState, ready)
|
super.onCreate(savedInstanceState, ready)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
|
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
|
||||||
setContentView(R.layout.activity_webrtc_tests)
|
setContentView(R.layout.activity_webrtc_tests)
|
||||||
volumeControlStream = AudioManager.STREAM_VOICE_CALL
|
volumeControlStream = AudioManager.STREAM_VOICE_CALL
|
||||||
|
|
||||||
@@ -81,16 +69,16 @@ class WebRtcCallActivity: PassphraseRequiredActionBarActivity() {
|
|||||||
.execute()
|
.execute()
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
// repeat on start or something
|
||||||
viewModel
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
registerReceiver(object: BroadcastReceiver() {
|
registerReceiver(object: BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}, IntentFilter(ACTION_END))
|
},IntentFilter(ACTION_END))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initializeResources() {
|
private fun initializeResources() {
|
||||||
|
@@ -24,7 +24,6 @@ import androidx.core.view.children
|
|||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import androidx.loader.app.LoaderManager
|
import androidx.loader.app.LoaderManager
|
||||||
import androidx.loader.content.Loader
|
import androidx.loader.content.Loader
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@@ -457,7 +456,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setUpLinkPreviewObserver() {
|
private fun setUpLinkPreviewObserver() {
|
||||||
val linkPreviewViewModel = ViewModelProviders.of(this, LinkPreviewViewModel.Factory(LinkPreviewRepository()))[LinkPreviewViewModel::class.java]
|
val linkPreviewViewModel = ViewModelProvider(this, LinkPreviewViewModel.Factory(LinkPreviewRepository()))[LinkPreviewViewModel::class.java]
|
||||||
this.linkPreviewViewModel = linkPreviewViewModel
|
this.linkPreviewViewModel = linkPreviewViewModel
|
||||||
if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) {
|
if (!TextSecurePreferences.isLinkPreviewsEnabled(this)) {
|
||||||
linkPreviewViewModel.onUserCancel(); return
|
linkPreviewViewModel.onUserCancel(); return
|
||||||
|
@@ -43,10 +43,10 @@ import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
|
|||||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||||
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity
|
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity
|
||||||
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.groupIDKey
|
import org.thoughtcrime.securesms.groups.EditClosedGroupActivity.Companion.groupIDKey
|
||||||
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||||
import org.thoughtcrime.securesms.util.getColorWithID
|
import org.thoughtcrime.securesms.util.getColorWithID
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
object ConversationMenuHelper {
|
object ConversationMenuHelper {
|
||||||
|
|
||||||
@@ -183,16 +183,14 @@ object ConversationMenuHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun call(context: Context, thread: Recipient) {
|
private fun call(context: Context, thread: Recipient) {
|
||||||
AlertDialog.Builder(context)
|
val service = WebRtcCallService.createCall(context, thread)
|
||||||
.setTitle("Call")
|
context.startService(service)
|
||||||
.setMessage("Use relay?")
|
|
||||||
.setPositiveButton("Use Relay") { d, w ->
|
val activity = Intent(context, WebRtcCallActivity::class.java).apply {
|
||||||
TODO()
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
}
|
}
|
||||||
.setNeutralButton("P2P only") { d, w ->
|
context.startActivity(activity)
|
||||||
TODO()
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@@ -148,49 +148,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), ConversationClickLis
|
|||||||
}
|
}
|
||||||
this.broadcastReceiver = broadcastReceiver
|
this.broadcastReceiver = broadcastReceiver
|
||||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
|
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("blockedContactsChanged"))
|
||||||
lifecycleScope.launchWhenCreated {
|
|
||||||
// web rtc channel handling
|
|
||||||
for (message in WebRtcUtils.SIGNAL_QUEUE) {
|
|
||||||
// TODO: check errors here in the
|
|
||||||
val sender = Address.fromSerialized(message.sender!!)
|
|
||||||
val callId = message.callId!!
|
|
||||||
synchronized(WebRtcUtils.callCache) {
|
|
||||||
val set = WebRtcUtils.callCache[callId] ?: mutableSetOf()
|
|
||||||
set += message
|
|
||||||
WebRtcUtils.callCache[callId] = set
|
|
||||||
}
|
|
||||||
when (message.type) {
|
|
||||||
SignalServiceProtos.CallMessage.Type.OFFER -> {
|
|
||||||
// show bottom sheet
|
|
||||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
|
|
||||||
CallBottomSheet().apply {
|
|
||||||
arguments = bundleOf(
|
|
||||||
CallBottomSheet.ARGUMENT_ADDRESS to sender,
|
|
||||||
CallBottomSheet.ARGUMENT_SDP to message.sdps.toTypedArray(),
|
|
||||||
CallBottomSheet.ARGUMENT_TYPE to message.type!!.number,
|
|
||||||
CallBottomSheet.ARGUMENT_CALL_ID to callId.toString()
|
|
||||||
)
|
|
||||||
show(this@HomeActivity.supportFragmentManager,"call-sheet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SignalServiceProtos.CallMessage.Type.END_CALL -> {
|
|
||||||
// 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[callId] = mutableSetOf()
|
|
||||||
}
|
|
||||||
sendBroadcast(Intent(WebRtcCallActivity.ACTION_END))
|
|
||||||
}
|
|
||||||
else -> { /* do nothing */ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenStarted {
|
lifecycleScope.launchWhenStarted {
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
|
@@ -86,6 +86,17 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
|||||||
|
|
||||||
fun acceptCallIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_ANSWER_CALL)
|
fun acceptCallIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_ANSWER_CALL)
|
||||||
|
|
||||||
|
fun createCall(context: Context, recipient: Recipient) = Intent(context, WebRtcCallService::class.java)
|
||||||
|
.setAction(ACTION_OUTGOING_CALL)
|
||||||
|
.putExtra(EXTRA_RECIPIENT_ADDRESS, recipient.address)
|
||||||
|
|
||||||
|
fun incomingCall(context: Context, address: Address, sdp: String, callId: UUID) =
|
||||||
|
Intent(context, WebRtcCallService::class.java)
|
||||||
|
.setAction(ACTION_INCOMING_CALL)
|
||||||
|
.putExtra(EXTRA_RECIPIENT_ADDRESS, address)
|
||||||
|
.putExtra(EXTRA_CALL_ID, callId)
|
||||||
|
.putExtra(EXTRA_REMOTE_DESCRIPTION, sdp)
|
||||||
|
|
||||||
fun denyCallIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_DENY_CALL)
|
fun denyCallIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_DENY_CALL)
|
||||||
|
|
||||||
fun hangupIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_LOCAL_HANGUP)
|
fun hangupIntent(context: Context) = Intent(context, WebRtcCallService::class.java).setAction(ACTION_LOCAL_HANGUP)
|
||||||
@@ -122,15 +133,14 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun terminate() {
|
private fun terminate() {
|
||||||
stopForeground(true)
|
sendBroadcast(Intent(WebRtcCallActivity.ACTION_END))
|
||||||
callManager.stop()
|
callManager.stop()
|
||||||
|
stopForeground(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isBusy() = callManager.isBusy(this)
|
private fun isBusy() = callManager.isBusy(this)
|
||||||
|
|
||||||
private fun initializeVideo() {
|
private fun isIdle() = callManager.isIdle()
|
||||||
callManager.initializeVideo(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? = null
|
override fun onBind(intent: Intent?): IBinder? = null
|
||||||
|
|
||||||
@@ -142,7 +152,7 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
|||||||
action == ACTION_INCOMING_CALL && isBusy() -> handleBusyCall(intent)
|
action == ACTION_INCOMING_CALL && isBusy() -> handleBusyCall(intent)
|
||||||
action == ACTION_REMOTE_BUSY -> handleBusyMessage(intent)
|
action == ACTION_REMOTE_BUSY -> handleBusyMessage(intent)
|
||||||
action == ACTION_INCOMING_CALL -> handleIncomingCall(intent)
|
action == ACTION_INCOMING_CALL -> handleIncomingCall(intent)
|
||||||
action == ACTION_OUTGOING_CALL -> handleOutgoingCall(intent)
|
action == ACTION_OUTGOING_CALL && isIdle() -> handleOutgoingCall(intent)
|
||||||
action == ACTION_ANSWER_CALL -> handleAnswerCall(intent)
|
action == ACTION_ANSWER_CALL -> handleAnswerCall(intent)
|
||||||
action == ACTION_DENY_CALL -> handleDenyCall(intent)
|
action == ACTION_DENY_CALL -> handleDenyCall(intent)
|
||||||
action == ACTION_LOCAL_HANGUP -> handleLocalHangup(intent)
|
action == ACTION_LOCAL_HANGUP -> handleLocalHangup(intent)
|
||||||
@@ -288,12 +298,11 @@ class WebRtcCallService: Service(), PeerConnection.Observer {
|
|||||||
callManager.initializeVideo(this)
|
callManager.initializeVideo(this)
|
||||||
|
|
||||||
callManager.postViewModelState(CallViewModel.State.CALL_OUTGOING)
|
callManager.postViewModelState(CallViewModel.State.CALL_OUTGOING)
|
||||||
// update phone state IN_CALL
|
lockManager.updatePhoneState(LockManager.PhoneState.IN_CALL)
|
||||||
callManager.initializeAudioForCall()
|
callManager.initializeAudioForCall()
|
||||||
callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING)
|
callManager.startOutgoingRinger(OutgoingRinger.Type.RINGING)
|
||||||
// bluetoothStateManager.setWantsConnection(true)
|
|
||||||
setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient)
|
setCallInProgressNotification(TYPE_OUTGOING_RINGING, callManager.recipient)
|
||||||
// DatabaseComponent.get(this).insertOutgoingCall(callManager.recipient!!.address)
|
// TODO: DatabaseComponent.get(this).insertOutgoingCall(callManager.recipient!!.address)
|
||||||
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES)
|
timeoutExecutor.schedule(TimeoutRunnable(callId, this), 2, TimeUnit.MINUTES)
|
||||||
|
|
||||||
val expectedState = callManager.currentConnectionState
|
val expectedState = callManager.currentConnectionState
|
||||||
|
@@ -54,17 +54,17 @@ class CallBottomSheet: BottomSheetDialogFragment() {
|
|||||||
nameTextView.text = recipient.name ?: address.serialize()
|
nameTextView.text = recipient.name ?: address.serialize()
|
||||||
|
|
||||||
acceptButton.setOnClickListener {
|
acceptButton.setOnClickListener {
|
||||||
val intent = Intent(requireContext(), WebRtcCallActivity::class.java)
|
// val intent = Intent(requireContext(), WebRtcCallActivity::class.java)
|
||||||
val bundle = bundleOf(
|
// val bundle = bundleOf(
|
||||||
WebRtcCallActivity.EXTRA_ADDRESS to address,
|
// WebRtcCallActivity.EXTRA_ADDRESS to address,
|
||||||
WebRtcCallActivity.EXTRA_CALL_ID to callId
|
// WebRtcCallActivity.EXTRA_CALL_ID to callId
|
||||||
)
|
// )
|
||||||
intent.action = WebRtcCallActivity.ACTION_ANSWER
|
// intent.action = WebRtcCallActivity.ACTION_ANSWER
|
||||||
bundle.putStringArray(WebRtcCallActivity.EXTRA_SDP, sdp)
|
// bundle.putStringArray(WebRtcCallActivity.EXTRA_SDP, sdp)
|
||||||
|
//
|
||||||
intent.putExtras(bundle)
|
// intent.putExtras(bundle)
|
||||||
startActivity(intent)
|
// startActivity(intent)
|
||||||
dismiss()
|
// dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
declineButton.setOnClickListener {
|
declineButton.setOnClickListener {
|
||||||
|
@@ -147,6 +147,8 @@ class CallManager(context: Context, audioManager: AudioManagerCompat): PeerConne
|
|||||||
fun isBusy(context: Context) = currentConnectionState != CallState.STATE_IDLE
|
fun isBusy(context: Context) = currentConnectionState != CallState.STATE_IDLE
|
||||||
|| context.getSystemService(TelephonyManager::class.java).callState != TelephonyManager.CALL_STATE_IDLE
|
|| context.getSystemService(TelephonyManager::class.java).callState != TelephonyManager.CALL_STATE_IDLE
|
||||||
|
|
||||||
|
fun isIdle() = currentConnectionState == CallState.STATE_IDLE
|
||||||
|
|
||||||
fun initializeVideo(context: Context) {
|
fun initializeVideo(context: Context) {
|
||||||
Util.runOnMainSync {
|
Util.runOnMainSync {
|
||||||
val base = EglBase.create()
|
val base = EglBase.create()
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
package org.thoughtcrime.securesms.webrtc
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
|
import androidx.lifecycle.coroutineScope
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.session.libsession.messaging.messages.control.CallMessage
|
||||||
|
import org.session.libsession.messaging.utilities.WebRtcUtils
|
||||||
|
import org.session.libsession.utilities.Address
|
||||||
|
import org.session.libsignal.protos.SignalServiceProtos
|
||||||
|
import org.session.libsignal.protos.SignalServiceProtos.CallMessage.Type.OFFER
|
||||||
|
import org.thoughtcrime.securesms.service.WebRtcCallService
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
|
class CallMessageProcessor(private val context: Context, lifecycle: Lifecycle) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle.coroutineScope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
val nextMessage = WebRtcUtils.SIGNAL_QUEUE.receive()
|
||||||
|
when {
|
||||||
|
// TODO: handle messages as they come in
|
||||||
|
nextMessage.type == OFFER -> incomingCall(nextMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun incomingCall(callMessage: CallMessage) {
|
||||||
|
val recipientAddress = callMessage.recipient ?: return
|
||||||
|
val callId = callMessage.callId ?: return
|
||||||
|
val sdp = callMessage.sdps.firstOrNull() ?: return
|
||||||
|
val incomingIntent = WebRtcCallService.incomingCall(
|
||||||
|
context = context,
|
||||||
|
address = Address.fromSerialized(recipientAddress),
|
||||||
|
sdp = sdp,
|
||||||
|
callId = callId
|
||||||
|
)
|
||||||
|
context.startService(incomingIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -165,7 +165,7 @@ class PeerConnectionWrapper(context: Context,
|
|||||||
fun createOffer(mediaConstraints: MediaConstraints): SessionDescription {
|
fun createOffer(mediaConstraints: MediaConstraints): SessionDescription {
|
||||||
val future = SettableFuture<SessionDescription>()
|
val future = SettableFuture<SessionDescription>()
|
||||||
|
|
||||||
peerConnection.createAnswer(object:SdpObserver {
|
peerConnection.createOffer(object:SdpObserver {
|
||||||
override fun onCreateSuccess(sdp: SessionDescription?) {
|
override fun onCreateSuccess(sdp: SessionDescription?) {
|
||||||
future.set(sdp)
|
future.set(sdp)
|
||||||
}
|
}
|
||||||
|
@@ -37,9 +37,9 @@ class SignalAudioManager(private val context: Context,
|
|||||||
private val androidAudioManager: AudioManagerCompat) {
|
private val androidAudioManager: AudioManagerCompat) {
|
||||||
|
|
||||||
private var commandAndControlThread: HandlerThread? = HandlerThread("call-audio").apply { start() }
|
private var commandAndControlThread: HandlerThread? = HandlerThread("call-audio").apply { start() }
|
||||||
private var handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
private var handler: SignalAudioHandler? = null
|
||||||
|
|
||||||
private val signalBluetoothManager = SignalBluetoothManager(context, this, androidAudioManager, handler)
|
private var signalBluetoothManager: SignalBluetoothManager? = null
|
||||||
|
|
||||||
private var state: State = State.UNINITIALIZED
|
private var state: State = State.UNINITIALIZED
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
private var wiredHeadsetReceiver: WiredHeadsetReceiver? = null
|
private var wiredHeadsetReceiver: WiredHeadsetReceiver? = null
|
||||||
|
|
||||||
fun handleCommand(command: AudioManagerCommand) {
|
fun handleCommand(command: AudioManagerCommand) {
|
||||||
handler.post {
|
handler?.post {
|
||||||
when (command) {
|
when (command) {
|
||||||
is AudioManagerCommand.Initialize -> initialize()
|
is AudioManagerCommand.Initialize -> initialize()
|
||||||
is AudioManagerCommand.Shutdown -> shutdown()
|
is AudioManagerCommand.Shutdown -> shutdown()
|
||||||
@@ -89,6 +89,10 @@ class SignalAudioManager(private val context: Context,
|
|||||||
commandAndControlThread = HandlerThread("call-audio").apply { start() }
|
commandAndControlThread = HandlerThread("call-audio").apply { start() }
|
||||||
handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
handler = SignalAudioHandler(commandAndControlThread!!.looper)
|
||||||
|
|
||||||
|
signalBluetoothManager = SignalBluetoothManager(context, this, androidAudioManager, handler!!)
|
||||||
|
|
||||||
|
handler!!.post {
|
||||||
|
|
||||||
savedAudioMode = androidAudioManager.mode
|
savedAudioMode = androidAudioManager.mode
|
||||||
savedIsSpeakerPhoneOn = androidAudioManager.isSpeakerphoneOn
|
savedIsSpeakerPhoneOn = androidAudioManager.isSpeakerphoneOn
|
||||||
savedIsMicrophoneMute = androidAudioManager.isMicrophoneMute
|
savedIsMicrophoneMute = androidAudioManager.isMicrophoneMute
|
||||||
@@ -100,7 +104,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
|
|
||||||
audioDevices.clear()
|
audioDevices.clear()
|
||||||
|
|
||||||
signalBluetoothManager.start()
|
signalBluetoothManager!!.start()
|
||||||
|
|
||||||
updateAudioDeviceState()
|
updateAudioDeviceState()
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
Log.d(TAG, "Initialized")
|
Log.d(TAG, "Initialized")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
Log.d(TAG, "Starting. state: $state")
|
Log.d(TAG, "Starting. state: $state")
|
||||||
@@ -159,7 +164,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
}
|
}
|
||||||
wiredHeadsetReceiver = null
|
wiredHeadsetReceiver = null
|
||||||
|
|
||||||
signalBluetoothManager.stop()
|
signalBluetoothManager?.stop()
|
||||||
|
|
||||||
setSpeakerphoneOn(savedIsSpeakerPhoneOn)
|
setSpeakerphoneOn(savedIsSpeakerPhoneOn)
|
||||||
setMicrophoneMute(savedIsMicrophoneMute)
|
setMicrophoneMute(savedIsMicrophoneMute)
|
||||||
@@ -172,7 +177,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun shutdown() {
|
private fun shutdown() {
|
||||||
handler.post {
|
handler?.post {
|
||||||
stop(false)
|
stop(false)
|
||||||
if (commandAndControlThread != null) {
|
if (commandAndControlThread != null) {
|
||||||
Log.i(TAG, "Shutting down command and control")
|
Log.i(TAG, "Shutting down command and control")
|
||||||
@@ -183,25 +188,25 @@ class SignalAudioManager(private val context: Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateAudioDeviceState() {
|
private fun updateAudioDeviceState() {
|
||||||
handler.assertHandlerThread()
|
handler!!.assertHandlerThread()
|
||||||
|
|
||||||
Log.i(
|
Log.i(
|
||||||
TAG,
|
TAG,
|
||||||
"updateAudioDeviceState(): " +
|
"updateAudioDeviceState(): " +
|
||||||
"wired: $hasWiredHeadset " +
|
"wired: $hasWiredHeadset " +
|
||||||
"bt: ${signalBluetoothManager.state} " +
|
"bt: ${signalBluetoothManager!!.state} " +
|
||||||
"available: $audioDevices " +
|
"available: $audioDevices " +
|
||||||
"selected: $selectedAudioDevice " +
|
"selected: $selectedAudioDevice " +
|
||||||
"userSelected: $userSelectedAudioDevice"
|
"userSelected: $userSelectedAudioDevice"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (signalBluetoothManager.state.shouldUpdate()) {
|
if (signalBluetoothManager!!.state.shouldUpdate()) {
|
||||||
signalBluetoothManager.updateDevice()
|
signalBluetoothManager!!.updateDevice()
|
||||||
}
|
}
|
||||||
|
|
||||||
val newAudioDevices = mutableSetOf(AudioDevice.SPEAKER_PHONE)
|
val newAudioDevices = mutableSetOf(AudioDevice.SPEAKER_PHONE)
|
||||||
|
|
||||||
if (signalBluetoothManager.state.hasDevice()) {
|
if (signalBluetoothManager!!.state.hasDevice()) {
|
||||||
newAudioDevices += AudioDevice.BLUETOOTH
|
newAudioDevices += AudioDevice.BLUETOOTH
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +222,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
var audioDeviceSetUpdated = audioDevices != newAudioDevices
|
var audioDeviceSetUpdated = audioDevices != newAudioDevices
|
||||||
audioDevices = newAudioDevices
|
audioDevices = newAudioDevices
|
||||||
|
|
||||||
if (signalBluetoothManager.state == SignalBluetoothManager.State.UNAVAILABLE && userSelectedAudioDevice == AudioDevice.BLUETOOTH) {
|
if (signalBluetoothManager!!.state == SignalBluetoothManager.State.UNAVAILABLE && userSelectedAudioDevice == AudioDevice.BLUETOOTH) {
|
||||||
userSelectedAudioDevice = AudioDevice.NONE
|
userSelectedAudioDevice = AudioDevice.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,33 +235,33 @@ class SignalAudioManager(private val context: Context,
|
|||||||
userSelectedAudioDevice = AudioDevice.NONE
|
userSelectedAudioDevice = AudioDevice.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val needBluetoothAudioStart = signalBluetoothManager.state == SignalBluetoothManager.State.AVAILABLE &&
|
val needBluetoothAudioStart = signalBluetoothManager!!.state == SignalBluetoothManager.State.AVAILABLE &&
|
||||||
(userSelectedAudioDevice == AudioDevice.NONE || userSelectedAudioDevice == AudioDevice.BLUETOOTH || autoSwitchToBluetooth)
|
(userSelectedAudioDevice == AudioDevice.NONE || userSelectedAudioDevice == AudioDevice.BLUETOOTH || autoSwitchToBluetooth)
|
||||||
|
|
||||||
val needBluetoothAudioStop = (signalBluetoothManager.state == SignalBluetoothManager.State.CONNECTED || signalBluetoothManager.state == SignalBluetoothManager.State.CONNECTING) &&
|
val needBluetoothAudioStop = (signalBluetoothManager!!.state == SignalBluetoothManager.State.CONNECTED || signalBluetoothManager!!.state == SignalBluetoothManager.State.CONNECTING) &&
|
||||||
(userSelectedAudioDevice != AudioDevice.NONE && userSelectedAudioDevice != AudioDevice.BLUETOOTH)
|
(userSelectedAudioDevice != AudioDevice.NONE && userSelectedAudioDevice != AudioDevice.BLUETOOTH)
|
||||||
|
|
||||||
if (signalBluetoothManager.state.hasDevice()) {
|
if (signalBluetoothManager!!.state.hasDevice()) {
|
||||||
Log.i(TAG, "Need bluetooth audio: state: ${signalBluetoothManager.state} start: $needBluetoothAudioStart stop: $needBluetoothAudioStop")
|
Log.i(TAG, "Need bluetooth audio: state: ${signalBluetoothManager!!.state} start: $needBluetoothAudioStart stop: $needBluetoothAudioStop")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needBluetoothAudioStop) {
|
if (needBluetoothAudioStop) {
|
||||||
signalBluetoothManager.stopScoAudio()
|
signalBluetoothManager!!.stopScoAudio()
|
||||||
signalBluetoothManager.updateDevice()
|
signalBluetoothManager!!.updateDevice()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!autoSwitchToBluetooth && signalBluetoothManager.state == SignalBluetoothManager.State.UNAVAILABLE) {
|
if (!autoSwitchToBluetooth && signalBluetoothManager!!.state == SignalBluetoothManager.State.UNAVAILABLE) {
|
||||||
autoSwitchToBluetooth = true
|
autoSwitchToBluetooth = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needBluetoothAudioStart && !needBluetoothAudioStop) {
|
if (needBluetoothAudioStart && !needBluetoothAudioStop) {
|
||||||
if (!signalBluetoothManager.startScoAudio()) {
|
if (!signalBluetoothManager!!.startScoAudio()) {
|
||||||
audioDevices.remove(AudioDevice.BLUETOOTH)
|
audioDevices.remove(AudioDevice.BLUETOOTH)
|
||||||
audioDeviceSetUpdated = true
|
audioDeviceSetUpdated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (autoSwitchToBluetooth && signalBluetoothManager.state == SignalBluetoothManager.State.CONNECTED) {
|
if (autoSwitchToBluetooth && signalBluetoothManager!!.state == SignalBluetoothManager.State.CONNECTED) {
|
||||||
userSelectedAudioDevice = AudioDevice.BLUETOOTH
|
userSelectedAudioDevice = AudioDevice.BLUETOOTH
|
||||||
autoSwitchToBluetooth = false
|
autoSwitchToBluetooth = false
|
||||||
}
|
}
|
||||||
@@ -373,7 +378,7 @@ class SignalAudioManager(private val context: Context,
|
|||||||
val pluggedIn = intent.getIntExtra("state", 0) == 1
|
val pluggedIn = intent.getIntExtra("state", 0) == 1
|
||||||
val hasMic = intent.getIntExtra("microphone", 0) == 1
|
val hasMic = intent.getIntExtra("microphone", 0) == 1
|
||||||
|
|
||||||
handler.post { onWiredHeadsetChange(pluggedIn, hasMic) }
|
handler?.post { onWiredHeadsetChange(pluggedIn, hasMic) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 834 B |
BIN
app/src/main/res/drawable-anydpi-v24/ic_close_grey600_32dp.webp
Normal file
BIN
app/src/main/res/drawable-anydpi-v24/ic_close_grey600_32dp.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 404 B |
BIN
app/src/main/res/drawable-anydpi-v24/ic_phone_grey600_32dp.webp
Normal file
BIN
app/src/main/res/drawable-anydpi-v24/ic_phone_grey600_32dp.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 966 B |
@@ -51,6 +51,6 @@ allprojects {
|
|||||||
|
|
||||||
project.ext {
|
project.ext {
|
||||||
androidMinimumSdkVersion = 23
|
androidMinimumSdkVersion = 23
|
||||||
androidCompileSdkVersion = 31
|
androidCompileSdkVersion = 30
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user