Fixed methods called from wrong thread, other code cleanup / improvements

This commit is contained in:
Sylvain Berfini 2025-08-19 10:33:29 +02:00
parent a27c251d7a
commit 4982c3e81d
17 changed files with 47 additions and 29 deletions

View file

@ -175,7 +175,7 @@ class VFS {
val cipher = Cipher.getInstance(TRANSFORMATION) val cipher = Cipher.getInstance(TRANSFORMATION)
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey()) cipher.init(Cipher.ENCRYPT_MODE, getSecretKey())
val iv = cipher.iv val iv = cipher.iv
return Pair<ByteArray, ByteArray>( return Pair(
iv, iv,
cipher.doFinal(textToEncrypt.toByteArray(StandardCharsets.UTF_8)) cipher.doFinal(textToEncrypt.toByteArray(StandardCharsets.UTF_8))
) )
@ -193,7 +193,7 @@ class VFS {
@Throws(java.lang.Exception::class) @Throws(java.lang.Exception::class)
private fun encryptToken(token: String): Pair<String?, String?> { private fun encryptToken(token: String): Pair<String?, String?> {
val encryptedData = encryptData(token) val encryptedData = encryptData(token)
return Pair<String?, String?>( return Pair(
Base64.encodeToString(encryptedData.first, Base64.DEFAULT), Base64.encodeToString(encryptedData.first, Base64.DEFAULT),
Base64.encodeToString(encryptedData.second, Base64.DEFAULT) Base64.encodeToString(encryptedData.second, Base64.DEFAULT)
) )

View file

@ -1781,7 +1781,7 @@ class NotificationsManager
@AnyThread @AnyThread
fun foregroundServiceTypeMaskToString(mask: Int): String { fun foregroundServiceTypeMaskToString(mask: Int): String {
var stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
val values = hashMapOf( val values = hashMapOf(
"PHONE_CALL" to Compatibility.FOREGROUND_SERVICE_TYPE_PHONE_CALL, "PHONE_CALL" to Compatibility.FOREGROUND_SERVICE_TYPE_PHONE_CALL,
"MICROPHONE" to Compatibility.FOREGROUND_SERVICE_TYPE_MICROPHONE, "MICROPHONE" to Compatibility.FOREGROUND_SERVICE_TYPE_MICROPHONE,

View file

@ -24,6 +24,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -65,6 +66,7 @@ class ConferenceAddParticipantsFragment : GenericAddressPickerFragment() {
return false return false
} }
@WorkerThread
override fun onSingleAddressSelected(address: Address, friend: Friend) { override fun onSingleAddressSelected(address: Address, friend: Friend) {
Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!") Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!")
} }

View file

@ -175,7 +175,9 @@ class ConferenceParticipantsListFragment : GenericCallFragment() {
model.confirmEvent.observe(viewLifecycleOwner) { model.confirmEvent.observe(viewLifecycleOwner) {
it.consume { it.consume {
viewModel.conferenceModel.kickParticipant(participant) coreContext.postOnCoreThread {
viewModel.conferenceModel.kickParticipant(participant)
}
val message = getString(R.string.conference_participant_was_kicked_out_toast) val message = getString(R.string.conference_participant_was_kicked_out_toast)
val icon = R.drawable.check val icon = R.drawable.check
(requireActivity() as GenericActivity).showGreenToast(message, icon) (requireActivity() as GenericActivity).showGreenToast(message, icon)

View file

@ -156,7 +156,7 @@ class ConferenceViewModel
} else { } else {
Log.w("$TAG Notified active speaker participant device is null, using first one that's not us") Log.w("$TAG Notified active speaker participant device is null, using first one that's not us")
val firstNotUs = participantDevices.value.orEmpty().find { val firstNotUs = participantDevices.value.orEmpty().find {
it.isMe == false !it.isMe
} }
if (firstNotUs != null) { if (firstNotUs != null) {
Log.i("$TAG Newly active speaker participant is [${firstNotUs.name}]") Log.i("$TAG Newly active speaker participant is [${firstNotUs.name}]")

View file

@ -47,7 +47,6 @@ import org.linphone.ui.main.history.viewmodel.StartCallViewModel
import org.linphone.utils.ConfirmationDialogModel import org.linphone.utils.ConfirmationDialogModel
import org.linphone.utils.AppUtils import org.linphone.utils.AppUtils
import org.linphone.utils.DialogUtils import org.linphone.utils.DialogUtils
import org.linphone.utils.LinphoneUtils
import org.linphone.utils.RecyclerViewHeaderDecoration import org.linphone.utils.RecyclerViewHeaderDecoration
import org.linphone.utils.hideKeyboard import org.linphone.utils.hideKeyboard
import org.linphone.utils.setKeyboardInsetListener import org.linphone.utils.setKeyboardInsetListener
@ -233,8 +232,10 @@ class TransferCallFragment : GenericCallFragment() {
} }
viewModel.initiateBlindTransferEvent.observe(viewLifecycleOwner) { viewModel.initiateBlindTransferEvent.observe(viewLifecycleOwner) {
it.consume { address -> it.consume { pair ->
showConfirmBlindTransferDialog(address, LinphoneUtils.getDisplayName(address)) val address = pair.first
val displayName = pair.second
showConfirmBlindTransferDialog(address, displayName)
} }
} }

View file

@ -251,7 +251,7 @@ class MainActivity : GenericActivity() {
viewModel.clearFilesOrTextPendingSharingEvent.observe(this) { viewModel.clearFilesOrTextPendingSharingEvent.observe(this) {
it.consume { it.consume {
sharedViewModel.filesToShareFromIntent.value = arrayListOf<String>() sharedViewModel.filesToShareFromIntent.value = arrayListOf()
sharedViewModel.textToShareFromIntent.value = "" sharedViewModel.textToShareFromIntent.value = ""
} }
} }

View file

@ -544,8 +544,8 @@ open class ConversationFragment : SlidingPaneChildFragment() {
"$TAG Voice record playback finished, looking for voice record in next message" "$TAG Voice record playback finished, looking for voice record in next message"
) )
val list = viewModel.eventsList val list = viewModel.eventsList
val model = list.find { val model = list.find { eventLogModel ->
(it.model as? MessageModel)?.id == id (eventLogModel.model as? MessageModel)?.id == id
} }
if (model != null) { if (model != null) {
val index = list.indexOf(model) val index = list.indexOf(model)
@ -770,13 +770,15 @@ open class ConversationFragment : SlidingPaneChildFragment() {
viewModel.sipUriToCallEvent.observe(viewLifecycleOwner) { viewModel.sipUriToCallEvent.observe(viewLifecycleOwner) {
it.consume { sipUri -> it.consume { sipUri ->
if (messageLongPressViewModel.visible.value == true) return@consume coreContext.postOnCoreThread {
val address = coreContext.core.interpretUrl(sipUri, false) if (messageLongPressViewModel.visible.value == true) return@postOnCoreThread
if (address != null) { val address = coreContext.core.interpretUrl(sipUri, false)
Log.i("$TAG Starting audio call to parsed SIP URI [${address.asStringUriOnly()}]") if (address != null) {
coreContext.startAudioCall(address) Log.i("$TAG Starting audio call to parsed SIP URI [${address.asStringUriOnly()}]")
} else { coreContext.startAudioCall(address)
Log.w("$TAG Failed to parse [$sipUri] as SIP URI") } else {
Log.w("$TAG Failed to parse [$sipUri] as SIP URI")
}
} }
} }
} }

View file

@ -251,8 +251,8 @@ class ConversationsListFragment : AbstractMainFragment() {
sharedViewModel.updateConversationLastMessageEvent.observe(viewLifecycleOwner) { sharedViewModel.updateConversationLastMessageEvent.observe(viewLifecycleOwner) {
it.consume { conversationId -> it.consume { conversationId ->
val model = listViewModel.conversations.value.orEmpty().find { val model = listViewModel.conversations.value.orEmpty().find { conversationModel ->
it.id == conversationId conversationModel.id == conversationId
} }
model?.updateLastMessageInfo() model?.updateLastMessageInfo()
} }

View file

@ -280,7 +280,6 @@ class SendMessageInConversationViewModel
voiceMessage.send() voiceMessage.send()
} else { } else {
message.addContent(content) message.addContent(content)
contentAdded = true
} }
} else { } else {
Log.e("$TAG Voice recording content couldn't be created!") Log.e("$TAG Voice recording content couldn't be created!")

View file

@ -197,7 +197,10 @@ class ContactsListViewModel
fun toggleFavouritesVisibility() { fun toggleFavouritesVisibility() {
val show = showFavourites.value == false val show = showFavourites.value == false
showFavourites.value = show showFavourites.value = show
corePreferences.showFavoriteContacts = show
coreContext.postOnCoreThread {
corePreferences.showFavoriteContacts = show
}
} }
@UiThread @UiThread

View file

@ -35,6 +35,7 @@ import androidx.navigation.fragment.findNavController
import androidx.slidingpanelayout.widget.SlidingPaneLayout import androidx.slidingpanelayout.widget.SlidingPaneLayout
import androidx.slidingpanelayout.widget.SlidingPaneLayout.PanelSlideListener import androidx.slidingpanelayout.widget.SlidingPaneLayout.PanelSlideListener
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
import org.linphone.core.tools.Log import org.linphone.core.tools.Log
@ -192,7 +193,9 @@ abstract class AbstractMainFragment : GenericMainFragment() {
sharedViewModel.forceUpdateAvailableNavigationItems.observe(viewLifecycleOwner) { sharedViewModel.forceUpdateAvailableNavigationItems.observe(viewLifecycleOwner) {
it.consume { it.consume {
viewModel.updateAvailableMenus() coreContext.postOnCoreThread {
viewModel.updateAvailableMenus()
}
} }
} }
} }

View file

@ -25,6 +25,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.annotation.UiThread import androidx.annotation.UiThread
import androidx.annotation.WorkerThread
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
@ -91,6 +92,7 @@ class AddParticipantsFragment : GenericAddressPickerFragment() {
return false return false
} }
@WorkerThread
override fun onSingleAddressSelected(address: Address, friend: Friend) { override fun onSingleAddressSelected(address: Address, friend: Friend) {
Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!") Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!")
} }

View file

@ -80,8 +80,8 @@ class StartCallViewModel
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
} }
val initiateBlindTransferEvent: MutableLiveData<Event<Address>> by lazy { val initiateBlindTransferEvent: MutableLiveData<Event<Pair<Address, String>>> by lazy {
MutableLiveData<Event<Address>>() MutableLiveData<Event<Pair<Address, String>>>()
} }
private val conferenceListener = object : ConferenceListenerStub() { private val conferenceListener = object : ConferenceListenerStub() {
@ -153,7 +153,7 @@ class StartCallViewModel
LinphoneUtils.applyInternationalPrefix() LinphoneUtils.applyInternationalPrefix()
) )
if (address != null) { if (address != null) {
initiateBlindTransferEvent.postValue(Event(address)) initiateBlindTransferEvent.postValue(Event(Pair(address, LinphoneUtils.getDisplayName(address))))
leaveFragmentEvent.postValue(Event(true)) leaveFragmentEvent.postValue(Event(true))
} else { } else {
Log.e("$TAG Failed to parse [$suggestion] as SIP address") Log.e("$TAG Failed to parse [$suggestion] as SIP address")

View file

@ -187,7 +187,7 @@ class MeetingsListFragment : AbstractMainFragment() {
adapter.meetingLongClickedEvent.observe(viewLifecycleOwner) { adapter.meetingLongClickedEvent.observe(viewLifecycleOwner) {
it.consume { model -> it.consume { model ->
val isUserOrganizer = model.isOrganizer() && !model.isCancelled val isUserOrganizer = model.isMyselfOrganizer && !model.isCancelled
val modalBottomSheet = MeetingsMenuDialogFragment( val modalBottomSheet = MeetingsMenuDialogFragment(
isUserOrganizer, isUserOrganizer,
{ // onDismiss { // onDismiss

View file

@ -55,6 +55,8 @@ class MeetingModel
val time = "$startTime - $endTime" val time = "$startTime - $endTime"
val isMyselfOrganizer = isOrganizer()
val isBroadcast = MutableLiveData<Boolean>() val isBroadcast = MutableLiveData<Boolean>()
val subject = MutableLiveData<String>() val subject = MutableLiveData<String>()
@ -90,7 +92,7 @@ class MeetingModel
} }
@WorkerThread @WorkerThread
fun isOrganizer(): Boolean { private fun isOrganizer(): Boolean {
return coreContext.core.accountList.find { account -> return coreContext.core.accountList.find { account ->
val address = account.params.identityAddress val address = account.params.identityAddress
address != null && conferenceInfo.organizer?.weakEqual(address) == true address != null && conferenceInfo.organizer?.weakEqual(address) == true

View file

@ -866,7 +866,9 @@ class SettingsViewModel
if (newValue.isNotEmpty()) { if (newValue.isNotEmpty()) {
try { try {
val delay = newValue.toInt() val delay = newValue.toInt()
corePreferences.autoAnswerDelay = delay coreContext.postOnCoreThread {
corePreferences.autoAnswerDelay = delay
}
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
Log.e("$TAG Ignoring new auto answer incoming calls delay as it can't be converted to int: $nfe") Log.e("$TAG Ignoring new auto answer incoming calls delay as it can't be converted to int: $nfe")
} }