From 40af691386b449e478c0841439e9939129fb6576 Mon Sep 17 00:00:00 2001
From: Taras Smakula <tarassmakula@gmail.com>
Date: Thu, 21 Mar 2024 14:45:56 +0200
Subject: [PATCH] Implement select password on login

---
 .../uia/stages/password/PasswordDataSource.kt |  2 +-
 .../uia/stages/password/PasswordFragment.kt   | 10 ++++--
 .../uia/stages/password/PasswordViewModel.kt  | 36 ++++++++++++++++++-
 .../src/main/res/layout/fragment_password.xml |  1 +
 .../futo/circles/core/view/LoadingButton.kt   |  6 ++--
 5 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt
index 9f78db94d..2e8adc523 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordDataSource.kt
@@ -55,7 +55,7 @@ class PasswordDataSource @Inject constructor(
             is Response.Error -> result
         }
     }
-    
+
     private suspend fun processDirectPasswordStage(password: String): Response<Unit> {
         val result = createResult {
             MatrixInstanceProvider.matrix.authenticationService().getLoginWizard().login(
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
index d446f5aba..33884332d 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordFragment.kt
@@ -16,6 +16,7 @@ import org.futo.circles.auth.feature.uia.stages.password.confirmation.SetupPassw
 import org.futo.circles.core.base.fragment.HasLoadingState
 import org.futo.circles.core.base.fragment.ParentBackPressOwnerFragment
 import org.futo.circles.core.extensions.getText
+import org.futo.circles.core.extensions.observeData
 import org.futo.circles.core.extensions.observeResponse
 import org.futo.circles.core.extensions.setIsVisible
 import org.futo.circles.core.extensions.showError
@@ -33,6 +34,7 @@ class PasswordFragment : ParentBackPressOwnerFragment(R.layout.fragment_password
         super.onViewCreated(view, savedInstanceState)
         setupViews()
         setupObservers()
+        if (!isSignupMode()) viewModel.getCredentials(requireContext())
     }
 
     override fun onResume() {
@@ -73,14 +75,18 @@ class PasswordFragment : ParentBackPressOwnerFragment(R.layout.fragment_password
     private fun setupObservers() {
         viewModel.passwordResponseLiveData.observeResponse(this,
             error = { showError(getString(R.string.invalid_password)) })
+        viewModel.passwordSelectedEventLiveData.observeData(this) {
+            startLoading(binding.btnLogin)
+            binding.etPassword.setText(it)
+        }
     }
 
     private fun onPasswordDataChanged() {
         val password = binding.tilPassword.getText()
         val repeat = binding.tilRepeatPassword.getText()
         binding.btnLogin.isEnabled = if (isSignupMode()) {
-            binding.vPasswordStrength.isPasswordStrong() && password == repeat && password.isNotEmpty()
-        } else password.isNotEmpty()
+            binding.vPasswordStrength.isPasswordStrong() && password == repeat && password.isNotEmpty() && !binding.btnLogin.isLoading
+        } else password.isNotEmpty() && !binding.btnLogin.isLoading
     }
 
     private fun showPasswordWarningIfNeeded() {
diff --git a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
index 015471921..febaa25ce 100644
--- a/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
+++ b/auth/src/main/java/org/futo/circles/auth/feature/uia/stages/password/PasswordViewModel.kt
@@ -1,10 +1,17 @@
 package org.futo.circles.auth.feature.uia.stages.password
 
+import android.content.Context
+import androidx.credentials.CredentialManager
+import androidx.credentials.GetCredentialRequest
+import androidx.credentials.GetPasswordOption
+import androidx.credentials.PasswordCredential
 import androidx.lifecycle.ViewModel
 import dagger.hilt.android.lifecycle.HiltViewModel
+import org.futo.circles.auth.feature.uia.UIADataSourceProvider
 import org.futo.circles.core.base.SingleEventLiveData
 import org.futo.circles.core.extensions.Response
 import org.futo.circles.core.extensions.launchBg
+import org.matrix.android.sdk.api.extensions.tryOrNull
 import javax.inject.Inject
 
 @HiltViewModel
@@ -14,13 +21,40 @@ class PasswordViewModel @Inject constructor(
 
     private var isPasswordWarningConfirmed: Boolean = false
     val passwordResponseLiveData = SingleEventLiveData<Response<Unit>>()
+    val passwordSelectedEventLiveData = SingleEventLiveData<String>()
 
     fun processPasswordStage(password: String) {
+        launchBg { handlePasswordRequest(password) }
+    }
+
+    fun getCredentials(activityContext: Context) {
         launchBg {
-            passwordResponseLiveData.postValue(passwordDataSource.processPasswordStage(password))
+            tryOrNull {
+                val credentialManager = CredentialManager.create(activityContext)
+                val userName = UIADataSourceProvider.getDataSourceOrThrow().userName
+                val request = GetCredentialRequest(
+                    listOf(GetPasswordOption(allowedUserIds = setOf(userName)))
+                )
+
+                val result = credentialManager.getCredential(
+                    context = activityContext,
+                    request = request
+                ).credential
+
+                if (result is PasswordCredential) {
+                    val password = result.password
+                    passwordSelectedEventLiveData.postValue(password)
+                    handlePasswordRequest(password)
+                }
+            }
         }
     }
 
+    private suspend fun handlePasswordRequest(password: String) {
+        val result = passwordDataSource.processPasswordStage(password)
+        passwordResponseLiveData.postValue(result)
+    }
+
     fun isPasswordWarningConfirmed() = isPasswordWarningConfirmed
     fun confirmPasswordWarning() {
         isPasswordWarningConfirmed = true
diff --git a/auth/src/main/res/layout/fragment_password.xml b/auth/src/main/res/layout/fragment_password.xml
index 638421e67..4b3e6e6c8 100644
--- a/auth/src/main/res/layout/fragment_password.xml
+++ b/auth/src/main/res/layout/fragment_password.xml
@@ -61,6 +61,7 @@
                 android:autofillHints="password"
                 android:imeOptions="actionDone"
                 android:inputType="textPassword"
+                android:isCredential="true"
                 android:padding="8dp" />
 
             <org.futo.circles.auth.view.PasswordStrengthView
diff --git a/core/src/main/java/org/futo/circles/core/view/LoadingButton.kt b/core/src/main/java/org/futo/circles/core/view/LoadingButton.kt
index 1ceec3f8b..0a9be1355 100644
--- a/core/src/main/java/org/futo/circles/core/view/LoadingButton.kt
+++ b/core/src/main/java/org/futo/circles/core/view/LoadingButton.kt
@@ -7,7 +7,6 @@ import android.util.TypedValue
 import android.view.LayoutInflater
 import android.view.View
 import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.doOnLayout
 import org.futo.circles.core.R
 import org.futo.circles.core.databinding.ViewLoadingButtonBinding
 import org.futo.circles.core.extensions.getAttributes
@@ -31,7 +30,8 @@ class LoadingButton(
         ViewLoadingButtonBinding.inflate(LayoutInflater.from(context), this)
 
     private var buttonText: String = ""
-    private var isLoading: Boolean = false
+    var isLoading: Boolean = false
+        private set
 
     init {
         getAttributes(attrs, R.styleable.LoadingButton) {
@@ -60,7 +60,7 @@ class LoadingButton(
     override fun onRestoreInstanceState(state: Parcelable?) {
         val loadingButtonState = state as? LoadingButtonState
         super.onRestoreInstanceState(loadingButtonState?.superSavedState ?: state)
-        doOnLayout {
+        post {
             setText(loadingButtonState?.text ?: "")
             setIsLoading(loadingButtonState?.isLoading ?: false)
             isEnabled = loadingButtonState?.isEnabled ?: true
-- 
GitLab