Skip to content

Commit

Permalink
refactor: moved upi setup to a module (#1673)
Browse files Browse the repository at this point in the history
Co-authored-by: Rajan Maurya <therajanmaurya@users.noreply.github.com>
  • Loading branch information
PratyushSingh07 and therajanmaurya committed Jun 23, 2024
1 parent 9b6ddd1 commit c39b0b4
Show file tree
Hide file tree
Showing 21 changed files with 1,020 additions and 2 deletions.
1 change: 1 addition & 0 deletions feature/upi-setup/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
15 changes: 15 additions & 0 deletions feature/upi-setup/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
alias(libs.plugins.mifospay.android.feature)
alias(libs.plugins.mifospay.android.library.compose)
}

android {
namespace = "org.mifospay.feature.upi_setup"
}

dependencies {
implementation(projects.core.data)

// need this because of M2 dependency in this module
implementation("androidx.compose.material:material:1.6.0")
}
Empty file.
21 changes: 21 additions & 0 deletions feature/upi-setup/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.mifospay.feature.upi_setup

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("org.mifospay.feature.upi_setup.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions feature/upi-setup/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package org.mifospay.feature.upi_setup.screens

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.Card
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.mifos.mobilewallet.mifospay.ui.VerifyStepHeader
import org.mifospay.core.designsystem.component.MifosLoadingWheel
import org.mifospay.core.designsystem.theme.MifosTheme
import org.mifospay.feature.upi_setup.viewmodel.DebitCardUiState
import org.mifospay.feature.upi_setup.viewmodel.DebitCardViewModel

@Composable
fun DebitCardScreen(
viewModel: DebitCardViewModel = hiltViewModel(),
verificationStatus: Boolean = false,
isContentVisible: Boolean = true,
onDebitCardVerified: (String) -> Unit,
onDebitCardVerificationFailed: (String) -> Unit
) {
val debitCardUiState by viewModel.debitCardUiState.collectAsStateWithLifecycle()
DebitCardScreenWithHeaderAndContent(
debitCardUiState = debitCardUiState,
verificationStatus = verificationStatus,
isContentVisible = isContentVisible,
onDebitCardVerified = onDebitCardVerified,
onDebitCardVerificationFailed = onDebitCardVerificationFailed
)
}

@Composable
fun DebitCardScreenWithHeaderAndContent(
debitCardUiState: DebitCardUiState = DebitCardUiState.Initials,
verificationStatus: Boolean = false,
isContentVisible: Boolean = true,
onDebitCardVerified: (String) -> Unit,
onDebitCardVerificationFailed: (String) -> Unit
) {

Card(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
elevation = 1.dp
) {
Column(
modifier = Modifier.padding(
top = 15.dp,
bottom = 15.dp,
start = 10.dp,
end = 10.dp
)
) {
VerifyStepHeader("Debit Card Details ", verificationStatus)
if (isContentVisible) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(start = 12.dp)
) {
//handle debit card ui state
Box(
modifier = Modifier.fillMaxWidth()
) {
DebitCardScreenContents()
when (debitCardUiState) {
is DebitCardUiState.Initials -> {

}
is DebitCardUiState.Verifying -> {
MifosLoadingWheel(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center),
contentDesc = "Verifying Debit Card"
)
}
is DebitCardUiState.Verified -> {
onDebitCardVerified((debitCardUiState).otp)
}
is DebitCardUiState.VerificationFailed -> {
onDebitCardVerificationFailed((debitCardUiState).errorMessage)
}
else -> {}
}
}
}
}
}
}
}

@Preview
@Composable
fun DebitCardScreenInitialsPreview() {
MifosTheme {
DebitCardScreenWithHeaderAndContent(debitCardUiState = DebitCardUiState.Initials,
onDebitCardVerified = {},
onDebitCardVerificationFailed = {})
}
}

@Preview
@Composable
fun DebitCardScreenLoadingPreview() {
MifosTheme {
DebitCardScreenWithHeaderAndContent(debitCardUiState = DebitCardUiState.Verifying,
onDebitCardVerified = {},
onDebitCardVerificationFailed = {})
}
}

@Preview
@Composable
fun DebitCardScreenVerificationFailedPreview() {
MifosTheme {
DebitCardScreenWithHeaderAndContent(debitCardUiState = DebitCardUiState.VerificationFailed("Error Message"),
onDebitCardVerified = {},
onDebitCardVerificationFailed = {})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package org.mifospay.feature.upi_setup.screens

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.TextFieldDefaults
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.focus.FocusDirection
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import org.mifospay.feature.upi_setup.viewmodel.DebitCardViewModel
import org.mifos.mobilewallet.mifospay.ui.ExpiryDateInput


@Composable
fun DebitCardScreenContents(
viewModel: DebitCardViewModel = hiltViewModel()
) {
var cardNumber by rememberSaveable { mutableStateOf("") }
var expiryDate by rememberSaveable { mutableStateOf("") }
val focusManager = LocalFocusManager.current
Column {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth(),
label = {
Text(
text = "Debit Card Number",
style = TextStyle(color = Color.Black)
)
},
value = cardNumber,
onValueChange = {
cardNumber = it
},
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = {
focusManager.moveFocus(FocusDirection.Down)
}),
visualTransformation = VisualTransformation {
val formattedCardNumber = formatCardNumber(it)
formattedCardNumber
},
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = Color.DarkGray,
unfocusedBorderColor = Color.LightGray,
cursorColor = Color.Black
)
)
Spacer(modifier = Modifier.height(8.dp))
ExpiryDateInput(
date = expiryDate,
onDateChange = { expiryDate = it },
onDone = {
viewModel.verifyDebitCard(cardNumber, "month, year", expiryDate)
}
)
}
}

fun formatCardNumber(text: AnnotatedString): TransformedText {
val trimmed = if (text.text.length >= 16) text.text.substring(0..15) else text.text
var out = ""
for (i in trimmed.indices) {
out += trimmed[i]
if (i % 4 == 3 && i != 15) {
out += "-"
}
}
val creditCardOffsetTranslator = object : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
if (offset <= 3) {
return offset
}
if (offset <= 7) {
return offset + 1
}
if (offset <= 11) {
return offset + 2
}
if (offset <= 16) {
return offset + 3
}
return 19
}

override fun transformedToOriginal(offset: Int): Int {
if (offset <= 4) {
return offset
}
if (offset <= 9) {
return offset - 1
}
if (offset <= 14) {
return offset - 2
}
if (offset <= 19) {
return offset - 3
}
return 16
}
}
return TransformedText(AnnotatedString(out), creditCardOffsetTranslator)
}

@Preview
@Composable
fun DebitCardScreenContentsPreview() {
DebitCardScreenContents()
}
Loading

0 comments on commit c39b0b4

Please sign in to comment.