Skip to content

Commit a1ca686

Browse files
authored
Feat: Migrated Passcode Module to KMP (#1783)
1 parent ae2b15b commit a1ca686

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+609
-510
lines changed

libs/mifos-passcode/build.gradle.kts

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,39 +8,61 @@
88
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
99
*/
1010
plugins {
11-
alias(libs.plugins.mifospay.android.library)
12-
alias(libs.plugins.mifospay.android.library.compose)
13-
id("com.google.devtools.ksp")
11+
alias(libs.plugins.mifospay.cmp.feature)
12+
alias(libs.plugins.kotlin.parcelize)
13+
alias(libs.plugins.kotlin.serialization)
14+
alias(libs.plugins.protobuf)
1415
}
1516

1617
android {
1718
namespace = "com.mifos.library.passcode"
1819
}
1920

20-
dependencies {
21-
implementation(libs.androidx.core.ktx)
21+
kotlin {
22+
sourceSets {
23+
commonMain.dependencies {
24+
implementation(compose.ui)
25+
implementation(compose.foundation)
26+
implementation(compose.material3)
27+
implementation(compose.materialIconsExtended)
28+
implementation(compose.components.resources)
29+
implementation(compose.components.uiToolingPreview)
2230

23-
implementation(libs.androidx.compose.foundation)
24-
implementation(libs.androidx.compose.foundation.layout)
25-
implementation(libs.androidx.compose.material.iconsExtended)
26-
implementation(libs.androidx.compose.material3)
27-
implementation(libs.androidx.compose.runtime)
28-
implementation(libs.androidx.compose.ui.util)
31+
implementation(libs.koin.compose.viewmodel)
32+
implementation(libs.koin.compose)
2933

30-
implementation(libs.androidx.lifecycle.runtimeCompose)
31-
implementation(libs.androidx.lifecycle.viewModelCompose)
32-
implementation(libs.androidx.navigation.compose)
34+
implementation(libs.jb.kotlin.stdlib)
35+
implementation(libs.kotlin.reflect)
3336

34-
implementation(platform(libs.koin.bom))
35-
implementation(libs.koin.core)
36-
implementation(libs.koin.android)
37-
implementation(libs.koin.androidx.navigation)
38-
implementation(libs.koin.androidx.compose)
39-
implementation(libs.koin.core.viewmodel)
37+
api(libs.protobuf.kotlin.lite)
38+
implementation(libs.kotlinx.serialization.core)
4039

40+
implementation(libs.multiplatform.settings)
41+
implementation(libs.multiplatform.settings.serialization)
42+
implementation(libs.multiplatform.settings.coroutines)
4143

42-
testImplementation(libs.koin.test)
43-
testImplementation(libs.koin.test.junit4)
44-
testImplementation(libs.koin.test.junit5)
44+
implementation(libs.kotlinx.coroutines.core)
45+
implementation(libs.kotlinx.serialization.core)
46+
}
4547

48+
desktopMain.dependencies {
49+
implementation(libs.kotlinx.coroutines.swing)
50+
}
51+
}
4652
}
53+
54+
// Setup protobuf configuration, generating lite Java and Kotlin classes
55+
protobuf {
56+
protoc {
57+
artifact = libs.protobuf.protoc.get().toString()
58+
}
59+
generateProtoTasks {
60+
all().forEach { task ->
61+
task.builtins {
62+
register("kotlin") {
63+
option("lite")
64+
}
65+
}
66+
}
67+
}
68+
}
File renamed without changes.

libs/mifos-passcode/src/main/res/drawable/lib_mifos_passcode_mifos_logo.jpg renamed to libs/mifos-passcode/src/commonMain/composeResources/drawable/lib_mifos_passcode_mifos_logo.jpg

File renamed without changes.

libs/mifos-passcode/src/main/res/font/lib_mifos_passcode_lato_black.ttf renamed to libs/mifos-passcode/src/commonMain/composeResources/font/lib_mifos_passcode_lato_black.ttf

File renamed without changes.

libs/mifos-passcode/src/main/res/font/lib_mifos_passcode_lato_bold.ttf renamed to libs/mifos-passcode/src/commonMain/composeResources/font/lib_mifos_passcode_lato_bold.ttf

File renamed without changes.

libs/mifos-passcode/src/main/res/font/lib_mifos_passcode_lato_regular.ttf renamed to libs/mifos-passcode/src/commonMain/composeResources/font/lib_mifos_passcode_lato_regular.ttf

File renamed without changes.

libs/mifos-passcode/src/main/res/values/strings.xml renamed to libs/mifos-passcode/src/commonMain/composeResources/values/strings.xml

File renamed without changes.

libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PassCodeScreen.kt renamed to libs/mifos-passcode/src/commonMain/kotlin/org/mifos/library/passcode/PassCodeScreen.kt

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ import androidx.compose.material3.IconButton
3232
import androidx.compose.material3.Scaffold
3333
import androidx.compose.material3.Text
3434
import androidx.compose.runtime.Composable
35-
import androidx.compose.runtime.LaunchedEffect
3635
import androidx.compose.runtime.getValue
3736
import androidx.compose.runtime.mutableStateOf
3837
import androidx.compose.runtime.remember
38+
import androidx.compose.runtime.rememberCoroutineScope
3939
import androidx.compose.runtime.setValue
4040
import androidx.compose.ui.Alignment
4141
import androidx.compose.ui.Modifier
4242
import androidx.compose.ui.graphics.Color
43-
import androidx.compose.ui.platform.LocalContext
44-
import androidx.compose.ui.tooling.preview.Preview
4543
import androidx.compose.ui.unit.IntOffset
4644
import androidx.compose.ui.unit.dp
4745
import androidx.lifecycle.compose.collectAsStateWithLifecycle
48-
import org.koin.androidx.compose.koinViewModel
46+
import kotlinx.coroutines.launch
47+
import org.jetbrains.compose.ui.tooling.preview.Preview
48+
import org.koin.compose.viewmodel.koinViewModel
4949
import org.mifos.library.passcode.component.MifosIcon
5050
import org.mifos.library.passcode.component.PasscodeForgotButton
5151
import org.mifos.library.passcode.component.PasscodeHeader
@@ -55,10 +55,11 @@ import org.mifos.library.passcode.component.PasscodeSkipButton
5555
import org.mifos.library.passcode.component.PasscodeToolbar
5656
import org.mifos.library.passcode.theme.blueTint
5757
import org.mifos.library.passcode.utility.Constants.PASSCODE_LENGTH
58-
import org.mifos.library.passcode.utility.PreferenceManager
5958
import org.mifos.library.passcode.utility.ShakeAnimation.performShakeAnimation
60-
import org.mifos.library.passcode.utility.VibrationFeedback.vibrateFeedback
59+
import org.mifos.library.passcode.viewmodels.PasscodeAction
60+
import org.mifos.library.passcode.viewmodels.PasscodeEvent
6161
import org.mifos.library.passcode.viewmodels.PasscodeViewModel
62+
import org.mifospay.core.ui.utils.EventsEffect
6263

6364
@Composable
6465
internal fun PasscodeScreen(
@@ -69,29 +70,25 @@ internal fun PasscodeScreen(
6970
modifier: Modifier = Modifier,
7071
viewModel: PasscodeViewModel = koinViewModel(),
7172
) {
72-
val context = LocalContext.current
73-
val preferenceManager = remember { PreferenceManager(context) }
74-
75-
val activeStep by viewModel.activeStep.collectAsStateWithLifecycle()
76-
val filledDots by viewModel.filledDots.collectAsStateWithLifecycle()
77-
val passcodeVisible by viewModel.passcodeVisible.collectAsStateWithLifecycle()
78-
val currentPasscode by viewModel.currentPasscodeInput.collectAsStateWithLifecycle()
73+
val scope = rememberCoroutineScope()
74+
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
7975

8076
val xShake = remember { Animatable(initialValue = 0.0F) }
8177
var passcodeRejectedDialogVisible by remember { mutableStateOf(false) }
8278

83-
LaunchedEffect(key1 = viewModel.onPasscodeConfirmed) {
84-
viewModel.onPasscodeConfirmed.collect {
85-
onPasscodeConfirm(it)
86-
}
87-
}
79+
EventsEffect(viewModel) { event ->
80+
when (event) {
81+
is PasscodeEvent.PasscodeConfirmed -> {
82+
onPasscodeConfirm(event.passcode)
83+
}
8884

89-
LaunchedEffect(key1 = viewModel.onPasscodeRejected) {
90-
viewModel.onPasscodeRejected.collect {
91-
passcodeRejectedDialogVisible = true
92-
vibrateFeedback(context)
93-
performShakeAnimation(xShake)
94-
onPasscodeRejected()
85+
is PasscodeEvent.PasscodeRejected -> {
86+
passcodeRejectedDialogVisible = true
87+
scope.launch {
88+
performShakeAnimation(xShake)
89+
}
90+
onPasscodeRejected()
91+
}
9592
}
9693
}
9794

@@ -106,10 +103,10 @@ internal fun PasscodeScreen(
106103
.padding(paddingValues),
107104
horizontalAlignment = Alignment.CenterHorizontally,
108105
) {
109-
PasscodeToolbar(activeStep = activeStep, preferenceManager.hasPasscode)
106+
PasscodeToolbar(activeStep = state.activeStep, state.hasPasscode)
110107

111108
PasscodeSkipButton(
112-
hasPassCode = preferenceManager.hasPasscode,
109+
hasPassCode = state.hasPasscode,
113110
onSkipButton = onSkipButton,
114111
)
115112

@@ -122,15 +119,19 @@ internal fun PasscodeScreen(
122119
horizontalAlignment = Alignment.CenterHorizontally,
123120
) {
124121
PasscodeHeader(
125-
activeStep = activeStep,
126-
isPasscodeAlreadySet = preferenceManager.hasPasscode,
122+
activeStep = state.activeStep,
123+
isPasscodeAlreadySet = state.hasPasscode,
127124
)
128125
PasscodeView(
129-
restart = { viewModel.restart() },
130-
togglePasscodeVisibility = { viewModel.togglePasscodeVisibility() },
131-
filledDots = filledDots,
132-
passcodeVisible = passcodeVisible,
133-
currentPasscode = currentPasscode,
126+
restart = remember(viewModel) {
127+
{ viewModel.trySendAction(PasscodeAction.Restart) }
128+
},
129+
togglePasscodeVisibility = remember(viewModel) {
130+
{ viewModel.trySendAction(PasscodeAction.TogglePasscodeVisibility) }
131+
},
132+
filledDots = state.filledDots,
133+
passcodeVisible = state.passcodeVisible,
134+
currentPasscode = state.currentPasscodeInput,
134135
passcodeRejectedDialogVisible = passcodeRejectedDialogVisible,
135136
onDismissDialog = { passcodeRejectedDialogVisible = false },
136137
xShake = xShake,
@@ -140,16 +141,21 @@ internal fun PasscodeScreen(
140141
Spacer(modifier = Modifier.height(6.dp))
141142

142143
PasscodeKeys(
143-
enterKey = viewModel::enterKey,
144-
deleteKey = viewModel::deleteKey,
145-
deleteAllKeys = viewModel::deleteAllKeys,
146-
modifier = Modifier.padding(horizontal = 12.dp),
144+
enterKey = remember(viewModel) {
145+
{ viewModel.trySendAction(PasscodeAction.EnterKey(it)) }
146+
},
147+
deleteKey = remember(viewModel) {
148+
{ viewModel.trySendAction(PasscodeAction.DeleteKey) }
149+
},
150+
deleteAllKeys = remember(viewModel) {
151+
{ viewModel.trySendAction(PasscodeAction.DeleteAllKeys) }
152+
},
147153
)
148154

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

151157
PasscodeForgotButton(
152-
hasPassCode = preferenceManager.hasPasscode,
158+
hasPassCode = state.hasPasscode,
153159
onForgotButton = onForgotButton,
154160
)
155161
}
@@ -230,7 +236,7 @@ private fun PasscodeView(
230236
}
231237
}
232238

233-
@Preview(showBackground = true)
239+
@Preview
234240
@Composable
235241
private fun PasscodeScreenPreview() {
236242
PasscodeScreen(

libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt renamed to libs/mifos-passcode/src/commonMain/kotlin/org/mifos/library/passcode/PasscodeNavigation.kt

File renamed without changes.

libs/mifos-passcode/src/main/kotlin/org/mifos/library/passcode/component/MifosIcon.kt renamed to libs/mifos-passcode/src/commonMain/kotlin/org/mifos/library/passcode/component/MifosIcon.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ import androidx.compose.foundation.layout.Row
1515
import androidx.compose.foundation.layout.size
1616
import androidx.compose.runtime.Composable
1717
import androidx.compose.ui.Modifier
18-
import androidx.compose.ui.res.painterResource
1918
import androidx.compose.ui.unit.dp
20-
import com.mifos.library.passcode.R
19+
import mobile_wallet.libs.mifos_passcode.generated.resources.Res
20+
import mobile_wallet.libs.mifos_passcode.generated.resources.lib_mifos_passcode_mifos_logo
21+
import org.jetbrains.compose.resources.painterResource
2122

2223
@Composable
2324
internal fun MifosIcon(modifier: Modifier = Modifier) {
@@ -27,7 +28,7 @@ internal fun MifosIcon(modifier: Modifier = Modifier) {
2728
) {
2829
Image(
2930
modifier = Modifier.size(180.dp),
30-
painter = painterResource(id = R.drawable.lib_mifos_passcode_mifos_logo),
31+
painter = painterResource(resource = Res.drawable.lib_mifos_passcode_mifos_logo),
3132
contentDescription = null,
3233
)
3334
}

0 commit comments

Comments
 (0)