Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
migrate update password screen to compose
Browse files Browse the repository at this point in the history
migrate update password screen to compose

migrate update password screen  to compose

migrate update password screen to compose

Migration of update password screen to compose

Migration of update password screen to compose

migrate update update password to compose

migration of Update password screen to compose

Refractor: Migration of Update Password to compose

Refractor: Migration of Update Password to compose

migration of update password to compose

migrate Update password to compose

update Password to compose migration

update password screen from XML to Compose
itsPronay committed Apr 5, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 203cd9c commit 6edc262
Showing 12 changed files with 450 additions and 237 deletions.
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -86,6 +86,10 @@
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize" />

<activity android:name=".ui.update_password.UpdatePasswordActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize" />

<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
android:configChanges="orientation|screenSize"
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import android.os.Bundle
import org.mifos.mobile.R
import org.mifos.mobile.databinding.ActivityEditUserDetailBinding
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.fragments.UpdatePasswordFragment
import org.mifos.mobile.ui.update_password.UpdatePasswordFragment

/*
* Created by saksham on 14/July/2018
Original file line number Diff line number Diff line change
@@ -17,6 +17,8 @@ import org.mifos.mobile.R
import org.mifos.mobile.api.local.PreferencesHelper
import org.mifos.mobile.ui.activities.PassCodeActivity
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.update_password.UpdatePasswordActivity
import org.mifos.mobile.ui.update_password.UpdatePasswordFragment
import org.mifos.mobile.utils.ConfigurationDialogFragmentCompat
import org.mifos.mobile.utils.ConfigurationPreference
import org.mifos.mobile.utils.Constants
@@ -84,6 +86,7 @@ class SettingsFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeLis

override fun onResume() {
super.onResume()
(activity as? BaseActivity)?.showToolbar()
activity?.title = getString(R.string.settings)
preferenceScreen.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -3,8 +3,8 @@ package org.mifos.mobile.ui.registration
import android.os.Bundle
import org.mifos.mobile.R
import org.mifos.mobile.databinding.ActivityRegistrationBinding
import org.mifos.mobile.databinding.ActivityUserProfileBinding
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.registration.RegistrationFragment
import org.mifos.mobile.utils.MaterialDialog

class RegistrationActivity : BaseActivity() {
@@ -27,5 +27,4 @@ class RegistrationActivity : BaseActivity() {
.createMaterialDialog()
.show()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.mifos.mobile.ui.update_password

import android.os.Bundle
import org.mifos.mobile.R
import org.mifos.mobile.databinding.ActivityContainerBinding
import org.mifos.mobile.ui.activities.base.BaseActivity

class UpdatePasswordActivity : BaseActivity() {
private lateinit var binding: ActivityContainerBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityContainerBinding.inflate(layoutInflater)
setContentView(binding.root)
replaceFragment(UpdatePasswordFragment.newInstance(), false, R.id.container)
}

override fun onBackPressed() {
super.onBackPressed()
if (fragmentManager?.backStackEntryCount!! > 0) {
fragmentManager?.popBackStack()
} else {
super.onBackPressed()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package org.mifos.mobile.ui.update_password

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.mifos.mobile.R
import org.mifos.mobile.core.ui.theme.MifosMobileTheme
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.fragments.SettingsFragment
import org.mifos.mobile.ui.fragments.base.BaseFragment
import org.mifos.mobile.utils.Network
import org.mifos.mobile.utils.RegistrationUiState
import org.mifos.mobile.utils.Toaster

/*
* Created by saksham on 13/July/2018
*/
@AndroidEntryPoint
class UpdatePasswordFragment : BaseFragment() {

private lateinit var viewModel: UpdatePasswordViewModel

private lateinit var newPasswordContent: String
private lateinit var confirmPasswordContent: String

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
viewModel = ViewModelProvider(this)[UpdatePasswordViewModel::class.java]
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MifosMobileTheme {
UpdatePasswordScreen(
changePassword = { newPassword, confirmPassword ->
newPasswordContent = newPassword
confirmPasswordContent = confirmPassword
updatePassword()
},
getNewPasswordError = { newPassword ->
newPasswordContent = newPassword
showNewPasswordError()
},
getConfirmPasswordError = { confirmPassword ->
confirmPasswordContent = confirmPassword
showConfirmPasswordError()
},
getBackToPreviousScreen = {
navigateBack()
}
)
}
}
}
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.updatePasswordUiState.collect { state ->
when (state) {
RegistrationUiState.Loading -> showProgress()

RegistrationUiState.Success -> {
hideProgress()
showPasswordUpdatedSuccessfully()
}

is RegistrationUiState.Error -> {
hideProgress()
showError(getString(state.exception))
}

RegistrationUiState.Initial -> {}
}
}
}
}
}

private fun updatePassword() {
val newPassword = newPasswordContent
val confirmPassword = confirmPasswordContent

if (Network.isConnected(activity)) {
if (passwordMatches()) {
viewModel.updateAccountPassword(newPassword, confirmPassword)
} else {
showError(getString(R.string.error_password_not_match))
}
} else {
Toaster.show(view, getString(R.string.no_internet_connection))
}
}

private fun passwordMatches(): Boolean {
return viewModel.validatePasswordMatch(newPasswordContent, confirmPasswordContent)
}

private fun showNewPasswordError(): String {
var passwordError = ""
when {
viewModel.isInputFieldEmpty(newPasswordContent) -> {
passwordError = getString(
R.string.error_validation_blank,
getString(R.string.new_password),
)
}

viewModel.isInputLengthInadequate(newPasswordContent) -> {
passwordError = getString(
R.string.error_validation_minimum_chars,
getString(R.string.new_password),
resources.getInteger(R.integer.password_minimum_length),
)
}
}
return passwordError
}

private fun showConfirmPasswordError(): String {
var passwordError = ""
when {
viewModel.isInputFieldEmpty(confirmPasswordContent) -> {
passwordError = getString(
R.string.error_validation_blank,
getString(R.string.confirm_password),
)
}

viewModel.isInputLengthInadequate(confirmPasswordContent) -> {
passwordError = getString(
R.string.error_validation_minimum_chars,
getString(R.string.confirm_password),
resources.getInteger(R.integer.password_minimum_length),
)
}
}
return passwordError
}

fun showError(message: String?) {
var errorMessage = message
Toaster.show(view, errorMessage)
}

private fun showPasswordUpdatedSuccessfully() {
Toast.makeText(
context,
getString(
R.string.string_changed_successfully,
getString(R.string.password),
),
Toast.LENGTH_SHORT,
).show()
(activity as BaseActivity).clearFragmentBackStack()
(activity as BaseActivity).replaceFragment(
SettingsFragment.newInstance(),
true,
R.id.container,
)
}

private fun navigateBack() {
/*
If we navigate to update password from user profile there is nothing in backStackEntry
But when we migrate from settings to update password there is one backStackEntry
Since, we need a code to work for both we used this condition here.
*/
if (fragmentManager?.backStackEntryCount!! > 0) {
fragmentManager?.popBackStack()
} else {
activity?.finish()
}
}

fun showProgress() {
showMifosProgressDialog(getString(R.string.progress_message_loading))
}

fun hideProgress() {
hideMifosProgressDialog()
}

override fun onResume() {
super.onResume()
(activity as? BaseActivity)?.hideToolbar()
}

companion object {
fun newInstance(): UpdatePasswordFragment {
return UpdatePasswordFragment()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package org.mifos.mobile.ui.update_password

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.R
import org.mifos.mobile.core.ui.component.MifosOutlinedTextField
import org.mifos.mobile.core.ui.component.MifosTopBar

@Composable
fun UpdatePasswordScreen(
changePassword: (newPassword: String, confirmPassword: String) -> Unit,
getNewPasswordError: (newPassword: String) -> String,
getConfirmPasswordError: (confirmPassword: String) -> String,
getBackToPreviousScreen: () -> Unit
) {
var newPassword by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(
TextFieldValue("")
)
}
var confirmPassword by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(
TextFieldValue("")
)
}
var viewNewPassword by rememberSaveable {
mutableStateOf(false)
}
var viewConfirmPassword by rememberSaveable {
mutableStateOf(false)
}

var newPasswordError by rememberSaveable { mutableStateOf(false) }
var confirmPasswordError by rememberSaveable { mutableStateOf(false) }

var newPasswordErrorContent by rememberSaveable {
mutableStateOf("")
}
var confirmPasswordErrorContent by rememberSaveable {
mutableStateOf("")
}

Column(
modifier = Modifier
.fillMaxSize()
) {
MifosTopBar(
title = {
Text(text = stringResource(id = R.string.change_password))
},
navigateBack = getBackToPreviousScreen
)

MifosOutlinedTextField(
value = newPassword,
onValueChange = {
newPassword = it
newPasswordError = false
},
label = R.string.new_password,
supportingText = newPasswordErrorContent,
icon = R.drawable.ic_lock_black_24dp,
trailingIcon = {
var image = if (viewNewPassword) Icons.Filled.Visibility
else Icons.Filled.VisibilityOff

if (!newPasswordError) {
IconButton(onClick = { viewNewPassword = !viewNewPassword }) {
Icon(
imageVector = image,
contentDescription = "password visibility button"
)
}
} else {
Icon(imageVector = Icons.Filled.Error, contentDescription = null)
}
},
error = newPasswordError,
visualTransformation = if (viewNewPassword) VisualTransformation.None else PasswordVisualTransformation(),
keyboardType = KeyboardType.Password
)

Spacer(modifier = Modifier.height(8.dp))

MifosOutlinedTextField(
value = confirmPassword,
onValueChange = {
confirmPassword = it
confirmPasswordError = false
},
label = R.string.confirm_password,
supportingText = confirmPasswordErrorContent,
icon = R.drawable.ic_lock_black_24dp,
trailingIcon = {
var image = if (viewConfirmPassword) Icons.Filled.Visibility
else Icons.Filled.VisibilityOff

if (!confirmPasswordError) {
IconButton(onClick = { viewConfirmPassword = !viewConfirmPassword }) {
Icon(imageVector = image, contentDescription = "password visibility Button")
}
} else {
Icon(imageVector = Icons.Filled.Error, contentDescription = null)
}
},
error = confirmPasswordError,
visualTransformation = if (viewConfirmPassword) VisualTransformation.None else PasswordVisualTransformation(),
keyboardType = KeyboardType.Password

)

Spacer(modifier = Modifier.height(8.dp))

Button(
onClick = {
newPasswordErrorContent = getNewPasswordError.invoke(newPassword.text)
confirmPasswordErrorContent = getConfirmPasswordError.invoke(confirmPassword.text)

when {
newPasswordErrorContent.isEmpty() && confirmPasswordErrorContent.isEmpty() -> {
changePassword.invoke(
newPassword.text,
confirmPassword.text
)
}

newPasswordErrorContent.isEmpty() && confirmPasswordErrorContent.isNotEmpty() -> {
confirmPasswordError = true
}

newPasswordErrorContent.isNotEmpty() && confirmPasswordErrorContent.isEmpty() -> {
newPasswordError = true
}

else -> {
confirmPasswordError = true
newPasswordError = true
}
}
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 4.dp),
contentPadding = PaddingValues(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = if (isSystemInDarkTheme()) Color(
0xFF9bb1e3
) else Color(0xFF325ca8)
)
) {
Text(text = stringResource(id = R.string.change_password))
}

}
}


