diff --git a/app/src/main/java/org/linphone/core/CorePreferences.kt b/app/src/main/java/org/linphone/core/CorePreferences.kt index d74051d10..953931de7 100644 --- a/app/src/main/java/org/linphone/core/CorePreferences.kt +++ b/app/src/main/java/org/linphone/core/CorePreferences.kt @@ -40,84 +40,84 @@ class CorePreferences private var _config: Config? = null - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var config: Config get() = _config ?: coreContext.core.config set(value) { _config = value } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var printLogsInLogcat: Boolean get() = config.getBool("app", "debug", BuildConfig.DEBUG) set(value) { config.setBool("app", "debug", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var sendLogsToCrashlytics: Boolean get() = config.getBool("app", "send_logs_to_crashlytics", BuildConfig.CRASHLYTICS_ENABLED) set(value) { config.setBool("app", "send_logs_to_crashlytics", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var firstLaunch: Boolean get() = config.getBool("app", "first_6.0_launch", true) set(value) { config.setBool("app", "first_6.0_launch", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var linphoneConfigurationVersion: Int get() = config.getInt("app", "config_version", 52005) set(value) { config.setInt("app", "config_version", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var autoStart: Boolean get() = config.getBool("app", "auto_start", true) set(value) { config.setBool("app", "auto_start", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var checkForUpdateServerUrl: String get() = config.getString("misc", "version_check_url_root", "").orEmpty() set(value) { config.setString("misc", "version_check_url_root", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var conditionsAndPrivacyPolicyAccepted: Boolean get() = config.getBool("app", "read_and_agree_terms_and_privacy", false) set(value) { config.setBool("app", "read_and_agree_terms_and_privacy", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var publishPresence: Boolean get() = config.getBool("app", "publish_presence", true) set(value) { config.setBool("app", "publish_presence", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var keepServiceAlive: Boolean get() = config.getBool("app", "keep_service_alive", false) set(value) { config.setBool("app", "keep_service_alive", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var deviceName: String get() = config.getString("app", "device", "").orEmpty().trim() set(value) { config.setString("app", "device", value.trim()) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var showDeveloperSettings: Boolean get() = config.getBool("ui", "show_developer_settings", false) set(value) { @@ -127,63 +127,63 @@ class CorePreferences // Call settings // This won't be done if bluetooth or wired headset is used - @get:WorkerThread @set:WorkerThread + @get:AnyThread @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 + @get:AnyThread @set:WorkerThread var callRecordingUseSmffFormat: Boolean get() = config.getBool("app", "use_smff_for_call_recording", false) set(value) { config.setBool("app", "use_smff_for_call_recording", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var automaticallyStartCallRecording: Boolean get() = config.getBool("app", "auto_start_call_record", false) set(value) { config.setBool("app", "auto_start_call_record", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var showDialogWhenCallingDeviceUuidDirectly: Boolean get() = config.getBool("app", "show_confirmation_dialog_zrtp_trust_call", true) set(value) { config.setBool("app", "show_confirmation_dialog_zrtp_trust_call", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var acceptEarlyMedia: Boolean get() = config.getBool("sip", "incoming_calls_early_media", false) set(value) { config.setBool("sip", "incoming_calls_early_media", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var allowOutgoingEarlyMedia: Boolean get() = config.getBool("misc", "real_early_media", false) set(value) { config.setBool("misc", "real_early_media", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var autoAnswerEnabled: Boolean get() = config.getBool("app", "auto_answer", false) set(value) { config.setBool("app", "auto_answer", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var autoAnswerDelay: Int get() = config.getInt("app", "auto_answer_delay", 0) set(value) { config.setInt("app", "auto_answer_delay", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var autoAnswerVideoCallsWithVideoDirectionSendReceive: Boolean get() = config.getBool("app", "auto_answer_video_send_receive", false) set(value) { @@ -192,13 +192,14 @@ class CorePreferences // Conversation related - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var markConversationAsReadWhenDismissingMessageNotification: Boolean get() = config.getBool("app", "mark_as_read_notif_dismissal", false) set(value) { config.setBool("app", "mark_as_read_notif_dismissal", value) } + @get:AnyThread @set:WorkerThread var makePublicMediaFilesDownloaded: Boolean // Keep old name for backward compatibility get() = config.getBool("app", "make_downloaded_images_public_in_gallery", false) @@ -208,7 +209,7 @@ class CorePreferences // Conference related - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var createEndToEndEncryptedMeetingsAndGroupCalls: Boolean get() = config.getBool("app", "create_e2e_encrypted_conferences", false) set(value) { @@ -217,35 +218,35 @@ class CorePreferences // Contacts related - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var sortContactsByFirstName: Boolean get() = config.getBool("ui", "sort_contacts_by_first_name", true) // If disabled, last name will be used set(value) { config.setBool("ui", "sort_contacts_by_first_name", value) } - @get:WorkerThread + @get:AnyThread var hideContactsWithoutPhoneNumberOrSipAddress: Boolean get() = config.getBool("ui", "hide_contacts_without_phone_number_or_sip_address", false) set(value) { config.setBool("ui", "hide_contacts_without_phone_number_or_sip_address", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var contactsFilter: String get() = config.getString("ui", "contacts_filter", "")!! // Default value must be empty! set(value) { config.setString("ui", "contacts_filter", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var showFavoriteContacts: Boolean get() = config.getBool("ui", "show_favorites_contacts", true) set(value) { config.setBool("ui", "show_favorites_contacts", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var friendListInWhichStoreNewlyCreatedFriends: String get() = config.getString( "app", @@ -258,7 +259,7 @@ class CorePreferences // Voice recordings related - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var voiceRecordingMaxDuration: Int get() = config.getInt("app", "voice_recording_max_duration", 600000) // in ms set(value) = config.setInt("app", "voice_recording_max_duration", value) @@ -266,7 +267,7 @@ class CorePreferences // User interface related // -1 means auto, 0 no, 1 yes - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var darkMode: Int get() { if (!darkModeAllowed) return 0 @@ -277,21 +278,21 @@ class CorePreferences } // Allows to make screenshots - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var enableSecureMode: Boolean get() = config.getBool("ui", "enable_secure_mode", true) set(value) { config.setBool("ui", "enable_secure_mode", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var automaticallyShowDialpad: Boolean get() = config.getBool("ui", "automatically_show_dialpad", false) set(value) { config.setBool("ui", "automatically_show_dialpad", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var themeMainColor: String get() = config.getString("ui", "theme_main_color", "orange")!! set(value) { @@ -300,109 +301,109 @@ class CorePreferences // Customization options - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var showMicrophoneAndSpeakerVuMeters: Boolean get() = config.getBool("ui", "show_mic_speaker_vu_meter", false) set(value) { config.setBool("ui", "show_mic_speaker_vu_meter", value) } - @get:WorkerThread @set:WorkerThread + @get:AnyThread @set:WorkerThread var pushNotificationCompatibleDomains: Array get() = config.getStringList("app", "push_notification_domains", arrayOf("sip.linphone.org")) set(value) { config.setStringList("app", "push_notification_domains", value) } - @get:WorkerThread + @get:AnyThread val defaultDomain: String get() = config.getString("app", "default_domain", "sip.linphone.org")!! - @get:WorkerThread + @get:AnyThread val darkModeAllowed: Boolean get() = config.getBool("ui", "dark_mode_allowed", true) - @get:WorkerThread + @get:AnyThread val changeMainColorAllowed: Boolean get() = config.getBool("ui", "change_main_color_allowed", false) - @get:WorkerThread + @get:AnyThread val onlyDisplaySipUriUsername: Boolean get() = config.getBool("ui", "only_display_sip_uri_username", false) - @get:WorkerThread + @get:AnyThread val hideSipAddresses: Boolean get() = config.getBool("ui", "hide_sip_addresses", false) - @get:WorkerThread + @get:AnyThread val disableChat: Boolean get() = config.getBool("ui", "disable_chat_feature", false) - @get:WorkerThread + @get:AnyThread val disableMeetings: Boolean get() = config.getBool("ui", "disable_meetings_feature", false) - @get:WorkerThread + @get:AnyThread val disableBroadcasts: Boolean get() = config.getBool("ui", "disable_broadcast_feature", true) // TODO FIXME: not implemented yet - @get:WorkerThread + @get:AnyThread val disableCallRecordings: Boolean get() = config.getBool("ui", "disable_call_recordings_feature", false) - @get:WorkerThread + @get:AnyThread val maxAccountsCount: Int get() = config.getInt("ui", "max_account", 0) // 0 means no max - @get:WorkerThread + @get:AnyThread val hidePhoneNumbers: Boolean get() = config.getBool("ui", "hide_phone_numbers", false) - @get:WorkerThread + @get:AnyThread val hideSettings: Boolean get() = config.getBool("ui", "hide_settings", false) - @get:WorkerThread + @get:AnyThread val hideAccountSettings: Boolean get() = config.getBool("ui", "hide_account_settings", false) - @get:WorkerThread + @get:AnyThread val hideAdvancedSettings: Boolean get() = config.getBool("ui", "hide_advanced_settings", false) - @get:WorkerThread + @get:AnyThread val hideAssistantCreateAccount: Boolean get() = config.getBool("ui", "assistant_hide_create_account", false) - @get:WorkerThread + @get:AnyThread val hideAssistantScanQrCode: Boolean get() = config.getBool("ui", "assistant_disable_qr_code", false) - @get:WorkerThread + @get:AnyThread val hideAssistantThirdPartySipAccount: Boolean get() = config.getBool("ui", "assistant_hide_third_party_account", false) - @get:WorkerThread + @get:AnyThread val magicSearchResultsLimit: Int get() = config.getInt("ui", "max_number_of_magic_search_results", 300) - @get:WorkerThread + @get:AnyThread val singleSignOnClientId: String get() = config.getString("app", "oidc_client_id", "linphone")!! - @get:WorkerThread + @get:AnyThread val useUsernameAsSingleSignOnLoginHint: Boolean get() = config.getBool("ui", "use_username_as_sso_login_hint", false) - @get:WorkerThread + @get:AnyThread val thirdPartySipAccountDefaultTransport: String get() = config.getString("ui", "assistant_third_party_sip_account_transport", "tls")!! - @get:WorkerThread + @get:AnyThread val thirdPartySipAccountDefaultDomain: String get() = config.getString("ui", "assistant_third_party_sip_account_domain", "")!! - @get:WorkerThread + @get:AnyThread val assistantDirectlyGoToThirdPartySipAccountLogin: Boolean get() = config.getBool( "ui", @@ -410,11 +411,11 @@ class CorePreferences false ) - @get:WorkerThread + @get:AnyThread val fetchContactsFromDefaultDirectory: Boolean get() = config.getBool("app", "fetch_contacts_from_default_directory", true) - @get:WorkerThread + @get:AnyThread val showLettersOnDialpad: Boolean get() = config.getBool("ui", "show_letters_on_dialpad", true) diff --git a/app/src/main/java/org/linphone/ui/call/conference/fragment/ConferenceAddParticipantsFragment.kt b/app/src/main/java/org/linphone/ui/call/conference/fragment/ConferenceAddParticipantsFragment.kt index 998cf6dd4..5688a4e59 100644 --- a/app/src/main/java/org/linphone/ui/call/conference/fragment/ConferenceAddParticipantsFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/conference/fragment/ConferenceAddParticipantsFragment.kt @@ -24,12 +24,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.UiThread -import androidx.annotation.WorkerThread import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController -import org.linphone.core.Address -import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.databinding.GenericAddParticipantsFragmentBinding import org.linphone.ui.call.viewmodel.CurrentCallViewModel @@ -66,11 +63,6 @@ class ConferenceAddParticipantsFragment : GenericAddressPickerFragment() { return false } - @WorkerThread - override fun onSingleAddressSelected(address: Address, friend: Friend) { - Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!") - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel = ViewModelProvider(this)[AddParticipantsViewModel::class.java] diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationForwardMessageFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationForwardMessageFragment.kt index 09014f800..b9b888897 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationForwardMessageFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationForwardMessageFragment.kt @@ -149,7 +149,7 @@ class ConversationForwardMessageFragment : SlidingPaneChildFragment() { } } - viewModel.hideNumberOrAddressPickerDialogEvent.observe(viewLifecycleOwner) { + viewModel.dismissNumberOrAddressPickerDialogEvent.observe(viewLifecycleOwner) { it.consume { numberOrAddressPickerDialog?.dismiss() numberOrAddressPickerDialog = null @@ -172,7 +172,7 @@ class ConversationForwardMessageFragment : SlidingPaneChildFragment() { } } - private fun showNumberOrAddressPickerDialog(list: ArrayList) { + private fun showNumberOrAddressPickerDialog(list: List) { val numberOrAddressModel = NumberOrAddressPickerDialogModel(list) val dialog = DialogUtils.getNumberOrAddressPickerDialog( diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/StartConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/StartConversationFragment.kt index a0039ebe7..807ea9fe3 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/fragment/StartConversationFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/StartConversationFragment.kt @@ -24,12 +24,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.UiThread -import androidx.annotation.WorkerThread import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider import org.linphone.R -import org.linphone.core.Address -import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.databinding.StartChatFragmentBinding import org.linphone.ui.GenericActivity @@ -119,11 +116,6 @@ class StartConversationFragment : GenericAddressPickerFragment() { } } - @WorkerThread - override fun onSingleAddressSelected(address: Address, friend: Friend) { - viewModel.createOneToOneChatRoomWith(address) - } - private fun showGroupConversationSubjectDialog() { val model = GroupSetOrEditSubjectDialogModel("", isGroupConversation = true) diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt index 569359e30..bec36d2be 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationForwardMessageViewModel.kt @@ -30,9 +30,8 @@ import org.linphone.core.Address import org.linphone.core.ChatRoom import org.linphone.core.ChatRoomListenerStub import org.linphone.core.Conference +import org.linphone.core.Friend import org.linphone.core.tools.Log -import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener -import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel import org.linphone.ui.main.viewmodel.AddressSelectionViewModel import org.linphone.utils.AppUtils @@ -52,31 +51,6 @@ class ConversationForwardMessageViewModel MutableLiveData>() } - val showNumberOrAddressPickerDialogEvent: MutableLiveData>> by lazy { - MutableLiveData>>() - } - - val hideNumberOrAddressPickerDialogEvent: MutableLiveData> by lazy { - MutableLiveData>() - } - - private val listener = object : ContactNumberOrAddressClickListener { - @UiThread - override fun onClicked(model: ContactNumberOrAddressModel) { - val address = model.address - coreContext.postOnCoreThread { - if (address != null) { - Log.i("$TAG Selected address is [${model.address.asStringUriOnly()}]") - onAddressSelected(model.address) - } - } - } - - @UiThread - override fun onLongPress(model: ContactNumberOrAddressModel) { - } - } - private val chatRoomListener = object : ChatRoomListenerStub() { @WorkerThread override fun onStateChanged(chatRoom: ChatRoom, newState: ChatRoom.State?) { @@ -105,8 +79,8 @@ class ConversationForwardMessageViewModel } @WorkerThread - private fun onAddressSelected(address: Address) { - hideNumberOrAddressPickerDialogEvent.postValue(Event(true)) + override fun onSingleAddressSelected(address: Address, friend: Friend?) { + dismissNumberOrAddressPickerDialogEvent.postValue(Event(true)) createOneToOneChatRoomWith(address) @@ -136,7 +110,7 @@ class ConversationForwardMessageViewModel val friend = model.friend if (friend == null) { Log.i("$TAG Friend is null, using address [${model.address.asStringUriOnly()}]") - onAddressSelected(model.address) + onSingleAddressSelected(model.address, null) return@postOnCoreThread } @@ -145,9 +119,9 @@ class ConversationForwardMessageViewModel Log.i( "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], using it" ) - onAddressSelected(singleAvailableAddress) + onSingleAddressSelected(singleAvailableAddress, friend) } else { - val list = friend.getListOfSipAddressesAndPhoneNumbers(listener) + val list = friend.getListOfSipAddressesAndPhoneNumbers(numberOrAddressClickListener) Log.i( "$TAG [${list.size}] numbers or addresses found for contact [${friend.name}], showing selection dialog" ) diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/StartConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/StartConversationViewModel.kt index 4523e96d9..98e0a6920 100644 --- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/StartConversationViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/StartConversationViewModel.kt @@ -30,6 +30,7 @@ import org.linphone.core.Address import org.linphone.core.ChatRoom import org.linphone.core.ChatRoomListenerStub import org.linphone.core.Conference +import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.ui.main.viewmodel.AddressSelectionViewModel import org.linphone.utils.AppUtils @@ -93,6 +94,11 @@ class StartConversationViewModel updateGroupChatButtonVisibility() } + @WorkerThread + override fun onSingleAddressSelected(address: Address, friend: Friend?) { + createOneToOneChatRoomWith(address) + } + @UiThread fun createGroupChatRoom() { coreContext.postOnCoreThread { core -> diff --git a/app/src/main/java/org/linphone/ui/main/fragment/AddParticipantsFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/AddParticipantsFragment.kt index f9acb82ab..85c370eb4 100644 --- a/app/src/main/java/org/linphone/ui/main/fragment/AddParticipantsFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/fragment/AddParticipantsFragment.kt @@ -25,13 +25,10 @@ import android.view.View import android.view.ViewGroup import androidx.activity.OnBackPressedCallback import androidx.annotation.UiThread -import androidx.annotation.WorkerThread import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import org.linphone.core.Address -import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.databinding.GenericAddParticipantsFragmentBinding import org.linphone.ui.main.viewmodel.AddParticipantsViewModel @@ -92,11 +89,6 @@ class AddParticipantsFragment : GenericAddressPickerFragment() { return false } - @WorkerThread - override fun onSingleAddressSelected(address: Address, friend: Friend) { - Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!") - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { viewModel = ViewModelProvider(this)[AddParticipantsViewModel::class.java] diff --git a/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt b/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt index 23c599f57..98e8d1ace 100644 --- a/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/fragment/GenericAddressPickerFragment.kt @@ -23,23 +23,13 @@ import android.app.Dialog import android.os.Bundle import android.view.View import androidx.annotation.UiThread -import androidx.annotation.WorkerThread import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import org.linphone.LinphoneApplication.Companion.coreContext -import org.linphone.contacts.getListOfSipAddressesAndPhoneNumbers -import org.linphone.core.Address -import org.linphone.core.Friend -import org.linphone.core.tools.Log import org.linphone.ui.main.adapter.ConversationsContactsAndSuggestionsListAdapter -import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel -import org.linphone.ui.main.model.ConversationContactOrSuggestionModel -import org.linphone.ui.main.model.SelectedAddressModel import org.linphone.ui.main.viewmodel.AddressSelectionViewModel import org.linphone.utils.DialogUtils -import org.linphone.utils.LinphoneUtils import org.linphone.utils.RecyclerViewHeaderDecoration @UiThread @@ -56,31 +46,6 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { private lateinit var recyclerView: RecyclerView - private val listener = object : ContactNumberOrAddressClickListener { - @UiThread - override fun onClicked(model: ContactNumberOrAddressModel) { - val address = model.address - coreContext.postOnCoreThread { - if (address != null) { - Log.i( - "$TAG Selected address [${model.address.asStringUriOnly()}] from friend [${model.friend.name}]" - ) - onAddressSelected(model.address, model.friend) - } - } - - numberOrAddressPickerDialog?.dismiss() - numberOrAddressPickerDialog = null - } - - @UiThread - override fun onLongPress(model: ContactNumberOrAddressModel) { - } - } - - @WorkerThread - abstract fun onSingleAddressSelected(address: Address, friend: Friend) - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -92,7 +57,7 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { adapter.onClickedEvent.observe(viewLifecycleOwner) { it.consume { model -> - handleClickOnContactModel(model) + viewModel.handleClickOnContactModel(model) } } @@ -100,6 +65,19 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { val trimmed = filter.trim() viewModel.applyFilter(trimmed) } + + viewModel.showNumberOrAddressPickerDialogEvent.observe(viewLifecycleOwner) { + it.consume { list -> + showNumbersOrAddressesDialog(list) + } + } + + viewModel.dismissNumberOrAddressPickerDialogEvent.observe(viewLifecycleOwner) { + it.consume { + numberOrAddressPickerDialog?.dismiss() + numberOrAddressPickerDialog = null + } + } } override fun onPause() { @@ -109,7 +87,6 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { numberOrAddressPickerDialog = null } - @UiThread protected fun setupRecyclerView(view: RecyclerView) { recyclerView = view recyclerView.setHasFixedSize(true) @@ -119,7 +96,6 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { recyclerView.addItemDecoration(headerItemDecoration) } - @UiThread protected fun attachAdapter() { if (::recyclerView.isInitialized) { if (recyclerView.adapter != adapter) { @@ -128,82 +104,21 @@ abstract class GenericAddressPickerFragment : GenericMainFragment() { } } - @WorkerThread - fun onAddressSelected(address: Address, friend: Friend) { - if (viewModel.multipleSelectionMode.value == true) { - val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address) - val model = SelectedAddressModel(address, avatarModel) { - viewModel.removeAddressModelFromSelection(it) - } - viewModel.addAddressModelToSelection(model) - } else { - onSingleAddressSelected(address, friend) - } - - // Clear filter after it was used - coreContext.postOnMainThread { - viewModel.clearFilter() - } - } - - private fun handleClickOnContactModel(model: ConversationContactOrSuggestionModel) { - if (model.selected.value == true) { - Log.i( - "$TAG User clicked on already selected item [${model.name}], removing it from selection" + private fun showNumbersOrAddressesDialog(list: List) { + val numberOrAddressModel = NumberOrAddressPickerDialogModel(list) + val dialog = + DialogUtils.getNumberOrAddressPickerDialog( + requireActivity(), + numberOrAddressModel ) - val found = viewModel.selection.value.orEmpty().find { - it.address.weakEqual(model.address) || it.avatarModel?.friend == model.friend - } - if (found != null) { - coreContext.postOnCoreThread { - viewModel.removeAddressModelFromSelection(found) - } - return - } else { - Log.e("$TAG Failed to find already selected entry matching the one clicked") + numberOrAddressPickerDialog = dialog + + numberOrAddressModel.dismissEvent.observe(viewLifecycleOwner) { event -> + event.consume { + dialog.dismiss() } } - coreContext.postOnCoreThread { core -> - val friend = model.friend - if (friend == null) { - Log.i("$TAG Friend is null, using address [${model.address.asStringUriOnly()}]") - val fakeFriend = core.createFriend() - fakeFriend.addAddress(model.address) - onAddressSelected(model.address, fakeFriend) - return@postOnCoreThread - } - - val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) - if (singleAvailableAddress != null) { - Log.i( - "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], using it" - ) - onAddressSelected(singleAvailableAddress, friend) - } else { - val list = friend.getListOfSipAddressesAndPhoneNumbers(listener) - Log.i( - "$TAG [${list.size}] numbers or addresses found for contact [${friend.name}], showing selection dialog" - ) - - coreContext.postOnMainThread { - val numberOrAddressModel = NumberOrAddressPickerDialogModel(list) - val dialog = - DialogUtils.getNumberOrAddressPickerDialog( - requireActivity(), - numberOrAddressModel - ) - numberOrAddressPickerDialog = dialog - - numberOrAddressModel.dismissEvent.observe(viewLifecycleOwner) { event -> - event.consume { - dialog.dismiss() - } - } - - dialog.show() - } - } - } + dialog.show() } } diff --git a/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt b/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt index 573d16b8c..ee0724088 100644 --- a/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/main/history/fragment/StartCallFragment.kt @@ -26,15 +26,12 @@ import android.view.View import android.view.ViewGroup import androidx.activity.OnBackPressedCallback import androidx.annotation.UiThread -import androidx.annotation.WorkerThread import androidx.core.view.doOnPreDraw import androidx.lifecycle.ViewModelProvider import com.google.android.material.bottomsheet.BottomSheetBehavior import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R -import org.linphone.core.Address -import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.databinding.StartCallFragmentBinding import org.linphone.ui.GenericActivity @@ -42,7 +39,6 @@ import org.linphone.ui.main.fragment.GenericAddressPickerFragment import org.linphone.ui.main.history.viewmodel.StartCallViewModel import org.linphone.ui.main.model.GroupSetOrEditSubjectDialogModel import org.linphone.utils.DialogUtils -import org.linphone.utils.Event import org.linphone.utils.addCharacterAtPosition import org.linphone.utils.hideKeyboard import org.linphone.utils.removeCharacterAtPosition @@ -209,19 +205,11 @@ class StartCallFragment : GenericAddressPickerFragment() { ) } - @WorkerThread - override fun onSingleAddressSelected(address: Address, friend: Friend) { - coreContext.startAudioCall(address) - viewModel.leaveFragmentEvent.postValue(Event(true)) - } - override fun onResume() { super.onResume() - coreContext.postOnCoreThread { - if (corePreferences.automaticallyShowDialpad) { - viewModel.isNumpadVisible.postValue(true) - } + if (corePreferences.automaticallyShowDialpad) { + viewModel.isNumpadVisible.value = true } } diff --git a/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt b/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt index f253ef44b..817b9f3ba 100644 --- a/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/history/viewmodel/StartCallViewModel.kt @@ -32,6 +32,7 @@ import org.linphone.R import org.linphone.core.Address import org.linphone.core.Conference import org.linphone.core.ConferenceListenerStub +import org.linphone.core.Friend import org.linphone.core.MediaDirection import org.linphone.core.tools.Log import org.linphone.ui.main.history.model.NumpadModel @@ -174,6 +175,12 @@ class StartCallViewModel updateGroupCallButtonVisibility() } + @WorkerThread + override fun onSingleAddressSelected(address: Address, friend: Friend?) { + coreContext.startAudioCall(address) + leaveFragmentEvent.postValue(Event(true)) + } + @UiThread fun updateGroupCallButtonVisibility() { coreContext.postOnCoreThread { core -> diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/AddParticipantsViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/AddParticipantsViewModel.kt index a78be1140..a979ebfa3 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/AddParticipantsViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/AddParticipantsViewModel.kt @@ -20,10 +20,12 @@ package org.linphone.ui.main.viewmodel import androidx.annotation.UiThread +import androidx.annotation.WorkerThread import androidx.lifecycle.MutableLiveData import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.core.Address +import org.linphone.core.Friend import org.linphone.core.tools.Log import org.linphone.ui.main.model.SelectedAddressModel import org.linphone.utils.AppUtils @@ -43,6 +45,11 @@ class AddParticipantsViewModel switchToMultipleSelectionMode() } + @WorkerThread + override fun onSingleAddressSelected(address: Address, friend: Friend?) { + Log.e("$TAG This shouldn't happen as we should always be in multiple selection mode here!") + } + @UiThread fun isSelectionEmpty(): Boolean { return selection.value.orEmpty().isEmpty() diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt index 6f24d4d09..590010450 100644 --- a/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt +++ b/app/src/main/java/org/linphone/ui/main/viewmodel/AddressSelectionViewModel.kt @@ -28,6 +28,7 @@ import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.R import org.linphone.contacts.ContactsManager +import org.linphone.contacts.getListOfSipAddressesAndPhoneNumbers import org.linphone.core.Address import org.linphone.core.ChatRoom import org.linphone.core.Friend @@ -36,10 +37,15 @@ import org.linphone.core.MagicSearchListenerStub import org.linphone.core.SearchResult import org.linphone.mediastream.Log import org.linphone.ui.main.contacts.model.ContactAvatarModel +import org.linphone.ui.main.contacts.model.ContactNumberOrAddressClickListener +import org.linphone.ui.main.contacts.model.ContactNumberOrAddressModel import org.linphone.ui.main.model.ConversationContactOrSuggestionModel import org.linphone.ui.main.model.SelectedAddressModel import org.linphone.utils.AppUtils +import org.linphone.utils.Event import org.linphone.utils.LinphoneUtils +import kotlin.collections.find +import kotlin.collections.orEmpty abstract class AddressSelectionViewModel @UiThread @@ -64,6 +70,14 @@ abstract class AddressSelectionViewModel val showResultsLimitReached = MutableLiveData() + val showNumberOrAddressPickerDialogEvent: MutableLiveData>> by lazy { + MutableLiveData>>() + } + + val dismissNumberOrAddressPickerDialogEvent: MutableLiveData> by lazy { + MutableLiveData>() + } + protected var magicSearchSourceFlags = MagicSearch.Source.All.toInt() protected var skipConversation: Boolean = true @@ -75,6 +89,27 @@ abstract class AddressSelectionViewModel private lateinit var favouritesMagicSearch: MagicSearch + protected val numberOrAddressClickListener = object : ContactNumberOrAddressClickListener { + @UiThread + override fun onClicked(model: ContactNumberOrAddressModel) { + val address = model.address + coreContext.postOnCoreThread { + if (address != null) { + Log.i( + "$TAG Selected address [${model.address.asStringUriOnly()}] from friend [${model.friend.name}]" + ) + onAddressSelected(model.address, model.friend) + } + } + + dismissNumberOrAddressPickerDialogEvent.postValue(Event(true)) + } + + @UiThread + override fun onLongPress(model: ContactNumberOrAddressModel) { + } + } + private val magicSearchListener = object : MagicSearchListenerStub() { @WorkerThread override fun onSearchResultsReceived(magicSearch: MagicSearch) { @@ -108,6 +143,9 @@ abstract class AddressSelectionViewModel override fun onContactFoundInRemoteDirectory(friend: Friend) { } } + @WorkerThread + abstract fun onSingleAddressSelected(address: Address, friend: Friend?) + init { multipleSelectionMode.value = false isEmpty.value = true @@ -471,4 +509,68 @@ abstract class AddressSelectionViewModel } return conversationsList } + + @UiThread + fun handleClickOnContactModel(model: ConversationContactOrSuggestionModel) { + if (model.selected.value == true) { + org.linphone.core.tools.Log.i( + "$TAG User clicked on already selected item [${model.name}], removing it from selection" + ) + val found = selection.value.orEmpty().find { + it.address.weakEqual(model.address) || it.avatarModel?.friend == model.friend + } + if (found != null) { + coreContext.postOnCoreThread { + removeAddressModelFromSelection(found) + } + return + } else { + org.linphone.core.tools.Log.e("$TAG Failed to find already selected entry matching the one clicked") + } + } + + coreContext.postOnCoreThread { core -> + val friend = model.friend + if (friend == null) { + org.linphone.core.tools.Log.i("$TAG Friend is null, using address [${model.address.asStringUriOnly()}]") + val fakeFriend = core.createFriend() + fakeFriend.addAddress(model.address) + onAddressSelected(model.address, fakeFriend) + return@postOnCoreThread + } + + val singleAvailableAddress = LinphoneUtils.getSingleAvailableAddressForFriend(friend) + if (singleAvailableAddress != null) { + org.linphone.core.tools.Log.i( + "$TAG Only 1 SIP address or phone number found for contact [${friend.name}], using it" + ) + onAddressSelected(singleAvailableAddress, friend) + } else { + val list = friend.getListOfSipAddressesAndPhoneNumbers(numberOrAddressClickListener) + org.linphone.core.tools.Log.i( + "$TAG [${list.size}] numbers or addresses found for contact [${friend.name}], showing selection dialog" + ) + + showNumberOrAddressPickerDialogEvent.postValue(Event(list)) + } + } + } + + @WorkerThread + private fun onAddressSelected(address: Address, friend: Friend) { + if (multipleSelectionMode.value == true) { + val avatarModel = coreContext.contactsManager.getContactAvatarModelForAddress(address) + val model = SelectedAddressModel(address, avatarModel) { + removeAddressModelFromSelection(it) + } + addAddressModelToSelection(model) + } else { + onSingleAddressSelected(address, friend) + } + + // Clear filter after it was used + coreContext.postOnMainThread { + clearFilter() + } + } }