Started work on multiple calls

This commit is contained in:
Juha Heinanen
2026-01-12 01:02:21 +02:00
parent feecb7dcf0
commit 04656b4449
3 changed files with 136 additions and 172 deletions

View File

@ -1,5 +1,8 @@
package com.tutpro.baresip
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import java.util.*
class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: String, var status: String) {
@ -15,6 +18,27 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
var referTo = ""
var dumpfiles = arrayOf("", "")
// UI state properties
val callUri: MutableState<String> = mutableStateOf("")
val callUriEnabled: MutableState<Boolean> = mutableStateOf(true)
val callUriLabel: MutableState<String> = mutableStateOf("")
val securityIconTint: MutableState<Int> = mutableIntStateOf(-1)
val showCallTimer: MutableState<Boolean> = mutableStateOf(false)
var callDuration: Int = 0
val showSuggestions: MutableState<Boolean> = mutableStateOf(false)
val showCallButton: MutableState<Boolean> = mutableStateOf(true)
val callButtonEnabled: MutableState<Boolean> = mutableStateOf(true)
val showCancelButton: MutableState<Boolean> = mutableStateOf(false)
val showAnswerRejectButtons: MutableState<Boolean> = mutableStateOf(false)
val showHangupButton: MutableState<Boolean> = mutableStateOf(false)
val showOnHoldNotice: MutableState<Boolean> = mutableStateOf(false)
val callOnHold: MutableState<Boolean> = mutableStateOf(false)
val transferButtonEnabled: MutableState<Boolean> = mutableStateOf(false)
val callTransfer: MutableState<Boolean> = mutableStateOf(false)
val dtmfText: MutableState<String> = mutableStateOf("")
val dtmfEnabled: MutableState<Boolean> = mutableStateOf(false)
val focusDtmf: MutableState<Boolean> = mutableStateOf(false)
fun add() {
BaresipService.calls.add(0, this)
}

View File

@ -841,26 +841,6 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
}
}
private val callUri = mutableStateOf("")
private var callUriEnabled = mutableStateOf(true)
private val callUriLabel = mutableStateOf("")
private var securityIconTint = mutableIntStateOf(-1)
private val showCallTimer = mutableStateOf(false)
private var callDuration = 0
private val showSuggestions = mutableStateOf(false)
private val showCallButton = mutableStateOf(true)
private val callButtonEnabled = mutableStateOf(true)
private val showCancelButton = mutableStateOf(false)
private val showAnswerRejectButtons = mutableStateOf(false)
private val showHangupButton = mutableStateOf(false)
private val showOnHoldNotice = mutableStateOf(false)
private var callOnHold = mutableStateOf(false)
private val transferButtonEnabled = mutableStateOf(false)
private val callTransfer = mutableStateOf(false)
private var dtmfText = mutableStateOf("")
private val dtmfEnabled = mutableStateOf(false)
private val focusDtmf = mutableStateOf(false)
private val alertTitle = mutableStateOf("")
private val alertMessage = mutableStateOf("")
private val showAlert = mutableStateOf(false)
@ -2116,24 +2096,12 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal
val call = showCall ?: ua.currentCall()
if (call == null) {
pullToRefreshEnabled.value = true
if (ua.account.resumeUri != "")
callUri.value = ua.account.resumeUri
else
callUri.value = ""
callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
callUriEnabled.value = true
showCallTimer.value = false
securityIconTint.intValue = -1
showHangupButton.value = false
callTransfer.value = false
dtmfText.value = ""
dtmfEnabled.value = false
focusDtmf.value = false
showCallButton.value = true
callButtonEnabled.value = true
showCancelButton.value = false
showAnswerRejectButtons.value = false
showOnHoldNotice.value = false
viewModel.dialerState.callUri.value = ua.account.resumeUri
viewModel.dialerState.callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
viewModel.dialerState.callUriEnabled.value = true
viewModel.dialerState.showCallButton.value = true
viewModel.dialerState.callButtonEnabled.value = true
viewModel.dialerState.showSuggestions.value = false
dialpadButtonEnabled.value = true
if (BaresipService.isMicMuted) {
BaresipService.isMicMuted = false
@ -2141,84 +2109,84 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal
}
} else {
pullToRefreshEnabled.value = false
callUriEnabled.value = false
call.callUriEnabled.value = false
val isLandscape = ctx.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
if (isLandscape || call.held || call.status != "connected") {
focusDtmf.value = false
dtmfEnabled.value = !call.held
call.focusDtmf.value = false
call.dtmfEnabled.value = !call.held
Handler(Looper.getMainLooper()).postDelayed({
viewModel.requestHideKeyboard()
}, 25)
}
else {
dtmfEnabled.value = true
focusDtmf.value = true
call.dtmfEnabled.value = true
call.focusDtmf.value = true
viewModel.requestShowKeyboard()
}
when (call.status) {
"outgoing", "transferring", "answered" -> {
callUriLabel.value = if (call.status == "answered")
call.callUriLabel.value = if (call.status == "answered")
ctx.getString(R.string.incoming_call_from_dots)
else
ctx.getString(R.string.outgoing_call_to_dots)
callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
showCallTimer.value = false
securityIconTint.intValue = -1
showCallButton.value = false
showCancelButton.value = call.status == "outgoing"
showHangupButton.value = !showCancelButton.value
showAnswerRejectButtons.value = false
showOnHoldNotice.value = false
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
call.showCallTimer.value = false
call.securityIconTint.value = -1
call.showCallButton.value = false
call.showCancelButton.value = call.status == "outgoing"
call.showHangupButton.value = !call.showCancelButton.value
call.showAnswerRejectButtons.value = false
call.showOnHoldNotice.value = false
dialpadButtonEnabled.value = false
}
"incoming" -> {
showCallTimer.value = false
securityIconTint.intValue = -1
call.showCallTimer.value = false
call.securityIconTint.value = -1
val uri = call.diverterUri()
if (uri != "") {
callUriLabel.value = ctx.getString(R.string.diverted_by_dots)
callUri.value = Utils.friendlyUri(ctx, uri, ua.account)
call.callUriLabel.value = ctx.getString(R.string.diverted_by_dots)
call.callUri.value = Utils.friendlyUri(ctx, uri, ua.account)
}
else {
callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
call.callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
}
showCallButton.value = false
showCancelButton.value = false
showHangupButton.value = false
showAnswerRejectButtons.value = true
showOnHoldNotice.value = false
call.showCallButton.value = false
call.showCancelButton.value = false
call.showHangupButton.value = false
call.showAnswerRejectButtons.value = true
call.showOnHoldNotice.value = false
dialpadButtonEnabled.value = false
}
"connected" -> {
if (call.referTo != "") {
callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
callUri.value = Utils.friendlyUri(ctx, call.referTo, ua.account)
transferButtonEnabled.value = false
call.callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
call.callUri.value = Utils.friendlyUri(ctx, call.referTo, ua.account)
call.transferButtonEnabled.value = false
} else {
if (call.dir == "out") {
callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
call.callUriLabel.value = ctx.getString(R.string.outgoing_call_to_dots)
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
} else {
callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
call.callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
}
transferButtonEnabled.value = true
call.transferButtonEnabled.value = true
}
callTransfer.value = call.onHoldCall != null
callDuration = call.duration()
showCallTimer.value = true
call.callTransfer.value = call.onHoldCall != null
call.callDuration = call.duration()
call.showCallTimer.value = true
if (ua.account.mediaEnc == "")
securityIconTint.intValue = -1
call.securityIconTint.value = -1
else
securityIconTint.intValue = call.security
showCallButton.value = false
showCancelButton.value = false
showHangupButton.value = true
showAnswerRejectButtons.value = false
callOnHold.value = call.onhold
call.securityIconTint.value = call.security
call.showCallButton.value = false
call.showCancelButton.value = false
call.showHangupButton.value = true
call.showAnswerRejectButtons.value = false
call.callOnHold.value = call.onhold
Handler(Looper.getMainLooper()).postDelayed({
showOnHoldNotice.value = call.held
call.showOnHoldNotice.value = call.held
}, 100)
}
}

View File

@ -1,118 +1,90 @@
package com.tutpro.baresip
import android.app.Application
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Mic
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
sealed class NavigationCommand {
data class NavigateToChat(val aor: String, val peer: String?) : NavigationCommand()
data class NavigateToCalls(val aor: String) : NavigationCommand()
object NavigateToHome: NavigationCommand()
// Add other navigation commands as needed
}
class ViewModel: ViewModel() {
data class AorPeer(val aor: String, val peer: String)
data class DialerState(
val callUri: MutableState<String> = mutableStateOf(""),
val callUriEnabled: MutableState<Boolean> = mutableStateOf(true),
val callUriLabel: MutableState<String> = mutableStateOf(""),
val showSuggestions: MutableState<Boolean> = mutableStateOf(false),
val showCallButton: MutableState<Boolean> = mutableStateOf(true),
val callButtonEnabled: MutableState<Boolean> = mutableStateOf(true),
)
class ViewModel(application: Application) : AndroidViewModel(application) {
val dialerState = DialerState()
private val _calls = MutableStateFlow<List<Call>>(emptyList())
val calls = _calls.asStateFlow()
private val _selectedAor = MutableStateFlow("")
val selectedAor: StateFlow<String> = _selectedAor.asStateFlow()
fun updateSelectedAor(newValue: String) {
_selectedAor.value = newValue
}
val selectedAor = _selectedAor.asStateFlow()
private val _accountUpdate = MutableStateFlow(0)
val accountUpdate: StateFlow<Int> = _accountUpdate
fun triggerAccountUpdate() {
_accountUpdate.value++
}
private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, String>())
fun updateAorPeerMessage(aor: String, peerUri: String, message: String) {
val key = AorPeer(aor, peerUri)
if (message == "")
_aorPeerMessage.value.remove(key)
else
_aorPeerMessage.value[key] = message
}
fun getAorPeerMessage(aor: String, peerUri: String): String {
val key = AorPeer(aor, peerUri)
return _aorPeerMessage.value.getOrDefault(key, "")
}
private val _showKeyboard = mutableIntStateOf(0)
val showKeyboard: State<Int> = _showKeyboard
fun requestShowKeyboard() {
_showKeyboard.intValue++
}
private val _hideKeyboard = mutableIntStateOf(0)
val hideKeyboard: State<Int> = _hideKeyboard
fun requestHideKeyboard() {
_hideKeyboard.intValue++
}
val accountUpdate = _accountUpdate.asStateFlow()
private val _micIcon = MutableStateFlow(Icons.Filled.Mic)
val micIcon: StateFlow<ImageVector> = _micIcon.asStateFlow()
val micIcon = _micIcon.asStateFlow()
private val _isDialpadVisible = MutableStateFlow(false)
val isDialpadVisible = _isDialpadVisible.asStateFlow()
private val _showKeyboard = MutableStateFlow(0)
val showKeyboard = _showKeyboard.asStateFlow()
private val _hideKeyboard = MutableStateFlow(0)
val hideKeyboard = _hideKeyboard.asStateFlow()
fun onNewMessageReceived(aor: String, peerUri: String) {
val acc = Account.ofAor(aor)
if (acc != null) {
acc.unreadMessages = true
triggerAccountUpdate()
}
}
fun updateCalls(calls: List<Call>) {
_calls.value = calls
}
fun updateSelectedAor(aor: String) {
_selectedAor.value = aor
}
fun triggerAccountUpdate() {
_accountUpdate.value = _accountUpdate.value + 1
}
fun updateMicIcon(icon: ImageVector) {
_micIcon.value = icon
}
private val _isDialpadVisible = MutableStateFlow(false)
val isDialpadVisible: StateFlow<Boolean> = _isDialpadVisible
fun toggleDialpadVisibility() {
_isDialpadVisible.value = !_isDialpadVisible.value
}
private val _selectedCallRow = MutableStateFlow<CallRow?>(null)
fun selectCallRow(callRow: CallRow) {
_selectedCallRow.value = callRow
fun requestShowKeyboard() {
_showKeyboard.value = _showKeyboard.value + 1
}
fun consumeSelectedCallRow(): CallRow? {
val callRow = _selectedCallRow.value
_selectedCallRow.value = null
return callRow
}
private val _navigationCommand = MutableSharedFlow<NavigationCommand>()
val navigationCommand = _navigationCommand.asSharedFlow()
fun onNewMessageReceived(aor: String, peer: String) {
viewModelScope.launch {
_navigationCommand.emit(NavigationCommand.NavigateToChat(aor, peer))
}
}
fun navigateToCalls(aor: String) {
viewModelScope.launch {
_navigationCommand.emit(NavigationCommand.NavigateToCalls(aor))
}
fun requestHideKeyboard() {
_hideKeyboard.value = _hideKeyboard.value + 1
}
fun navigateToHome() {
viewModelScope.launch {
_navigationCommand.emit(NavigationCommand.NavigateToHome)
}
// This function can be used to navigate to the main screen
}
}
fun navigateToCalls(aor: String) {
// This function can be used to navigate to the calls screen
}
}