mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Added pause/resume action to long press menu in calls list + fixing issues with multi calls
This commit is contained in:
parent
51179c083c
commit
a60c66ad33
16 changed files with 330 additions and 152 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,13 @@ class CallMenuDialogFragment(
|
|||
dismiss()
|
||||
}
|
||||
|
||||
view.setPauseResumeClickListener {
|
||||
callModel.togglePauseResume()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
view.isPaused = callModel.isPaused.value == true
|
||||
|
||||
return view.root
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ArrayList<CallModel>>()
|
||||
|
|
@ -55,37 +49,6 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
val noMoreCallEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
val showLowWifiSignalEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
val showLowCellularSignalEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
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 -> {
|
||||
|
|
|
|||
|
|
@ -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<ContactAvatarModel>()
|
||||
|
|
@ -115,7 +125,13 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
|
|||
MutableLiveData<Event<Boolean>>()
|
||||
}
|
||||
|
||||
private lateinit var call: Call
|
||||
// Alerts related
|
||||
|
||||
val showLowWifiSignalEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
val showLowCellularSignalEvent = MutableLiveData<Event<Boolean>>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/context_menu_action_label_style"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.name, default=`Piel 6 Ori`}"
|
||||
android:background="@drawable/menu_item_background"
|
||||
android:text="@{model.name, default=`Speaker`}"
|
||||
android:textColor="@color/white"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:drawableStart="@{model.isHeadset ? @drawable/headset : model.isBluetooth ? @drawable/bluetooth : model.isSpeaker ? @drawable/speaker_high : @drawable/ear, default=@drawable/speaker_high}"
|
||||
android:drawableTint="@color/gray_main2_300"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
|
|
|||
|
|
@ -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}">
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,15 @@
|
|||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="pauseResumeClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="hangUpClickListener"
|
||||
type="View.OnClickListener" />
|
||||
<variable
|
||||
name="isPaused"
|
||||
type="Boolean" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
@ -15,7 +21,37 @@
|
|||
android:background="@color/gray_main2_200">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/delete"
|
||||
android:id="@+id/pause"
|
||||
android:onClick="@{pauseResumeClickListener}"
|
||||
android:visibility="@{isPaused ? View.GONE : View.VISIBLE}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/call_action_pause_call"
|
||||
style="@style/context_menu_action_label_style"
|
||||
android:background="@drawable/menu_item_background"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:drawableStart="@drawable/pause_call"
|
||||
app:layout_constraintBottom_toTopOf="@id/hang_up"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/resume"
|
||||
android:onClick="@{pauseResumeClickListener}"
|
||||
android:visibility="@{isPaused ? View.VISIBLE : View.GONE, default=gone}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/call_action_resume_call"
|
||||
style="@style/context_menu_action_label_style"
|
||||
android:background="@drawable/menu_item_background"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:drawableStart="@drawable/pause_call"
|
||||
app:layout_constraintBottom_toTopOf="@id/hang_up"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/hang_up"
|
||||
android:onClick="@{hangUpClickListener}"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@
|
|||
<string name="call_action_show_dialer">Dialer</string>
|
||||
<string name="call_action_show_messages">Messages</string>
|
||||
<string name="call_action_pause_call">Pause</string>
|
||||
<string name="call_action_resume_call">Pause</string>
|
||||
<string name="call_action_resume_call">Resume</string>
|
||||
<string name="call_action_record_call">Record</string>
|
||||
<string name="call_action_hang_up">Hang up</string>
|
||||
<string name="call_state_outgoing_progress">In progress</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue