diff --git a/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt b/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt new file mode 100644 index 0000000..83d21fa --- /dev/null +++ b/app/src/main/java/com/isolaatti/auth/domain/SignOutUC.kt @@ -0,0 +1,17 @@ +package com.isolaatti.auth.domain + +import android.content.Context +import com.isolaatti.auth.data.local.TokenStorage +import com.isolaatti.settings.domain.AccountSettingsRepository +import dagger.hilt.android.qualifiers.ActivityContext +import javax.inject.Inject + +class SignOutUC @Inject constructor( + @ActivityContext private val context: Context, + private val tokenStorage: TokenStorage, + private val accountSettingsRepository: AccountSettingsRepository +) { + operator fun invoke() { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/settings/presentation/ChangePasswordViewModel.kt b/app/src/main/java/com/isolaatti/settings/presentation/ChangePasswordViewModel.kt index 9c14d29..0b32f2b 100644 --- a/app/src/main/java/com/isolaatti/settings/presentation/ChangePasswordViewModel.kt +++ b/app/src/main/java/com/isolaatti/settings/presentation/ChangePasswordViewModel.kt @@ -1,6 +1,60 @@ package com.isolaatti.settings.presentation +import android.util.Log +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.isolaatti.settings.data.remote.ChangePasswordResponseDto +import com.isolaatti.settings.domain.AccountSettingsRepository +import com.isolaatti.utils.Resource +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import javax.inject.Inject -class ChangePasswordViewModel : ViewModel() { +@HiltViewModel +class ChangePasswordViewModel @Inject constructor(private val accountSettingsRepository: AccountSettingsRepository) : ViewModel() { + + var oldPassword = "" + var newPassword = "" + set(value) { + field = value + validatePassword(value) + } + var signOut = false + var signOutCurrent = true + + + val newPasswordIsValid: MutableLiveData = MutableLiveData() + + private val passwordRules = listOf( + "[a-z]", + "[A-Z]", + "[0-9]", + "[!@#\$%^&*\\(\\)_\\+\\-\\={}<>,\\.\\|\"\"'~`:;\\\\?\\/\\[\\]]" + ) + private fun validatePassword(newPassword: String) { + var count = 0 + for(exp in passwordRules) { + val matches = exp.toRegex().containsMatchIn(newPassword) + Log.d("ChangePasswordViewModel", "$exp matches: $matches") + if(matches) { + count++ + } + } + newPasswordIsValid.value = count > 3 && newPassword.length >= 8 + } + + val passwordChangeResource: MutableLiveData> = MutableLiveData() + + fun changePassword() { + viewModelScope.launch { + accountSettingsRepository.changePassword(oldPassword, newPassword, signOut, signOutCurrent).onEach { + passwordChangeResource.postValue(it) + }.flowOn(Dispatchers.IO).launchIn(this) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt b/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt index fe56080..80abbd3 100644 --- a/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt +++ b/app/src/main/java/com/isolaatti/settings/ui/ChangePasswordFragment.kt @@ -4,14 +4,22 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog import androidx.core.widget.doOnTextChanged import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.isolaatti.R +import com.isolaatti.auth.domain.SignOutUC import com.isolaatti.databinding.FragmentSettingsChangePasswordBinding import com.isolaatti.settings.presentation.ChangePasswordViewModel +import com.isolaatti.utils.Resource +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject -class ChangePasswordFragment : Fragment() { +@AndroidEntryPoint +class ChangePasswordFragment @Inject constructor(private val signOutUC: SignOutUC) : Fragment() { lateinit var viewBinding: FragmentSettingsChangePasswordBinding private val viewModel: ChangePasswordViewModel by viewModels() @@ -38,6 +46,7 @@ class ChangePasswordFragment : Fragment() { super.onViewCreated(view, savedInstanceState) setupListeners() + setupObservers() viewBinding.continueCurrentPassword.isEnabled = !viewBinding.currentPasswordTextInput.editText?.text.isNullOrBlank() } @@ -50,16 +59,76 @@ class ChangePasswordFragment : Fragment() { viewBinding.prevNewPasswordButton.setOnClickListener(clickListener) viewBinding.currentPasswordTextInput.editText?.doOnTextChanged { text, start, before, count -> + viewModel.oldPassword = text.toString() viewBinding.continueCurrentPassword.isEnabled = !text.isNullOrBlank() } viewBinding.newPasswordInputText.editText?.doOnTextChanged { text, start, before, count -> - + viewModel.newPassword = text.toString() } - viewBinding.newPasswordInputTextConfirm.editText?.doOnTextChanged { text, start, before, count -> + viewBinding.signOutAll.setOnCheckedChangeListener { buttonView, isChecked -> + viewBinding.signOutCurrent.isEnabled = isChecked + viewModel.signOutCurrent = isChecked + if(!isChecked){ + viewBinding.signOutCurrent.isChecked = false + } + } + viewBinding.signOutCurrent.setOnCheckedChangeListener { buttonView, isChecked -> + viewModel.signOutCurrent = isChecked + } + + viewBinding.continueNewPasswordButton.setOnClickListener { + viewModel.changePassword() } } + private fun lockControls(lock: Boolean) { + viewBinding.prevNewPasswordButton.isEnabled = !lock + viewBinding.continueNewPasswordButton.isEnabled = !lock + viewBinding.newPasswordInputText.isEnabled = !lock + } + + private var loadingDialog: AlertDialog? = null + private fun showLoadingDialog() { + loadingDialog = MaterialAlertDialogBuilder(requireContext()).setMessage(R.string.changing_password).setCancelable(false).show() + } + + private fun signOut() { + signOutUC() + } + + private fun setupObservers() { + viewModel.newPasswordIsValid.observe(viewLifecycleOwner) { valid -> + viewBinding.continueNewPasswordButton.isEnabled = valid + + viewBinding.newPasswordInputText.error = if(valid) null else getString(R.string.password_req) + } + + viewModel.passwordChangeResource.observe(viewLifecycleOwner) { + when(it) { + is Resource.Loading -> { + lockControls(true) + showLoadingDialog() + } + is Resource.Error -> { + lockControls(false) + loadingDialog?.dismiss() + loadingDialog = null + } + + is Resource.Success -> { + loadingDialog?.dismiss() + loadingDialog = null + if(viewModel.signOutCurrent) { + signOut() + } else { + + findNavController().popBackStack() + } + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings_change_password.xml b/app/src/main/res/layout/fragment_settings_change_password.xml index 1dde3c5..8d9b9ff 100644 --- a/app/src/main/res/layout/fragment_settings_change_password.xml +++ b/app/src/main/res/layout/fragment_settings_change_password.xml @@ -108,45 +108,51 @@ android:inputType="textPassword" /> - - - - - + app:layout_constraintTop_toBottomOf="@+id/sign_out_current" /> + + + + Delete sessions The selected sessions will become invalid, sign out those devices. Current + Sign out all sessions + Previous + Sign out this device + Continue + Password must contain at least 8 characters an have the following: 1-9, a-z, A-Z, special character. + Changing password... \ No newline at end of file