NTP-5471 | Abha aadhar fix (#13683)

Co-authored-by: Shivam Goyal <shivam.goyal@navi.com>
This commit is contained in:
Balrambhai Sharma
2024-11-20 19:46:23 +05:30
committed by GitHub
parent 70111615d7
commit 7a060f3187
7 changed files with 228 additions and 54 deletions

View File

@@ -0,0 +1,57 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.base.utils
class AadhaarUtils {
private val d =
arrayOf(
intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
intArrayOf(1, 2, 3, 4, 0, 6, 7, 8, 9, 5),
intArrayOf(2, 3, 4, 0, 1, 7, 8, 9, 5, 6),
intArrayOf(3, 4, 0, 1, 2, 8, 9, 5, 6, 7),
intArrayOf(4, 0, 1, 2, 3, 9, 5, 6, 7, 8),
intArrayOf(5, 9, 8, 7, 6, 0, 4, 3, 2, 1),
intArrayOf(6, 5, 9, 8, 7, 1, 0, 4, 3, 2),
intArrayOf(7, 6, 5, 9, 8, 2, 1, 0, 4, 3),
intArrayOf(8, 7, 6, 5, 9, 3, 2, 1, 0, 4),
intArrayOf(9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
)
private val p =
arrayOf(
intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
intArrayOf(1, 5, 7, 6, 2, 8, 3, 0, 9, 4),
intArrayOf(5, 8, 0, 3, 7, 9, 6, 1, 4, 2),
intArrayOf(8, 9, 1, 6, 0, 4, 3, 5, 2, 7),
intArrayOf(9, 4, 5, 3, 1, 2, 6, 8, 7, 0),
intArrayOf(4, 2, 8, 6, 5, 7, 3, 9, 0, 1),
intArrayOf(2, 7, 9, 3, 8, 0, 6, 4, 1, 5),
intArrayOf(7, 0, 4, 6, 9, 1, 3, 2, 5, 8)
)
private val inv = intArrayOf(0, 4, 3, 2, 1, 5, 6, 7, 8, 9)
fun validateVerhoeff(num: String): Boolean {
var charIndex = 0
val myArray = stringToReversedIntArray(num)
for (index in myArray.indices) {
charIndex = d[charIndex][p[index % 8][myArray[index]]]
}
return charIndex == 0
}
fun stringToReversedIntArray(num: String): IntArray {
val myArray = num.map { it.toString().toInt() }.toIntArray()
return myArray.reversedArray()
}
fun isValidAadhaar(aadhaarNumber: String): Boolean {
val aadhaarPattern = Regex("\\d{12}")
return aadhaarPattern.matches(aadhaarNumber) && validateVerhoeff(aadhaarNumber)
}
}

View File

@@ -0,0 +1,73 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.base
import com.navi.base.utils.AadhaarUtils
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
class AadhaarUtilsTest {
private val aadhaarUtils = AadhaarUtils()
@Test
fun `validateAadhaarNumber returns true for valid Aadhaar number`() {
val validAadhaar = "273838226780"
assertTrue(aadhaarUtils.isValidAadhaar(validAadhaar))
}
@Test
fun `validateAadhaarNumber returns false for invalid Aadhaar number`() {
val invalidAadhaar = "123456781235"
assertFalse(aadhaarUtils.isValidAadhaar(invalidAadhaar))
}
@Test
fun `validateAadhaarNumber returns false for non-numeric input`() {
val invalidAadhaar = "1234A6781234"
assertFalse(aadhaarUtils.isValidAadhaar(invalidAadhaar))
}
@Test
fun `validateAadhaarNumber returns false for input with less than 12 digits`() {
val shortAadhaar = "123456"
assertFalse(aadhaarUtils.isValidAadhaar(shortAadhaar))
}
@Test
fun `validateAadhaarNumber returns false for input with more than 12 digits`() {
val longAadhaar = "1234567890123"
assertFalse(aadhaarUtils.isValidAadhaar(longAadhaar))
}
@Test
fun `validateVerhoeff returns true for valid Verhoeff number`() {
val validVerhoeff = "236"
assertTrue(aadhaarUtils.validateVerhoeff(validVerhoeff))
}
@Test
fun `validateVerhoeff returns false for invalid Verhoeff number`() {
val invalidVerhoeff = "123"
assertFalse(aadhaarUtils.validateVerhoeff(invalidVerhoeff))
}
@Test
fun `stringToReversedIntArray correctly reverses string to int array`() {
val input = "12345"
val expected = intArrayOf(5, 4, 3, 2, 1)
assertArrayEquals(expected, aadhaarUtils.stringToReversedIntArray(input))
}
@Test(expected = NumberFormatException::class)
fun `stringToReversedIntArray throws exception for non-numeric input`() {
val input = "12A45"
aadhaarUtils.stringToReversedIntArray(input)
}
}

View File

@@ -8,6 +8,7 @@
package com.navi.insurance.abha
import androidx.lifecycle.viewModelScope
import com.navi.base.utils.AadhaarUtils
import com.navi.base.utils.isNotNull
import com.navi.base.utils.isNull
import com.navi.base.utils.orZero
@@ -54,14 +55,29 @@ constructor(
val footerState: StateFlow<String>
get() = _footerState.asStateFlow()
private val _errorState: MutableStateFlow<Int?> = MutableStateFlow(null)
val errorState: StateFlow<Int?>
get() = _errorState.asStateFlow()
private var otpPageValidation: Int = 0
private var aadharPageValidation: Int = 0
var validAadharWidgets: HashMap<String, GenericWidgetDataInfo> = hashMapOf()
var validAadharWidgets: HashMap<String, Pair<GenericWidgetDataInfo, Int>> = hashMapOf()
var validOtpWidgets: HashMap<String, GenericWidgetDataInfo> = hashMapOf()
private var policyId: String? = null
private var pageSource: String? = null
private var linkAbhaRequest: LinkAbhaRequest = LinkAbhaRequest()
fun updateErrorIndex(index: Int?) {
_errorState.value = index
}
fun isValidAadhar(): Boolean {
return (validAadharWidgets[AADHAR_NUMBER_EXTRA]?.first as? EditableTextWidgetData)
?.widgetData
?.value
?.let { AadhaarUtils().isValidAadhaar(it) } ?: false
}
fun setPolicyId(policyId: String?) {
this.policyId = policyId
}
@@ -135,9 +151,13 @@ constructor(
}
}
fun updateAadharValidWidgets(widgetDataInfo: GenericWidgetDataInfo, isValid: Boolean) {
fun updateAadharValidWidgets(
widgetDataInfo: GenericWidgetDataInfo,
isValid: Boolean,
index: Int = -1
) {
when {
isValid -> addValidWidget(widgetDataInfo)
isValid -> addValidWidget(widgetDataInfo, index)
else -> removeValidWidget(widgetDataInfo)
}
@@ -149,16 +169,19 @@ constructor(
}
}
private fun addValidWidget(widgetDataInfo: GenericWidgetDataInfo) {
private fun addValidWidget(widgetDataInfo: GenericWidgetDataInfo, index: Int) {
when (widgetDataInfo) {
is EditableTextWidgetData -> {
when (widgetDataInfo.widgetData?.id) {
AADHAR_NUMBER_EXTRA -> validAadharWidgets[AADHAR_NUMBER_EXTRA] = widgetDataInfo
MOBILE_NUMBER_EXTRA -> validAadharWidgets[MOBILE_NUMBER_EXTRA] = widgetDataInfo
AADHAR_NUMBER_EXTRA ->
validAadharWidgets[AADHAR_NUMBER_EXTRA] = Pair(widgetDataInfo, index)
MOBILE_NUMBER_EXTRA ->
validAadharWidgets[MOBILE_NUMBER_EXTRA] = Pair(widgetDataInfo, index)
}
}
is SelectableTextWidgetData -> {
validAadharWidgets[WidgetTypes.SELECTABLE_TEXT_WIDGET.value] = widgetDataInfo
validAadharWidgets[WidgetTypes.SELECTABLE_TEXT_WIDGET.value] =
Pair(widgetDataInfo, index)
}
}
}
@@ -167,7 +190,10 @@ constructor(
when (widgetDataInfo) {
is EditableTextWidgetData -> {
when (widgetDataInfo.widgetData?.id) {
AADHAR_NUMBER_EXTRA -> validAadharWidgets.remove(AADHAR_NUMBER_EXTRA)
AADHAR_NUMBER_EXTRA -> {
_errorState.value = null
validAadharWidgets.remove(AADHAR_NUMBER_EXTRA)
}
MOBILE_NUMBER_EXTRA -> validAadharWidgets.remove(MOBILE_NUMBER_EXTRA)
}
}

View File

@@ -139,6 +139,7 @@ class ABHAaadharFragment : BaseFragment(), WidgetCallback {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun AbhaAadharContentScreen(data: ABHAPageResponse) {
val errorIndex = viewModel.errorState.collectAsState()
val widgetCallback: WidgetCallback = this
Scaffold(
modifier = Modifier.fillMaxSize().background(color = Color.White),
@@ -163,8 +164,9 @@ class ABHAaadharFragment : BaseFragment(), WidgetCallback {
data = genericWidgetData,
widgetCallback = widgetCallback,
onWidgetUpdate = { data, isValid ->
viewModel.updateAadharValidWidgets(data, isValid)
}
viewModel.updateAadharValidWidgets(data, isValid, index)
},
showError = errorIndex.value == index
)
}
}
@@ -189,43 +191,49 @@ class ABHAaadharFragment : BaseFragment(), WidgetCallback {
openHelpCenter()
}
CtaType.NAVIGATE_TO_NEW_SCREEN.value -> {
val params = naviClickAction?.parameters?.toMutableList()
viewModel.validAadharWidgets.forEach { widgetDataInfo ->
if (
widgetDataInfo.value is EditableTextWidgetData &&
widgetDataInfo?.key == AADHAR_NUMBER_EXTRA
) {
params?.add(
LineItem(
AADHAR_NUMBER_EXTRA,
(widgetDataInfo.value as EditableTextWidgetData)
?.widgetData
?.value
if (viewModel.isValidAadhar()) {
val params = naviClickAction?.parameters?.toMutableList()
viewModel.validAadharWidgets.forEach { widgetDataInfo ->
if (
widgetDataInfo.value.first is EditableTextWidgetData &&
widgetDataInfo?.key == AADHAR_NUMBER_EXTRA
) {
params?.add(
LineItem(
AADHAR_NUMBER_EXTRA,
(widgetDataInfo.value?.first as EditableTextWidgetData)
?.widgetData
?.value
)
)
)
}
}
if (
widgetDataInfo.value is EditableTextWidgetData &&
widgetDataInfo?.key == MOBILE_NUMBER_EXTRA
) {
params?.add(
LineItem(
MOBILE_NUMBER_EXTRA,
(widgetDataInfo.value as EditableTextWidgetData)
?.widgetData
?.value
if (
widgetDataInfo.value.first is EditableTextWidgetData &&
widgetDataInfo?.key == MOBILE_NUMBER_EXTRA
) {
params?.add(
LineItem(
MOBILE_NUMBER_EXTRA,
(widgetDataInfo.value?.first as EditableTextWidgetData)
?.widgetData
?.value
)
)
)
}
}
val redirectionCta = naviClickAction.copy(parameters = params)
NaviInsuranceDeeplinkNavigator.navigate(
activity,
ctaData = redirectionCta,
finish = redirectionCta.finish.orFalse(),
clearTask = redirectionCta.clearTask.orFalse()
)
} else {
viewModel.updateErrorIndex(
viewModel.validAadharWidgets.get(AADHAR_NUMBER_EXTRA)?.second
)
}
val redirectionCta = naviClickAction.copy(parameters = params)
NaviInsuranceDeeplinkNavigator.navigate(
activity,
ctaData = redirectionCta,
finish = redirectionCta.finish.orFalse(),
clearTask = redirectionCta.clearTask.orFalse()
)
}
else -> {
NaviInsuranceDeeplinkNavigator.navigate(

View File

@@ -94,7 +94,8 @@ fun GenericComposableWidgetFactory(
widgetCallback: WidgetCallback? = null,
isFirstItemVisible: Boolean = false,
onWidgetUpdate: (updatedData: GenericWidgetDataInfo, isValid: Boolean) -> Unit = { _, _ -> },
state: String = FooterButtonState.ENABLED.name
state: String = FooterButtonState.ENABLED.name,
showError: Boolean = false
) {
LaunchedEffect(key1 = data.toString()) {
data?.widgetAnalyticsProperties?.let {
@@ -264,7 +265,8 @@ fun GenericComposableWidgetFactory(
EditableTextWidgetComposable(
data as? EditableTextWidgetData,
widgetCallback,
onWidgetUpdate
onWidgetUpdate,
showError
)
}
WidgetTypes.SELECTABLE_TEXT_WIDGET.value -> {

View File

@@ -30,6 +30,7 @@ data class EditableTextWidgetContentData(
@SerializedName("title") val title: TextFieldData? = null,
@SerializedName("fixedTitle") val fixedTitle: TextFieldData? = null,
@SerializedName("textRules") val textRules: TextRules? = null,
@SerializedName("errorText") val errorText: TextFieldData? = null,
@SerializedName("value") val value: String? = null,
@SerializedName("id") val id: String? = null
)

View File

@@ -8,7 +8,7 @@
package com.navi.naviwidgets.composewidget.widgets
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.OutlinedTextField
@@ -18,7 +18,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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.text.TextStyle
@@ -30,6 +29,7 @@ import com.navi.design.theme.ttComposeFontFamily
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.composewidget.model.EditableTextWidgetData
import com.navi.naviwidgets.composewidget.reusable.colorBorderAlt
import com.navi.naviwidgets.composewidget.reusable.colorInputFields
import com.navi.naviwidgets.extensions.NaviTextWidgetized
import com.navi.naviwidgets.extensions.getKeyBoardType
import com.navi.naviwidgets.extensions.hexToColor
@@ -39,15 +39,13 @@ import com.navi.naviwidgets.extensions.setWidgetLayoutParams
fun EditableTextWidgetComposable(
editableTextData: EditableTextWidgetData?,
widgetCallback: WidgetCallback? = null,
onWidgetUpdate: (updatedData: EditableTextWidgetData, isValid: Boolean) -> Unit
onWidgetUpdate: (updatedData: EditableTextWidgetData, isValid: Boolean) -> Unit,
isError: Boolean = false
) {
var textLimit by remember { mutableStateOf(editableTextData?.widgetData?.title?.text ?: "") }
editableTextData?.widgetData?.let { data ->
setWidgetLayoutParams(widgetLayoutParams = editableTextData?.widgetLayoutParams) {
Row(
modifier = Modifier.background(Color.White).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.background(Color.White).fillMaxWidth()) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = textLimit,
@@ -73,6 +71,7 @@ fun EditableTextWidgetComposable(
}
}
},
isError = isError,
placeholder = { NaviTextWidgetized(data?.hintText) },
leadingIcon =
if (data?.fixedTitle != null) {
@@ -85,13 +84,21 @@ fun EditableTextWidgetComposable(
),
colors =
OutlinedTextFieldDefaults.colors(
focusedBorderColor = colorBorderAlt,
unfocusedBorderColor = colorBorderAlt,
focusedBorderColor = if (isError) colorInputFields else colorBorderAlt,
unfocusedBorderColor =
if (isError) colorInputFields else colorBorderAlt,
cursorColor = Color.Black,
focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White,
errorCursorColor = Color.Black
errorCursorColor = Color.Black,
errorBorderColor = colorInputFields
),
supportingText =
if (isError) {
{ NaviTextWidgetized(data.errorText) }
} else {
null
},
textStyle =
TextStyle(
fontFamily = ttComposeFontFamily,