From a707c6c988ce8a9e477c82b2fe5b13e74340d08d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Mon, 26 Feb 2024 11:55:32 +0100 Subject: [PATCH] Added auth requested dialog --- .../java/org/linphone/ui/main/MainActivity.kt | 28 ++++ .../main/fragment/AuthRequestedDialogModel.kt | 53 ++++++++ .../ui/main/viewmodel/MainViewModel.kt | 47 +++++++ .../java/org/linphone/utils/DialogUtils.kt | 18 +++ .../layout/dialog_update_account_password.xml | 128 ++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 6 files changed, 277 insertions(+) create mode 100644 app/src/main/java/org/linphone/ui/main/fragment/AuthRequestedDialogModel.kt create mode 100644 app/src/main/res/layout/dialog_update_account_password.xml 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