Replaced deprecated ProxyConfig object by Account

This commit is contained in:
Sylvain Berfini 2020-10-21 09:22:52 +02:00
parent 4f6b416b7e
commit daa789e672
25 changed files with 264 additions and 159 deletions

View file

@ -102,7 +102,7 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
startActivity(intent) startActivity(intent)
} }
if (coreContext.core.proxyConfigList.isEmpty()) { if (coreContext.core.accountList.isEmpty()) {
if (corePreferences.firstStart) { if (corePreferences.firstStart) {
corePreferences.firstStart = false corePreferences.firstStart = false
startActivity(Intent(this, AssistantActivity::class.java)) startActivity(Intent(this, AssistantActivity::class.java))
@ -371,7 +371,7 @@ class MainActivity : GenericActivity(), SnackBarActivity, NavController.OnDestin
val peerAddress = coreContext.core.interpretUrl(addressToIM)?.asStringUriOnly() val peerAddress = coreContext.core.interpretUrl(addressToIM)?.asStringUriOnly()
val localAddress = val localAddress =
coreContext.core.defaultProxyConfig?.contact?.asStringUriOnly() coreContext.core.defaultAccount?.params?.identityAddress?.asStringUriOnly()
val deepLink = "linphone-android://chat-room/$localAddress/$peerAddress" val deepLink = "linphone-android://chat-room/$localAddress/$peerAddress"
Log.i("[Main Activity] Starting deep link: $deepLink") Log.i("[Main Activity] Starting deep link: $deepLink")
findNavController(R.id.nav_host_fragment).navigate(Uri.parse(deepLink)) findNavController(R.id.nav_host_fragment).navigate(Uri.parse(deepLink))

View file

@ -101,7 +101,7 @@ class ChatRoomCreationContactsAdapter(
securityEnabled: Boolean securityEnabled: Boolean
) { ) {
val searchAddress = searchResult.address val searchAddress = searchResult.address
val isMyself = securityEnabled && searchAddress != null && coreContext.core.defaultProxyConfig?.identityAddress?.weakEqual(searchAddress) ?: false val isMyself = securityEnabled && searchAddress != null && coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(searchAddress) ?: false
val limeCheck = !securityEnabled || (securityEnabled && searchResult.hasCapability(FriendCapability.LimeX3Dh)) val limeCheck = !securityEnabled || (securityEnabled && searchResult.hasCapability(FriendCapability.LimeX3Dh))
val groupCheck = !groupChatEnabled || (groupChatEnabled && searchResult.hasCapability(FriendCapability.GroupChat)) val groupCheck = !groupChatEnabled || (groupChatEnabled && searchResult.hasCapability(FriendCapability.GroupChat))
val disabled = if (searchResult.friend != null) !limeCheck || !groupCheck || isMyself else false // Generated entry from search filter val disabled = if (searchResult.friend != null) !limeCheck || !groupCheck || isMyself else false // Generated entry from search filter

View file

@ -107,7 +107,7 @@ class ChatRoomCreationViewModel : ErrorReportingViewModel() {
} }
fun updateContactsList() { fun updateContactsList() {
val domain = if (sipContactsSelected.value == true) coreContext.core.defaultProxyConfig?.domain ?: "" else "" val domain = if (sipContactsSelected.value == true) coreContext.core.defaultAccount?.params?.domain ?: "" else ""
val results = coreContext.contactsManager.magicSearch.getContactListFromFilter(filter.value.orEmpty(), domain) val results = coreContext.contactsManager.magicSearch.getContactListFromFilter(filter.value.orEmpty(), domain)
val list = arrayListOf<SearchResult>() val list = arrayListOf<SearchResult>()
@ -145,7 +145,7 @@ class ChatRoomCreationViewModel : ErrorReportingViewModel() {
fun createOneToOneChat(searchResult: SearchResult) { fun createOneToOneChat(searchResult: SearchResult) {
waitForChatRoomCreation.value = true waitForChatRoomCreation.value = true
val defaultProxyConfig = coreContext.core.defaultProxyConfig val defaultAccount = coreContext.core.defaultAccount
var room: ChatRoom? var room: ChatRoom?
val address = searchResult.address ?: coreContext.core.interpretUrl(searchResult.phoneNumber ?: "") val address = searchResult.address ?: coreContext.core.interpretUrl(searchResult.phoneNumber ?: "")
@ -167,12 +167,13 @@ class ChatRoomCreationViewModel : ErrorReportingViewModel() {
} }
val participants = arrayOf(address) val participants = arrayOf(address)
val localAddress = defaultProxyConfig?.identityAddress val localAddress: Address? = defaultAccount?.params?.identityAddress
room = coreContext.core.searchChatRoom(params, localAddress, null, participants) room = coreContext.core.searchChatRoom(params, localAddress, null, participants)
if (room == null) { if (room == null) {
Log.w("[Chat Room Creation] Couldn't find existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}") Log.w("[Chat Room Creation] Couldn't find existing 1-1 chat room with remote ${address.asStringUriOnly()}, encryption=$encrypted and local identity ${localAddress?.asStringUriOnly()}")
room = coreContext.core.createChatRoom(params, localAddress, participants) room = coreContext.core.createChatRoom(params, localAddress, participants)
if (encrypted) { if (encrypted) {
room?.addListener(listener) room?.addListener(listener)
} else { } else {

View file

@ -125,7 +125,7 @@ class GroupInfoViewModel(val chatRoom: ChatRoom?) : ErrorReportingViewModel() {
index += 1 index += 1
} }
val chatRoom: ChatRoom? = coreContext.core.createChatRoom(params, coreContext.core.defaultProxyConfig?.identityAddress, addresses) val chatRoom: ChatRoom? = coreContext.core.createChatRoom(params, coreContext.core.defaultAccount?.params?.identityAddress, addresses)
chatRoom?.addListener(listener) chatRoom?.addListener(listener)
if (chatRoom == null) { if (chatRoom == null) {
Log.e("[Chat Room Group Info] Couldn't create chat room!") Log.e("[Chat Room Group Info] Couldn't create chat room!")

View file

@ -169,9 +169,9 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
val value = address.asStringUriOnly() val value = address.asStringUriOnly()
val presenceModel = contact.friend?.getPresenceModelForUriOrTel(value) val presenceModel = contact.friend?.getPresenceModelForUriOrTel(value)
val hasPresence = presenceModel?.basicStatus == PresenceBasicStatus.Open val hasPresence = presenceModel?.basicStatus == PresenceBasicStatus.Open
val isMe = coreContext.core.defaultProxyConfig?.identityAddress?.weakEqual(address) ?: false val isMe = coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(address) ?: false
val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(value)?.hasCapability(FriendCapability.LimeX3Dh) ?: false val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(value)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
val displayValue = if (coreContext.core.defaultProxyConfig?.domain == address.domain) (address.username ?: value) else value val displayValue = if (coreContext.core.defaultAccount?.params?.domain == address.domain) (address.username ?: value) else value
val noa = ContactNumberOrAddressViewModel(address, hasPresence, displayValue, showSecureChat = secureChatAllowed, listener = listener) val noa = ContactNumberOrAddressViewModel(address, hasPresence, displayValue, showSecureChat = secureChatAllowed, listener = listener)
list.add(noa) list.add(noa)
} }
@ -181,7 +181,7 @@ class ContactViewModel(private val c: Contact) : ErrorReportingViewModel(), Cont
val hasPresence = presenceModel != null && presenceModel.basicStatus == PresenceBasicStatus.Open val hasPresence = presenceModel != null && presenceModel.basicStatus == PresenceBasicStatus.Open
val contactAddress = presenceModel?.contact ?: number val contactAddress = presenceModel?.contact ?: number
val address = coreContext.core.interpretUrl(contactAddress) val address = coreContext.core.interpretUrl(contactAddress)
val isMe = if (address != null) coreContext.core.defaultProxyConfig?.identityAddress?.weakEqual(address) ?: false else false val isMe = if (address != null) coreContext.core.defaultAccount?.params?.identityAddress?.weakEqual(address) ?: false else false
val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(number)?.hasCapability(FriendCapability.LimeX3Dh) ?: false val secureChatAllowed = !isMe && contact.friend?.getPresenceModelForUriOrTel(number)?.hasCapability(FriendCapability.LimeX3Dh) ?: false
val noa = ContactNumberOrAddressViewModel(address, hasPresence, number, isSip = false, showSecureChat = secureChatAllowed, typeLabel = phoneNumber.typeLabel, listener = listener) val noa = ContactNumberOrAddressViewModel(address, hasPresence, number, isSip = false, showSecureChat = secureChatAllowed, typeLabel = phoneNumber.typeLabel, listener = listener)
list.add(noa) list.add(noa)

View file

@ -48,11 +48,11 @@ class StatusFragment : GenericFragment<StatusFragmentBinding>() {
ViewModelProvider(this).get(SharedMainViewModel::class.java) ViewModelProvider(this).get(SharedMainViewModel::class.java)
} }
sharedViewModel.proxyConfigRemoved.observe(viewLifecycleOwner, { sharedViewModel.accountRemoved.observe(viewLifecycleOwner, {
Log.i("[Status Fragment] A proxy config was removed, update default proxy state") Log.i("[Status Fragment] An account was removed, update default account state")
val defaultProxy = coreContext.core.defaultProxyConfig val defaultAccount = coreContext.core.defaultAccount
if (defaultProxy != null) { if (defaultAccount != null) {
viewModel.updateDefaultProxyConfigRegistrationStatus(defaultProxy.state) viewModel.updateDefaultAccountRegistrationStatus(defaultAccount.state)
} }
}) })

View file

@ -56,9 +56,9 @@ class AccountSettingsFragment : GenericFragment<SettingsAccountFragmentBinding>(
viewModel.linkPhoneNumberEvent.observe(viewLifecycleOwner, { viewModel.linkPhoneNumberEvent.observe(viewLifecycleOwner, {
it.consume { it.consume {
val authInfo = viewModel.proxyConfig.findAuthInfo() val authInfo = viewModel.account.findAuthInfo()
if (authInfo == null) { if (authInfo == null) {
Log.e("[Account Settings] Failed to find auth info for proxy config ${viewModel.proxyConfig}") Log.e("[Account Settings] Failed to find auth info for account ${viewModel.account}")
} else { } else {
val args = Bundle() val args = Bundle()
args.putString("Username", authInfo.username) args.putString("Username", authInfo.username)
@ -69,9 +69,9 @@ class AccountSettingsFragment : GenericFragment<SettingsAccountFragmentBinding>(
} }
}) })
viewModel.proxyConfigRemovedEvent.observe(viewLifecycleOwner, { viewModel.accountRemovedEvent.observe(viewLifecycleOwner, {
it.consume { it.consume {
sharedViewModel.proxyConfigRemoved.value = true sharedViewModel.accountRemoved.value = true
findNavController().navigateUp() findNavController().navigateUp()
} }
}) })

View file

@ -54,8 +54,8 @@ class SettingsFragment : SecureFragment<SettingsFragmentBinding>() {
binding.setBackClickListener { findNavController().popBackStack() } binding.setBackClickListener { findNavController().popBackStack() }
sharedViewModel.proxyConfigRemoved.observe(viewLifecycleOwner, { sharedViewModel.accountRemoved.observe(viewLifecycleOwner, {
Log.i("[Settings] Proxy config removed, update accounts list") Log.i("[Settings] Account removed, update accounts list")
viewModel.updateAccountsList() viewModel.updateAccountsList()
}) })
@ -68,7 +68,7 @@ class SettingsFragment : SecureFragment<SettingsFragmentBinding>() {
viewModel.accountsSettingsListener = object : SettingListenerStub() { viewModel.accountsSettingsListener = object : SettingListenerStub() {
override fun onAccountClicked(identity: String) { override fun onAccountClicked(identity: String) {
Log.i("[Settings] Navigation to settings for proxy with identity: $identity") Log.i("[Settings] Navigation to settings for account with identity: $identity")
navigateToAccountSettings(identity) navigateToAccountSettings(identity)
} }
} }

View file

@ -38,16 +38,16 @@ class AccountSettingsViewModelFactory(private val identity: String) :
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T { override fun <T : ViewModel?> create(modelClass: Class<T>): T {
for (proxy in coreContext.core.proxyConfigList) { for (account in coreContext.core.accountList) {
if (proxy.identityAddress?.asStringUriOnly() == identity) { if (account.params.identityAddress?.asStringUriOnly() == identity) {
return AccountSettingsViewModel(proxy) as T return AccountSettingsViewModel(account) as T
} }
} }
return AccountSettingsViewModel(coreContext.core.defaultProxyConfig!!) as T return AccountSettingsViewModel(coreContext.core.defaultAccount!!) as T
} }
} }
class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsViewModel() { class AccountSettingsViewModel(val account: Account) : GenericSettingsViewModel() {
val isDefault = MutableLiveData<Boolean>() val isDefault = MutableLiveData<Boolean>()
val displayName = MutableLiveData<String>() val displayName = MutableLiveData<String>()
@ -61,25 +61,24 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val waitForUnregister = MutableLiveData<Boolean>() val waitForUnregister = MutableLiveData<Boolean>()
val proxyConfigRemovedEvent: MutableLiveData<Event<Boolean>> by lazy { val accountRemovedEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
} }
val displayUsernameInsteadOfIdentity = corePreferences.replaceSipUriByUsername val displayUsernameInsteadOfIdentity = corePreferences.replaceSipUriByUsername
private var proxyConfigToDelete: ProxyConfig? = null private var accountToDelete: Account? = null
val listener: CoreListenerStub = object : CoreListenerStub() { val listener: AccountListenerStub = object : AccountListenerStub() {
override fun onRegistrationStateChanged( override fun onRegistrationStateChanged(
core: Core, account: Account,
cfg: ProxyConfig,
state: RegistrationState, state: RegistrationState,
message: String message: String
) { ) {
if (state == RegistrationState.Cleared && cfg == proxyConfigToDelete) { if (state == RegistrationState.Cleared && account == accountToDelete) {
Log.i("[Account Settings] Proxy config to remove registration is now cleared") Log.i("[Account Settings] Account to remove registration is now cleared")
waitForUnregister.value = false waitForUnregister.value = false
deleteProxyConfig(cfg) deleteAccount(account)
} else { } else {
update() update()
} }
@ -90,44 +89,94 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val userNameListener = object : SettingListenerStub() { val userNameListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
// TODO val params = account.params.clone()
val identity = params.identityAddress
if (identity != null) {
identity.username = newValue
params.identityAddress = identity
account.params = params
} else {
Log.e("[Account Settings] Account doesn't have an identity yet")
}
} }
} }
val userName = MutableLiveData<String>() val userName = MutableLiveData<String>()
val userIdListener = object : SettingListenerStub() { val userIdListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
// TODO val authInfo = account.findAuthInfo()
if (authInfo != null) {
authInfo.userid = newValue
} else {
Log.e("[Account Settings] Failed to find the matching auth info")
}
} }
} }
val userId = MutableLiveData<String>() val userId = MutableLiveData<String>()
val passwordListener = object : SettingListenerStub() { val passwordListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
// TODO val authInfo = account.findAuthInfo()
if (authInfo != null) {
authInfo.password = newValue
} else {
Log.w("[Account Settings] Failed to find the matching auth info")
val params = account.params
val identity = params.identityAddress
if (identity != null && identity.username != null) {
val newAuthInfo = Factory.instance()
.createAuthInfo(identity.username!!, userId.value, newValue, null, null, identity.domain)
core.addAuthInfo(newAuthInfo)
} else {
Log.e("[Account Settings] Failed to find the user's identity, can't create a new auth info")
}
}
} }
} }
val password = MutableLiveData<String>() val password = MutableLiveData<String>()
val domainListener = object : SettingListenerStub() { val domainListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
// TODO val params = account.params.clone()
val identity = params.identityAddress
if (identity != null) {
val authInfo = account.findAuthInfo()
if (authInfo != null) {
authInfo.domain = newValue
} else {
Log.e("[Account Settings] Failed to find the matching auth info")
}
identity.domain = newValue
params.identityAddress = identity
account.params = params
} else {
Log.e("[Account Settings] Account doesn't have an identity yet")
}
} }
} }
val domain = MutableLiveData<String>() val domain = MutableLiveData<String>()
val displayNameListener = object : SettingListenerStub() { val displayNameListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
proxyConfig.identityAddress?.displayName = newValue val params = account.params.clone()
val identity = params.identityAddress
if (identity != null) {
identity.displayName = newValue
params.identityAddress = identity
account.params = params
} else {
Log.e("[Account Settings] Account doesn't have an identity yet")
}
} }
} }
// displayName mutable is above // displayName mutable is above
val disableListener = object : SettingListenerStub() { val disableListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
proxyConfig.edit() val params = account.params.clone()
proxyConfig.enableRegister(!newValue) params.registerEnabled = !newValue
proxyConfig.done() account.params = params
} }
} }
val disable = MutableLiveData<Boolean>() val disable = MutableLiveData<Boolean>()
@ -135,14 +184,14 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val isDefaultListener = object : SettingListenerStub() { val isDefaultListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
if (newValue) { if (newValue) {
core.defaultProxyConfig = proxyConfig core.defaultAccount = account
} }
} }
} }
// isDefault mutable is above // isDefault mutable is above
private fun deleteProxyConfig(cfg: ProxyConfig) { private fun deleteAccount(account: Account) {
val authInfo = cfg.findAuthInfo() val authInfo = account.findAuthInfo()
if (authInfo != null) { if (authInfo != null) {
Log.i("[Account Settings] Found auth info $authInfo, removing it.") Log.i("[Account Settings] Found auth info $authInfo, removing it.")
core.removeAuthInfo(authInfo) core.removeAuthInfo(authInfo)
@ -150,42 +199,44 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
Log.w("[Account Settings] Couldn't find matching auth info...") Log.w("[Account Settings] Couldn't find matching auth info...")
} }
core.removeProxyConfig(cfg) core.removeAccount(account)
proxyConfigRemovedEvent.value = Event(true) accountRemovedEvent.value = Event(true)
} }
val deleteListener = object : SettingListenerStub() { val deleteListener = object : SettingListenerStub() {
override fun onClicked() { override fun onClicked() {
proxyConfigToDelete = proxyConfig accountToDelete = account
val registered = proxyConfig.state == RegistrationState.Ok val registered = account.state == RegistrationState.Ok
waitForUnregister.value = registered waitForUnregister.value = registered
if (core.defaultProxyConfig == proxyConfig) { if (core.defaultAccount == account) {
Log.i("[Account Settings] Proxy config was default, let's look for a replacement") Log.i("[Account Settings] Account was default, let's look for a replacement")
for (proxyConfigIterator in core.proxyConfigList) { for (accountIterator in core.accountList) {
if (proxyConfig != proxyConfigIterator) { if (account != accountIterator) {
core.defaultProxyConfig = proxyConfigIterator core.defaultAccount = accountIterator
Log.i("[Account Settings] New default proxy config is $proxyConfigIterator") Log.i("[Account Settings] New default account is $accountIterator")
break break
} }
} }
} }
proxyConfig.edit() val params = account.params.clone()
proxyConfig.enableRegister(false) params.registerEnabled = false
proxyConfig.done() account.params = params
if (!registered) { if (!registered) {
Log.w("[Account Settings] Proxy config isn't registered, don't unregister before removing it") Log.w("[Account Settings] Account isn't registered, don't unregister before removing it")
deleteProxyConfig(proxyConfig) deleteAccount(account)
} }
} }
} }
val pushNotificationListener = object : SettingListenerStub() { val pushNotificationListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
proxyConfig.isPushNotificationAllowed = newValue val params = account.params.clone()
params.pushNotificationAllowed = newValue
account.params = params
} }
} }
val pushNotification = MutableLiveData<Boolean>() val pushNotification = MutableLiveData<Boolean>()
@ -193,7 +244,10 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val transportListener = object : SettingListenerStub() { val transportListener = object : SettingListenerStub() {
override fun onListValueChanged(position: Int) { override fun onListValueChanged(position: Int) {
// TODO val params = account.params.clone()
params.transport = TransportType.fromInt(position)
account.params = params
proxy.value = account.params.serverAddr
} }
} }
val transportIndex = MutableLiveData<Int>() val transportIndex = MutableLiveData<Int>()
@ -201,40 +255,48 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val proxyListener = object : SettingListenerStub() { val proxyListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
proxyConfig.serverAddr = newValue val params = account.params.clone()
if (outboundProxy.value == true) { params.serverAddr = newValue
// TODO account.params = params
} transportIndex.value = account.params.transport.toInt()
} }
} }
val proxy = MutableLiveData<String>() val proxy = MutableLiveData<String>()
val outboundProxyListener = object : SettingListenerStub() { val outboundProxyListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
// TODO val params = account.params.clone()
params.outboundProxyEnabled = newValue
account.params = params
} }
} }
val outboundProxy = MutableLiveData<Boolean>() val outboundProxy = MutableLiveData<Boolean>()
val stunServerListener = object : SettingListenerStub() { val stunServerListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
proxyConfig.natPolicy?.stunServer = newValue val params = account.params.clone()
params.natPolicy?.stunServer = newValue
if (newValue.isEmpty()) ice.value = false if (newValue.isEmpty()) ice.value = false
stunServer.value = newValue stunServer.value = newValue
account.params = params
} }
} }
val stunServer = MutableLiveData<String>() val stunServer = MutableLiveData<String>()
val iceListener = object : SettingListenerStub() { val iceListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
proxyConfig.natPolicy?.enableIce(newValue) val params = account.params.clone()
params.natPolicy?.enableIce(newValue)
account.params = params
} }
} }
val ice = MutableLiveData<Boolean>() val ice = MutableLiveData<Boolean>()
val avpfListener = object : SettingListenerStub() { val avpfListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
proxyConfig.avpfMode = if (newValue) AVPFMode.Enabled else AVPFMode.Disabled val params = account.params.clone()
params.avpfMode = if (newValue) AVPFMode.Enabled else AVPFMode.Disabled
account.params = params
} }
} }
val avpf = MutableLiveData<Boolean>() val avpf = MutableLiveData<Boolean>()
@ -242,8 +304,11 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val avpfRrIntervalListener = object : SettingListenerStub() { val avpfRrIntervalListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
try { try {
proxyConfig.avpfRrInterval = newValue.toInt() val params = account.params.clone()
params.avpfRrInterval = newValue.toInt()
account.params = params
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
Log.e("[Account Settings] Failed to set AVPF RR interval ($newValue): $nfe")
} }
} }
} }
@ -252,23 +317,39 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
val expiresListener = object : SettingListenerStub() { val expiresListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
try { try {
proxyConfig.expires = newValue.toInt() val params = account.params.clone()
params.expires = newValue.toInt()
account.params = params
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
Log.e("[Account Settings] Failed to set expires ($newValue): $nfe")
} }
} }
} }
val expires = MutableLiveData<Int>() val expires = MutableLiveData<Int>()
val dialPrefixListener = object : SettingListenerStub() { val prefixListener = object : SettingListenerStub() {
override fun onTextValueChanged(newValue: String) { override fun onTextValueChanged(newValue: String) {
proxyConfig.dialPrefix = newValue val params = account.params.clone()
params.internationalPrefix = newValue
account.params = params
} }
} }
val dialPrefix = MutableLiveData<String>() val prefix = MutableLiveData<String>()
val dialPrefixListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) {
val params = account.params.clone()
params.useInternationalPrefixForCallsAndChats = newValue
account.params = params
}
}
val dialPrefix = MutableLiveData<Boolean>()
val escapePlusListener = object : SettingListenerStub() { val escapePlusListener = object : SettingListenerStub() {
override fun onBoolValueChanged(newValue: Boolean) { override fun onBoolValueChanged(newValue: Boolean) {
proxyConfig.dialEscapePlus = newValue val params = account.params.clone()
params.dialEscapePlusEnabled = newValue
account.params = params
} }
} }
val escapePlus = MutableLiveData<Boolean>() val escapePlus = MutableLiveData<Boolean>()
@ -282,61 +363,67 @@ class AccountSettingsViewModel(val proxyConfig: ProxyConfig) : GenericSettingsVi
init { init {
update() update()
core.addListener(listener) account.addListener(listener)
initTransportList() initTransportList()
} }
override fun onCleared() { override fun onCleared() {
core.removeListener(listener) account.removeListener(listener)
super.onCleared() super.onCleared()
} }
private fun update() { private fun update() {
isDefault.value = core.defaultProxyConfig == proxyConfig isDefault.value = core.defaultAccount == account
val identityAddress = proxyConfig.identityAddress val params = account.params
val identityAddress = params.identityAddress
if (identityAddress != null) { if (identityAddress != null) {
displayName.value = identityAddress.displayName ?: "" displayName.value = identityAddress.displayName ?: ""
identity.value = identityAddress.asStringUriOnly() identity.value = identityAddress.asStringUriOnly()
} }
iconResource.value = when (proxyConfig.state) { iconResource.value = when (account.state) {
RegistrationState.Ok -> R.drawable.led_registered RegistrationState.Ok -> R.drawable.led_registered
RegistrationState.Failed -> R.drawable.led_error RegistrationState.Failed -> R.drawable.led_error
RegistrationState.Progress -> R.drawable.led_registration_in_progress RegistrationState.Progress -> R.drawable.led_registration_in_progress
else -> R.drawable.led_not_registered else -> R.drawable.led_not_registered
} }
iconContentDescription.value = when (proxyConfig.state) { iconContentDescription.value = when (account.state) {
RegistrationState.Ok -> R.string.status_connected RegistrationState.Ok -> R.string.status_connected
RegistrationState.Progress -> R.string.status_in_progress RegistrationState.Progress -> R.string.status_in_progress
RegistrationState.Failed -> R.string.status_error RegistrationState.Failed -> R.string.status_error
else -> R.string.status_not_connected else -> R.string.status_not_connected
} }
userName.value = proxyConfig.identityAddress?.username userName.value = params.identityAddress?.username
userId.value = proxyConfig.findAuthInfo()?.userid userId.value = account.findAuthInfo()?.userid
domain.value = proxyConfig.identityAddress?.domain domain.value = params.identityAddress?.domain
disable.value = !proxyConfig.registerEnabled() disable.value = !params.registerEnabled
pushNotification.value = proxyConfig.isPushNotificationAllowed pushNotification.value = params.pushNotificationAllowed
pushNotificationsAvailable.value = core.isPushNotificationAvailable pushNotificationsAvailable.value = core.isPushNotificationAvailable
proxy.value = proxyConfig.serverAddr proxy.value = params.serverAddr
outboundProxy.value = proxyConfig.serverAddr == proxyConfig.routes.firstOrNull() outboundProxy.value = params.outboundProxyEnabled
stunServer.value = proxyConfig.natPolicy?.stunServer stunServer.value = params.natPolicy?.stunServer
ice.value = proxyConfig.natPolicy?.iceEnabled() ice.value = params.natPolicy?.iceEnabled()
avpf.value = proxyConfig.avpfEnabled() avpf.value = params.avpfMode == AVPFMode.Enabled
avpfRrInterval.value = proxyConfig.avpfRrInterval avpfRrInterval.value = params.avpfRrInterval
expires.value = proxyConfig.expires expires.value = params.expires
dialPrefix.value = proxyConfig.dialPrefix prefix.value = params.internationalPrefix
escapePlus.value = proxyConfig.dialEscapePlus dialPrefix.value = params.useInternationalPrefixForCallsAndChats
escapePlus.value = params.dialEscapePlusEnabled
} }
private fun initTransportList() { private fun initTransportList() {
val labels = arrayListOf<String>() val labels = arrayListOf<String>()
// Keep following in the same order as TransportType enum
labels.add(prefs.getString(R.string.account_settings_transport_udp)) labels.add(prefs.getString(R.string.account_settings_transport_udp))
labels.add(prefs.getString(R.string.account_settings_transport_tcp)) labels.add(prefs.getString(R.string.account_settings_transport_tcp))
labels.add(prefs.getString(R.string.account_settings_transport_tls)) labels.add(prefs.getString(R.string.account_settings_transport_tls))
if (corePreferences.allowDtlsTransport) {
labels.add(prefs.getString(R.string.account_settings_transport_dtls))
}
transportLabels.value = labels transportLabels.value = labels
transportIndex.value = labels.indexOf(proxyConfig.transport.toUpperCase(Locale.getDefault())) transportIndex.value = account.params.transport.toInt()
} }
} }

