diff --git a/app/src/main/java/org/linphone/ui/main/MainActivity.kt b/app/src/main/java/org/linphone/ui/main/MainActivity.kt
index e704c1bb6..659e668f0 100644
--- a/app/src/main/java/org/linphone/ui/main/MainActivity.kt
+++ b/app/src/main/java/org/linphone/ui/main/MainActivity.kt
@@ -55,9 +55,11 @@ import org.linphone.databinding.MainActivityBinding
import org.linphone.ui.GenericActivity
import org.linphone.ui.assistant.AssistantActivity
import org.linphone.ui.main.chat.fragment.ConversationsListFragmentDirections
+import org.linphone.ui.main.fragment.AuthRequestedDialogModel
import org.linphone.ui.main.viewmodel.MainViewModel
import org.linphone.ui.main.viewmodel.SharedMainViewModel
import org.linphone.ui.welcome.WelcomeActivity
+import org.linphone.utils.DialogUtils
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.LinphoneUtils
@@ -164,6 +166,12 @@ class MainActivity : GenericActivity() {
}
}
+ viewModel.authenticationRequestedEvent.observe(this) {
+ it.consume { identity ->
+ showAuthenticationRequestedDialog(identity)
+ }
+ }
+
binding.root.doOnAttach {
Log.i("$TAG Report UI has been fully drawn (TTFD)")
try {
@@ -615,4 +623,24 @@ class MainActivity : GenericActivity() {
}
}*/
}
+
+ private fun showAuthenticationRequestedDialog(identity: String) {
+ val model = AuthRequestedDialogModel(identity)
+ val dialog = DialogUtils.getAuthRequestedDialog(this, model)
+
+ model.dismissEvent.observe(this) {
+ it.consume {
+ dialog.dismiss()
+ }
+ }
+
+ model.confirmEvent.observe(this) {
+ it.consume { password ->
+ viewModel.updateAuthInfo(password)
+ dialog.dismiss()
+ }
+ }
+
+ dialog.show()
+ }
}
diff --git a/app/src/main/java/org/linphone/ui/main/fragment/AuthRequestedDialogModel.kt b/app/src/main/java/org/linphone/ui/main/fragment/AuthRequestedDialogModel.kt
new file mode 100644
index 000000000..276ae57b4
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/main/fragment/AuthRequestedDialogModel.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-android
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.linphone.ui.main.fragment
+
+import androidx.annotation.UiThread
+import androidx.lifecycle.MutableLiveData
+import org.linphone.R
+import org.linphone.utils.AppUtils
+import org.linphone.utils.Event
+
+class AuthRequestedDialogModel @UiThread constructor(identity: String) {
+ val message = MutableLiveData()
+
+ val password = MutableLiveData()
+
+ val dismissEvent = MutableLiveData>()
+
+ val confirmEvent = MutableLiveData>()
+
+ init {
+ message.value = AppUtils.getFormattedString(
+ R.string.dialog_account_invalid_password_message,
+ identity
+ )
+ }
+
+ @UiThread
+ fun dismiss() {
+ dismissEvent.value = Event(true)
+ }
+
+ @UiThread
+ fun confirm() {
+ confirmEvent.value = Event(password.value.orEmpty())
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/MainViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/MainViewModel.kt
index b7f09f1b3..95a5b7d25 100644
--- a/app/src/main/java/org/linphone/ui/main/viewmodel/MainViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/viewmodel/MainViewModel.kt
@@ -33,6 +33,8 @@ import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R
import org.linphone.core.Account
+import org.linphone.core.AuthInfo
+import org.linphone.core.AuthMethod
import org.linphone.core.Call
import org.linphone.core.Core
import org.linphone.core.CoreListenerStub
@@ -88,6 +90,10 @@ class MainViewModel @UiThread constructor() : ViewModel() {
MutableLiveData>()
}
+ val authenticationRequestedEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
+ }
+
var accountsFound = -1
var mainIntentHandled = false
@@ -100,6 +106,8 @@ class MainViewModel @UiThread constructor() : ViewModel() {
private var firstAccountRegistered: Boolean = false
+ private var authInfoPendingPasswordUpdate: AuthInfo? = null
+
private val coreListener = object : CoreListenerStub() {
@WorkerThread
override fun onLastCallEnded(core: Core) {
@@ -108,6 +116,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
atLeastOneCall.postValue(false)
}
+ @WorkerThread
override fun onFirstCallStarted(core: Core) {
Log.i("$TAG First call started, adding in-call 'alert'")
updateCallAlert()
@@ -225,6 +234,7 @@ class MainViewModel @UiThread constructor() : ViewModel() {
// TODO: compute other calls notifications count
}
+ @WorkerThread
override fun onAccountRemoved(core: Core, account: Account) {
accountsFound -= 1
@@ -235,6 +245,27 @@ class MainViewModel @UiThread constructor() : ViewModel() {
core.defaultAccount = core.accountList.firstOrNull()
}
}
+
+ @WorkerThread
+ override fun onAuthenticationRequested(core: Core, authInfo: AuthInfo, method: AuthMethod) {
+ if (authInfo.username == null || authInfo.domain == null || authInfo.realm == null) {
+ return
+ }
+
+ Log.w(
+ "$TAG Authentication requested for account [${authInfo.username}@${authInfo.domain}] with realm [${authInfo.realm}] using method [$method]"
+ )
+ val accountFound = core.accountList.find {
+ it.params.identityAddress?.username == authInfo.username && it.params.identityAddress?.domain == authInfo.domain
+ }
+ if (accountFound == null) {
+ Log.w("$TAG Failed to find account matching auth info, aborting auth dialog")
+ return
+ }
+ val identity = "${authInfo.username}@${authInfo.domain}"
+ authInfoPendingPasswordUpdate = authInfo
+ authenticationRequestedEvent.postValue(Event(identity))
+ }
}
init {
@@ -308,6 +339,22 @@ class MainViewModel @UiThread constructor() : ViewModel() {
}
}
+ @UiThread
+ fun updateAuthInfo(password: String) {
+ coreContext.postOnCoreThread { core ->
+ val authInfo = authInfoPendingPasswordUpdate
+ if (authInfo != null) {
+ Log.i(
+ "$TAG Updating password for username [${authInfo.username}] using auth info [$authInfo]"
+ )
+ authInfo.password = password
+ core.addAuthInfo(authInfo)
+ authInfoPendingPasswordUpdate = null
+ core.refreshRegisters()
+ }
+ }
+ }
+
@WorkerThread
private fun updateCallAlert() {
val core = coreContext.core
diff --git a/app/src/main/java/org/linphone/utils/DialogUtils.kt b/app/src/main/java/org/linphone/utils/DialogUtils.kt
index 72608c1a7..7a99d7f6c 100644
--- a/app/src/main/java/org/linphone/utils/DialogUtils.kt
+++ b/app/src/main/java/org/linphone/utils/DialogUtils.kt
@@ -45,12 +45,14 @@ import org.linphone.databinding.DialogRemoveAccountBinding
import org.linphone.databinding.DialogRemoveAllCallLogsBinding
import org.linphone.databinding.DialogRemoveCallLogsBinding
import org.linphone.databinding.DialogSetOrEditGroupSubjectBindingImpl
+import org.linphone.databinding.DialogUpdateAccountPasswordBinding
import org.linphone.databinding.DialogUpdateAvailableBinding
import org.linphone.ui.assistant.model.AcceptConditionsAndPolicyDialogModel
import org.linphone.ui.assistant.model.ConfirmPhoneNumberDialogModel
import org.linphone.ui.call.model.ZrtpSasConfirmationDialogModel
import org.linphone.ui.main.contacts.model.NumberOrAddressPickerDialogModel
import org.linphone.ui.main.contacts.model.TrustCallDialogModel
+import org.linphone.ui.main.fragment.AuthRequestedDialogModel
import org.linphone.ui.main.fragment.GroupSetOrEditSubjectDialogModel
import org.linphone.ui.main.history.model.ConfirmationDialogModel
@@ -301,6 +303,22 @@ class DialogUtils {
return getDialog(context, binding)
}
+ @UiThread
+ fun getAuthRequestedDialog(
+ context: Context,
+ viewModel: AuthRequestedDialogModel
+ ): Dialog {
+ val binding: DialogUpdateAccountPasswordBinding = DataBindingUtil.inflate(
+ LayoutInflater.from(context),
+ R.layout.dialog_update_account_password,
+ null,
+ false
+ )
+ binding.viewModel = viewModel
+
+ return getDialog(context, binding)
+ }
+
@UiThread
fun getZrtpSasConfirmationDialog(
context: Context,
diff --git a/app/src/main/res/layout/dialog_update_account_password.xml b/app/src/main/res/layout/dialog_update_account_password.xml
new file mode 100644
index 000000000..08644bd4b
--- /dev/null
+++ b/app/src/main/res/layout/dialog_update_account_password.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b984c22ab..7597434e3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -127,6 +127,9 @@
Confirm
Set group call subject
Group call subject
+ Authentication needed
+ Connection failed because authentication is missing or invalid for account \n%s.\n\nYou can provide password again, or check your account configuration in the settings.
+ New password
Invalid QR code!
SIP address copied into clipboard