@Composable
@Preview(showBackground = true)
fun PreviewUpdatePasswordScreen() {
UpdatePasswordScreen(
changePassword = { newPassword, confirmPassword -> },
getNewPasswordError = { "" },
getConfirmPasswordError = { "" },
getBackToPreviousScreen = { }
)
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package org.mifos.mobile.viewModels
package org.mifos.mobile.ui.update_password

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.Observer
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -12,6 +13,7 @@ import org.mifos.mobile.repositories.ClientRepository
import org.mifos.mobile.repositories.UserAuthRepository
import org.mifos.mobile.utils.RegistrationUiState
import javax.inject.Inject
import retrofit2.Response

@HiltViewModel
class UpdatePasswordViewModel @Inject constructor(
Original file line number Diff line number Diff line change
@@ -20,10 +20,10 @@ import org.mifos.mobile.api.local.PreferencesHelper
import org.mifos.mobile.core.ui.theme.MifosMobileTheme
import org.mifos.mobile.models.client.Client
import org.mifos.mobile.models.client.Group
import org.mifos.mobile.ui.activities.EditUserDetailActivity
import org.mifos.mobile.ui.activities.base.BaseActivity
import org.mifos.mobile.ui.fragments.base.BaseFragment
import org.mifos.mobile.ui.getThemeAttributeColor
import org.mifos.mobile.ui.update_password.UpdatePasswordActivity
import org.mifos.mobile.utils.Constants
import org.mifos.mobile.utils.DateHelper
import org.mifos.mobile.utils.Network
@@ -230,7 +230,7 @@ class UserProfileFragment : BaseFragment() {
}

private fun changePassword() {
startActivity(Intent(context, EditUserDetailActivity::class.java))
startActivity(Intent(activity, UpdatePasswordActivity::class.java))
}

private fun backToHome() {
2 changes: 2 additions & 0 deletions app/src/main/java/org/mifos/mobile/utils/MaterialDialog.kt
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.mifos.mobile.R
import org.mifos.mobile.ui.registration.RegistrationActivity
import org.mifos.mobile.ui.update_password.UpdatePasswordActivity

/**
* This Class is the Material Dialog Builder Class
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ package org.mifos.mobile.viewModels

import CoroutineTestRule
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer
import app.cash.turbine.test
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -13,13 +12,13 @@ import org.junit.*
import org.junit.runner.RunWith
import org.mifos.mobile.repositories.ClientRepository
import org.mifos.mobile.repositories.UserAuthRepository
import org.mifos.mobile.ui.update_password.UpdatePasswordViewModel
import org.mifos.mobile.util.RxSchedulersOverrideRule
import org.mifos.mobile.utils.RegistrationUiState
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.junit.MockitoJUnitRunner
import retrofit2.Response

@RunWith(MockitoJUnitRunner::class)
class UpdatePasswordViewModelTest {

0 comments on commit 6edc262

Please sign in to comment.