View file

@ -70,9 +70,9 @@ class SettingsViewModel : ViewModel() {
fun updateAccountsList() { fun updateAccountsList() {
val list = arrayListOf<AccountSettingsViewModel>() val list = arrayListOf<AccountSettingsViewModel>()
if (coreContext.core.proxyConfigList.isNotEmpty()) { if (coreContext.core.accountList.isNotEmpty()) {
for (proxy in coreContext.core.proxyConfigList) { for (account in coreContext.core.accountList) {
val viewModel = AccountSettingsViewModel(proxy) val viewModel = AccountSettingsViewModel(account)
viewModel.accountsSettingsListener = accountClickListener viewModel.accountsSettingsListener = accountClickListener
list.add(viewModel) list.add(viewModel)
} }

View file

@ -67,8 +67,8 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
ViewModelProvider(this).get(SharedMainViewModel::class.java) ViewModelProvider(this).get(SharedMainViewModel::class.java)
} }
sharedViewModel.proxyConfigRemoved.observe(viewLifecycleOwner, { sharedViewModel.accountRemoved.observe(viewLifecycleOwner, {
Log.i("[Side Menu] Proxy config removed, update accounts list") Log.i("[Side Menu] Account removed, update accounts list")
viewModel.updateAccountsList() viewModel.updateAccountsList()
}) })
@ -76,7 +76,7 @@ class SideMenuFragment : GenericFragment<SideMenuFragmentBinding>() {
override fun onAccountClicked(identity: String) { override fun onAccountClicked(identity: String) {
val args = Bundle() val args = Bundle()
args.putString("Identity", identity) args.putString("Identity", identity)
Log.i("[Side Menu] Navigation to settings for proxy with identity: $identity") Log.i("[Side Menu] Navigation to settings for account with identity: $identity")
sharedViewModel.toggleDrawerEvent.value = Event(true) sharedViewModel.toggleDrawerEvent.value = Event(true)
navigateToAccountSettings(identity) navigateToAccountSettings(identity)

View file

@ -35,7 +35,7 @@ class SideMenuViewModel : ViewModel() {
val showAbout: Boolean = corePreferences.showAboutInSideMenu val showAbout: Boolean = corePreferences.showAboutInSideMenu
val showQuit: Boolean = corePreferences.showQuitInSideMenu val showQuit: Boolean = corePreferences.showQuitInSideMenu
val defaultAccount = MutableLiveData<AccountSettingsViewModel>() val defaultAccountViewModel = MutableLiveData<AccountSettingsViewModel>()
val defaultAccountFound = MutableLiveData<Boolean>() val defaultAccountFound = MutableLiveData<Boolean>()
val defaultAccountAvatar = MutableLiveData<String>() val defaultAccountAvatar = MutableLiveData<String>()
@ -50,14 +50,14 @@ class SideMenuViewModel : ViewModel() {
} }
private val listener: CoreListenerStub = object : CoreListenerStub() { private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onRegistrationStateChanged( override fun onAccountRegistrationStateChanged(
core: Core, core: Core,
cfg: ProxyConfig, account: Account,
state: RegistrationState, state: RegistrationState,
message: String message: String
) { ) {
if (coreContext.core.proxyConfigList.size != accounts.value?.size) { if (coreContext.core.accountList.size != accounts.value?.size) {
// Only refresh the list if a proxy has been added or removed // Only refresh the list if an account has been added or removed
updateAccountsList() updateAccountsList()
} }
} }
@ -77,18 +77,18 @@ class SideMenuViewModel : ViewModel() {
fun updateAccountsList() { fun updateAccountsList() {
val list = arrayListOf<AccountSettingsViewModel>() val list = arrayListOf<AccountSettingsViewModel>()
if (coreContext.core.proxyConfigList.isNotEmpty()) { if (coreContext.core.accountList.isNotEmpty()) {
val defaultProxyConfig = coreContext.core.defaultProxyConfig val defaultAccount = coreContext.core.defaultAccount
if (defaultProxyConfig != null) { if (defaultAccount != null) {
val defaultViewModel = AccountSettingsViewModel(defaultProxyConfig) val defaultViewModel = AccountSettingsViewModel(defaultAccount)
defaultViewModel.accountsSettingsListener = accountClickListener defaultViewModel.accountsSettingsListener = accountClickListener
defaultAccount.value = defaultViewModel defaultAccountViewModel.value = defaultViewModel
defaultAccountFound.value = true defaultAccountFound.value = true
} }
for (proxy in coreContext.core.proxyConfigList) { for (account in coreContext.core.accountList) {
if (proxy != coreContext.core.defaultProxyConfig) { if (account != coreContext.core.defaultAccount) {
val viewModel = AccountSettingsViewModel(proxy) val viewModel = AccountSettingsViewModel(account)
viewModel.accountsSettingsListener = accountClickListener viewModel.accountsSettingsListener = accountClickListener
list.add(viewModel) list.add(viewModel)
} }

View file

@ -58,7 +58,7 @@ class SharedMainViewModel : ViewModel() {
/* Accounts */ /* Accounts */
val proxyConfigRemoved = MutableLiveData<Boolean>() val accountRemoved = MutableLiveData<Boolean>()
/* Call */ /* Call */

View file

@ -35,13 +35,15 @@ open class StatusViewModel : ViewModel() {
val voiceMailCount = MutableLiveData<Int>() val voiceMailCount = MutableLiveData<Int>()
private val listener: CoreListenerStub = object : CoreListenerStub() { private val listener: CoreListenerStub = object : CoreListenerStub() {
override fun onRegistrationStateChanged( override fun onAccountRegistrationStateChanged(
core: Core, core: Core,
proxyConfig: ProxyConfig, account: Account,
state: RegistrationState, state: RegistrationState,
message: String message: String
) { ) {
updateDefaultProxyConfigRegistrationStatus(state) if (account == core.defaultAccount) {
updateDefaultAccountRegistrationStatus(state)
}
} }
override fun onNotifyReceived( override fun onNotifyReceived(
@ -71,11 +73,11 @@ open class StatusViewModel : ViewModel() {
core.addListener(listener) core.addListener(listener)
var state: RegistrationState = RegistrationState.None var state: RegistrationState = RegistrationState.None
val defaultProxyConfig = core.defaultProxyConfig val defaultAccount = core.defaultAccount
if (defaultProxyConfig != null) { if (defaultAccount != null) {
state = defaultProxyConfig.state state = defaultAccount.state
} }
updateDefaultProxyConfigRegistrationStatus(state) updateDefaultAccountRegistrationStatus(state)
} }
override fun onCleared() { override fun onCleared() {
@ -87,7 +89,7 @@ open class StatusViewModel : ViewModel() {
coreContext.core.refreshRegisters() coreContext.core.refreshRegisters()
} }
fun updateDefaultProxyConfigRegistrationStatus(state: RegistrationState) { fun updateDefaultAccountRegistrationStatus(state: RegistrationState) {
registrationStatusText.value = getStatusIconText(state) registrationStatusText.value = getStatusIconText(state)
registrationStatusDrawable.value = getStatusIconResource(state) registrationStatusDrawable.value = getStatusIconResource(state)
} }

View file

@ -133,7 +133,7 @@ class ContactsManager(private val context: Context) {
} }
fun shouldDisplaySipContactsList(): Boolean { fun shouldDisplaySipContactsList(): Boolean {
return coreContext.core.defaultProxyConfig?.identityAddress?.domain == corePreferences.defaultDomain return coreContext.core.defaultAccount?.params?.identityAddress?.domain == corePreferences.defaultDomain
} }
@Synchronized @Synchronized

View file

@ -312,15 +312,15 @@ class CoreContext(val context: Context, coreConfig: Config) {
computeUserAgent() computeUserAgent()
for (lpc in core.proxyConfigList) { for (account in core.accountList) {
if (lpc.identityAddress?.domain == corePreferences.defaultDomain) { if (account.params.identityAddress?.domain == corePreferences.defaultDomain) {
// Ensure conference URI is set on sip.linphone.org proxy configs // Ensure conference URI is set on sip.linphone.org proxy configs
if (lpc.conferenceFactoryUri == null) { if (account.params.conferenceFactoryUri == null) {
lpc.edit() val params = account.params.clone()
val uri = corePreferences.conferenceServerUri val uri = corePreferences.conferenceServerUri
Log.i("[Context] Setting conference factory on proxy config ${lpc.identityAddress?.asString()} to default value: $uri") Log.i("[Context] Setting conference factory on proxy config ${params.identityAddress?.asString()} to default value: $uri")
lpc.conferenceFactoryUri = uri params.conferenceFactoryUri = uri
lpc.done() account.params = params
} }
// Ensure LIME server URL is set if at least one sip.linphone.org proxy // Ensure LIME server URL is set if at least one sip.linphone.org proxy

View file

@ -391,6 +391,9 @@ class CorePreferences constructor(private val context: Context) {
/* Settings */ /* Settings */
val allowDtlsTransport: Boolean
get() = config.getBool("app", "allow_dtls_transport", false)
val showAccountSettings: Boolean val showAccountSettings: Boolean
get() = config.getBool("app", "settings_accounts", true) get() = config.getBool("app", "settings_accounts", true)

View file

@ -56,17 +56,17 @@ class LinphoneUtils {
val core = coreContext.core val core = coreContext.core
return core.limeX3DhAvailable() && core.limeX3DhEnabled() && return core.limeX3DhAvailable() && core.limeX3DhEnabled() &&
core.limeX3DhServerUrl != null && core.limeX3DhServerUrl != null &&
core.defaultProxyConfig?.conferenceFactoryUri != null core.defaultAccount?.params?.conferenceFactoryUri != null
} }
fun isGroupChatAvailable(): Boolean { fun isGroupChatAvailable(): Boolean {
val core = coreContext.core val core = coreContext.core
return core.defaultProxyConfig?.conferenceFactoryUri != null return core.defaultAccount?.params?.conferenceFactoryUri != null
} }
fun createOneToOneChatRoom(participant: Address, isSecured: Boolean = false): ChatRoom? { fun createOneToOneChatRoom(participant: Address, isSecured: Boolean = false): ChatRoom? {
val core: Core = coreContext.core val core: Core = coreContext.core
val defaultProxyConfig = core.defaultProxyConfig val defaultAccount = core.defaultAccount
val params = core.createDefaultChatRoomParams() val params = core.createDefaultChatRoomParams()
params.enableGroup(false) params.enableGroup(false)
@ -79,8 +79,8 @@ class LinphoneUtils {
val participants = arrayOf(participant) val participants = arrayOf(participant)
return core.searchChatRoom(params, defaultProxyConfig?.contact, null, participants) return core.searchChatRoom(params, defaultAccount?.contactAddress, null, participants)
?: core.createChatRoom(params, defaultProxyConfig?.contact, participants) ?: core.createChatRoom(params, defaultAccount?.contactAddress, participants)
} }
fun deleteFilesAttachedToEventLog(eventLog: EventLog) { fun deleteFilesAttachedToEventLog(eventLog: EventLog) {

View file

@ -88,7 +88,7 @@
linphone:subtitle="@{@string/account_settings_password_summary}" linphone:subtitle="@{@string/account_settings_password_summary}"
linphone:listener="@{viewModel.passwordListener}" linphone:listener="@{viewModel.passwordListener}"
linphone:defaultValue="@{viewModel.password}" linphone:defaultValue="@{viewModel.password}"
linphone:inputType="@{InputType.TYPE_TEXT_VARIATION_PASSWORD}"/> linphone:inputType="@{InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD}"/>
<include <include
layout="@layout/settings_widget_text" layout="@layout/settings_widget_text"
@ -224,12 +224,19 @@
<include <include
layout="@layout/settings_widget_text" layout="@layout/settings_widget_text"
linphone:title="@{@string/account_settings_dial_prefix_title}" linphone:title="@{@string/account_settings_prefix_title}"
linphone:subtitle="@{@string/account_settings_dial_prefix_summary}" linphone:subtitle="@{@string/account_settings_prefix_summary}"
linphone:listener="@{viewModel.dialPrefixListener}" linphone:listener="@{viewModel.prefixListener}"
linphone:defaultValue="@{viewModel.dialPrefix.toString()}" linphone:defaultValue="@{viewModel.prefix}"
linphone:inputType="@{InputType.TYPE_CLASS_NUMBER}"/> linphone:inputType="@{InputType.TYPE_CLASS_NUMBER}"/>
<include
layout="@layout/settings_widget_switch"
linphone:title="@{@string/account_settings_apply_prefix_for_calls_title}"
linphone:subtitle="@{@string/account_settings_apply_prefix_for_calls_summary}"
linphone:listener="@{viewModel.dialPrefixListener}"
linphone:checked="@={viewModel.dialPrefix}"/>
<include <include
layout="@layout/settings_widget_switch" layout="@layout/settings_widget_switch"
linphone:title="@{@string/account_settings_escape_plus_title}" linphone:title="@{@string/account_settings_escape_plus_title}"

View file

@ -35,7 +35,7 @@
<RelativeLayout <RelativeLayout
android:id="@+id/main_account" android:id="@+id/main_account"
android:onClick="@{() -> viewModel.defaultAccount.accountsSettingsListener.onAccountClicked(viewModel.defaultAccount.identity)}" android:onClick="@{() -> viewModel.defaultAccountViewModel.accountsSettingsListener.onAccountClicked(viewModel.defaultAccountViewModel.identity)}"
android:enabled="@{viewModel.defaultAccountFound}" android:enabled="@{viewModel.defaultAccountFound}"
android:visibility="@{viewModel.showAccounts ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.showAccounts ? View.VISIBLE : View.GONE}"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -82,7 +82,7 @@
<TextView <TextView
android:visibility="@{viewModel.defaultAccountFound ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.defaultAccountFound ? View.VISIBLE : View.GONE}"
android:text="@{viewModel.defaultAccount.displayName.isEmpty() ? viewModel.defaultAccount.userName : viewModel.defaultAccount.displayName}" android:text="@{viewModel.defaultAccountViewModel.displayName.isEmpty() ? viewModel.defaultAccountViewModel.userName : viewModel.defaultAccountViewModel.displayName}"
android:textColor="?attr/lightToolbarTextColor" android:textColor="?attr/lightToolbarTextColor"
android:textStyle="bold" android:textStyle="bold"
android:textSize="18sp" android:textSize="18sp"
@ -91,7 +91,7 @@
<TextView <TextView
android:visibility="@{viewModel.defaultAccountFound ? View.VISIBLE : View.GONE}" android:visibility="@{viewModel.defaultAccountFound ? View.VISIBLE : View.GONE}"
android:text="@{viewModel.defaultAccount.displayUsernameInsteadOfIdentity ? viewModel.defaultAccount.userName : viewModel.defaultAccount.identity}" android:text="@{viewModel.defaultAccountViewModel.displayUsernameInsteadOfIdentity ? viewModel.defaultAccountViewModel.userName : viewModel.defaultAccountViewModel.identity}"
style="@style/sip_uri_small_font" style="@style/sip_uri_small_font"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -100,8 +100,8 @@
<ImageView <ImageView
android:background="@drawable/led_background" android:background="@drawable/led_background"
android:src="@{viewModel.defaultAccount.iconResource, default=@drawable/led_not_registered}" android:src="@{viewModel.defaultAccountViewModel.iconResource, default=@drawable/led_not_registered}"
android:contentDescription="@{viewModel.defaultAccount.iconContentDescription}" android:contentDescription="@{viewModel.defaultAccountViewModel.iconContentDescription}"
android:layout_width="20dp" android:layout_width="20dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"

View file

@ -541,6 +541,7 @@
<string name="account_settings_transport_title">Transporte</string> <string name="account_settings_transport_title">Transporte</string>
<string name="account_settings_transport_tcp">TCP</string> <string name="account_settings_transport_tcp">TCP</string>
<string name="account_settings_transport_tls">TLS</string> <string name="account_settings_transport_tls">TLS</string>
<string name="account_settings_transport_dtls">DTLS</string>
<string name="account_settings_proxy_title">Proxy SIP</string> <string name="account_settings_proxy_title">Proxy SIP</string>
<string name="account_settings_outbound_proxy_summary">Enruta todas las llamadas a través del proxy SIP de arriba</string> <string name="account_settings_outbound_proxy_summary">Enruta todas las llamadas a través del proxy SIP de arriba</string>
<string name="account_settings_stun_server_title">Servidor STUN / TURN</string> <string name="account_settings_stun_server_title">Servidor STUN / TURN</string>
@ -549,8 +550,8 @@
<string name="account_settings_avpf_rr_interval_title">AVPF de intervalo regular de RTCP</string> <string name="account_settings_avpf_rr_interval_title">AVPF de intervalo regular de RTCP</string>
<string name="account_settings_expires_title">Caduce</string> <string name="account_settings_expires_title">Caduce</string>
<string name="account_settings_expires_summary">en segundos</string> <string name="account_settings_expires_summary">en segundos</string>
<string name="account_settings_dial_prefix_title">Prefijo de tu país</string> <string name="account_settings_prefix_title">Prefijo de tu país</string>
<string name="account_settings_dial_prefix_summary">sin el +</string> <string name="account_settings_prefix_summary">sin el +</string>
<string name="account_settings_escape_plus_title">Reemplazar + por 00</string> <string name="account_settings_escape_plus_title">Reemplazar + por 00</string>
<string name="notification_channel_incoming_call_name">&appName; notificaciones de llamadas entrantes</string> <string name="notification_channel_incoming_call_name">&appName; notificaciones de llamadas entrantes</string>
<string name="notification_channel_chat_name">&appName; notificaciones de mensajes</string> <string name="notification_channel_chat_name">&appName; notificaciones de mensajes</string>

View file

@ -389,6 +389,7 @@
<string name="account_settings_transport_udp">UDP</string> <string name="account_settings_transport_udp">UDP</string>
<string name="account_settings_transport_tcp">TCP</string> <string name="account_settings_transport_tcp">TCP</string>
<string name="account_settings_transport_tls">TLS</string> <string name="account_settings_transport_tls">TLS</string>
<string name="account_settings_transport_dtls">DTLS</string>
<string name="account_settings_proxy_title">Proxy SIP</string> <string name="account_settings_proxy_title">Proxy SIP</string>
<string name="account_settings_outbound_proxy_title">Outbound proxy</string> <string name="account_settings_outbound_proxy_title">Outbound proxy</string>
<string name="account_settings_stun_server_title">Serveur TURN / STUN</string> <string name="account_settings_stun_server_title">Serveur TURN / STUN</string>
@ -397,8 +398,8 @@
<string name="account_settings_avpf_rr_interval_summary">en secondes (entre 1 et 5)</string> <string name="account_settings_avpf_rr_interval_summary">en secondes (entre 1 et 5)</string>
<string name="account_settings_expires_title">Expiration</string> <string name="account_settings_expires_title">Expiration</string>
<string name="account_settings_expires_summary">en secondes</string> <string name="account_settings_expires_summary">en secondes</string>
<string name="account_settings_dial_prefix_summary">(sans le +)</string> <string name="account_settings_prefix_summary">(sans le +)</string>
<string name="account_settings_dial_prefix_title">Préfixe de votre pays</string> <string name="account_settings_prefix_title">Préfixe de votre pays</string>
<string name="account_settings_escape_plus_title">Remplacer + par 00</string> <string name="account_settings_escape_plus_title">Remplacer + par 00</string>
<string name="notification_channel_incoming_call_name">Notifications d\'appels entrants &appName;</string> <string name="notification_channel_incoming_call_name">Notifications d\'appels entrants &appName;</string>
<string name="incoming_call_notification_title">Appel entrant</string> <string name="incoming_call_notification_title">Appel entrant</string>

View file

@ -160,8 +160,8 @@
<string name="account_settings_avpf_rr_interval_summary">以秒为单位1到5之间</string> <string name="account_settings_avpf_rr_interval_summary">以秒为单位1到5之间</string>
<string name="account_settings_expires_title">到期</string> <string name="account_settings_expires_title">到期</string>
<string name="account_settings_expires_summary">以秒为单位</string> <string name="account_settings_expires_summary">以秒为单位</string>
<string name="account_settings_dial_prefix_title">您所在国家的前缀</string> <string name="account_settings_prefix_title">您所在国家的前缀</string>
<string name="account_settings_dial_prefix_summary">不包含 +</string> <string name="account_settings_prefix_summary">不包含 +</string>
<string name="account_settings_escape_plus_title">用00代替+</string> <string name="account_settings_escape_plus_title">用00代替+</string>
<string name="notification_channel_service_name">&appName; 服务通知</string> <string name="notification_channel_service_name">&appName; 服务通知</string>
<string name="notification_channel_chat_name">&appName; 即时通讯通知</string> <string name="notification_channel_chat_name">&appName; 即时通讯通知</string>

View file

@ -421,8 +421,8 @@
<string name="account_settings_avpf_rr_interval_summary">以秒為單位1到5之間</string> <string name="account_settings_avpf_rr_interval_summary">以秒為單位1到5之間</string>
<string name="account_settings_expires_title">到期</string> <string name="account_settings_expires_title">到期</string>
<string name="account_settings_expires_summary">以秒為單位</string> <string name="account_settings_expires_summary">以秒為單位</string>
<string name="account_settings_dial_prefix_title">您所在國家的前綴</string> <string name="account_settings_prefix_title">您所在國家的前綴</string>
<string name="account_settings_dial_prefix_summary">不包含+</string> <string name="account_settings_prefix_summary">不包含+</string>
<string name="account_settings_escape_plus_title">用00代替+</string> <string name="account_settings_escape_plus_title">用00代替+</string>
<string name="notification_channel_incoming_call_name">&appName;來電通知</string> <string name="notification_channel_incoming_call_name">&appName;來電通知</string>
<string name="notification_channel_chat_name">&appName;即時通訊通知</string> <string name="notification_channel_chat_name">&appName;即時通訊通知</string>

View file

@ -528,6 +528,7 @@
<string name="account_settings_transport_udp">UDP</string> <string name="account_settings_transport_udp">UDP</string>
<string name="account_settings_transport_tcp">TCP</string> <string name="account_settings_transport_tcp">TCP</string>
<string name="account_settings_transport_tls">TLS</string> <string name="account_settings_transport_tls">TLS</string>
<string name="account_settings_transport_dtls">DTLS</string>
<string name="account_settings_proxy_title">SIP proxy</string> <string name="account_settings_proxy_title">SIP proxy</string>
<string name="account_settings_proxy_summary"></string> <string name="account_settings_proxy_summary"></string>
<string name="account_settings_outbound_proxy_title">Outbound proxy</string> <string name="account_settings_outbound_proxy_title">Outbound proxy</string>
@ -542,8 +543,10 @@
<string name="account_settings_avpf_rr_interval_summary">in seconds (between 1 and 5)</string> <string name="account_settings_avpf_rr_interval_summary">in seconds (between 1 and 5)</string>
<string name="account_settings_expires_title">Expire</string> <string name="account_settings_expires_title">Expire</string>
<string name="account_settings_expires_summary">in seconds</string> <string name="account_settings_expires_summary">in seconds</string>
<string name="account_settings_dial_prefix_title">Prefix for your country</string> <string name="account_settings_prefix_title">Prefix for your country</string>
<string name="account_settings_dial_prefix_summary">without the +</string> <string name="account_settings_prefix_summary">without the +</string>
<string name="account_settings_apply_prefix_for_calls_title">Apply prefix for outgoing calls and chat</string>
<string name="account_settings_apply_prefix_for_calls_summary">If a number is entered, apply prefix to number</string>
<string name="account_settings_escape_plus_title">Replace + by 00</string> <string name="account_settings_escape_plus_title">Replace + by 00</string>
<string name="account_settings_escape_plus_summary"></string> <string name="account_settings_escape_plus_summary"></string>