mirror of
https://github.com/openMF/mobile-wallet.git
synced 2026-02-06 14:16:54 +00:00
refactor: Redesign payment screen (#1773)
* refactor: Redesign payment screen * resolved detekt error * refactor : changed current theme instead of using NewUi * resolved spotless errors
This commit is contained in:
parent
8bd08c248e
commit
da4106fd95
@ -5,9 +5,11 @@ import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
internal fun Project.configureDetekt(extension: DetektExtension) = extension.apply {
|
||||
tasks.named<Detekt>("detekt") {
|
||||
jvmTarget = "17"
|
||||
reports {
|
||||
xml.required.set(true)
|
||||
html.required.set(true)
|
||||
|
||||
@ -12,6 +12,7 @@ package org.mifospay.core.designsystem.component
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedIconButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -21,7 +22,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mifospay.core.designsystem.icon.MifosIcons
|
||||
import org.mifospay.core.designsystem.theme.MifosTheme
|
||||
import org.mifospay.core.designsystem.theme.NewUi
|
||||
|
||||
@Composable
|
||||
fun IconBox(
|
||||
@ -33,7 +33,7 @@ fun IconBox(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
border = BorderStroke(2.dp, NewUi.onSurface.copy(alpha = 0.1f)),
|
||||
border = BorderStroke(2.dp, MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
|
||||
@ -12,6 +12,7 @@ package org.mifospay.core.designsystem.component
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -43,6 +44,7 @@ fun MifosScaffold(
|
||||
onClick = content.onClick,
|
||||
contentColor = content.contentColor,
|
||||
content = content.content,
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@ -9,12 +9,17 @@
|
||||
*/
|
||||
package org.mifospay.core.designsystem.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun MifosTab(
|
||||
@ -22,18 +27,25 @@ fun MifosTab(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
selectedContentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
unselectedContentColor: Color = Color.LightGray,
|
||||
selectedContentColor: Color = MaterialTheme.colorScheme.primary,
|
||||
unselectedContentColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
) {
|
||||
Tab(
|
||||
text = {
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
color = if (selected) {
|
||||
MaterialTheme.colorScheme.onPrimary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
},
|
||||
)
|
||||
},
|
||||
selected = selected,
|
||||
modifier = modifier,
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(25.dp))
|
||||
.background(if (selected) selectedContentColor else unselectedContentColor)
|
||||
.padding(horizontal = 20.dp),
|
||||
selectedContentColor = selectedContentColor,
|
||||
unselectedContentColor = unselectedContentColor,
|
||||
onClick = onClick,
|
||||
|
||||
@ -14,6 +14,7 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@ -57,7 +58,6 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import org.mifospay.core.designsystem.theme.MifosTheme
|
||||
import org.mifospay.core.designsystem.theme.NewUi
|
||||
|
||||
@Composable
|
||||
fun MfOutlinedTextField(
|
||||
@ -84,18 +84,15 @@ fun MfOutlinedTextField(
|
||||
},
|
||||
singleLine = singleLine,
|
||||
trailingIcon = trailingIcon,
|
||||
keyboardActions =
|
||||
KeyboardActions {
|
||||
keyboardActions = KeyboardActions {
|
||||
onKeyboardActions?.invoke()
|
||||
},
|
||||
keyboardOptions = keyboardOptions,
|
||||
colors =
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedLabelColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
textStyle =
|
||||
LocalDensity.current.run {
|
||||
textStyle = LocalDensity.current.run {
|
||||
TextStyle(fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface)
|
||||
},
|
||||
)
|
||||
@ -118,8 +115,7 @@ fun MfPasswordTextField(
|
||||
onValueChange = onPasswordChange,
|
||||
label = { Text(label) },
|
||||
isError = isError,
|
||||
visualTransformation =
|
||||
if (isPasswordVisible) {
|
||||
visualTransformation = if (isPasswordVisible) {
|
||||
VisualTransformation.None
|
||||
} else {
|
||||
PasswordVisualTransformation()
|
||||
@ -157,14 +153,12 @@ fun MifosOutlinedTextField(
|
||||
onValueChange = onValueChange,
|
||||
label = { Text(stringResource(id = label)) },
|
||||
modifier = modifier,
|
||||
leadingIcon =
|
||||
if (icon != null) {
|
||||
leadingIcon = if (icon != null) {
|
||||
{
|
||||
Image(
|
||||
painter = painterResource(id = icon),
|
||||
contentDescription = null,
|
||||
colorFilter =
|
||||
ColorFilter.tint(
|
||||
colorFilter = ColorFilter.tint(
|
||||
MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
)
|
||||
@ -175,13 +169,11 @@ fun MifosOutlinedTextField(
|
||||
trailingIcon = trailingIcon,
|
||||
maxLines = maxLines,
|
||||
singleLine = singleLine,
|
||||
colors =
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = MaterialTheme.colorScheme.onSurface,
|
||||
focusedLabelColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
textStyle =
|
||||
LocalDensity.current.run {
|
||||
textStyle = LocalDensity.current.run {
|
||||
TextStyle(fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface)
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
@ -209,6 +201,9 @@ fun MifosTextField(
|
||||
minLines: Int = 1,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
|
||||
trailingIcon: @Composable (() -> Unit)? = null,
|
||||
leadingIcon: @Composable (() -> Unit)? = null,
|
||||
indicatorColor: Color? = null,
|
||||
) {
|
||||
var isFocused by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@ -232,31 +227,56 @@ fun MifosTextField(
|
||||
singleLine = singleLine,
|
||||
maxLines = maxLines,
|
||||
minLines = minLines,
|
||||
cursorBrush = SolidColor(NewUi.primaryColor),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
|
||||
decorationBox = { innerTextField ->
|
||||
Column {
|
||||
Text(
|
||||
text = label,
|
||||
color = NewUi.primaryColor,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
modifier = Modifier.align(alignment = Alignment.Start),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
|
||||
innerTextField()
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
if (leadingIcon != null) {
|
||||
leadingIcon()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = if (isFocused) {
|
||||
NewUi.secondaryColor
|
||||
} else {
|
||||
NewUi.onSurface.copy(alpha = 0.05f)
|
||||
},
|
||||
)
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
innerTextField()
|
||||
}
|
||||
|
||||
if (trailingIcon != null) {
|
||||
trailingIcon()
|
||||
}
|
||||
}
|
||||
indicatorColor?.let { color ->
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = if (isFocused) {
|
||||
color
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f)
|
||||
},
|
||||
)
|
||||
} ?: run {
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = if (isFocused) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -11,9 +11,9 @@ package org.mifospay.core.designsystem.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val md_theme_light_primary = Color(0xFF000000)
|
||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_primaryContainer = Color(0xFFFFD9E2)
|
||||
val md_theme_light_primary = Color(0xFF0673BA) // primary
|
||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF) // gradientOne
|
||||
val md_theme_light_primaryContainer = Color(0xFFF5F5F5) // container color
|
||||
val md_theme_light_onPrimaryContainer = Color(0xFF3E001D)
|
||||
val md_theme_light_secondary = Color(0xFF984061)
|
||||
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
|
||||
@ -30,7 +30,7 @@ val md_theme_light_onErrorContainer = Color(0xFF410002)
|
||||
val md_theme_light_background = Color(0xFFFFFBFF)
|
||||
val md_theme_light_onBackground = Color(0xFF330045)
|
||||
val md_theme_light_surface = Color(0xFFFFFBFF)
|
||||
val md_theme_light_onSurface = Color(0xFF000000)
|
||||
val md_theme_light_onSurface = Color(0xFF333333) // onSurface
|
||||
val md_theme_light_surfaceVariant = Color(0xFFF2DDE1)
|
||||
val md_theme_light_onSurfaceVariant = Color(0xFF514347)
|
||||
val md_theme_light_outline = Color(0xFF837377)
|
||||
|
||||
@ -29,9 +29,9 @@ fun MifosScrollableTabRow(
|
||||
tabContents: List<TabContent>,
|
||||
pagerState: PagerState,
|
||||
modifier: Modifier = Modifier,
|
||||
containerColor: Color = MaterialTheme.colorScheme.surface,
|
||||
selectedContentColor: Color = MaterialTheme.colorScheme.onSurface,
|
||||
unselectedContentColor: Color = Color.LightGray,
|
||||
containerColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
selectedContentColor: Color = MaterialTheme.colorScheme.primary,
|
||||
unselectedContentColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
edgePadding: Dp = 8.dp,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
@ -41,6 +41,8 @@ fun MifosScrollableTabRow(
|
||||
containerColor = containerColor,
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
edgePadding = edgePadding,
|
||||
indicator = {},
|
||||
divider = {},
|
||||
) {
|
||||
tabContents.forEachIndexed { index, currentTab ->
|
||||
MifosTab(
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
package org.mifospay.feature.history
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -21,6 +22,8 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -46,7 +49,6 @@ import org.mifospay.core.designsystem.component.MifosBottomSheet
|
||||
import org.mifospay.core.designsystem.component.MifosButton
|
||||
import org.mifospay.core.designsystem.component.MifosLoadingWheel
|
||||
import org.mifospay.core.designsystem.icon.MifosIcons
|
||||
import org.mifospay.core.designsystem.theme.lightGrey
|
||||
import org.mifospay.core.ui.EmptyContentScreen
|
||||
import org.mifospay.core.ui.TransactionItemScreen
|
||||
import org.mifospay.feature.transaction.detail.TransactionDetailScreen
|
||||
@ -141,6 +143,8 @@ private fun HistoryScreen(
|
||||
modifier = Modifier
|
||||
.clickable { transactionDetailState = it },
|
||||
)
|
||||
HorizontalDivider(thickness = 0.5.dp, modifier = Modifier.padding(5.dp))
|
||||
Spacer(modifier = Modifier.height(15.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,9 +182,19 @@ private fun Chip(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val backgroundColor = if (selected) MaterialTheme.colorScheme.primary else lightGrey
|
||||
val backgroundColor = MaterialTheme.colorScheme.onPrimary
|
||||
MifosButton(
|
||||
modifier = modifier,
|
||||
modifier = modifier.then(
|
||||
if (selected) {
|
||||
Modifier.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(25.dp),
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
onClick = {
|
||||
onClick()
|
||||
Toast.makeText(context, label, Toast.LENGTH_SHORT).show()
|
||||
@ -190,7 +204,7 @@ private fun Chip(
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp, end = 16.dp),
|
||||
text = label,
|
||||
color = if (selected) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,11 +10,13 @@
|
||||
package org.mifospay.feature.payments
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -66,56 +68,45 @@ internal fun RequestScreenContent(
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
|
||||
Row(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
.padding(start = 20.dp, top = 20.dp, end = 15.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 20.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
Column {
|
||||
Text(text = stringResource(id = R.string.feature_payments_virtual_payment_address_vpa))
|
||||
Text(
|
||||
text = vpa,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(top = 10.dp)) {
|
||||
Text(text = stringResource(id = R.string.feature_payments_mobile_number))
|
||||
Text(
|
||||
text = mobile,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.feature_payments_virtual_payment_address_vpa))
|
||||
Text(
|
||||
text = vpa,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
IconButton(
|
||||
onClick = { showQr(vpa) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = MifosIcons.QrCode,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
contentDescription = stringResource(id = R.string.feature_payments_show_code),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy
|
||||
(alpha = 0.05f),
|
||||
)
|
||||
Column(modifier = Modifier.padding(top = 10.dp)) {
|
||||
Text(text = stringResource(id = R.string.feature_payments_mobile_number))
|
||||
Text(
|
||||
text = stringResource(id = R.string.feature_payments_show_code),
|
||||
text = mobile,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
-->
|
||||
<resources>
|
||||
<string name="feature_payments_virtual_payment_address_vpa">Virtual Payment Address (VPA)</string>
|
||||
<string name="feature_payments_mobile_number">Mobile Number</string>
|
||||
<string name="feature_payments_mobile_number">Phone Number</string>
|
||||
<string name="feature_payments_receive">Receive</string>
|
||||
<string name="feature_payments_show_code">Show code</string>
|
||||
|
||||
|
||||
@ -26,7 +26,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mifospay.core.designsystem.theme.NewUi
|
||||
|
||||
@Composable
|
||||
fun ProfileDetailsCard(
|
||||
@ -45,7 +44,7 @@ fun ProfileDetailsCard(
|
||||
),
|
||||
shape = RoundedCornerShape(15.dp),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = NewUi.containerColor,
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
@ -79,7 +78,7 @@ fun ProfileItem(
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
color = NewUi.primaryColor,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
@ -92,7 +91,7 @@ fun ProfileItem(
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = NewUi.onSurface.copy(alpha = 0.05f),
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.05f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import android.provider.ContactsContract
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
@ -22,13 +23,15 @@ import androidx.compose.foundation.layout.Row
|
||||
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.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AttachMoney
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -42,7 +45,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -53,15 +55,16 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.google.mlkit.vision.barcode.common.Barcode
|
||||
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
|
||||
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
|
||||
import com.mifos.library.countrycodepicker.CountryCodePicker
|
||||
import com.mifos.library.countrycodepicker.CountryCodePickerPayment
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.mifospay.core.designsystem.component.MfOutlinedTextField
|
||||
import org.mifospay.core.designsystem.component.MfOverlayLoadingWheel
|
||||
import org.mifospay.core.designsystem.component.MifosButton
|
||||
import org.mifospay.core.designsystem.component.MifosNavigationTopAppBar
|
||||
import org.mifospay.core.designsystem.component.MifosTextField
|
||||
import org.mifospay.core.designsystem.icon.MifosIcons
|
||||
import org.mifospay.core.designsystem.theme.MifosBlue
|
||||
import org.mifospay.core.designsystem.theme.styleMedium16sp
|
||||
import org.mifospay.core.designsystem.theme.styleNormal18sp
|
||||
|
||||
@ -182,7 +185,11 @@ internal fun SendMoneyScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Box(modifier) {
|
||||
Box(
|
||||
modifier
|
||||
.padding(top = 5.dp)
|
||||
.imePadding(),
|
||||
) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
if (showToolBar) {
|
||||
MifosNavigationTopAppBar(
|
||||
@ -190,113 +197,125 @@ internal fun SendMoneyScreen(
|
||||
onNavigationClick = onBackClick,
|
||||
)
|
||||
}
|
||||
|
||||
MifosTextField(
|
||||
value = amount,
|
||||
label = stringResource(id = R.string.feature_send_money_amount),
|
||||
onValueChange = {
|
||||
amount = it
|
||||
validateInfo()
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = Icons.Default.AttachMoney,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
)
|
||||
},
|
||||
indicatorColor = MifosBlue,
|
||||
)
|
||||
|
||||
when (sendMethodType) {
|
||||
SendMethodType.VPA -> {
|
||||
MifosTextField(
|
||||
value = vpa,
|
||||
label = stringResource(id = R.string.feature_send_money_virtual_payment_address),
|
||||
onValueChange = {
|
||||
vpa = it
|
||||
validateInfo()
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
trailingIcon = {
|
||||
IconButton(
|
||||
onClick = { startScan() },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = MifosIcons.QrCode2,
|
||||
contentDescription = "Scan QR",
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
},
|
||||
indicatorColor = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
|
||||
SendMethodType.MOBILE -> {
|
||||
EnterPhoneScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
initialPhoneNumber = mobileNumber,
|
||||
onNumberUpdated = { _, fullPhone, valid ->
|
||||
if (valid) {
|
||||
mobileNumber = fullPhone
|
||||
}
|
||||
isValidMobileNumber = valid
|
||||
validateInfo()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 20.dp, top = 20.dp),
|
||||
text = stringResource(id = R.string.feature_send_money_select_transfer_method),
|
||||
style = styleNormal18sp,
|
||||
)
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 20.dp, bottom = 20.dp),
|
||||
) {
|
||||
VpaMobileChip(
|
||||
label = stringResource(id = R.string.feature_send_money_vpa),
|
||||
selected = sendMethodType == SendMethodType.VPA,
|
||||
onClick = { sendMethodType = SendMethodType.VPA },
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
VpaMobileChip(
|
||||
label = stringResource(id = R.string.feature_send_money_mobile),
|
||||
selected = sendMethodType == SendMethodType.MOBILE,
|
||||
onClick = { sendMethodType = SendMethodType.MOBILE },
|
||||
)
|
||||
}
|
||||
MfOutlinedTextField(
|
||||
value = amount,
|
||||
label = stringResource(id = R.string.feature_send_money_amount),
|
||||
onValueChange = {
|
||||
amount = it
|
||||
validateInfo()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number),
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
) {
|
||||
VpaMobileChip(
|
||||
label = stringResource(id = R.string.feature_send_money_vpa),
|
||||
selected = sendMethodType == SendMethodType.VPA,
|
||||
onClick = { sendMethodType = SendMethodType.VPA },
|
||||
)
|
||||
when (sendMethodType) {
|
||||
SendMethodType.VPA -> {
|
||||
MfOutlinedTextField(
|
||||
value = vpa,
|
||||
label = stringResource(id = R.string.feature_send_money_virtual_payment_address),
|
||||
onValueChange = {
|
||||
vpa = it
|
||||
validateInfo()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
trailingIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
startScan()
|
||||
},
|
||||
) {
|
||||
Icon(
|
||||
imageVector = MifosIcons.QrCode2,
|
||||
contentDescription = "Scan QR",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
VpaMobileChip(
|
||||
label = stringResource(id = R.string.feature_send_money_mobile),
|
||||
selected = sendMethodType == SendMethodType.MOBILE,
|
||||
onClick = { sendMethodType = SendMethodType.MOBILE },
|
||||
)
|
||||
}
|
||||
|
||||
SendMethodType.MOBILE -> {
|
||||
EnterPhoneScreen(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 8.dp),
|
||||
initialPhoneNumber = mobileNumber,
|
||||
onNumberUpdated = { _, fullPhone, valid ->
|
||||
if (valid) {
|
||||
mobileNumber = fullPhone
|
||||
}
|
||||
isValidMobileNumber = valid
|
||||
validateInfo()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
MifosButton(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
enabled = isValidInfo,
|
||||
onClick = {
|
||||
if (!isValidInfo) return@MifosButton
|
||||
onSubmit(
|
||||
amount,
|
||||
when (sendMethodType) {
|
||||
SendMethodType.VPA -> vpa
|
||||
SendMethodType.MOBILE -> mobileNumber
|
||||
},
|
||||
sendMethodType,
|
||||
)
|
||||
// TODO: Navigate to MakeTransferScreenRoute
|
||||
},
|
||||
contentPadding = PaddingValues(12.dp),
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = R.string.feature_send_money_submit),
|
||||
style = styleMedium16sp.copy(color = MaterialTheme.colorScheme.surface),
|
||||
MifosButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
enabled = isValidInfo,
|
||||
onClick = {
|
||||
if (!isValidInfo) return@MifosButton
|
||||
onSubmit(
|
||||
amount,
|
||||
when (sendMethodType) {
|
||||
SendMethodType.VPA -> vpa
|
||||
SendMethodType.MOBILE -> mobileNumber
|
||||
},
|
||||
sendMethodType,
|
||||
)
|
||||
}
|
||||
// TODO: Navigate to MakeTransferScreenRoute
|
||||
},
|
||||
contentPadding = PaddingValues(18.dp),
|
||||
) {
|
||||
Text(
|
||||
stringResource(id = R.string.feature_send_money_submit),
|
||||
style = styleMedium16sp.copy(color = MaterialTheme.colorScheme.surface),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,9 +334,8 @@ private fun EnterPhoneScreen(
|
||||
initialPhoneNumber: String? = null,
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
CountryCodePicker(
|
||||
CountryCodePickerPayment(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
focusedBorderColor = MaterialTheme.colorScheme.primary,
|
||||
),
|
||||
@ -325,8 +343,15 @@ private fun EnterPhoneScreen(
|
||||
onValueChange = { (code, phone), isValid ->
|
||||
onNumberUpdated(phone, code + phone, isValid)
|
||||
},
|
||||
label = { Text(stringResource(id = R.string.feature_send_money_phone_number)) },
|
||||
label = {
|
||||
Text(
|
||||
stringResource(id = R.string.feature_send_money_phone_number),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
},
|
||||
keyboardActions = KeyboardActions { keyboardController?.hide() },
|
||||
indicatorColor = MaterialTheme.colorScheme.primary,
|
||||
errorIndicatorColor = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
|
||||
@ -339,14 +364,26 @@ private fun VpaMobileChip(
|
||||
) {
|
||||
MifosButton(
|
||||
onClick = onClick,
|
||||
color = if (selected) MaterialTheme.colorScheme.primary else Color.LightGray,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier =
|
||||
modifier
|
||||
.wrapContentSize()
|
||||
.padding(4.dp)
|
||||
.wrapContentSize(),
|
||||
.then(
|
||||
if (selected) {
|
||||
Modifier.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(25.dp),
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
},
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 4.dp, bottom = 4.dp),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
text = label,
|
||||
)
|
||||
}
|
||||
|
||||
@ -13,12 +13,12 @@
|
||||
<string name="feature_send_money_error_fetching_balance">Error fetching balance</string>
|
||||
<string name="feature_send_money_not_allowed">Self Account transfer is not allowed</string>
|
||||
<string name="feature_send_money_send">Send</string>
|
||||
<string name="feature_send_money_select_transfer_method">Select transfer method</string>
|
||||
<string name="feature_send_money_select_transfer_method">Select Method</string>
|
||||
<string name="feature_send_money_vpa">VPA</string>
|
||||
<string name="feature_send_money_mobile">Mobile number</string>
|
||||
<string name="feature_send_money_amount">Amount</string>
|
||||
<string name="feature_send_money_amount">Enter your Amount</string>
|
||||
<string name="feature_send_money_virtual_payment_address">Virtual Payment Address</string>
|
||||
<string name="feature_send_money_submit">Submit</string>
|
||||
<string name="feature_send_money_submit">Proceed</string>
|
||||
<string name="feature_send_money_please_wait">Please wait…</string>
|
||||
<string name="feature_send_money_phone_number">Phone Number</string>
|
||||
<string name="feature_send_money_phone_number">Enter Mobile Number</string>
|
||||
</resources>
|
||||
@ -57,7 +57,7 @@ internal fun StandingInstructionScreen(
|
||||
) {
|
||||
val floatingActionButtonContent = FloatingActionButtonContent(
|
||||
onClick = onNewSI,
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
content = {
|
||||
Icon(
|
||||
imageVector = MifosIcons.Add,
|
||||
@ -70,8 +70,7 @@ internal fun StandingInstructionScreen(
|
||||
backPress = onBackPress,
|
||||
floatingActionButtonContent = floatingActionButtonContent,
|
||||
modifier = modifier,
|
||||
scaffoldContent = {
|
||||
},
|
||||
scaffoldContent = {},
|
||||
) {
|
||||
when (standingInstructionsUiState) {
|
||||
StandingInstructionsUiState.Empty -> {
|
||||
|
||||
@ -15,12 +15,19 @@ import androidx.compose.foundation.focusable
|
||||
import androidx.compose.foundation.interaction.InteractionSource
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
@ -37,6 +44,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.autofill.AutofillType
|
||||
@ -279,6 +287,246 @@ fun CountryCodePicker(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalComposeUiApi::class)
|
||||
@Suppress("LongMethod", "LongParameterList", "ComplexMethod")
|
||||
@Composable
|
||||
fun CountryCodePickerPayment(
|
||||
onValueChange: (Pair<PhoneCode, String>, Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
autoDetectCode: Boolean = false,
|
||||
enabled: Boolean = true,
|
||||
showCountryCode: Boolean = true,
|
||||
showCountryFlag: Boolean = true,
|
||||
colors: TextFieldColors = OutlinedTextFieldDefaults.colors(),
|
||||
fallbackCountry: CountryData = CountryData.UnitedStates,
|
||||
showPlaceholder: Boolean = true,
|
||||
includeOnly: ImmutableSet<String>? = null,
|
||||
clearIcon: ImageVector? = Icons.Filled.Clear,
|
||||
initialPhoneNumber: String? = null,
|
||||
initialCountryIsoCode: Iso31661alpha2? = null,
|
||||
initialCountryPhoneCode: PhoneCode? = null,
|
||||
label: @Composable (() -> Unit)? = null,
|
||||
textStyle: TextStyle = LocalTextStyle.current,
|
||||
keyboardOptions: KeyboardOptions? = null,
|
||||
keyboardActions: KeyboardActions? = null,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
showError: Boolean = true,
|
||||
indicatorColor: Color? = null,
|
||||
errorIndicatorColor: Color? = null,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
val countryCode = autoDetectedCountryCode(
|
||||
autoDetectCode = autoDetectCode,
|
||||
initialPhoneNumber = initialPhoneNumber,
|
||||
)
|
||||
|
||||
val phoneNumberWithoutCode = if (countryCode != null) {
|
||||
initialPhoneNumber?.replace(countryCode, "")
|
||||
} else {
|
||||
initialPhoneNumber
|
||||
}
|
||||
|
||||
var phoneNumber by remember {
|
||||
mutableStateOf(
|
||||
TextFieldValue(
|
||||
text = phoneNumberWithoutCode.orEmpty(),
|
||||
selection = TextRange(phoneNumberWithoutCode?.length ?: 0),
|
||||
),
|
||||
)
|
||||
}
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
var country: CountryData by rememberSaveable(
|
||||
context,
|
||||
countryCode,
|
||||
initialCountryPhoneCode,
|
||||
initialCountryIsoCode,
|
||||
) {
|
||||
mutableStateOf(
|
||||
configureInitialCountry(
|
||||
initialCountryPhoneCode = countryCode ?: initialCountryPhoneCode,
|
||||
context = context,
|
||||
initialCountryIsoCode = initialCountryIsoCode,
|
||||
fallbackCountry = fallbackCountry,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val phoneNumberTransformation = remember(country) {
|
||||
PhoneNumberTransformation(country.countryIso, context)
|
||||
}
|
||||
val validatePhoneNumber = remember(context) { ValidatePhoneNumber(context) }
|
||||
|
||||
var isNumberValid: Boolean by rememberSaveable(country, phoneNumber) {
|
||||
mutableStateOf(
|
||||
validatePhoneNumber(
|
||||
fullPhoneNumber = country.countryPhoneCode + phoneNumber.text,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
BasicTextField(
|
||||
value = phoneNumber,
|
||||
onValueChange = { enteredPhoneNumber ->
|
||||
val preFilteredPhoneNumber = phoneNumberTransformation.preFilter(enteredPhoneNumber)
|
||||
phoneNumber = TextFieldValue(
|
||||
text = preFilteredPhoneNumber,
|
||||
selection = TextRange(preFilteredPhoneNumber.length),
|
||||
)
|
||||
isNumberValid = validatePhoneNumber(
|
||||
fullPhoneNumber = country.countryPhoneCode + phoneNumber.text,
|
||||
)
|
||||
onValueChange(country.countryPhoneCode to phoneNumber.text, isNumberValid)
|
||||
},
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.focusable()
|
||||
.autofill(
|
||||
autofillTypes = listOf(AutofillType.PhoneNumberNational),
|
||||
onFill = { filledPhoneNumber ->
|
||||
val preFilteredPhoneNumber =
|
||||
phoneNumberTransformation.preFilter(filledPhoneNumber)
|
||||
phoneNumber = TextFieldValue(
|
||||
text = preFilteredPhoneNumber,
|
||||
selection = TextRange(preFilteredPhoneNumber.length),
|
||||
)
|
||||
isNumberValid = validatePhoneNumber(
|
||||
fullPhoneNumber = country.countryPhoneCode + phoneNumber.text,
|
||||
)
|
||||
onValueChange(country.countryPhoneCode to phoneNumber.text, isNumberValid)
|
||||
keyboardController?.hide()
|
||||
coroutineScope.launch {
|
||||
focusRequester.safeFreeFocus()
|
||||
}
|
||||
},
|
||||
focusRequester = focusRequester,
|
||||
)
|
||||
.focusRequester(focusRequester = focusRequester),
|
||||
enabled = enabled,
|
||||
textStyle = textStyle,
|
||||
decorationBox = { innerTextField ->
|
||||
Column {
|
||||
if (label != null) {
|
||||
label()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
CountryCodeDialogWrapper(
|
||||
country = country,
|
||||
onCountryChange = { countryData ->
|
||||
country = countryData
|
||||
isNumberValid = validatePhoneNumber(
|
||||
fullPhoneNumber = country.countryPhoneCode + phoneNumber.text,
|
||||
)
|
||||
onValueChange(
|
||||
country.countryPhoneCode to phoneNumber.text,
|
||||
isNumberValid,
|
||||
)
|
||||
},
|
||||
includeOnly = includeOnly,
|
||||
showCountryCode = showCountryCode,
|
||||
showFlag = showCountryFlag,
|
||||
textStyle = textStyle,
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
innerTextField()
|
||||
if (showPlaceholder && phoneNumber.text.isEmpty()) {
|
||||
PlaceholderNumberHint(country.countryIso)
|
||||
}
|
||||
}
|
||||
if (clearIcon != null) {
|
||||
ClearIconButtonWrapper(
|
||||
clearIcon = clearIcon,
|
||||
colors = colors,
|
||||
isNumberValid = !showError || isNumberValid,
|
||||
onClearClick = {
|
||||
phoneNumber = TextFieldValue("")
|
||||
isNumberValid = false
|
||||
onValueChange(
|
||||
country.countryPhoneCode to phoneNumber.text,
|
||||
isNumberValid,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showError && !isNumberValid) {
|
||||
errorIndicatorColor?.let {
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = it,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
indicatorColor?.let {
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
interactionSource = interactionSource,
|
||||
visualTransformation = phoneNumberTransformation,
|
||||
keyboardOptions = keyboardOptions ?: KeyboardOptions.Default.copy(
|
||||
keyboardType = KeyboardType.Phone,
|
||||
autoCorrect = true,
|
||||
imeAction = ImeAction.Done,
|
||||
),
|
||||
keyboardActions = keyboardActions ?: KeyboardActions(
|
||||
onDone = {
|
||||
keyboardController?.hide()
|
||||
coroutineScope.launch {
|
||||
focusRequester.safeFreeFocus()
|
||||
}
|
||||
},
|
||||
),
|
||||
singleLine = true,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CountryCodeDialogWrapper(
|
||||
country: CountryData,
|
||||
onCountryChange: (CountryData) -> Unit,
|
||||
includeOnly: ImmutableSet<String>?,
|
||||
showCountryCode: Boolean,
|
||||
showFlag: Boolean,
|
||||
textStyle: TextStyle,
|
||||
) {
|
||||
CountryCodeDialog(
|
||||
selectedCountry = country,
|
||||
includeOnly = includeOnly,
|
||||
onCountryChange = onCountryChange,
|
||||
showCountryCode = showCountryCode,
|
||||
showFlag = showFlag,
|
||||
textStyle = textStyle,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ClearIconButtonWrapper(
|
||||
clearIcon: ImageVector,
|
||||
colors: TextFieldColors,
|
||||
isNumberValid: Boolean,
|
||||
onClearClick: () -> Unit,
|
||||
) {
|
||||
ClearIconButton(
|
||||
imageVector = clearIcon,
|
||||
colors = colors,
|
||||
isNumberValid = isNumberValid,
|
||||
onClick = onClearClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun configureInitialCountry(
|
||||
initialCountryPhoneCode: PhoneCode?,
|
||||
context: Context,
|
||||
|
||||
@ -53,6 +53,9 @@ class PhoneNumberTransformation(countryCode: String, context: Context) : VisualT
|
||||
|
||||
@Suppress("AvoidMutableCollections", "AvoidVarsExceptWithDelegate")
|
||||
private fun reformat(s: CharSequence, cursor: Int): Transformation {
|
||||
if (s.isEmpty()) {
|
||||
return Transformation("", listOf(0), listOf(0))
|
||||
}
|
||||
phoneNumberFormatter.clear()
|
||||
|
||||
val curIndex = cursor - 1
|
||||
@ -79,17 +82,24 @@ class PhoneNumberTransformation(countryCode: String, context: Context) : VisualT
|
||||
val originalToTransformed = mutableListOf<Int>()
|
||||
val transformedToOriginal = mutableListOf<Int>()
|
||||
var specialCharsCount = 0
|
||||
formatted?.forEachIndexed { index, char ->
|
||||
if (!PhoneNumberUtils.isNonSeparator(char)) {
|
||||
specialCharsCount++
|
||||
} else {
|
||||
originalToTransformed.add(index)
|
||||
if (formatted != null) {
|
||||
formatted?.forEachIndexed { index, char ->
|
||||
if (!PhoneNumberUtils.isNonSeparator(char)) {
|
||||
specialCharsCount++
|
||||
} else {
|
||||
originalToTransformed.add(index)
|
||||
}
|
||||
transformedToOriginal.add(index - specialCharsCount)
|
||||
}
|
||||
transformedToOriginal.add(index - specialCharsCount)
|
||||
originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
|
||||
transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)
|
||||
} else {
|
||||
originalToTransformed.add(0)
|
||||
transformedToOriginal.add(0)
|
||||
}
|
||||
if (transformedToOriginal.any { it < 0 }) {
|
||||
transformedToOriginal.replaceAll { if (it < 0) 0 else it }
|
||||
}
|
||||
originalToTransformed.add(originalToTransformed.maxOrNull()?.plus(1) ?: 0)
|
||||
transformedToOriginal.add(transformedToOriginal.maxOrNull()?.plus(1) ?: 0)
|
||||
|
||||
return Transformation(formatted, originalToTransformed, transformedToOriginal)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package: name='org.mifospay' versionCode='1' versionName='0.0.4-beta.0.6' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14'
|
||||
package: name='org.mifospay' versionCode='1' versionName='0.0.1-beta.0.836' platformBuildVersionName='14' platformBuildVersionCode='34' compileSdkVersion='34' compileSdkVersionCodename='14'
|
||||
sdkVersion:'26'
|
||||
targetSdkVersion:'34'
|
||||
uses-permission: name='android.permission.INTERNET'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user