From a60c66ad33a38e3d463ce7c7f3aac2d3323452b4 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 25 Sep 2023 11:54:19 +0200 Subject: [PATCH] Added pause/resume action to long press menu in calls list + fixing issues with multi calls --- .../org/linphone/contacts/ContactsManager.kt | 1 - .../java/org/linphone/core/CoreContext.kt | 10 +- .../java/org/linphone/core/CorePreferences.kt | 12 + .../NotificationBroadcastReceiver.kt | 6 +- .../notifications/NotificationsManager.kt | 1 - .../telecom/TelecomCallControlCallback.kt | 3 +- .../java/org/linphone/ui/call/CallActivity.kt | 68 +++--- .../ui/call/fragment/ActiveCallFragment.kt | 8 + .../call/fragment/CallMenuDialogFragment.kt | 7 + .../ui/call/viewmodel/CallsViewModel.kt | 80 ++----- .../ui/call/viewmodel/CurrentCallViewModel.kt | 222 ++++++++++++++---- .../java/org/linphone/utils/LinphoneUtils.kt | 16 ++ .../layout/call_audio_device_list_cell.xml | 7 +- .../res/layout/call_audio_devices_menu.xml | 1 + .../res/layout/calls_list_long_press_menu.xml | 38 ++- app/src/main/res/values/strings.xml | 2 +- 16 files changed, 330 insertions(+), 152 deletions(-) diff --git a/app/src/main/java/org/linphone/contacts/ContactsManager.kt b/app/src/main/java/org/linphone/contacts/ContactsManager.kt index c9d232be5..f01a2ef03 100644 --- a/app/src/main/java/org/linphone/contacts/ContactsManager.kt +++ b/app/src/main/java/org/linphone/contacts/ContactsManager.kt @@ -47,7 +47,6 @@ import org.linphone.ui.main.MainActivity import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.model.isInSecureMode -import org.linphone.utils.ImageUtils import org.linphone.utils.LinphoneUtils import org.linphone.utils.PhoneNumberUtils diff --git a/app/src/main/java/org/linphone/core/CoreContext.kt b/app/src/main/java/org/linphone/core/CoreContext.kt index 5aefda648..ba206ab77 100644 --- a/app/src/main/java/org/linphone/core/CoreContext.kt +++ b/app/src/main/java/org/linphone/core/CoreContext.kt @@ -74,7 +74,15 @@ class CoreContext @UiThread constructor(val context: Context) : HandlerThread("C private val coreListener = object : CoreListenerStub() { @WorkerThread override fun onGlobalStateChanged(core: Core, state: GlobalState, message: String) { - Log.i("$TAG Global state changed: $state") + Log.i("$TAG Global state changed [$state]") + } + + override fun onConfiguringStatus( + core: Core, + status: Config.ConfiguringState?, + message: String? + ) { + Log.i("$TAG Configuring state changed [$status]") } @WorkerThread diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index d62536fe7..aef02cbab 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -64,12 +64,22 @@ class CorePreferences @UiThread constructor(private val context: Context) { // Calls settings + @get:WorkerThread @set:WorkerThread var routeAudioToBluetoothIfAvailable: Boolean get() = config.getBool("app", "route_audio_to_bluetooth_if_available", true) set(value) { config.setBool("app", "route_audio_to_bluetooth_if_available", value) } + // This won't be done if bluetooth or wired headset is used + @get:WorkerThread @set:WorkerThread + var routeAudioToSpeakerWhenVideoIsEnabled: Boolean + get() = config.getBool("app", "route_audio_to_speaker_when_video_enabled", true) + set(value) { + config.setBool("app", "route_audio_to_speaker_when_video_enabled", value) + } + + @get:WorkerThread @set:WorkerThread var automaticallyStartCallRecording: Boolean get() = config.getBool("app", "auto_start_call_record", false) set(value) { @@ -105,9 +115,11 @@ class CorePreferences @UiThread constructor(private val context: Context) { val thirdPartyDefaultValuesPath: String get() = context.filesDir.absolutePath + "/assistant_third_party_default_values" + @get:AnyThread private val ringtonesPath: String get() = context.filesDir.absolutePath + "/share/sounds/linphone/rings/" + @get:AnyThread val defaultRingtonePath: String get() = ringtonesPath + "notes_of_the_optimistic.mkv" diff --git a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt index 75703f5d9..e1db4e5f7 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.kt @@ -25,8 +25,8 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.core.Call import org.linphone.core.tools.Log +import org.linphone.utils.LinphoneUtils class NotificationBroadcastReceiver : BroadcastReceiver() { companion object { @@ -61,9 +61,7 @@ class NotificationBroadcastReceiver : BroadcastReceiver() { if (intent.action == NotificationsManager.INTENT_ANSWER_CALL_NOTIF_ACTION) { coreContext.answerCall(call) } else { - if (call.state == Call.State.IncomingReceived || - call.state == Call.State.IncomingEarlyMedia - ) { + if (LinphoneUtils.isCallIncoming(call.state)) { coreContext.declineCall(call) } else { coreContext.terminateCall(call) diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt index 9927cb8c4..7a9f7c9ea 100644 --- a/app/src/main/java/org/linphone/notifications/NotificationsManager.kt +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.kt @@ -61,7 +61,6 @@ import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.ui.call.CallActivity import org.linphone.utils.AppUtils -import org.linphone.utils.ImageUtils import org.linphone.utils.LinphoneUtils class NotificationsManager @MainThread constructor(private val context: Context) { diff --git a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt index 30c520187..601f1e090 100644 --- a/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt +++ b/app/src/main/java/org/linphone/telecom/TelecomCallControlCallback.kt @@ -35,6 +35,7 @@ import org.linphone.core.Call import org.linphone.core.CallListenerStub import org.linphone.core.tools.Log import org.linphone.utils.AudioRouteUtils +import org.linphone.utils.LinphoneUtils class TelecomCallControlCallback constructor( private val call: Call, @@ -207,7 +208,7 @@ class TelecomCallControlCallback constructor( override suspend fun onAnswer(callType: Int): Boolean { Log.i("$TAG We're asked to answer the call with type [$callType]") coreContext.postOnCoreThread { - if (call.state == Call.State.IncomingReceived || call.state == Call.State.IncomingEarlyMedia) { + if (LinphoneUtils.isCallIncoming(call.state)) { Log.i("$TAG Answering call") coreContext.answerCall(call) // TODO: use call type } diff --git a/app/src/main/java/org/linphone/ui/call/CallActivity.kt b/app/src/main/java/org/linphone/ui/call/CallActivity.kt index 182b77c9a..e92375bbe 100644 --- a/app/src/main/java/org/linphone/ui/call/CallActivity.kt +++ b/app/src/main/java/org/linphone/ui/call/CallActivity.kt @@ -105,6 +105,40 @@ class CallActivity : AppCompatActivity() { } } + callViewModel.showLowWifiSignalEvent.observe(this) { + it.consume { show -> + if (show) { + showRedToast( + getString(R.string.toast_alert_low_wifi_signal), + R.drawable.wifi_low + ) + } else { + hideRedToast() + showGreenToast( + getString(R.string.toast_alert_low_wifi_signal_cleared), + R.drawable.wifi_high + ) + } + } + } + + callViewModel.showLowCellularSignalEvent.observe(this) { + it.consume { show -> + if (show) { + showRedToast( + getString(R.string.toast_alert_low_cellular_signal), + R.drawable.cell_signal_low + ) + } else { + hideRedToast() + showGreenToast( + getString(R.string.toast_alert_low_cellular_signal_cleared), + R.drawable.cell_signal_full + ) + } + } + } + callsViewModel.showIncomingCallEvent.observe(this) { it.consume { val action = IncomingCallFragmentDirections.actionGlobalIncomingCallFragment() @@ -143,40 +177,6 @@ class CallActivity : AppCompatActivity() { } } - callsViewModel.showLowWifiSignalEvent.observe(this) { - it.consume { show -> - if (show) { - showRedToast( - getString(R.string.toast_alert_low_wifi_signal), - R.drawable.wifi_low - ) - } else { - hideRedToast() - showGreenToast( - getString(R.string.toast_alert_low_wifi_signal_cleared), - R.drawable.wifi_high - ) - } - } - } - - callsViewModel.showLowCellularSignalEvent.observe(this) { - it.consume { show -> - if (show) { - showRedToast( - getString(R.string.toast_alert_low_cellular_signal), - R.drawable.cell_signal_low - ) - } else { - hideRedToast() - showGreenToast( - getString(R.string.toast_alert_low_cellular_signal_cleared), - R.drawable.cell_signal_full - ) - } - } - } - sharedViewModel.toggleFullScreenEvent.observe(this) { it.consume { hide -> hideUI(hide) diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt index 843eab1af..e4fbf026b 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/ActiveCallFragment.kt @@ -20,6 +20,7 @@ package org.linphone.ui.call.fragment import android.annotation.SuppressLint +import android.app.Dialog import android.os.Bundle import android.os.SystemClock import android.view.LayoutInflater @@ -61,6 +62,8 @@ class ActiveCallFragment : GenericCallFragment() { private lateinit var callsViewModel: CallsViewModel + private var zrtpSasDialog: Dialog? = null + // For moving video preview purposes private var previewX: Float = 0f @@ -181,6 +184,7 @@ class ActiveCallFragment : GenericCallFragment() { } dialog.show() + zrtpSasDialog = dialog } } @@ -253,6 +257,10 @@ class ActiveCallFragment : GenericCallFragment() { @SuppressLint("ClickableViewAccessibility") override fun onPause() { super.onPause() + + zrtpSasDialog?.dismiss() + zrtpSasDialog = null + binding.localPreviewVideoSurface.setOnTouchListener(null) } diff --git a/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt index 00f469b8c..1863a1e60 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/CallMenuDialogFragment.kt @@ -58,6 +58,13 @@ class CallMenuDialogFragment( dismiss() } + view.setPauseResumeClickListener { + callModel.togglePauseResume() + dismiss() + } + + view.isPaused = callModel.isPaused.value == true + return view.root } } diff --git a/app/src/main/java/org/linphone/ui/call/viewmodel/CallsViewModel.kt b/app/src/main/java/org/linphone/ui/call/viewmodel/CallsViewModel.kt index 8027a3dd7..f54390d8b 100644 --- a/app/src/main/java/org/linphone/ui/call/viewmodel/CallsViewModel.kt +++ b/app/src/main/java/org/linphone/ui/call/viewmodel/CallsViewModel.kt @@ -24,23 +24,17 @@ import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.core.Alert -import org.linphone.core.AlertListenerStub import org.linphone.core.Call import org.linphone.core.Core import org.linphone.core.CoreListenerStub import org.linphone.core.tools.Log import org.linphone.ui.call.model.CallModel import org.linphone.utils.Event +import org.linphone.utils.LinphoneUtils class CallsViewModel @UiThread constructor() : ViewModel() { companion object { private const val TAG = "[Calls ViewModel]" - - // Keys are hardcoded in SDK - private const val ALERT_NETWORK_TYPE_KEY = "network-type" - private const val ALERT_NETWORK_TYPE_WIFI = "wifi" - private const val ALERT_NETWORK_TYPE_CELLULAR = "mobile" } val calls = MutableLiveData>() @@ -55,37 +49,6 @@ class CallsViewModel @UiThread constructor() : ViewModel() { val noMoreCallEvent = MutableLiveData>() - val showLowWifiSignalEvent = MutableLiveData>() - - val showLowCellularSignalEvent = MutableLiveData>() - - private val alertListener = object : AlertListenerStub() { - @WorkerThread - override fun onTerminated(alert: Alert) { - val remote = alert.call.remoteAddress.asStringUriOnly() - Log.w("$TAG Alert of type [${alert.type}] dismissed for call from [$remote]") - alert.removeListener(this) - - if (alert.type == Alert.Type.QoSLowSignal) { - when (val signalType = alert.informations?.getString(ALERT_NETWORK_TYPE_KEY)) { - ALERT_NETWORK_TYPE_WIFI -> { - Log.i("$TAG Wi-Fi signal no longer low") - showLowWifiSignalEvent.postValue(Event(false)) - } - ALERT_NETWORK_TYPE_CELLULAR -> { - Log.i("$TAG Cellular signal no longer low") - showLowCellularSignalEvent.postValue(Event(false)) - } - else -> { - Log.w( - "$TAG Unexpected type of signal [$signalType] found in alert information" - ) - } - } - } - } - } - private val coreListener = object : CoreListenerStub() { @WorkerThread override fun onLastCallEnded(core: Core) { @@ -100,6 +63,8 @@ class CallsViewModel @UiThread constructor() : ViewModel() { state: Call.State, message: String ) { + Log.i("$TAG Call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]") + // Update calls list if needed val found = calls.value.orEmpty().find { it.call == call @@ -125,10 +90,12 @@ class CallsViewModel @UiThread constructor() : ViewModel() { list.addAll(calls.value.orEmpty()) list.remove(found) calls.postValue(list) + callsCount.postValue(list.size) found.destroy() } } + // Update currently displayed fragment according to call state if (call == core.currentCall || core.currentCall == null) { Log.i( "$TAG Current call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]" @@ -141,28 +108,20 @@ class CallsViewModel @UiThread constructor() : ViewModel() { } } } - } - - @WorkerThread - override fun onNewAlertTriggered(core: Core, alert: Alert) { - val remote = alert.call.remoteAddress.asStringUriOnly() - Log.w("$TAG Alert of type [${alert.type}] triggered for call from [$remote]") - alert.addListener(alertListener) - - if (alert.type == Alert.Type.QoSLowSignal) { - when (val networkType = alert.informations?.getString(ALERT_NETWORK_TYPE_KEY)) { - ALERT_NETWORK_TYPE_WIFI -> { - Log.i("$TAG Triggered low signal alert is for Wi-Fi") - showLowWifiSignalEvent.postValue(Event(true)) - } - ALERT_NETWORK_TYPE_CELLULAR -> { - Log.i("$TAG Triggered low signal alert is for cellular") - showLowCellularSignalEvent.postValue(Event(true)) - } - else -> { - Log.w( - "$TAG Unexpected type of signal [$networkType] found in alert information" - ) + if (LinphoneUtils.isCallIncoming(call.state)) { + Log.i("$TAG Asking activity to show incoming call fragment") + showIncomingCallEvent.postValue(Event(true)) + } else if (LinphoneUtils.isCallEnding(call.state)) { + if (core.callsNb > 0) { + val newCurrentCall = core.currentCall ?: core.calls.firstOrNull() + if (newCurrentCall != null) { + if (LinphoneUtils.isCallIncoming(newCurrentCall.state)) { + Log.i("$TAG Asking activity to show incoming call fragment") + showIncomingCallEvent.postValue(Event(true)) + } else { + Log.i("$TAG Asking activity to show active call fragment") + goToActiveCallEvent.postValue(Event(true)) + } } } } @@ -183,6 +142,7 @@ class CallsViewModel @UiThread constructor() : ViewModel() { callsCount.postValue(list.size) val currentCall = core.currentCall ?: core.calls.first() + Log.i("$TAG Current call is [${currentCall.remoteAddress.asStringUriOnly()}]") when (currentCall.state) { Call.State.Connected, Call.State.StreamsRunning, Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote, Call.State.UpdatedByRemote, Call.State.Updating -> { diff --git a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt index 604ba3c91..bfbe965af 100644 --- a/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/call/viewmodel/CurrentCallViewModel.kt @@ -28,10 +28,15 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import java.util.Locale import org.linphone.LinphoneApplication.Companion.coreContext +import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R +import org.linphone.core.Alert +import org.linphone.core.AlertListenerStub import org.linphone.core.AudioDevice import org.linphone.core.Call import org.linphone.core.CallListenerStub +import org.linphone.core.Core +import org.linphone.core.CoreListenerStub import org.linphone.core.MediaDirection import org.linphone.core.MediaEncryption import org.linphone.core.tools.Log @@ -46,6 +51,11 @@ import org.linphone.utils.LinphoneUtils class CurrentCallViewModel @UiThread constructor() : ViewModel() { companion object { private const val TAG = "[Current Call ViewModel]" + + // Keys are hardcoded in SDK + private const val ALERT_NETWORK_TYPE_KEY = "network-type" + private const val ALERT_NETWORK_TYPE_WIFI = "wifi" + private const val ALERT_NETWORK_TYPE_CELLULAR = "mobile" } val contact = MutableLiveData() @@ -115,7 +125,13 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { MutableLiveData>() } - private lateinit var call: Call + // Alerts related + + val showLowWifiSignalEvent = MutableLiveData>() + + val showLowCellularSignalEvent = MutableLiveData>() + + private lateinit var currentCall: Call private val callListener = object : CallListenerStub() { @WorkerThread @@ -124,24 +140,41 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } @WorkerThread - override fun onStateChanged(call: Call, state: Call.State?, message: String) { - if (CurrentCallViewModel@call != call) { - return - } + override fun onStateChanged(call: Call, state: Call.State, message: String) { Log.i("$TAG Call [${call.remoteAddress.asStringUriOnly()}] state changed [$state]") - if (LinphoneUtils.isCallOutgoing(call.state)) { isVideoEnabled.postValue(call.params.isVideoEnabled) + } else if (LinphoneUtils.isCallEnding(call.state)) { + // If current call is being terminated but there is at least one other call, switch + val core = call.core + val callsCount = core.callsNb + Log.i( + "$TAG Call is being ended, check for another current call (currently [$callsCount] calls)" + ) + if (callsCount > 0) { + val newCurrentCall = core.currentCall ?: core.calls.firstOrNull() + if (newCurrentCall != null) { + Log.i( + "$TAG From now on current call will be [${newCurrentCall.remoteAddress.asStringUriOnly()}]" + ) + configureCall(newCurrentCall) + } else { + Log.e("$TAG Failed to get a valid call to display!") + } + } } else { val videoEnabled = call.currentParams.isVideoEnabled if (videoEnabled && isVideoEnabled.value == false) { - Log.i("$TAG Video enabled, routing audio to speaker") - AudioRouteUtils.routeAudioToSpeaker(call) + if (corePreferences.routeAudioToSpeakerWhenVideoIsEnabled) { + Log.i("$TAG Video is now enabled, routing audio to speaker") + AudioRouteUtils.routeAudioToSpeaker(call) + } } isVideoEnabled.postValue(videoEnabled) // Toggle full screen OFF when remote disables video if (!videoEnabled && fullScreenMode.value == true) { + Log.w("$TAG Video is not longer enabled, leaving full screen mode") fullScreenMode.postValue(false) } } @@ -154,6 +187,100 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } } + private val coreListener = object : CoreListenerStub() { + override fun onCallStateChanged( + core: Core, + call: Call, + state: Call.State, + message: String + ) { + if (::currentCall.isInitialized) { + if (call != currentCall) { + if (call == currentCall.core.currentCall) { + Log.w( + "$TAG Current call has changed, now is [${call.remoteAddress.asStringUriOnly()}] with state [$state]" + ) + currentCall.removeListener(callListener) + configureCall(call) + } else if (LinphoneUtils.isCallIncoming(call.state)) { + Log.w( + "$TAG A call is being received [${call.remoteAddress.asStringUriOnly()}], using it as current call unless declined" + ) + currentCall.removeListener(callListener) + configureCall(call) + } + } + } else { + Log.w( + "$TAG There was no current call (shouldn't be possible), using [${call.remoteAddress.asStringUriOnly()}] anyway" + ) + configureCall(call) + } + } + + @WorkerThread + override fun onNewAlertTriggered(core: Core, alert: Alert) { + val remote = alert.call.remoteAddress.asStringUriOnly() + Log.w("$TAG Alert of type [${alert.type}] triggered for call from [$remote]") + alert.addListener(alertListener) + + if (alert.call != currentCall) { + Log.w("$TAG Terminated alert wasn't for current call, do not display it") + return + } + + if (alert.type == Alert.Type.QoSLowSignal) { + when (val networkType = alert.informations?.getString(ALERT_NETWORK_TYPE_KEY)) { + ALERT_NETWORK_TYPE_WIFI -> { + Log.i("$TAG Triggered low signal alert is for Wi-Fi") + showLowWifiSignalEvent.postValue(Event(true)) + } + ALERT_NETWORK_TYPE_CELLULAR -> { + Log.i("$TAG Triggered low signal alert is for cellular") + showLowCellularSignalEvent.postValue(Event(true)) + } + else -> { + Log.w( + "$TAG Unexpected type of signal [$networkType] found in alert information" + ) + } + } + } + } + } + + private val alertListener = object : AlertListenerStub() { + @WorkerThread + override fun onTerminated(alert: Alert) { + val remote = alert.call.remoteAddress.asStringUriOnly() + Log.w("$TAG Alert of type [${alert.type}] dismissed for call from [$remote]") + alert.removeListener(this) + + if (alert.call != currentCall) { + Log.w("$TAG Terminated alert wasn't for current call, do not display it") + return + } + + if (alert.type == Alert.Type.QoSLowSignal) { + when (val signalType = alert.informations?.getString(ALERT_NETWORK_TYPE_KEY)) { + ALERT_NETWORK_TYPE_WIFI -> { + Log.i("$TAG Wi-Fi signal no longer low") + showLowWifiSignalEvent.postValue(Event(false)) + } + ALERT_NETWORK_TYPE_CELLULAR -> { + Log.i("$TAG Cellular signal no longer low") + showLowCellularSignalEvent.postValue(Event(false)) + } + else -> { + Log.w( + "$TAG Unexpected type of signal [$signalType] found in alert information" + ) + } + } + } + } + } + init { isVideoEnabled.value = false isMicrophoneMuted.value = false @@ -161,10 +288,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { isActionsMenuExpanded.value = false coreContext.postOnCoreThread { core -> - val currentCall = core.currentCall ?: core.calls.firstOrNull() + core.addListener(coreListener) + val call = core.currentCall ?: core.calls.firstOrNull() - if (currentCall != null) { - call = currentCall + if (call != null) { Log.i("$TAG Found call [${call.remoteAddress.asStringUriOnly()}]") configureCall(call) } else { @@ -178,8 +305,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { { digit -> // onDigitClicked appendDigitToSearchBarEvent.value = Event(digit) coreContext.postOnCoreThread { - Log.i("$TAG Sending DTMF [${digit.first()}]") - call.sendDtmf(digit.first()) + if (::currentCall.isInitialized) { + Log.i("$TAG Sending DTMF [${digit.first()}]") + currentCall.sendDtmf(digit.first()) + } } }, { // OnBackspaceClicked @@ -194,9 +323,11 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { override fun onCleared() { super.onCleared() - coreContext.postOnCoreThread { - if (::call.isInitialized) { - call.removeListener(callListener) + coreContext.postOnCoreThread { core -> + core.removeListener(coreListener) + + if (::currentCall.isInitialized) { + currentCall.removeListener(callListener) } } } @@ -204,9 +335,9 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @UiThread fun answer() { coreContext.postOnCoreThread { - if (::call.isInitialized) { - Log.i("$TAG Answering call [$call]") - coreContext.answerCall(call) + if (::currentCall.isInitialized) { + Log.i("$TAG Answering call [$currentCall]") + coreContext.answerCall(currentCall) } } } @@ -214,9 +345,9 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @UiThread fun hangUp() { coreContext.postOnCoreThread { - if (::call.isInitialized) { - Log.i("$TAG Terminating call [${call.remoteAddress.asStringUriOnly()}]") - call.terminate() + if (::currentCall.isInitialized) { + Log.i("$TAG Terminating call [${currentCall.remoteAddress.asStringUriOnly()}]") + currentCall.terminate() } } } @@ -224,8 +355,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @UiThread fun updateZrtpSas(verified: Boolean) { coreContext.postOnCoreThread { - if (::call.isInitialized) { - call.authenticationTokenVerified = verified + if (::currentCall.isInitialized) { + currentCall.authenticationTokenVerified = verified } } } @@ -242,9 +373,9 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } coreContext.postOnCoreThread { - if (::call.isInitialized) { - call.microphoneMuted = !call.microphoneMuted - isMicrophoneMuted.postValue(call.microphoneMuted) + if (::currentCall.isInitialized) { + currentCall.microphoneMuted = !currentCall.microphoneMuted + isMicrophoneMuted.postValue(currentCall.microphoneMuted) } } } @@ -267,12 +398,12 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { // onSelected coreContext.postOnCoreThread { Log.i("$TAG Selected audio device with ID [${device.id}]") - if (::call.isInitialized) { + if (::currentCall.isInitialized) { when { - isHeadset -> AudioRouteUtils.routeAudioToHeadset(call) - isBluetooth -> AudioRouteUtils.routeAudioToBluetooth(call) - isSpeaker -> AudioRouteUtils.routeAudioToSpeaker(call) - else -> AudioRouteUtils.routeAudioToEarpiece(call) + isHeadset -> AudioRouteUtils.routeAudioToHeadset(currentCall) + isBluetooth -> AudioRouteUtils.routeAudioToBluetooth(currentCall) + isSpeaker -> AudioRouteUtils.routeAudioToSpeaker(currentCall) + else -> AudioRouteUtils.routeAudioToEarpiece(currentCall) } } } @@ -288,11 +419,11 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { Log.i( "$TAG Found less than two devices, simply switching between earpiece & speaker" ) - if (::call.isInitialized) { + if (::currentCall.isInitialized) { if (routeAudioToSpeaker) { - AudioRouteUtils.routeAudioToSpeaker(call) + AudioRouteUtils.routeAudioToSpeaker(currentCall) } else { - AudioRouteUtils.routeAudioToEarpiece(call) + AudioRouteUtils.routeAudioToEarpiece(currentCall) } } } @@ -311,9 +442,9 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { } coreContext.postOnCoreThread { core -> - if (::call.isInitialized) { - val params = core.createCallParams(call) - if (call.conference != null) { + if (::currentCall.isInitialized) { + val params = core.createCallParams(currentCall) + if (currentCall.conference != null) { if (params?.isVideoEnabled == false) { params.isVideoEnabled = true params.videoDirection = MediaDirection.SendRecv @@ -330,7 +461,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { "$TAG Updating call with video enabled set to ${params?.isVideoEnabled}" ) } - call.update(params) + currentCall.update(params) } } } @@ -362,7 +493,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { private fun showZrtpSasDialog(authToken: String) { val toRead: String val toListen: String - when (call.dir) { + when (currentCall.dir) { Call.Dir.Incoming -> { toRead = authToken.substring(0, 2) toListen = authToken.substring(2) @@ -377,10 +508,10 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @WorkerThread private fun updateEncryption(): Boolean { - when (call.currentParams.mediaEncryption) { + when (currentCall.currentParams.mediaEncryption) { MediaEncryption.ZRTP -> { - val authToken = call.authenticationToken - val deviceIsTrusted = call.authenticationTokenVerified && authToken != null + val authToken = currentCall.authenticationToken + val deviceIsTrusted = currentCall.authenticationTokenVerified && authToken != null Log.i( "$TAG Current call media encryption is ZRTP, auth token is ${if (deviceIsTrusted) "trusted" else "not trusted yet"}" ) @@ -404,6 +535,7 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @WorkerThread private fun configureCall(call: Call) { + currentCall = call call.addListener(callListener) if (call.dir == Call.Dir.Incoming) { @@ -458,8 +590,8 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() { @WorkerThread fun updateCallDuration() { - if (::call.isInitialized) { - callDuration.postValue(call.duration) + if (::currentCall.isInitialized) { + callDuration.postValue(currentCall.duration) } } diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt index 5369f4d6f..09abfceea 100644 --- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt +++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt @@ -110,6 +110,14 @@ class LinphoneUtils { return address.displayName ?: address.username ?: address.asString() } + @AnyThread + fun isCallIncoming(callState: Call.State): Boolean { + return when (callState) { + Call.State.IncomingReceived, Call.State.IncomingEarlyMedia -> true + else -> false + } + } + @AnyThread fun isCallOutgoing(callState: Call.State): Boolean { return when (callState) { @@ -126,6 +134,14 @@ class LinphoneUtils { } } + @AnyThread + fun isCallEnding(callState: Call.State): Boolean { + return when (callState) { + Call.State.End, Call.State.Error -> true + else -> false + } + } + @AnyThread @IntegerRes fun getIconResId(callStatus: Status, callDir: Dir): Int { diff --git a/app/src/main/res/layout/call_audio_device_list_cell.xml b/app/src/main/res/layout/call_audio_device_list_cell.xml index f349da51e..7ce481bbd 100644 --- a/app/src/main/res/layout/call_audio_device_list_cell.xml +++ b/app/src/main/res/layout/call_audio_device_list_cell.xml @@ -13,16 +13,17 @@ android:onClick="@{() -> model.onClicked()}" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/gray_main2_200"> + android:background="@color/black"> diff --git a/app/src/main/res/layout/call_audio_devices_menu.xml b/app/src/main/res/layout/call_audio_devices_menu.xml index 65cbb39a9..c90ac6a0f 100644 --- a/app/src/main/res/layout/call_audio_devices_menu.xml +++ b/app/src/main/res/layout/call_audio_devices_menu.xml @@ -12,6 +12,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:background="@color/gray_500" entries="@{devices}" layout="@{@layout/call_audio_device_list_cell}"> diff --git a/app/src/main/res/layout/calls_list_long_press_menu.xml b/app/src/main/res/layout/calls_list_long_press_menu.xml index f847f9c40..ebf0ef108 100644 --- a/app/src/main/res/layout/calls_list_long_press_menu.xml +++ b/app/src/main/res/layout/calls_list_long_press_menu.xml @@ -4,9 +4,15 @@ + + + + + + Dialer Messages Pause - Pause + Resume Record Hang up In progress