mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-04-18 06:08:29 +00:00
Fixed UI when merging calls into local conference
This commit is contained in:
parent
e8601d8dab
commit
91f13fe407
8 changed files with 107 additions and 78 deletions
|
|
@ -1040,6 +1040,11 @@ class CoreContext
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun terminateCall(call: Call) {
|
fun terminateCall(call: Call) {
|
||||||
|
val conference = call.conference
|
||||||
|
if (conference != null) {
|
||||||
|
Log.i("$TAG Terminating conference [${call.remoteAddress.asStringUriOnly()}]")
|
||||||
|
conference.terminate()
|
||||||
|
} else {
|
||||||
if (call.dir == Call.Dir.Incoming && LinphoneUtils.isCallIncoming(call.state)) {
|
if (call.dir == Call.Dir.Incoming && LinphoneUtils.isCallIncoming(call.state)) {
|
||||||
val reason = if (call.core.callsNb > 1) Reason.Busy else Reason.Declined
|
val reason = if (call.core.callsNb > 1) Reason.Busy else Reason.Declined
|
||||||
Log.i(
|
Log.i(
|
||||||
|
|
@ -1051,6 +1056,7 @@ class CoreContext
|
||||||
call.terminate()
|
call.terminate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
fun showCallActivity() {
|
fun showCallActivity() {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import androidx.databinding.DataBindingUtil
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.window.layout.FoldingFeature
|
import androidx.window.layout.FoldingFeature
|
||||||
import androidx.window.layout.WindowInfoTracker
|
import androidx.window.layout.WindowInfoTracker
|
||||||
import androidx.window.layout.WindowLayoutInfo
|
import androidx.window.layout.WindowLayoutInfo
|
||||||
|
|
@ -264,6 +265,18 @@ class CallActivity : GenericActivity() {
|
||||||
coreContext.enableProximitySensor(enabled)
|
coreContext.enableProximitySensor(enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callViewModel.goToCallEvent.observe(this) {
|
||||||
|
it.consume {
|
||||||
|
navigateToActiveCall(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callViewModel.goToConferenceEvent.observe(this) {
|
||||||
|
it.consume {
|
||||||
|
navigateToActiveCall(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
callsViewModel.showIncomingCallEvent.observe(this) {
|
callsViewModel.showIncomingCallEvent.observe(this) {
|
||||||
it.consume {
|
it.consume {
|
||||||
val action = IncomingCallFragmentDirections.actionGlobalIncomingCallFragment()
|
val action = IncomingCallFragmentDirections.actionGlobalIncomingCallFragment()
|
||||||
|
|
|
||||||
|
|
@ -237,17 +237,6 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callViewModel.goToCallEvent.observe(viewLifecycleOwner) {
|
|
||||||
it.consume {
|
|
||||||
if (findNavController().currentDestination?.id == R.id.activeConferenceCallFragment) {
|
|
||||||
Log.i("$TAG Going to active call fragment")
|
|
||||||
val action =
|
|
||||||
ActiveConferenceCallFragmentDirections.actionActiveConferenceCallFragmentToActiveCallFragment()
|
|
||||||
findNavController().navigate(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.setBackClickListener {
|
binding.setBackClickListener {
|
||||||
(requireActivity() as CallActivity).goToMainActivity()
|
(requireActivity() as CallActivity).goToMainActivity()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,8 @@ class ConferenceViewModel
|
||||||
MutableLiveData()
|
MutableLiveData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var conferenceConfigured = false
|
||||||
|
|
||||||
private lateinit var conference: Conference
|
private lateinit var conference: Conference
|
||||||
|
|
||||||
private val conferenceListener = object : ConferenceListenerStub() {
|
private val conferenceListener = object : ConferenceListenerStub() {
|
||||||
|
|
@ -289,6 +291,7 @@ class ConferenceViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
conferenceConfigured = false
|
||||||
isPaused.value = false
|
isPaused.value = false
|
||||||
isConversationAvailable.value = false
|
isConversationAvailable.value = false
|
||||||
isMeParticipantSendingVideo.value = false
|
isMeParticipantSendingVideo.value = false
|
||||||
|
|
@ -297,6 +300,7 @@ class ConferenceViewModel
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun destroy() {
|
fun destroy() {
|
||||||
|
conferenceConfigured = false
|
||||||
isCurrentCallInConference.postValue(false)
|
isCurrentCallInConference.postValue(false)
|
||||||
if (::conference.isInitialized) {
|
if (::conference.isInitialized) {
|
||||||
conference.removeListener(conferenceListener)
|
conference.removeListener(conferenceListener)
|
||||||
|
|
@ -314,6 +318,7 @@ class ConferenceViewModel
|
||||||
isCurrentCallInConference.postValue(true)
|
isCurrentCallInConference.postValue(true)
|
||||||
conference = conf
|
conference = conf
|
||||||
conference.addListener(conferenceListener)
|
conference.addListener(conferenceListener)
|
||||||
|
conferenceConfigured = true
|
||||||
|
|
||||||
val isIn = conference.isIn
|
val isIn = conference.isIn
|
||||||
val state = conf.state
|
val state = conf.state
|
||||||
|
|
@ -351,15 +356,15 @@ class ConferenceViewModel
|
||||||
Log.w(
|
Log.w(
|
||||||
"$TAG Conference has a participant sharing its screen, changing layout from mosaic to active speaker"
|
"$TAG Conference has a participant sharing its screen, changing layout from mosaic to active speaker"
|
||||||
)
|
)
|
||||||
setNewLayout(ACTIVE_SPEAKER_LAYOUT)
|
setNewLayout(ACTIVE_SPEAKER_LAYOUT, call)
|
||||||
} else if (currentLayout == AUDIO_ONLY_LAYOUT) {
|
} else if (currentLayout == AUDIO_ONLY_LAYOUT) {
|
||||||
val defaultLayout = call.core.defaultConferenceLayout.toInt()
|
val defaultLayout = call.core.defaultConferenceLayout.toInt()
|
||||||
if (defaultLayout == Conference.Layout.ActiveSpeaker.toInt()) {
|
if (defaultLayout == Conference.Layout.ActiveSpeaker.toInt()) {
|
||||||
Log.w("$TAG Joined conference in audio only layout, switching to active speaker layout")
|
Log.w("$TAG Joined conference in audio only layout, switching to active speaker layout")
|
||||||
setNewLayout(ACTIVE_SPEAKER_LAYOUT)
|
setNewLayout(ACTIVE_SPEAKER_LAYOUT, call)
|
||||||
} else {
|
} else {
|
||||||
Log.w("$TAG Joined conference in audio only layout, switching to grid layout")
|
Log.w("$TAG Joined conference in audio only layout, switching to grid layout")
|
||||||
setNewLayout(GRID_LAYOUT)
|
setNewLayout(GRID_LAYOUT, call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -461,9 +466,9 @@ class ConferenceViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
fun setNewLayout(newLayout: Int) {
|
fun setNewLayout(newLayout: Int, currentCall: Call? = null) {
|
||||||
if (::conference.isInitialized) {
|
if (::conference.isInitialized) {
|
||||||
val call = conference.call
|
val call = conference.call ?: currentCall
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
val params = call.core.createCallParams(call)
|
val params = call.core.createCallParams(call)
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
|
|
|
||||||
|
|
@ -331,17 +331,6 @@ class ActiveCallFragment : GenericCallFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callViewModel.goToConferenceEvent.observe(viewLifecycleOwner) {
|
|
||||||
it.consume {
|
|
||||||
if (findNavController().currentDestination?.id == R.id.activeCallFragment) {
|
|
||||||
Log.i("$TAG Going to conference fragment")
|
|
||||||
val action =
|
|
||||||
ActiveCallFragmentDirections.actionActiveCallFragmentToActiveConferenceCallFragment()
|
|
||||||
findNavController().navigate(action)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callViewModel.isReceivingVideo.observe(viewLifecycleOwner) { receiving ->
|
callViewModel.isReceivingVideo.observe(viewLifecycleOwner) { receiving ->
|
||||||
if (!receiving && callViewModel.fullScreenMode.value == true) {
|
if (!receiving && callViewModel.fullScreenMode.value == true) {
|
||||||
Log.i("$TAG We are no longer receiving video, leaving full screen mode")
|
Log.i("$TAG We are no longer receiving video, leaving full screen mode")
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,8 @@ class CallsViewModel
|
||||||
|
|
||||||
val callsCount = MutableLiveData<Int>()
|
val callsCount = MutableLiveData<Int>()
|
||||||
|
|
||||||
|
val allCallsIntoConference = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
val showTopBar = MutableLiveData<Boolean>()
|
val showTopBar = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
val goToActiveCallEvent = MutableLiveData<Event<Boolean>>()
|
val goToActiveCallEvent = MutableLiveData<Event<Boolean>>()
|
||||||
|
|
@ -234,6 +236,7 @@ class CallsViewModel
|
||||||
showRedToast(R.string.conference_failed_to_merge_calls_into_conference_toast, R.drawable.warning_circle)
|
showRedToast(R.string.conference_failed_to_merge_calls_into_conference_toast, R.drawable.warning_circle)
|
||||||
} else {
|
} else {
|
||||||
conference.addParticipants(core.calls)
|
conference.addParticipants(core.calls)
|
||||||
|
allCallsIntoConference.postValue(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -251,9 +254,16 @@ class CallsViewModel
|
||||||
}
|
}
|
||||||
callsExceptCurrentOne.postValue(list)
|
callsExceptCurrentOne.postValue(list)
|
||||||
|
|
||||||
if (core.callsNb > 1) {
|
val callsCount = core.callsNb
|
||||||
showTopBar.postValue(true)
|
if (callsCount > 1) {
|
||||||
if (core.callsNb == 2) {
|
val callsNotInConference = core.calls.filter {
|
||||||
|
it.conference == null
|
||||||
|
}
|
||||||
|
val callsNotInConferenceCount = callsNotInConference.count()
|
||||||
|
Log.i("$TAG Found [$callsNotInConferenceCount] calls not in conference over [$callsCount] calls")
|
||||||
|
allCallsIntoConference.postValue(callsNotInConferenceCount == 0)
|
||||||
|
|
||||||
|
if (callsNotInConferenceCount == 1) {
|
||||||
val found = core.calls.find {
|
val found = core.calls.find {
|
||||||
it.state == Call.State.Paused
|
it.state == Call.State.Paused
|
||||||
}
|
}
|
||||||
|
|
@ -273,19 +283,25 @@ class CallsViewModel
|
||||||
}
|
}
|
||||||
callsTopBarStatus.postValue(LinphoneUtils.callStateToString(found.state))
|
callsTopBarStatus.postValue(LinphoneUtils.callStateToString(found.state))
|
||||||
} else {
|
} else {
|
||||||
Log.e("$TAG Failed to find a paused call")
|
Log.w("$TAG Failed to find a paused call")
|
||||||
}
|
}
|
||||||
} else {
|
} else if (callsNotInConferenceCount > 1) {
|
||||||
callsTopBarLabel.postValue(
|
callsTopBarLabel.postValue(
|
||||||
AppUtils.getFormattedString(R.string.calls_paused_count_label, core.callsNb - 1)
|
AppUtils.getFormattedString(R.string.calls_paused_count_label, core.callsNb - 1)
|
||||||
)
|
)
|
||||||
callsTopBarStatus.postValue("") // TODO: improve ?
|
callsTopBarStatus.postValue("") // TODO: improve ?
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (core.callsNb == 1) {
|
configureTopBarForSingleCallOrConference()
|
||||||
|
}
|
||||||
|
} else if (core.callsNb == 1) {
|
||||||
|
configureTopBarForSingleCallOrConference()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configureTopBarForSingleCallOrConference() {
|
||||||
callsTopBarIcon.postValue(R.drawable.phone)
|
callsTopBarIcon.postValue(R.drawable.phone)
|
||||||
|
|
||||||
val call = core.calls.first()
|
val call = coreContext.core.calls.first()
|
||||||
val conference = call.conference
|
val conference = call.conference
|
||||||
if (conference != null) {
|
if (conference != null) {
|
||||||
callsTopBarLabel.postValue(conference.subject)
|
callsTopBarLabel.postValue(conference.subject)
|
||||||
|
|
@ -301,5 +317,3 @@ class CallsViewModel
|
||||||
callsTopBarStatus.postValue(LinphoneUtils.callStateToString(call.state))
|
callsTopBarStatus.postValue(LinphoneUtils.callStateToString(call.state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,13 @@ class CurrentCallViewModel
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (call.conference != null && !conferenceModel.conferenceConfigured) {
|
||||||
|
Log.i("$TAG Found conference on call but not conference model, initializing it now")
|
||||||
|
conferenceModel.configureFromCall(call)
|
||||||
|
updateMicrophoneMutedIcon()
|
||||||
|
goToConferenceEvent.postValue(Event(true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -695,21 +702,7 @@ class CurrentCallViewModel
|
||||||
@UiThread
|
@UiThread
|
||||||
fun refreshMicrophoneState() {
|
fun refreshMicrophoneState() {
|
||||||
coreContext.postOnCoreThread {
|
coreContext.postOnCoreThread {
|
||||||
if (::currentCall.isInitialized) {
|
updateMicrophoneMutedIcon()
|
||||||
val micMuted = if (currentCall.conference != null) {
|
|
||||||
currentCall.conference?.microphoneMuted == true
|
|
||||||
} else {
|
|
||||||
currentCall.microphoneMuted
|
|
||||||
}
|
|
||||||
if (micMuted != isMicrophoneMuted.value) {
|
|
||||||
if (micMuted) {
|
|
||||||
Log.w("$TAG Microphone is muted, updating button state accordingly")
|
|
||||||
} else {
|
|
||||||
Log.i("$TAG Microphone is not muted, updating button state accordingly")
|
|
||||||
}
|
|
||||||
isMicrophoneMuted.postValue(micMuted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1097,6 +1090,7 @@ class CurrentCallViewModel
|
||||||
conferenceModel.configureFromCall(call)
|
conferenceModel.configureFromCall(call)
|
||||||
goToConferenceEvent.postValue(Event(true))
|
goToConferenceEvent.postValue(Event(true))
|
||||||
} else {
|
} else {
|
||||||
|
Log.i("$TAG No conference attached to this call, going to call fragment")
|
||||||
conferenceModel.destroy()
|
conferenceModel.destroy()
|
||||||
goToCallEvent.postValue(Event(true))
|
goToCallEvent.postValue(Event(true))
|
||||||
}
|
}
|
||||||
|
|
@ -1262,6 +1256,25 @@ class CurrentCallViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private fun updateMicrophoneMutedIcon() {
|
||||||
|
if (::currentCall.isInitialized) {
|
||||||
|
val micMuted = if (currentCall.conference != null) {
|
||||||
|
currentCall.conference?.microphoneMuted == true
|
||||||
|
} else {
|
||||||
|
currentCall.microphoneMuted
|
||||||
|
}
|
||||||
|
if (micMuted != isMicrophoneMuted.value) {
|
||||||
|
if (micMuted) {
|
||||||
|
Log.w("$TAG Microphone is muted, updating button state accordingly")
|
||||||
|
} else {
|
||||||
|
Log.i("$TAG Microphone is not muted, updating button state accordingly")
|
||||||
|
}
|
||||||
|
isMicrophoneMuted.postValue(micMuted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private fun updateOutputAudioDevice(audioDevice: AudioDevice?) {
|
private fun updateOutputAudioDevice(audioDevice: AudioDevice?) {
|
||||||
Log.i("$TAG Output audio device updated to [${audioDevice?.deviceName} (${audioDevice?.type})]")
|
Log.i("$TAG Output audio device updated to [${audioDevice?.deviceName} (${audioDevice?.type})]")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@{viewModel.callsCount > 1 || viewModel.showTopBar ? @drawable/color_success_500 : @drawable/color_black, default=@drawable/color_black}"
|
android:background="@{(viewModel.callsCount > 1 && !viewModel.allCallsIntoConference) || viewModel.showTopBar ? @drawable/color_success_500 : @drawable/color_black, default=@drawable/color_black}"
|
||||||
android:onClick="@{() -> viewModel.topBarClicked()}">
|
android:onClick="@{() -> viewModel.topBarClicked()}">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:src="@{viewModel.callsTopBarIcon, default=@drawable/phone_pause}"
|
android:src="@{viewModel.callsTopBarIcon, default=@drawable/phone_pause}"
|
||||||
android:contentDescription="@null"
|
android:contentDescription="@null"
|
||||||
android:visibility="@{viewModel.callsCount > 1 || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{(viewModel.callsCount > 1 && !viewModel.allCallsIntoConference) || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@id/call_display_name"
|
app:layout_constraintTop_toTopOf="@id/call_display_name"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/call_display_name"
|
app:layout_constraintBottom_toBottomOf="@id/call_display_name"
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
android:text="@{viewModel.callsTopBarLabel, default=`John Doe`}"
|
android:text="@{viewModel.callsTopBarLabel, default=`John Doe`}"
|
||||||
android:textColor="@color/bc_white"
|
android:textColor="@color/bc_white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:visibility="@{viewModel.callsCount > 1 || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{(viewModel.callsCount > 1 && !viewModel.allCallsIntoConference) || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
app:layout_constraintEnd_toStartOf="@id/call_time"
|
app:layout_constraintEnd_toStartOf="@id/call_time"
|
||||||
app:layout_constraintStart_toEndOf="@id/call_icon"
|
app:layout_constraintStart_toEndOf="@id/call_icon"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
android:text="@{viewModel.callsTopBarStatus, default=`Paused`}"
|
android:text="@{viewModel.callsTopBarStatus, default=`Paused`}"
|
||||||
android:textColor="@color/bc_white"
|
android:textColor="@color/bc_white"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:visibility="@{viewModel.callsCount > 1 || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
android:visibility="@{(viewModel.callsCount > 1 && !viewModel.allCallsIntoConference) || viewModel.showTopBar ? View.VISIBLE : View.GONE, default=gone}"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@id/call_display_name"
|
app:layout_constraintStart_toEndOf="@id/call_display_name"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue