diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt index aa6e1cf2..95ff0f65 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Call.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -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 = mutableStateOf("") + val callUriEnabled: MutableState = mutableStateOf(true) + val callUriLabel: MutableState = mutableStateOf("") + val securityIconTint: MutableState = mutableIntStateOf(-1) + val showCallTimer: MutableState = mutableStateOf(false) + var callDuration: Int = 0 + val showSuggestions: MutableState = mutableStateOf(false) + val showCallButton: MutableState = mutableStateOf(true) + val callButtonEnabled: MutableState = mutableStateOf(true) + val showCancelButton: MutableState = mutableStateOf(false) + val showAnswerRejectButtons: MutableState = mutableStateOf(false) + val showHangupButton: MutableState = mutableStateOf(false) + val showOnHoldNotice: MutableState = mutableStateOf(false) + val callOnHold: MutableState = mutableStateOf(false) + val transferButtonEnabled: MutableState = mutableStateOf(false) + val callTransfer: MutableState = mutableStateOf(false) + val dtmfText: MutableState = mutableStateOf("") + val dtmfEnabled: MutableState = mutableStateOf(false) + val focusDtmf: MutableState = mutableStateOf(false) + fun add() { BaresipService.calls.add(0, this) } diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt index 4abddc61..5a2b8682 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainScreen.kt @@ -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) } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt b/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt index df66292c..ae6547cc 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ViewModel.kt @@ -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 = mutableStateOf(""), + val callUriEnabled: MutableState = mutableStateOf(true), + val callUriLabel: MutableState = mutableStateOf(""), + val showSuggestions: MutableState = mutableStateOf(false), + val showCallButton: MutableState = mutableStateOf(true), + val callButtonEnabled: MutableState = mutableStateOf(true), + ) -class ViewModel(application: Application) : AndroidViewModel(application) { + val dialerState = DialerState() + + private val _calls = MutableStateFlow>(emptyList()) + val calls = _calls.asStateFlow() private val _selectedAor = MutableStateFlow("") - val selectedAor: StateFlow = _selectedAor.asStateFlow() - - fun updateSelectedAor(newValue: String) { - _selectedAor.value = newValue - } + val selectedAor = _selectedAor.asStateFlow() private val _accountUpdate = MutableStateFlow(0) - val accountUpdate: StateFlow = _accountUpdate - - fun triggerAccountUpdate() { - _accountUpdate.value++ - } - - private val _aorPeerMessage = MutableStateFlow(mutableMapOf()) - - 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 = _showKeyboard - - fun requestShowKeyboard() { - _showKeyboard.intValue++ - } - - private val _hideKeyboard = mutableIntStateOf(0) - val hideKeyboard: State = _hideKeyboard - - fun requestHideKeyboard() { - _hideKeyboard.intValue++ - } + val accountUpdate = _accountUpdate.asStateFlow() private val _micIcon = MutableStateFlow(Icons.Filled.Mic) - val micIcon: StateFlow = _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) { + _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 = _isDialpadVisible - fun toggleDialpadVisibility() { _isDialpadVisible.value = !_isDialpadVisible.value } - private val _selectedCallRow = MutableStateFlow(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() - 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 + } + +} \ No newline at end of file