diff --git a/build-logic/convention/src/main/kotlin/ModuleFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/ModuleFeatureConventionPlugin.kt index 71bff90d4f..cbd59a94c4 100644 --- a/build-logic/convention/src/main/kotlin/ModuleFeatureConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/ModuleFeatureConventionPlugin.kt @@ -27,7 +27,6 @@ class ModuleFeatureConventionPlugin : Plugin { dependencies { add("implementation", project(":infra:ui-base")) - add("implementation", project(":infra:core")) } } } diff --git a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureContract.kt b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureContract.kt index 127f18418b..6b6bd21d28 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureContract.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureContract.kt @@ -1,14 +1,12 @@ package com.simprints.face.capture -import android.os.Bundle -import com.simprints.face.capture.screens.controller.FaceCaptureControllerFragmentArgs import com.simprints.infra.config.store.models.FaceConfiguration object FaceCaptureContract { val DESTINATION = R.id.faceCaptureControllerFragment - fun getArgs( + fun getParams( samplesToCapture: Int, faceSDK: FaceConfiguration.BioSdk, - ): Bundle = FaceCaptureControllerFragmentArgs(FaceCaptureParams(samplesToCapture, faceSDK)).toBundle() + ) = FaceCaptureParams(samplesToCapture, faceSDK) } diff --git a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureParams.kt b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureParams.kt index 8425499a1b..23149f7d61 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureParams.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureParams.kt @@ -1,13 +1,11 @@ package com.simprints.face.capture -import android.os.Parcelable import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams import com.simprints.infra.config.store.models.FaceConfiguration -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class FaceCaptureParams( val samplesToCapture: Int, val faceSDK: FaceConfiguration.BioSdk, -) : Parcelable +) : StepParams diff --git a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt index a5a029f611..23cd5993dc 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/FaceCaptureResult.kt @@ -1,20 +1,20 @@ package com.simprints.face.capture import androidx.annotation.Keep +import com.simprints.core.domain.step.StepResult import com.simprints.infra.images.model.SecuredImageRef -import java.io.Serializable @Keep data class FaceCaptureResult( val referenceId: String, val results: List, -) : Serializable { +) : StepResult { @Keep data class Item( val captureEventId: String?, val index: Int, val sample: Sample?, - ) : Serializable + ) : StepResult @Keep data class Sample( @@ -22,5 +22,5 @@ data class FaceCaptureResult( val template: ByteArray, val imageRef: SecuredImageRef?, val format: String, - ) : Serializable + ) : StepResult } diff --git a/face/capture/src/main/java/com/simprints/face/capture/screens/controller/FaceCaptureControllerFragment.kt b/face/capture/src/main/java/com/simprints/face/capture/screens/controller/FaceCaptureControllerFragment.kt index 654ccd8289..feb2e60d61 100644 --- a/face/capture/src/main/java/com/simprints/face/capture/screens/controller/FaceCaptureControllerFragment.kt +++ b/face/capture/src/main/java/com/simprints/face/capture/screens/controller/FaceCaptureControllerFragment.kt @@ -7,9 +7,9 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.NavController import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.livedata.LiveDataEventObserver import com.simprints.core.livedata.LiveDataEventWithContentObserver +import com.simprints.face.capture.FaceCaptureParams import com.simprints.face.capture.GraphFaceCaptureInternalDirections import com.simprints.face.capture.R import com.simprints.face.capture.screens.FaceCaptureViewModel @@ -22,11 +22,12 @@ import com.simprints.infra.logging.Simber import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.navigationParams import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint internal class FaceCaptureControllerFragment : Fragment(R.layout.fragment_face_capture) { - private val args: FaceCaptureControllerFragmentArgs by navArgs() + private val params: FaceCaptureParams by navigationParams() private val viewModel: FaceCaptureViewModel by activityViewModels() @@ -71,7 +72,7 @@ internal class FaceCaptureControllerFragment : Fragment(R.layout.fragment_face_c findNavController().finishWithResult(this, result) } - viewModel.setupCapture(args.params.samplesToCapture) + viewModel.setupCapture(params.samplesToCapture) initFaceBioSdk() viewModel.recaptureEvent.observe( viewLifecycleOwner, @@ -147,6 +148,6 @@ internal class FaceCaptureControllerFragment : Fragment(R.layout.fragment_face_c InvalidFaceLicenseAlert.toAlertArgs(), ) } - viewModel.initFaceBioSdk(requireActivity(), args.params.faceSDK) + viewModel.initFaceBioSdk(requireActivity(), params.faceSDK) } } diff --git a/face/capture/src/main/res/navigation/graph_face_capture.xml b/face/capture/src/main/res/navigation/graph_face_capture.xml index fffa462fcd..9f600ba850 100644 --- a/face/capture/src/main/res/navigation/graph_face_capture.xml +++ b/face/capture/src/main/res/navigation/graph_face_capture.xml @@ -10,7 +10,7 @@ android:label="FaceCaptureController"> + app:argType="com.simprints.core.domain.step.StepParams"/> diff --git a/feature/alert/src/main/java/com/simprints/feature/alert/AlertConfigurationBuilder.kt b/feature/alert/src/main/java/com/simprints/feature/alert/AlertConfigurationBuilder.kt index 35788c0f7d..b82705a373 100644 --- a/feature/alert/src/main/java/com/simprints/feature/alert/AlertConfigurationBuilder.kt +++ b/feature/alert/src/main/java/com/simprints/feature/alert/AlertConfigurationBuilder.kt @@ -6,8 +6,8 @@ import com.simprints.core.domain.response.AppErrorReason import com.simprints.feature.alert.config.AlertButtonConfig import com.simprints.feature.alert.config.AlertColor import com.simprints.feature.alert.config.AlertConfiguration -import com.simprints.feature.alert.screen.AlertFragmentArgs import com.simprints.infra.events.event.domain.models.AlertScreenEvent +import com.simprints.infra.uibase.navigation.toBundle import com.simprints.infra.resources.R as IDR data class AlertConfigurationBuilder( @@ -40,23 +40,22 @@ data class AlertConfigurationBuilder( * appErrorReason - Error code that will be returned in app result if the alert is terminal, default - null * eventType - Event type to be logged on alert opening, default - nothing * ``` + * ``` */ fun alertConfiguration(block: AlertConfigurationBuilder.() -> Unit) = AlertConfigurationBuilder().apply(block) -fun AlertConfigurationBuilder.toArgs() = AlertFragmentArgs( - AlertConfiguration( - color = this.color, - title = this.title, - titleRes = this.titleRes, - image = this.image, - message = this.message, - messageRes = this.messageRes, - messageIcon = this.messageIcon, - leftButton = this.leftButton, - rightButton = this.rightButton, - eventType = this.eventType, - appErrorReason = this.appErrorReason, - ), +fun AlertConfigurationBuilder.toArgs() = AlertConfiguration( + color = this.color, + title = this.title, + titleRes = this.titleRes, + image = this.image, + message = this.message, + messageRes = this.messageRes, + messageIcon = this.messageIcon, + leftButton = this.leftButton, + rightButton = this.rightButton, + eventType = this.eventType, + appErrorReason = this.appErrorReason, ).toBundle() data class AlertButtonBuilder( diff --git a/feature/alert/src/main/java/com/simprints/feature/alert/AlertResult.kt b/feature/alert/src/main/java/com/simprints/feature/alert/AlertResult.kt index 6aecefde1c..9517edc39d 100644 --- a/feature/alert/src/main/java/com/simprints/feature/alert/AlertResult.kt +++ b/feature/alert/src/main/java/com/simprints/feature/alert/AlertResult.kt @@ -2,10 +2,10 @@ package com.simprints.feature.alert import androidx.annotation.Keep import com.simprints.core.domain.response.AppErrorReason -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class AlertResult( val buttonKey: String, val appErrorReason: AppErrorReason? = null, -) : Serializable +) : StepResult diff --git a/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertButtonConfig.kt b/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertButtonConfig.kt index bb4a0d1270..66f3575f7d 100644 --- a/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertButtonConfig.kt +++ b/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertButtonConfig.kt @@ -1,20 +1,18 @@ package com.simprints.feature.alert.config -import android.os.Parcelable import androidx.annotation.Keep import androidx.annotation.StringRes import com.simprints.feature.alert.AlertContract -import kotlinx.parcelize.Parcelize +import java.io.Serializable import com.simprints.infra.resources.R as IDR @Keep -@Parcelize data class AlertButtonConfig( val text: String?, @StringRes val textRes: Int?, val resultKey: String?, val closeOnClick: Boolean, -) : Parcelable { +) : Serializable { companion object { val Close = AlertButtonConfig( text = null, diff --git a/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertConfiguration.kt b/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertConfiguration.kt index c9cbb46a00..c56b97642b 100644 --- a/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertConfiguration.kt +++ b/feature/alert/src/main/java/com/simprints/feature/alert/config/AlertConfiguration.kt @@ -1,15 +1,13 @@ package com.simprints.feature.alert.config -import android.os.Parcelable import androidx.annotation.DrawableRes import androidx.annotation.Keep import androidx.annotation.StringRes import com.simprints.core.domain.response.AppErrorReason +import com.simprints.core.domain.step.StepParams import com.simprints.infra.events.event.domain.models.AlertScreenEvent -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class AlertConfiguration( val color: AlertColor, val title: String?, @@ -22,4 +20,4 @@ data class AlertConfiguration( val rightButton: AlertButtonConfig?, val eventType: AlertScreenEvent.AlertScreenPayload.AlertScreenEventType?, val appErrorReason: AppErrorReason? = null, -) : Parcelable +) : StepParams diff --git a/feature/alert/src/main/java/com/simprints/feature/alert/screen/AlertFragment.kt b/feature/alert/src/main/java/com/simprints/feature/alert/screen/AlertFragment.kt index e21b6a4f0b..ceaef27487 100644 --- a/feature/alert/src/main/java/com/simprints/feature/alert/screen/AlertFragment.kt +++ b/feature/alert/src/main/java/com/simprints/feature/alert/screen/AlertFragment.kt @@ -10,20 +10,21 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.domain.response.AppErrorReason import com.simprints.feature.alert.AlertContract import com.simprints.feature.alert.AlertResult import com.simprints.feature.alert.R import com.simprints.feature.alert.config.AlertButtonConfig import com.simprints.feature.alert.config.AlertColor +import com.simprints.feature.alert.config.AlertConfiguration import com.simprints.feature.alert.databinding.FragmentAlertBinding import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ALERT import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets +import com.simprints.infra.uibase.navigation.navigationParams import com.simprints.infra.uibase.navigation.setResult import com.simprints.infra.uibase.system.Clipboard +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.view.setTextWithFallbacks import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint @@ -31,7 +32,7 @@ import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class AlertFragment : Fragment(R.layout.fragment_alert) { - private val args: AlertFragmentArgs by navArgs() + private val config: AlertConfiguration by navigationParams() private val vm by viewModels() private val binding by viewBinding(FragmentAlertBinding::bind) @@ -43,7 +44,6 @@ internal class AlertFragment : Fragment(R.layout.fragment_alert) { applySystemBarInsets(view) Simber.i("AlertFragment started", tag = ORCHESTRATION) - val config = args.alertConfiguration Simber.i("Alert reason: ${config.appErrorReason}", tag = ALERT) binding.root.setBackgroundColor( @@ -62,14 +62,15 @@ internal class AlertFragment : Fragment(R.layout.fragment_alert) { binding.alertImage.setImageResource(config.image) binding.alertMessage.setTextWithFallbacks(config.message, config.messageRes) - if (config.messageIcon != null) { - binding.alertMessage.setCompoundDrawablesWithIntrinsicBounds(config.messageIcon, 0, 0, 0) + + config.messageIcon?.let { + binding.alertMessage.setCompoundDrawablesWithIntrinsicBounds(it, 0, 0, 0) } binding.alertLeftButton.setupButton(config.leftButton, config.appErrorReason) binding.alertRightButton.isVisible = config.rightButton != null - if (config.rightButton != null) { - binding.alertRightButton.setupButton(config.rightButton, config.appErrorReason) + config.rightButton?.let { + binding.alertRightButton.setupButton(it, config.appErrorReason) } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { diff --git a/feature/alert/src/main/res/navigation/graph_alert.xml b/feature/alert/src/main/res/navigation/graph_alert.xml index d1236998b0..0555b0e710 100644 --- a/feature/alert/src/main/res/navigation/graph_alert.xml +++ b/feature/alert/src/main/res/navigation/graph_alert.xml @@ -11,8 +11,8 @@ android:label="AlertFragment" tools:layout="@layout/fragment_alert"> + android:name="param" + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/ConsentContract.kt b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentContract.kt index 69b45fc582..b16ac4e38d 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/ConsentContract.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentContract.kt @@ -1,9 +1,7 @@ package com.simprints.feature.consent -import com.simprints.feature.consent.screens.consent.ConsentFragmentArgs - object ConsentContract { val DESTINATION = R.id.consentFragment - fun getArgs(type: ConsentType) = ConsentFragmentArgs(type).toBundle() + fun getParams(type: ConsentType) = ConsentParams(type) } diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/ConsentParams.kt b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentParams.kt new file mode 100644 index 0000000000..efb77ef056 --- /dev/null +++ b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentParams.kt @@ -0,0 +1,9 @@ +package com.simprints.feature.consent + +import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams + +@Keep +data class ConsentParams( + val type: ConsentType, +) : StepParams diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/ConsentResult.kt b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentResult.kt index 0e80c60a39..34e87163d1 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/ConsentResult.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/ConsentResult.kt @@ -1,9 +1,9 @@ package com.simprints.feature.consent import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class ConsentResult( val accepted: Boolean, -) : Serializable +) : StepResult diff --git a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt index e70b22d85c..273b349379 100644 --- a/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt +++ b/feature/consent/src/main/java/com/simprints/feature/consent/screens/consent/ConsentFragment.kt @@ -9,27 +9,28 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.google.android.material.tabs.TabLayout import com.simprints.core.livedata.LiveDataEventObserver +import com.simprints.feature.consent.ConsentParams import com.simprints.feature.consent.R import com.simprints.feature.consent.databinding.FragmentConsentBinding import com.simprints.feature.exitform.ExitFormContract import com.simprints.feature.exitform.ExitFormResult import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.listeners.OnTabSelectedListener import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class ConsentFragment : Fragment(R.layout.fragment_consent) { - private val args by navArgs() + private val args: ConsentParams by navigationParams() private val binding by viewBinding(FragmentConsentBinding::bind) private val viewModel by viewModels() @@ -53,7 +54,7 @@ internal class ConsentFragment : Fragment(R.layout.fragment_consent) { ExitFormContract.DESTINATION, ) { viewModel.handleExitFormResponse(it) } - viewModel.loadConfiguration(args.consentType) + viewModel.loadConfiguration(args.type) } private fun handleClicks() { diff --git a/feature/consent/src/main/res/navigation/graph_consent.xml b/feature/consent/src/main/res/navigation/graph_consent.xml index d2ee90d7cb..5d852f0103 100644 --- a/feature/consent/src/main/res/navigation/graph_consent.xml +++ b/feature/consent/src/main/res/navigation/graph_consent.xml @@ -11,8 +11,8 @@ android:label="ConsentFragment" tools:layout="@layout/fragment_consent"> + android:name="params" + app:argType="com.simprints.core.domain.step.StepParams"/> , - ) = EnrolLastBiometricFragmentArgs( - EnrolLastBiometricParams( - projectId = projectId, - userId = userId, - moduleId = moduleId, - steps = steps, - ), - ).toBundle() + ) = EnrolLastBiometricParams( + projectId = projectId, + userId = userId, + moduleId = moduleId, + steps = steps, + ) } diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt index 13960ec7de..1f38f1fb95 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricParams.kt @@ -1,52 +1,45 @@ package com.simprints.feature.enrollast -import android.os.Parcelable import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.Finger import com.simprints.infra.config.store.models.FingerprintConfiguration -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class EnrolLastBiometricParams( val projectId: String, val userId: TokenizableString, val moduleId: TokenizableString, val steps: List, -) : Parcelable +) : StepParams -sealed class EnrolLastBiometricStepResult : Parcelable { +sealed class EnrolLastBiometricStepResult : StepParams { @Keep - @Parcelize data class EnrolLastBiometricsResult( val subjectId: String?, ) : EnrolLastBiometricStepResult() @Keep - @Parcelize data class FingerprintMatchResult( val results: List, val sdk: FingerprintConfiguration.BioSdk, ) : EnrolLastBiometricStepResult() @Keep - @Parcelize data class FaceMatchResult( val results: List, val sdk: FaceConfiguration.BioSdk, ) : EnrolLastBiometricStepResult() @Keep - @Parcelize data class FingerprintCaptureResult( val referenceId: String, val results: List, ) : EnrolLastBiometricStepResult() @Keep - @Parcelize data class FaceCaptureResult( val referenceId: String, val results: List, @@ -54,20 +47,18 @@ sealed class EnrolLastBiometricStepResult : Parcelable { } @Keep -@Parcelize data class MatchResult( val subjectId: String, val confidenceScore: Float, -) : Parcelable +) : StepParams @Keep -@Parcelize data class FingerTemplateCaptureResult( val finger: Finger, val template: ByteArray, val templateQualityScore: Int, val format: String, -) : Parcelable { +) : StepParams { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -92,11 +83,10 @@ data class FingerTemplateCaptureResult( } @Keep -@Parcelize data class FaceTemplateCaptureResult( val template: ByteArray, val format: String, -) : Parcelable { +) : StepParams { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricResult.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricResult.kt index 252afcefdd..872e6874c8 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricResult.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/EnrolLastBiometricResult.kt @@ -1,9 +1,9 @@ package com.simprints.feature.enrollast import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class EnrolLastBiometricResult( val newSubjectId: String?, -) : Serializable +) : StepResult diff --git a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricFragment.kt b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricFragment.kt index eb14dbe3e6..9411e13490 100644 --- a/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricFragment.kt +++ b/feature/enrol-last-biometric/src/main/java/com/simprints/feature/enrollast/screen/EnrolLastBiometricFragment.kt @@ -6,7 +6,6 @@ import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.domain.response.AppErrorReason import com.simprints.core.livedata.LiveDataEventWithContentObserver import com.simprints.feature.alert.AlertContract @@ -15,6 +14,7 @@ import com.simprints.feature.alert.alertConfiguration import com.simprints.feature.alert.config.AlertButtonConfig import com.simprints.feature.alert.config.AlertColor import com.simprints.feature.alert.toArgs +import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricResult import com.simprints.feature.enrollast.R import com.simprints.feature.enrollast.screen.EnrolLastState.ErrorType @@ -25,17 +25,18 @@ import com.simprints.infra.config.store.models.GeneralConfiguration.Modality import com.simprints.infra.events.event.domain.models.AlertScreenEvent import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import dagger.hilt.android.AndroidEntryPoint import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class EnrolLastBiometricFragment : Fragment(R.layout.fragment_enrol_last) { private val viewModel: EnrolLastBiometricViewModel by viewModels() - private val args: EnrolLastBiometricFragmentArgs by navArgs() + private val params: EnrolLastBiometricParams by navigationParams() override fun onViewCreated( view: View, @@ -52,7 +53,7 @@ internal class EnrolLastBiometricFragment : Fragment(R.layout.fragment_enrol_las ) { finishWithSubjectId(null) } viewModel.finish.observe(viewLifecycleOwner, LiveDataEventWithContentObserver { finishWithResult(it) }) - viewModel.onViewCreated(args.params) + viewModel.onViewCreated(params) } private fun finishWithResult(result: EnrolLastState) = when (result) { diff --git a/feature/enrol-last-biometric/src/main/res/navigation/graph_enrol_last.xml b/feature/enrol-last-biometric/src/main/res/navigation/graph_enrol_last.xml index a2a4a50a0e..5e7d9f162e 100644 --- a/feature/enrol-last-biometric/src/main/res/navigation/graph_enrol_last.xml +++ b/feature/enrol-last-biometric/src/main/res/navigation/graph_enrol_last.xml @@ -15,7 +15,7 @@ + app:argType="com.simprints.core.domain.step.StepParams"/> finishWithResult(true) + -> finishWithResult(true) FetchSubjectState.NotFound -> openAlert(FetchSubjectAlerts.subjectNotFoundOnline().toArgs()) FetchSubjectState.ConnectionError -> openAlert(FetchSubjectAlerts.subjectNotFoundOffline().toArgs()) diff --git a/feature/fetch-subject/src/main/res/navigation/graph_fetch_subject.xml b/feature/fetch-subject/src/main/res/navigation/graph_fetch_subject.xml index cafb636c96..6cbb56d4cb 100644 --- a/feature/fetch-subject/src/main/res/navigation/graph_fetch_subject.xml +++ b/feature/fetch-subject/src/main/res/navigation/graph_fetch_subject.xml @@ -12,12 +12,8 @@ tools:layout="@layout/fragment_subject_fetch"> - - + android:name="params" + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/feature/login/src/main/java/com/simprints/feature/login/LoginContract.kt b/feature/login/src/main/java/com/simprints/feature/login/LoginContract.kt index e4cccc7ac7..f83fca9d61 100644 --- a/feature/login/src/main/java/com/simprints/feature/login/LoginContract.kt +++ b/feature/login/src/main/java/com/simprints/feature/login/LoginContract.kt @@ -1,15 +1,12 @@ package com.simprints.feature.login import com.simprints.core.domain.tokenization.TokenizableString -import com.simprints.feature.login.screens.form.LoginFormFragmentArgs object LoginContract { val DESTINATION = R.id.loginFormFragment - fun toArgs( + fun getParams( projectId: String, userId: TokenizableString, - ) = LoginFormFragmentArgs( - LoginParams(projectId, userId), - ).toBundle() + ) = LoginParams(projectId, userId) } diff --git a/feature/login/src/main/java/com/simprints/feature/login/LoginParams.kt b/feature/login/src/main/java/com/simprints/feature/login/LoginParams.kt index 6e336e9fd5..2092b53409 100644 --- a/feature/login/src/main/java/com/simprints/feature/login/LoginParams.kt +++ b/feature/login/src/main/java/com/simprints/feature/login/LoginParams.kt @@ -1,13 +1,11 @@ package com.simprints.feature.login -import android.os.Parcelable import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class LoginParams( val projectId: String, val userId: TokenizableString, -) : Parcelable +) : StepParams diff --git a/feature/login/src/main/java/com/simprints/feature/login/LoginResult.kt b/feature/login/src/main/java/com/simprints/feature/login/LoginResult.kt index fcc8d2d7e4..faa5cf5d49 100644 --- a/feature/login/src/main/java/com/simprints/feature/login/LoginResult.kt +++ b/feature/login/src/main/java/com/simprints/feature/login/LoginResult.kt @@ -1,13 +1,13 @@ package com.simprints.feature.login import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class LoginResult( val isSuccess: Boolean, val error: LoginError? = null, -) : Serializable +) : StepResult @Keep enum class LoginError { diff --git a/feature/login/src/main/java/com/simprints/feature/login/screens/form/LoginFormFragment.kt b/feature/login/src/main/java/com/simprints/feature/login/screens/form/LoginFormFragment.kt index 6455eecc20..3203bb41ff 100644 --- a/feature/login/src/main/java/com/simprints/feature/login/screens/form/LoginFormFragment.kt +++ b/feature/login/src/main/java/com/simprints/feature/login/screens/form/LoginFormFragment.kt @@ -13,9 +13,9 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.simprints.feature.login.LoginError +import com.simprints.feature.login.LoginParams import com.simprints.feature.login.LoginResult import com.simprints.feature.login.R import com.simprints.feature.login.databinding.FragmentLoginFormBinding @@ -42,10 +42,11 @@ import com.simprints.feature.login.tools.play.GooglePlayServicesAvailabilityChec import com.simprints.infra.logging.LoggingConstants.CrashReportTag.LOGIN import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -53,7 +54,7 @@ import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class LoginFormFragment : Fragment(R.layout.fragment_login_form) { - private val args by navArgs() + private val params: LoginParams by navigationParams() private val binding by viewBinding(FragmentLoginFormBinding::bind) private val viewModel by viewModels() @@ -89,7 +90,7 @@ internal class LoginFormFragment : Fragment(R.layout.fragment_login_form) { viewLifecycleOwner, R.id.loginFormFragment, R.id.loginQrScanner, - ) { viewModel.handleQrResult(args.loginParams.projectId, it) } + ) { viewModel.handleQrResult(params.projectId, it) } initUi() observeUiState() @@ -99,8 +100,8 @@ internal class LoginFormFragment : Fragment(R.layout.fragment_login_form) { } private fun initUi() { - binding.loginUserId.setText(args.loginParams.userId.value) - binding.loginProjectId.setText(args.loginParams.projectId) + binding.loginUserId.setText(params.userId.value) + binding.loginProjectId.setText(params.projectId) binding.loginChangeUrlButton.setOnClickListener { Simber.i("Change URL button clicked", tag = LOGIN) @@ -114,7 +115,7 @@ internal class LoginFormFragment : Fragment(R.layout.fragment_login_form) { binding.loginButtonSignIn.setOnClickListener { Simber.i("Login button clicked", tag = LOGIN) viewModel.signInClicked( - args.loginParams, + params, binding.loginProjectId.text.toString(), binding.loginProjectSecret.text.toString(), ) diff --git a/feature/login/src/main/res/navigation/graph_login.xml b/feature/login/src/main/res/navigation/graph_login.xml index 2fcf031ead..ebd251126e 100644 --- a/feature/login/src/main/res/navigation/graph_login.xml +++ b/feature/login/src/main/res/navigation/graph_login.xml @@ -11,8 +11,8 @@ android:label="LoginFormFragment" tools:layout="@layout/fragment_login_form"> + android:name="params" + app:argType="com.simprints.core.domain.step.StepParams" /> = emptyList(), faceSamples: List = emptyList(), @@ -19,16 +18,14 @@ object MatchContract { flowType: FlowType, subjectQuery: SubjectQuery, biometricDataSource: BiometricDataSource, - ) = MatchFragmentArgs( - MatchParams( - referenceId, - faceSamples, - faceSDK, - fingerprintSamples, - fingerprintSDK, - flowType, - subjectQuery, - biometricDataSource, - ), - ).toBundle() + ) = MatchParams( + referenceId, + faceSamples, + faceSDK, + fingerprintSamples, + fingerprintSDK, + flowType, + subjectQuery, + biometricDataSource, + ) } diff --git a/feature/matcher/src/main/java/com/simprints/matcher/MatchParams.kt b/feature/matcher/src/main/java/com/simprints/matcher/MatchParams.kt index 52e0f1fe5e..56df8746ba 100644 --- a/feature/matcher/src/main/java/com/simprints/matcher/MatchParams.kt +++ b/feature/matcher/src/main/java/com/simprints/matcher/MatchParams.kt @@ -1,18 +1,16 @@ package com.simprints.matcher -import android.os.Parcelable import androidx.annotation.Keep import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.core.domain.step.StepParams import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery import com.simprints.infra.uibase.annotations.ExcludedFromGeneratedTestCoverageReports -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class MatchParams( val probeReferenceId: String, val probeFaceSamples: List = emptyList(), @@ -22,16 +20,15 @@ data class MatchParams( val flowType: FlowType, val queryForCandidates: SubjectQuery, val biometricDataSource: BiometricDataSource, -) : Parcelable { +) : StepParams { fun isFaceMatch() = probeFaceSamples.isNotEmpty() @Keep @ExcludedFromGeneratedTestCoverageReports("Generated code") - @Parcelize data class FaceSample( val faceId: String, val template: ByteArray, - ) : Parcelable { + ) : StepParams { // Auto-generated by Android Studio to ensure // byte array field is compared correctly override fun equals(other: Any?): Boolean { @@ -55,12 +52,11 @@ data class MatchParams( @Keep @ExcludedFromGeneratedTestCoverageReports("Generated code") - @Parcelize data class FingerprintSample( val fingerId: IFingerIdentifier, val format: String, val template: ByteArray, - ) : Parcelable { + ) : StepParams { // Auto-generated by Android Studio to ensure // byte array field is compared correctly override fun equals(other: Any?): Boolean { diff --git a/feature/matcher/src/main/java/com/simprints/matcher/MatchResult.kt b/feature/matcher/src/main/java/com/simprints/matcher/MatchResult.kt index db10dd97aa..17748a9494 100644 --- a/feature/matcher/src/main/java/com/simprints/matcher/MatchResult.kt +++ b/feature/matcher/src/main/java/com/simprints/matcher/MatchResult.kt @@ -1,12 +1,12 @@ package com.simprints.matcher import androidx.annotation.Keep +import com.simprints.core.domain.step.StepResult import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration -import java.io.Serializable @Keep -interface MatchResult : Serializable { +interface MatchResult : StepResult { val results: List } @@ -14,7 +14,7 @@ interface MatchResult : Serializable { * This is required to bridge different interfaces from moduleApi module. */ @Keep -interface MatchResultItem : Serializable { +interface MatchResultItem : StepResult { val subjectId: String val confidence: Float } diff --git a/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchFragment.kt b/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchFragment.kt index 91c7146e41..cb16d60914 100644 --- a/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchFragment.kt +++ b/feature/matcher/src/main/java/com/simprints/matcher/screen/MatchFragment.kt @@ -9,7 +9,6 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.domain.permission.PermissionStatus import com.simprints.core.livedata.LiveDataEventWithContentObserver import com.simprints.core.tools.extentions.applicationSettingsIntent @@ -17,9 +16,11 @@ import com.simprints.core.tools.extentions.hasPermission import com.simprints.core.tools.extentions.permissionFromResult import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.viewbinding.viewBinding +import com.simprints.matcher.MatchParams import com.simprints.matcher.R import com.simprints.matcher.databinding.FragmentMatcherBinding import com.simprints.matcher.screen.MatchViewModel.MatchState @@ -30,18 +31,18 @@ import com.simprints.infra.resources.R as IDR internal class MatchFragment : Fragment(R.layout.fragment_matcher) { private val viewModel: MatchViewModel by viewModels() private val binding by viewBinding(FragmentMatcherBinding::bind) - private val args by navArgs() + private val params: MatchParams by navigationParams() private val permissionCall = registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { granted -> - val status = args.params.biometricDataSource + val status = params.biometricDataSource .permissionName() ?.let { requireActivity().permissionFromResult(it, granted) } ?: PermissionStatus.Granted when (status) { - PermissionStatus.Granted -> viewModel.setupMatch(args.params) + PermissionStatus.Granted -> viewModel.setupMatch(params) PermissionStatus.Denied -> viewModel.noPermission(neverAskAgain = false) PermissionStatus.DeniedNeverAskAgain -> viewModel.noPermission(neverAskAgain = true) } @@ -53,14 +54,14 @@ internal class MatchFragment : Fragment(R.layout.fragment_matcher) { ) { super.onViewCreated(view, savedInstanceState) applySystemBarInsets(view) - Simber.i("MatchFragment started (isFace=${args.params.isFaceMatch()})", tag = ORCHESTRATION) + Simber.i("MatchFragment started (isFace=${params.isFaceMatch()})", tag = ORCHESTRATION) observeViewModel() } override fun onResume() { super.onResume() - val requiredPermissionName = args.params.biometricDataSource + val requiredPermissionName = params.biometricDataSource .permissionName() ?.takeUnless { requireActivity().hasPermission(it) } @@ -71,14 +72,17 @@ internal class MatchFragment : Fragment(R.layout.fragment_matcher) { if (requiredPermissionName != null) { permissionCall.launch(requiredPermissionName) } else { - viewModel.setupMatch(args.params) + viewModel.setupMatch(params) } } else { viewModel.shouldCheckPermission = true } } - private fun setIdentificationProgress(progress: Int, animate: Boolean) = requireActivity().runOnUiThread { + private fun setIdentificationProgress( + progress: Int, + animate: Boolean, + ) = requireActivity().runOnUiThread { if (animate) { ObjectAnimator .ofInt(binding.faceMatchProgress, "progress", binding.faceMatchProgress.progress, progress) @@ -120,7 +124,7 @@ internal class MatchFragment : Fragment(R.layout.fragment_matcher) { private fun renderLoadingCandidates(state: MatchState.LoadingCandidates) { binding.faceMatchPermissionRequestButton.isVisible = false binding.apply { - val text = if(state.total > 0) { + val text = if (state.total > 0) { getString(IDR.string.matcher_loading_candidates) + " (${state.loaded}/${state.total})" } else { getString(IDR.string.matcher_loading_candidates) @@ -203,7 +207,7 @@ internal class MatchFragment : Fragment(R.layout.fragment_matcher) { private fun renderNoPermission(state: MatchState.NoPermission) = with(binding) { faceMatchMessage.setText(IDR.string.matcher_missing_access_permission) - val name = args.params.biometricDataSource.permissionName() + val name = params.biometricDataSource.permissionName() faceMatchPermissionRequestButton.isVisible = name != null faceMatchPermissionRequestButton.setOnClickListener { diff --git a/feature/matcher/src/main/res/navigation/graph_matcher.xml b/feature/matcher/src/main/res/navigation/graph_matcher.xml index 5e7d20efce..f19a76f90b 100644 --- a/feature/matcher/src/main/res/navigation/graph_matcher.xml +++ b/feature/matcher/src/main/res/navigation/graph_matcher.xml @@ -10,6 +10,6 @@ android:label="FaceMatcherFragment"> + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorFragment.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorFragment.kt index 4d5ec49c06..c65da6682e 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorFragment.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorFragment.kt @@ -41,6 +41,7 @@ import com.simprints.infra.orchestration.data.results.AppResult import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.toBundle import com.simprints.matcher.MatchContract import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch @@ -157,7 +158,7 @@ internal class OrchestratorFragment : Fragment(R.layout.fragment_orchestrator) { findNavController().navigateSafely( currentFragment = this, actionId = R.id.action_orchestratorFragment_to_login, - args = LoginContract.toArgs(request.projectId, request.userId), + args = LoginContract.getParams(request.projectId, request.userId).toBundle(), ) }, ) @@ -213,7 +214,11 @@ internal class OrchestratorFragment : Fragment(R.layout.fragment_orchestrator) { LiveDataEventWithContentObserver { step -> if (step != null) { Simber.i("Executing step: ${step.id}", tag = ORCHESTRATION) - findNavController().navigateSafely(this, step.navigationActionId, step.payload) + findNavController().navigateSafely( + currentFragment = this, + actionId = step.navigationActionId, + args = step.params.toBundle(), + ) } }, ) diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt index 342c0025f8..c06f979bd5 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/OrchestratorViewModel.kt @@ -7,12 +7,14 @@ import androidx.lifecycle.viewModelScope import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.module.SimpleModule import com.simprints.core.domain.response.AppErrorReason +import com.simprints.core.domain.step.StepResult import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameSerializer import com.simprints.core.livedata.LiveDataEventWithContent import com.simprints.core.livedata.send import com.simprints.core.tools.json.JsonHelper +import com.simprints.face.capture.FaceCaptureParams import com.simprints.face.capture.FaceCaptureResult import com.simprints.feature.enrollast.EnrolLastBiometricContract import com.simprints.feature.enrollast.EnrolLastBiometricParams @@ -91,7 +93,7 @@ internal class OrchestratorViewModel @Inject constructor( doNextStep() } - fun handleResult(result: Serializable) = viewModelScope.launch { + fun handleResult(result: StepResult) = viewModelScope.launch { Simber.i("Handling step result: ${result.javaClass.simpleName}", tag = ORCHESTRATION) Simber.d(result.toString(), tag = ORCHESTRATION) @@ -175,11 +177,11 @@ internal class OrchestratorViewModel @Inject constructor( */ private fun updateEnrolLastBiometricParamsIfNeeded(step: Step) { if (step.id == StepId.ENROL_LAST_BIOMETRIC) { - step.payload.getParcelable("params")?.let { params -> + step.params?.let { it as? EnrolLastBiometricParams }?.let { params -> val updatedParams = params.copy( steps = mapStepsForLastBiometrics(steps.mapNotNull { it.result }), ) - step.payload = EnrolLastBiometricContract.getArgs( + step.params = EnrolLastBiometricContract.getParams( projectId = updatedParams.projectId, userId = updatedParams.userId, moduleId = updatedParams.moduleId, @@ -196,7 +198,7 @@ internal class OrchestratorViewModel @Inject constructor( projectConfiguration, actionRequest, steps.mapNotNull { it.result }, - project + project, ) updateDailyActivity(appResponse) @@ -209,29 +211,37 @@ internal class OrchestratorViewModel @Inject constructor( result: Serializable, ) { if (currentStep.id == StepId.FACE_CAPTURE && result is FaceCaptureResult) { - val matchingStep = steps.firstOrNull { it.id == StepId.FACE_MATCHER } + val captureParams = currentStep.params?.let { it as? FaceCaptureParams } + val matchingStep = steps.firstOrNull { step -> + if (step.id != StepId.FACE_MATCHER) { + false + } else { + val stepSdk = step.params?.let { it as? MatchStepStubPayload }?.faceSDK + stepSdk == captureParams?.faceSDK + } + } if (matchingStep != null) { val faceSamples = result.results .mapNotNull { it.sample } .map { MatchParams.FaceSample(it.faceId, it.template) } - val newPayload = matchingStep.payload - .getParcelable(MatchStepStubPayload.STUB_KEY) + val newPayload = matchingStep.params + ?.let { it as? MatchStepStubPayload } ?.toFaceStepArgs(result.referenceId, faceSamples) if (newPayload != null) { - matchingStep.payload = newPayload + matchingStep.params = newPayload } } } if (currentStep.id == StepId.FINGERPRINT_CAPTURE && result is FingerprintCaptureResult) { - val captureParams = currentStep.payload.getParcelable("params") + val captureParams = currentStep.params?.let { it as? FingerprintCaptureParams } // Find the matching step for the same fingerprint SDK as there may be multiple match steps val matchingStep = steps.firstOrNull { step -> if (step.id != StepId.FINGERPRINT_MATCHER) { false } else { - val stepSdk = step.payload.getParcelable(MatchStepStubPayload.STUB_KEY)?.fingerprintSDK + val stepSdk = step.params?.let { it as? MatchStepStubPayload }?.fingerprintSDK stepSdk == captureParams?.fingerprintSDK } } @@ -246,12 +256,12 @@ internal class OrchestratorViewModel @Inject constructor( template = it.template, ) } - val newPayload = matchingStep.payload - .getParcelable(MatchStepStubPayload.STUB_KEY) + val newPayload = matchingStep.params + ?.let { it as? MatchStepStubPayload } ?.toFingerprintStepArgs(result.referenceId, fingerprintSamples) if (newPayload != null) { - matchingStep.payload = newPayload + matchingStep.params = newPayload } } } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/cache/OrchestratorCache.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/cache/OrchestratorCache.kt index d8190e60e6..c88c9e02b1 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/cache/OrchestratorCache.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/cache/OrchestratorCache.kt @@ -1,15 +1,19 @@ package com.simprints.feature.orchestrator.cache -import android.os.Bundle import androidx.core.content.edit import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.module.SimpleModule +import com.simprints.core.domain.step.StepParams +import com.simprints.core.domain.step.StepResult +import com.simprints.core.domain.tokenization.TokenizableString +import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameDeserializer +import com.simprints.core.domain.tokenization.serialization.TokenizationClassNameSerializer import com.simprints.core.tools.json.JsonHelper -import com.simprints.feature.orchestrator.steps.SerializableMixin import com.simprints.feature.orchestrator.steps.Step +import com.simprints.feature.orchestrator.steps.StepParamsMixin +import com.simprints.feature.orchestrator.steps.StepResultMixin import com.simprints.infra.config.store.models.AgeGroup import com.simprints.infra.security.SecurityManager -import java.io.Serializable import javax.inject.Inject import javax.inject.Singleton @@ -21,7 +25,8 @@ internal class OrchestratorCache @Inject constructor( private val prefs = securityManager.buildEncryptedSharedPreferences(ORCHESTRATION_CACHE) private fun List.asJsonArray(jsonHelper: JsonHelper): String = with(jsonHelper) { - addMixin(Serializable::class.java, SerializableMixin::class.java) + addMixin(StepParams::class.java, StepParamsMixin::class.java) + addMixin(StepResult::class.java, StepResultMixin::class.java) return@with joinToString(separator = ",") { jsonHelper.toJson(it, module = stepsModule) }.let { "[$it]" } @@ -36,7 +41,8 @@ internal class OrchestratorCache @Inject constructor( get() = prefs .getString(KEY_STEPS, null) ?.let { jsonArray -> - jsonHelper.addMixin(Serializable::class.java, SerializableMixin::class.java) + jsonHelper.addMixin(StepParams::class.java, StepParamsMixin::class.java) + jsonHelper.addMixin(StepResult::class.java, StepResultMixin::class.java) jsonHelper.fromJson( json = jsonArray, module = stepsModule, @@ -68,8 +74,8 @@ internal class OrchestratorCache @Inject constructor( private const val KEY_AGE_GROUP = "age_group" private val stepsModule = SimpleModule().apply { - addSerializer(Bundle::class.java, BundleSerializer()) - addDeserializer(Bundle::class.java, BundleDeserializer()) + addSerializer(TokenizableString::class.java, TokenizationClassNameSerializer()) + addDeserializer(TokenizableString::class.java, TokenizationClassNameDeserializer()) } } } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt index 6b535a7b37..3f5f42b978 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/MatchStepStubPayload.kt @@ -1,15 +1,13 @@ package com.simprints.feature.orchestrator.steps -import android.os.Parcelable -import androidx.core.os.bundleOf import com.simprints.core.domain.common.FlowType +import com.simprints.core.domain.step.StepParams import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.config.store.models.FingerprintConfiguration import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery import com.simprints.matcher.MatchContract import com.simprints.matcher.MatchParams -import kotlinx.parcelize.Parcelize /** * Actual matching step payload is based on capture step results, so until the it is done we are storing @@ -17,18 +15,17 @@ import kotlinx.parcelize.Parcelize * * This also means that capture step MUST always strictly precede matching step. */ -@Parcelize internal data class MatchStepStubPayload( val flowType: FlowType, val subjectQuery: SubjectQuery, val biometricDataSource: BiometricDataSource, val fingerprintSDK: FingerprintConfiguration.BioSdk?, val faceSDK: FaceConfiguration.BioSdk?, -) : Parcelable { +) : StepParams { fun toFaceStepArgs( referenceId: String, samples: List, - ) = MatchContract.getArgs( + ) = MatchContract.getParams( referenceId = referenceId, faceSamples = samples, faceSDK = faceSDK, @@ -40,7 +37,7 @@ internal data class MatchStepStubPayload( fun toFingerprintStepArgs( referenceId: String, samples: List, - ) = MatchContract.getArgs( + ) = MatchContract.getParams( referenceId = referenceId, fingerprintSamples = samples, fingerprintSDK = fingerprintSDK, @@ -52,12 +49,12 @@ internal data class MatchStepStubPayload( companion object { const val STUB_KEY = "match_step_stub_payload" - fun asBundle( + fun getMatchStubParams( flowType: FlowType, subjectQuery: SubjectQuery, biometricDataSource: BiometricDataSource, fingerprintSDK: FingerprintConfiguration.BioSdk? = null, faceSDK: FaceConfiguration.BioSdk? = null, - ) = bundleOf(STUB_KEY to MatchStepStubPayload(flowType, subjectQuery, biometricDataSource, fingerprintSDK, faceSDK)) + ) = MatchStepStubPayload(flowType, subjectQuery, biometricDataSource, fingerprintSDK, faceSDK) } } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt index 04df05290d..f24eccd8ba 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/steps/Step.kt @@ -1,28 +1,45 @@ package com.simprints.feature.orchestrator.steps -import android.os.Bundle import androidx.annotation.IdRes import androidx.annotation.Keep import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.simprints.core.domain.step.StepParams +import com.simprints.core.domain.step.StepResult +import com.simprints.face.capture.FaceCaptureParams import com.simprints.face.capture.FaceCaptureResult import com.simprints.feature.alert.AlertResult +import com.simprints.feature.consent.ConsentParams import com.simprints.feature.consent.ConsentResult +import com.simprints.feature.enrollast.EnrolLastBiometricParams import com.simprints.feature.enrollast.EnrolLastBiometricResult +import com.simprints.feature.enrollast.EnrolLastBiometricStepResult +import com.simprints.feature.enrollast.FaceTemplateCaptureResult +import com.simprints.feature.enrollast.FingerTemplateCaptureResult +import com.simprints.feature.enrollast.MatchResult import com.simprints.feature.exitform.ExitFormResult +import com.simprints.feature.fetchsubject.FetchSubjectParams import com.simprints.feature.fetchsubject.FetchSubjectResult +import com.simprints.feature.login.LoginParams import com.simprints.feature.login.LoginResult import com.simprints.feature.selectagegroup.SelectSubjectAgeGroupResult +import com.simprints.feature.selectsubject.SelectSubjectParams import com.simprints.feature.selectsubject.SelectSubjectResult import com.simprints.feature.setup.SetupResult +import com.simprints.feature.validatepool.ValidateSubjectPoolFragmentParams import com.simprints.feature.validatepool.ValidateSubjectPoolResult +import com.simprints.fingerprint.capture.FingerprintCaptureParams import com.simprints.fingerprint.capture.FingerprintCaptureResult +import com.simprints.fingerprint.connect.FingerprintConnectParams import com.simprints.fingerprint.connect.FingerprintConnectResult +import com.simprints.infra.enrolment.records.repository.domain.models.BiometricDataSource +import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery import com.simprints.matcher.FaceMatchResult import com.simprints.matcher.FingerprintMatchResult +import com.simprints.matcher.MatchParams import java.io.Serializable -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "resultType") @JsonSubTypes( JsonSubTypes.Type(value = LoginResult::class, name = "LoginResult"), JsonSubTypes.Type(value = SetupResult::class, name = "SetupResult"), @@ -55,21 +72,74 @@ import java.io.Serializable JsonSubTypes.Type(value = ValidateSubjectPoolResult::class, name = "ValidateSubjectPoolResult"), JsonSubTypes.Type(value = SelectSubjectAgeGroupResult::class, name = "SelectSubjectAgeGroupResult"), ) -abstract class SerializableMixin : Serializable +abstract class StepResultMixin : StepResult + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "paramsType") +@JsonSubTypes( + JsonSubTypes.Type(value = LoginParams::class, name = "LoginParams"), + JsonSubTypes.Type(value = ConsentParams::class, name = "ConsentParams"), + JsonSubTypes.Type(value = FetchSubjectParams::class, name = "FetchSubjectParams"), + JsonSubTypes.Type(value = SelectSubjectParams::class, name = "SelectSubjectParams"), + JsonSubTypes.Type(value = ValidateSubjectPoolFragmentParams::class, name = "ValidateSubjectPoolFragmentParams"), + JsonSubTypes.Type(value = FaceCaptureParams::class, name = "FaceCaptureParams"), + JsonSubTypes.Type(value = FingerprintConnectParams::class, name = "FingerprintConnectParams"), + JsonSubTypes.Type(value = FingerprintCaptureParams::class, name = "FingerprintCaptureParams"), + // Match params are updated after capture steps + JsonSubTypes.Type(value = MatchStepStubPayload::class, name = "MatchStepStubPayload"), + JsonSubTypes.Type(value = MatchParams::class, name = "MatchParams"), + JsonSubTypes.Type(value = MatchParams.FaceSample::class, name = "MatchParams.FaceSample"), + JsonSubTypes.Type(value = MatchParams.FingerprintSample::class, name = "MatchParams.FingerprintSample"), + // Below are subclasses of enrol-last step that takes results of other steps as parameters + JsonSubTypes.Type(value = EnrolLastBiometricParams::class, name = "EnrolLastBiometricParams"), + JsonSubTypes.Type(value = EnrolLastBiometricStepResult::class, name = "EnrolLastBiometricStepResult"), + JsonSubTypes.Type( + value = EnrolLastBiometricStepResult.EnrolLastBiometricsResult::class, + name = "EnrolLastBiometricStepResult.EnrolLastBiometricsResult", + ), + JsonSubTypes.Type( + value = EnrolLastBiometricStepResult.FingerprintMatchResult::class, + name = "EnrolLastBiometricStepResult.FingerprintMatchResult", + ), + JsonSubTypes.Type( + value = EnrolLastBiometricStepResult.FaceMatchResult::class, + name = "EnrolLastBiometricStepResult.FaceMatchResult", + ), + JsonSubTypes.Type( + value = EnrolLastBiometricStepResult.FingerprintCaptureResult::class, + name = "EnrolLastBiometricStepResult.FingerprintCaptureResult", + ), + JsonSubTypes.Type( + value = EnrolLastBiometricStepResult.FaceCaptureResult::class, + name = "EnrolLastBiometricStepResult.FaceCaptureResult", + ), + JsonSubTypes.Type(value = MatchResult::class, name = "MatchResult"), + JsonSubTypes.Type(value = FingerTemplateCaptureResult::class, name = "FingerTemplateCaptureResult"), + JsonSubTypes.Type(value = FaceTemplateCaptureResult::class, name = "FaceTemplateCaptureResult"), + // Additional types that are used in top-level params + JsonSubTypes.Type(value = BiometricDataSource::class, name = "BiometricDataSource"), + JsonSubTypes.Type(value = SubjectQuery::class, name = "SubjectQuery"), +) +abstract class StepParamsMixin : StepParams @Keep internal data class Step( val id: Int, @IdRes val navigationActionId: Int, @IdRes val destinationId: Int, - var payload: Bundle, + var params: StepParams? = null, var status: StepStatus = StepStatus.NOT_STARTED, - var result: Serializable? = null, + var result: StepResult? = null, ) : Serializable { // Do not remove. // Even though it may be marked as unused by IDE, it is referenced in the JsonTypeInfo annotation @Suppress("unused") - val type: String + val paramsType: String + get() = this::class.java.simpleName + + // Do not remove. + // Even though it may be marked as unused by IDE, it is referenced in the JsonTypeInfo annotation + @Suppress("unused") + val resultType: String get() = this::class.java.simpleName } diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCase.kt index 4ace434b87..c4ac6f77e7 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCase.kt @@ -25,7 +25,14 @@ internal class MapRefusalOrErrorResultUseCase @Inject constructor( result: Serializable, projectConfiguration: ProjectConfiguration, ): AppResponse? = when (result) { - is ExitFormResult -> AppRefusalResponse.fromResult(result) + is ExitFormResult -> AppRefusalResponse( + result + .submittedOption() + ?.answer + ?.name + .orEmpty(), + result.reason.orEmpty(), + ) is FetchSubjectResult -> result.takeUnless { it.found }?.let { AppErrorResponse( diff --git a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt index e3d021d25f..67b4e7290c 100644 --- a/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt +++ b/feature/orchestrator/src/main/java/com/simprints/feature/orchestrator/usecases/steps/BuildStepsUseCase.kt @@ -1,6 +1,5 @@ package com.simprints.feature.orchestrator.usecases.steps -import androidx.core.os.bundleOf import com.simprints.core.domain.common.FlowType import com.simprints.face.capture.FaceCaptureContract import com.simprints.feature.consent.ConsentContract @@ -216,7 +215,6 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.SELECT_SUBJECT_AGE, navigationActionId = R.id.action_orchestratorFragment_to_age_group_selection, destinationId = SelectSubjectAgeGroupContract.DESTINATION, - payload = bundleOf(), ), ) } else if (projectConfiguration.allowedAgeRanges().none { it.includes(subjectAge) }) { @@ -232,7 +230,6 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.SETUP, navigationActionId = R.id.action_orchestratorFragment_to_setup, destinationId = SetupContract.DESTINATION, - payload = bundleOf(), ), ) @@ -252,7 +249,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.FETCH_GUID, navigationActionId = R.id.action_orchestratorFragment_to_fetchSubject, destinationId = FetchSubjectContract.DESTINATION, - payload = FetchSubjectContract.getArgs(projectId, subjectId), + params = FetchSubjectContract.getParams(projectId, subjectId), ), ) @@ -268,7 +265,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.CONSENT, navigationActionId = R.id.action_orchestratorFragment_to_consent, destinationId = ConsentContract.DESTINATION, - payload = ConsentContract.getArgs(consentType), + params = ConsentContract.getParams(consentType), ), ) } else { @@ -292,7 +289,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.VALIDATE_ID_POOL, navigationActionId = R.id.action_orchestratorFragment_to_validateSubjectPool, destinationId = ValidateSubjectPoolContract.DESTINATION, - payload = ValidateSubjectPoolContract.getArgs(subjectQuery), + params = ValidateSubjectPoolContract.getParams(subjectQuery), ), ) @@ -359,7 +356,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.FINGERPRINT_CAPTURE, navigationActionId = R.id.action_orchestratorFragment_to_fingerprintCapture, destinationId = FingerprintCaptureContract.DESTINATION, - payload = FingerprintCaptureContract.getArgs(flowType, fingersToCollect, bioSDK), + params = FingerprintCaptureContract.getParams(flowType, fingersToCollect, bioSDK), ) } } @@ -374,7 +371,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.FACE_CAPTURE, navigationActionId = R.id.action_orchestratorFragment_to_faceCapture, destinationId = FaceCaptureContract.DESTINATION, - payload = FaceCaptureContract.getArgs(samplesToCapture, bioSDK), + params = FaceCaptureContract.getParams(samplesToCapture, bioSDK), ) } } @@ -413,7 +410,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.FINGERPRINT_MATCHER, navigationActionId = R.id.action_orchestratorFragment_to_matcher, destinationId = MatchContract.DESTINATION, - payload = MatchStepStubPayload.asBundle( + params = MatchStepStubPayload.getMatchStubParams( flowType = flowType, subjectQuery = subjectQuery, biometricDataSource = biometricDataSource, @@ -430,7 +427,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.FACE_MATCHER, navigationActionId = R.id.action_orchestratorFragment_to_matcher, destinationId = MatchContract.DESTINATION, - payload = MatchStepStubPayload.asBundle( + params = MatchStepStubPayload.getMatchStubParams( flowType = flowType, subjectQuery = subjectQuery, biometricDataSource = biometricDataSource, @@ -464,7 +461,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.ENROL_LAST_BIOMETRIC, navigationActionId = R.id.action_orchestratorFragment_to_enrolLast, destinationId = EnrolLastBiometricContract.DESTINATION, - payload = EnrolLastBiometricContract.getArgs( + params = EnrolLastBiometricContract.getParams( projectId = action.projectId, userId = action.userId, moduleId = action.moduleId, @@ -479,7 +476,7 @@ internal class BuildStepsUseCase @Inject constructor( id = StepId.CONFIRM_IDENTITY, navigationActionId = R.id.action_orchestratorFragment_to_selectSubject, destinationId = SelectSubjectContract.DESTINATION, - payload = SelectSubjectContract.getArgs( + params = SelectSubjectContract.getParams( projectId = action.projectId, subjectId = action.selectedGuid, ), diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt index da5b09c520..9e39f0ef01 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/OrchestratorViewModelTest.kt @@ -1,6 +1,5 @@ package com.simprints.feature.orchestrator -import android.os.Bundle import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat @@ -8,6 +7,7 @@ import com.jraska.livedata.test import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.fingerprint.IFingerIdentifier import com.simprints.core.domain.response.AppErrorReason +import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString import com.simprints.face.capture.FaceCaptureResult import com.simprints.feature.consent.ConsentResult @@ -195,7 +195,7 @@ internal class OrchestratorViewModelTest { createMockStep(StepId.FACE_CAPTURE), createMockStep( StepId.FACE_MATCHER, - MatchStepStubPayload.asBundle( + MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, SubjectQuery(), BiometricDataSource.Simprints, @@ -219,7 +219,7 @@ internal class OrchestratorViewModelTest { createMockStep(StepId.FACE_CAPTURE), createMockStep( StepId.FACE_MATCHER, - MatchStepStubPayload.asBundle( + MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, SubjectQuery(), BiometricDataSource.Simprints, @@ -242,7 +242,7 @@ internal class OrchestratorViewModelTest { createMockStep(StepId.FINGERPRINT_CAPTURE), createMockStep( StepId.FINGERPRINT_MATCHER, - MatchStepStubPayload.asBundle( + MatchStepStubPayload.getMatchStubParams( FlowType.VERIFY, SubjectQuery(), BiometricDataSource.Simprints, @@ -264,7 +264,7 @@ internal class OrchestratorViewModelTest { every { stepsBuilder.build(any(), any()) } returns listOf( createMockStep( StepId.FINGERPRINT_CAPTURE, - FingerprintCaptureContract.getArgs( + FingerprintCaptureContract.getParams( flowType = FlowType.VERIFY, fingers = emptyList(), fingerprintSDK = SECUGEN_SIM_MATCHER, @@ -272,7 +272,7 @@ internal class OrchestratorViewModelTest { ), createMockStep( StepId.FINGERPRINT_MATCHER, - MatchStepStubPayload.asBundle( + MatchStepStubPayload.getMatchStubParams( flowType = FlowType.VERIFY, subjectQuery = SubjectQuery(), biometricDataSource = BiometricDataSource.Simprints, @@ -281,7 +281,7 @@ internal class OrchestratorViewModelTest { ), createMockStep( StepId.FINGERPRINT_CAPTURE, - FingerprintCaptureContract.getArgs( + FingerprintCaptureContract.getParams( flowType = FlowType.VERIFY, fingers = emptyList(), fingerprintSDK = NEC, @@ -289,7 +289,7 @@ internal class OrchestratorViewModelTest { ), createMockStep( StepId.FINGERPRINT_MATCHER, - MatchStepStubPayload.asBundle( + MatchStepStubPayload.getMatchStubParams( flowType = FlowType.VERIFY, subjectQuery = SubjectQuery(), biometricDataSource = BiometricDataSource.Simprints, @@ -323,7 +323,7 @@ internal class OrchestratorViewModelTest { viewModel.currentStep.test().value().peekContent()?.let { step -> assertThat(step.id).isEqualTo(StepId.FINGERPRINT_MATCHER) - val params = step.payload.getParcelable("params") + val params = step.params?.let { it as? MatchParams } assertThat(params).isNotNull() assertThat(params?.fingerprintSDK).isEqualTo(SECUGEN_SIM_MATCHER) assertThat(params?.probeFingerprintSamples?.size).isEqualTo(2) @@ -408,14 +408,11 @@ internal class OrchestratorViewModelTest { coEvery { mapRefusalOrErrorResult(any(), any()) } returns null val captureStep = createMockStep(StepId.FINGERPRINT_CAPTURE) val enrolLastStep = createMockStep(StepId.ENROL_LAST_BIOMETRIC) - enrolLastStep.payload.putParcelable( - "params", - EnrolLastBiometricParams( - "projectId", - TokenizableString.Tokenized("userId"), - TokenizableString.Tokenized("moduleId"), - listOf(mockk()), - ), + enrolLastStep.params = EnrolLastBiometricParams( + "projectId", + TokenizableString.Tokenized("userId"), + TokenizableString.Tokenized("moduleId"), + listOf(mockk()), ) every { stepsBuilder.build(any(), any()) } returns listOf( captureStep, @@ -430,19 +427,18 @@ internal class OrchestratorViewModelTest { viewModel.handleResult(FingerprintCaptureResult("", emptyList())) viewModel.currentStep.test().value().peekContent()?.let { step -> - assertThat(step.payload.getParcelable("params")?.steps) - .containsExactly(mockEnrolLastStep) + assertThat(step.params?.let { it as? EnrolLastBiometricParams }?.steps).containsExactly(mockEnrolLastStep) } } private fun createMockStep( stepId: Int, - payload: Bundle = Bundle(), + params: StepParams? = null, ) = Step( id = stepId, navigationActionId = 0, destinationId = 0, - payload = payload, + params = params, status = StepStatus.NOT_STARTED, result = null, ) diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt index 57e93b0da1..ab32d72657 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/cache/OrchestratorCacheIntegrationTest.kt @@ -1,15 +1,16 @@ package com.simprints.feature.orchestrator.cache import android.content.SharedPreferences -import androidx.core.os.bundleOf import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat import com.simprints.core.domain.fingerprint.IFingerIdentifier import com.simprints.core.tools.json.JsonHelper +import com.simprints.face.capture.FaceCaptureParams import com.simprints.feature.orchestrator.steps.Step import com.simprints.feature.orchestrator.steps.StepId import com.simprints.feature.orchestrator.steps.StepStatus import com.simprints.fingerprint.capture.FingerprintCaptureResult +import com.simprints.infra.config.store.models.FaceConfiguration import com.simprints.infra.events.sampledata.SampleDefaults.GUID1 import com.simprints.infra.security.SecurityManager import io.mockk.MockKAnnotations @@ -57,7 +58,6 @@ class OrchestratorCacheIntegrationTest { id = StepId.FINGERPRINT_CAPTURE, navigationActionId = 3, destinationId = 4, - payload = bundleOf("key" to "value"), status = StepStatus.COMPLETED, result = FingerprintCaptureResult( "", @@ -74,7 +74,7 @@ class OrchestratorCacheIntegrationTest { id = StepId.FACE_CAPTURE, navigationActionId = 5, destinationId = 6, - payload = bundleOf("key" to "value"), + params = FaceCaptureParams(3, FaceConfiguration.BioSdk.RANK_ONE), status = StepStatus.COMPLETED, result = null, ), @@ -97,8 +97,6 @@ class OrchestratorCacheIntegrationTest { assertThat(actual.destinationId).isEqualTo(expected.destinationId) assertThat(actual.status).isEqualTo(expected.status) assertThat(actual.result).isEqualTo(expected.result) - - // Direct comparison does not work with bundles due to implementation details - assertThat(actual.payload.keySet()).isEqualTo(expected.payload.keySet()) + assertThat(actual.params).isEqualTo(expected.params) } } diff --git a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt index 310279d2d3..2559681523 100644 --- a/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt +++ b/feature/orchestrator/src/test/java/com/simprints/feature/orchestrator/usecases/MapRefusalOrErrorResultUseCaseTest.kt @@ -3,6 +3,7 @@ package com.simprints.feature.orchestrator.usecases import com.google.common.truth.Truth.assertThat import com.simprints.face.capture.FaceCaptureResult import com.simprints.feature.alert.AlertResult +import com.simprints.feature.exitform.ExitFormOption import com.simprints.feature.exitform.ExitFormResult import com.simprints.feature.fetchsubject.FetchSubjectResult import com.simprints.feature.selectagegroup.SelectSubjectAgeGroupResult @@ -86,4 +87,18 @@ class MapRefusalOrErrorResultUseCaseTest { useCase(SelectSubjectAgeGroupResult(ageGroupNotSupported), projectConfiguration), ).isInstanceOf(AppErrorResponse::class.java) } + + @Test + fun `Maps exit form result to appropriate response`() = runTest { + val projectConfiguration = mockk(relaxed = true) + mapOf( + ExitFormResult(true, null, null) to AppRefusalResponse("", ""), + ExitFormResult(true, null, "reason") to AppRefusalResponse("", "reason"), + ExitFormResult(true, ExitFormOption.AppNotWorking, "") to AppRefusalResponse("APP_NOT_WORKING", ""), + ).forEach { (result, expected) -> + val actual = useCase(result, projectConfiguration) + + assertThat(actual).isEqualTo(expected) + } + } } diff --git a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/SelectSubjectAgeGroupResult.kt b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/SelectSubjectAgeGroupResult.kt index 190130f656..c085a6004d 100644 --- a/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/SelectSubjectAgeGroupResult.kt +++ b/feature/select-subject-age-group/src/main/java/com/simprints/feature/selectagegroup/SelectSubjectAgeGroupResult.kt @@ -1,10 +1,10 @@ package com.simprints.feature.selectagegroup import androidx.annotation.Keep +import com.simprints.core.domain.step.StepResult import com.simprints.infra.config.store.models.AgeGroup -import java.io.Serializable @Keep data class SelectSubjectAgeGroupResult( val ageGroup: AgeGroup, -) : Serializable +) : StepResult diff --git a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectContract.kt b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectContract.kt index b6d53bd665..920e368487 100644 --- a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectContract.kt +++ b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectContract.kt @@ -1,12 +1,10 @@ package com.simprints.feature.selectsubject -import com.simprints.feature.selectsubject.screen.SelectSubjectFragmentArgs - object SelectSubjectContract { val DESTINATION = R.id.selectSubjectFragment - fun getArgs( + fun getParams( projectId: String, subjectId: String, - ) = SelectSubjectFragmentArgs(projectId, subjectId).toBundle() + ) = SelectSubjectParams(projectId, subjectId) } diff --git a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectParams.kt b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectParams.kt new file mode 100644 index 0000000000..ecb6e06887 --- /dev/null +++ b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectParams.kt @@ -0,0 +1,10 @@ +package com.simprints.feature.selectsubject + +import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams + +@Keep +data class SelectSubjectParams( + val projectId: String, + val subjectId: String, +) : StepParams diff --git a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectResult.kt b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectResult.kt index 3a5c5a0637..2cf2df730e 100644 --- a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectResult.kt +++ b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/SelectSubjectResult.kt @@ -1,9 +1,9 @@ package com.simprints.feature.selectsubject import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class SelectSubjectResult( val success: Boolean, -) : Serializable +) : StepResult diff --git a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectFragment.kt b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectFragment.kt index 1b02ab713c..c4cf6ecd24 100644 --- a/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectFragment.kt +++ b/feature/select-subject/src/main/java/com/simprints/feature/selectsubject/screen/SelectSubjectFragment.kt @@ -5,19 +5,20 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.feature.selectsubject.R +import com.simprints.feature.selectsubject.SelectSubjectParams import com.simprints.feature.selectsubject.SelectSubjectResult import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint internal class SelectSubjectFragment : Fragment(R.layout.fragment_select_subject) { private val viewModel: SelectSubjectViewModel by viewModels() - private val args: SelectSubjectFragmentArgs by navArgs() + private val params: SelectSubjectParams by navigationParams() override fun onViewCreated( view: View, @@ -30,7 +31,7 @@ internal class SelectSubjectFragment : Fragment(R.layout.fragment_select_subject viewModel.finish.observe(viewLifecycleOwner) { it.getContentIfNotHandled()?.let(::finishWithResult) } - viewModel.saveGuidSelection(args.projectId, args.subjectId) + viewModel.saveGuidSelection(params.projectId, params.subjectId) } private fun finishWithResult(success: Boolean) { diff --git a/feature/select-subject/src/main/res/navigation/graph_select_subject.xml b/feature/select-subject/src/main/res/navigation/graph_select_subject.xml index 73793f99ea..8f5f4f8dfb 100644 --- a/feature/select-subject/src/main/res/navigation/graph_select_subject.xml +++ b/feature/select-subject/src/main/res/navigation/graph_select_subject.xml @@ -12,12 +12,8 @@ tools:layout="@layout/fragment_select_subject"> - - + android:name="params" + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/feature/setup/src/main/java/com/simprints/feature/setup/SetupResult.kt b/feature/setup/src/main/java/com/simprints/feature/setup/SetupResult.kt index 0905611319..7f8b63f83c 100644 --- a/feature/setup/src/main/java/com/simprints/feature/setup/SetupResult.kt +++ b/feature/setup/src/main/java/com/simprints/feature/setup/SetupResult.kt @@ -1,9 +1,9 @@ package com.simprints.feature.setup import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class SetupResult( val isSuccess: Boolean, -) : Serializable +) : StepResult diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt index 08d2c99fee..342b1fdfdf 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolContract.kt @@ -1,11 +1,9 @@ package com.simprints.feature.validatepool -import android.os.Bundle -import com.simprints.feature.validatepool.screen.ValidateSubjectPoolFragmentArgs import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery object ValidateSubjectPoolContract { - fun getArgs(subjectQuery: SubjectQuery): Bundle = ValidateSubjectPoolFragmentArgs(subjectQuery).toBundle() - val DESTINATION = R.id.validateSubjectPoolFragment + + fun getParams(subjectQuery: SubjectQuery) = ValidateSubjectPoolFragmentParams(subjectQuery) } diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt new file mode 100644 index 0000000000..321c02a780 --- /dev/null +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolFragmentParams.kt @@ -0,0 +1,10 @@ +package com.simprints.feature.validatepool + +import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams +import com.simprints.infra.enrolment.records.repository.domain.models.SubjectQuery + +@Keep +data class ValidateSubjectPoolFragmentParams( + val subjectQuery: SubjectQuery, +) : StepParams diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolResult.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolResult.kt index 1fdaf151d8..909b9c25c9 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolResult.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/ValidateSubjectPoolResult.kt @@ -1,9 +1,9 @@ package com.simprints.feature.validatepool import androidx.annotation.Keep -import java.io.Serializable +import com.simprints.core.domain.step.StepResult @Keep data class ValidateSubjectPoolResult( val isValid: Boolean, -) : Serializable +) : StepResult diff --git a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt index 777c0e7021..058dcd8cd2 100644 --- a/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt +++ b/feature/validate-subject-pool/src/main/java/com/simprints/feature/validatepool/screen/ValidateSubjectPoolFragment.kt @@ -7,15 +7,16 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.livedata.LiveDataEventWithContentObserver import com.simprints.feature.validatepool.R +import com.simprints.feature.validatepool.ValidateSubjectPoolFragmentParams import com.simprints.feature.validatepool.ValidateSubjectPoolResult import com.simprints.feature.validatepool.databinding.FragmentValidateSubjectPoolBinding import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint import com.simprints.infra.resources.R as IDR @@ -24,7 +25,7 @@ import com.simprints.infra.resources.R as IDR internal class ValidateSubjectPoolFragment : Fragment(R.layout.fragment_validate_subject_pool) { private val viewModel: ValidateSubjectPoolViewModel by viewModels() private val binding by viewBinding(FragmentValidateSubjectPoolBinding::bind) - private val args: ValidateSubjectPoolFragmentArgs by navArgs() + private val params: ValidateSubjectPoolFragmentParams by navigationParams() override fun onViewCreated( view: View, @@ -38,9 +39,9 @@ internal class ValidateSubjectPoolFragment : Fragment(R.layout.fragment_validate binding.validationActionsClose.setOnClickListener { finishWithResult(false) } binding.validationActionsContinue.setOnClickListener { finishWithResult(true) } - binding.validationActionsSync.setOnClickListener { viewModel.syncAndRetry(args.subjectQuery) } + binding.validationActionsSync.setOnClickListener { viewModel.syncAndRetry(params.subjectQuery) } - viewModel.checkIdentificationPool(args.subjectQuery) + viewModel.checkIdentificationPool(params.subjectQuery) } private fun renderState(state: ValidateSubjectPoolState) = when (state) { diff --git a/feature/validate-subject-pool/src/main/res/navigation/graph_validate_subject_pool.xml b/feature/validate-subject-pool/src/main/res/navigation/graph_validate_subject_pool.xml index b56395e2de..8fd30b95dd 100644 --- a/feature/validate-subject-pool/src/main/res/navigation/graph_validate_subject_pool.xml +++ b/feature/validate-subject-pool/src/main/res/navigation/graph_validate_subject_pool.xml @@ -12,8 +12,8 @@ tools:layout="@layout/fragment_validate_subject_pool"> + android:name="params" + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt index 5f263a4fdc..5f74639a5d 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureContract.kt @@ -2,15 +2,14 @@ package com.simprints.fingerprint.capture import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.fingerprint.IFingerIdentifier -import com.simprints.fingerprint.capture.screen.FingerprintCaptureFragmentArgs import com.simprints.infra.config.store.models.FingerprintConfiguration object FingerprintCaptureContract { val DESTINATION = R.id.fingerprintCaptureFragment - fun getArgs( + fun getParams( flowType: FlowType, fingers: List, fingerprintSDK: FingerprintConfiguration.BioSdk, - ) = FingerprintCaptureFragmentArgs(FingerprintCaptureParams(flowType, fingers, fingerprintSDK)).toBundle() + ) = FingerprintCaptureParams(flowType, fingers, fingerprintSDK) } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt index 22d22b9f2d..75c487a2bf 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureParams.kt @@ -1,16 +1,14 @@ package com.simprints.fingerprint.capture -import android.os.Parcelable import androidx.annotation.Keep import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.core.domain.step.StepParams import com.simprints.infra.config.store.models.FingerprintConfiguration -import kotlinx.parcelize.Parcelize @Keep -@Parcelize data class FingerprintCaptureParams( val flowType: FlowType, val fingerprintsToCapture: List, val fingerprintSDK: FingerprintConfiguration.BioSdk, -) : Parcelable +) : StepParams diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt index 5fae8aba48..ad73c56792 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/FingerprintCaptureResult.kt @@ -2,20 +2,20 @@ package com.simprints.fingerprint.capture import androidx.annotation.Keep import com.simprints.core.domain.fingerprint.IFingerIdentifier +import com.simprints.core.domain.step.StepResult import com.simprints.infra.images.model.SecuredImageRef -import java.io.Serializable @Keep data class FingerprintCaptureResult( val referenceId: String, var results: List, -) : Serializable { +) : StepResult { @Keep data class Item( val captureEventId: String?, val identifier: IFingerIdentifier, val sample: Sample?, - ) : Serializable + ) : StepResult @Keep data class Sample( @@ -24,5 +24,5 @@ data class FingerprintCaptureResult( val templateQualityScore: Int, val imageRef: SecuredImageRef?, val format: String, - ) : Serializable + ) : StepResult } diff --git a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt index 67dafb3195..511687aea0 100644 --- a/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt +++ b/fingerprint/capture/src/main/java/com/simprints/fingerprint/capture/screen/FingerprintCaptureFragment.kt @@ -14,7 +14,6 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController -import androidx.navigation.fragment.navArgs import com.simprints.core.domain.common.FlowType import com.simprints.core.domain.response.AppErrorReason import com.simprints.core.livedata.LiveDataEventObserver @@ -27,6 +26,7 @@ import com.simprints.feature.alert.config.AlertColor import com.simprints.feature.alert.toArgs import com.simprints.feature.exitform.ExitFormContract import com.simprints.feature.exitform.ExitFormResult +import com.simprints.fingerprint.capture.FingerprintCaptureParams import com.simprints.fingerprint.capture.R import com.simprints.fingerprint.capture.databinding.FragmentFingerprintCaptureBinding import com.simprints.fingerprint.capture.resources.buttonBackgroundColour @@ -44,11 +44,13 @@ import com.simprints.infra.logging.LoggingConstants.CrashReportTag.FINGER_CAPTUR import com.simprints.infra.logging.LoggingConstants.CrashReportTag.ORCHESTRATION import com.simprints.infra.logging.Simber import com.simprints.infra.uibase.extensions.showToast -import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.navigation.finishWithResult import com.simprints.infra.uibase.navigation.handleResult import com.simprints.infra.uibase.navigation.navigateSafely +import com.simprints.infra.uibase.navigation.navigationParams +import com.simprints.infra.uibase.navigation.toBundle import com.simprints.infra.uibase.system.Vibrate +import com.simprints.infra.uibase.view.applySystemBarInsets import com.simprints.infra.uibase.viewbinding.viewBinding import dagger.hilt.android.AndroidEntryPoint import java.io.Serializable @@ -57,7 +59,7 @@ import com.simprints.infra.resources.R as IDR @AndroidEntryPoint internal class FingerprintCaptureFragment : Fragment(R.layout.fragment_fingerprint_capture) { - private val args: FingerprintCaptureFragmentArgs by navArgs() + private val params: FingerprintCaptureParams by navigationParams() private val binding by viewBinding(FragmentFingerprintCaptureBinding::bind) private val vm: FingerprintCaptureViewModel by viewModels() @@ -123,14 +125,14 @@ internal class FingerprintCaptureFragment : Fragment(R.layout.fragment_fingerpri vm.launchReconnect.observe(viewLifecycleOwner, LiveDataEventObserver { launchConnection() }) vm.handleOnViewCreated( - args.params.fingerprintsToCapture, - args.params.fingerprintSDK, + params.fingerprintsToCapture, + params.fingerprintSDK, ) initUI() } private fun initUI() { - initToolbar(args.params.flowType) + initToolbar(params.flowType) initMissingFingerButton() initViewPagerManager() initScanButton() @@ -305,7 +307,7 @@ internal class FingerprintCaptureFragment : Fragment(R.layout.fragment_fingerpri findNavController().navigateSafely( this, R.id.action_fingerprintCaptureFragment_to_graphConnectScanner, - FingerprintConnectContract.getArgs(args.params.fingerprintSDK), + FingerprintConnectContract.getParams(params.fingerprintSDK).toBundle(), ) } catch (e: Exception) { Simber.i("Error launching scanner connection screen", e, tag = FINGER_CAPTURE) @@ -317,7 +319,7 @@ internal class FingerprintCaptureFragment : Fragment(R.layout.fragment_fingerpri vm.handleOnResume() observeFingerprintScanStatus( viewLifecycleOwner.lifecycleScope, - args.params.fingerprintSDK, + params.fingerprintSDK, ) val color = vm.stateLiveData.value ?.currentCaptureState() diff --git a/fingerprint/capture/src/main/res/navigation/graph_fingerprint_capture.xml b/fingerprint/capture/src/main/res/navigation/graph_fingerprint_capture.xml index 9c75698752..00539a274f 100644 --- a/fingerprint/capture/src/main/res/navigation/graph_fingerprint_capture.xml +++ b/fingerprint/capture/src/main/res/navigation/graph_fingerprint_capture.xml @@ -11,7 +11,7 @@ + app:argType="com.simprints.core.domain.step.StepParams" /> + app:argType="com.simprints.core.domain.step.StepParams" /> diff --git a/infra/core/src/main/java/com/simprints/core/domain/step/StepParams.kt b/infra/core/src/main/java/com/simprints/core/domain/step/StepParams.kt new file mode 100644 index 0000000000..8a20969b08 --- /dev/null +++ b/infra/core/src/main/java/com/simprints/core/domain/step/StepParams.kt @@ -0,0 +1,7 @@ +package com.simprints.core.domain.step + +import androidx.annotation.Keep +import java.io.Serializable + +@Keep +interface StepParams : Serializable diff --git a/infra/core/src/main/java/com/simprints/core/domain/step/StepResult.kt b/infra/core/src/main/java/com/simprints/core/domain/step/StepResult.kt new file mode 100644 index 0000000000..c8e458852e --- /dev/null +++ b/infra/core/src/main/java/com/simprints/core/domain/step/StepResult.kt @@ -0,0 +1,7 @@ +package com.simprints.core.domain.step + +import androidx.annotation.Keep +import java.io.Serializable + +@Keep +interface StepResult : Serializable diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/BiometricDataSource.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/BiometricDataSource.kt index c1d9e7de23..de47c05542 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/BiometricDataSource.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/BiometricDataSource.kt @@ -1,23 +1,19 @@ package com.simprints.infra.enrolment.records.repository.domain.models -import android.os.Parcelable import androidx.annotation.Keep -import kotlinx.parcelize.Parcelize +import com.simprints.core.domain.step.StepParams @Keep -sealed class BiometricDataSource : Parcelable { +sealed class BiometricDataSource : StepParams { open fun callerPackageName(): String = "" open fun permissionName(): String? = null - @Parcelize - data object Simprints : BiometricDataSource(), Parcelable + data object Simprints : BiometricDataSource() - @Parcelize data class CommCare( private val callerPackageName: String, - ) : BiometricDataSource(), - Parcelable { + ) : BiometricDataSource() { override fun callerPackageName() = callerPackageName override fun permissionName() = "$callerPackageName.provider.cases.read" diff --git a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt index c16b6693c9..73f3af0d43 100644 --- a/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt +++ b/infra/enrolment-records/repository/src/main/java/com/simprints/infra/enrolment/records/repository/domain/models/SubjectQuery.kt @@ -1,8 +1,8 @@ package com.simprints.infra.enrolment.records.repository.domain.models import androidx.annotation.Keep +import com.simprints.core.domain.step.StepParams import com.simprints.core.domain.tokenization.TokenizableString -import java.io.Serializable @Keep data class SubjectQuery( @@ -17,4 +17,4 @@ data class SubjectQuery( val sort: Boolean = false, val afterSubjectId: String? = null, val metadata: String? = null, -) : Serializable +) : StepParams diff --git a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponse.kt b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponse.kt index 9da871a63b..709fd51e5c 100644 --- a/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponse.kt +++ b/infra/orchestrator-data/src/main/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponse.kt @@ -2,7 +2,6 @@ package com.simprints.infra.orchestration.data.responses import androidx.annotation.Keep import com.simprints.core.ExcludedFromGeneratedTestCoverageReports -import com.simprints.feature.exitform.ExitFormResult import kotlinx.parcelize.Parcelize @Keep @@ -11,15 +10,4 @@ import kotlinx.parcelize.Parcelize data class AppRefusalResponse( val reason: String, val extra: String, -) : AppResponse() { - companion object { - fun fromResult(result: ExitFormResult) = AppRefusalResponse( - result - .submittedOption() - ?.answer - ?.name - .orEmpty(), - result.reason.orEmpty(), - ) - } -} +) : AppResponse() diff --git a/infra/orchestrator-data/src/test/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponseTest.kt b/infra/orchestrator-data/src/test/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponseTest.kt deleted file mode 100644 index e318703793..0000000000 --- a/infra/orchestrator-data/src/test/java/com/simprints/infra/orchestration/data/responses/AppRefusalResponseTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.simprints.infra.orchestration.data.responses - -import com.google.common.truth.Truth.assertThat -import com.simprints.feature.exitform.ExitFormOption -import com.simprints.feature.exitform.ExitFormResult -import org.junit.Test - -class AppRefusalResponseTest { - @Test - fun testFromResult() { - mapOf( - ExitFormResult(true, null, null) to AppRefusalResponse("", ""), - ExitFormResult(true, null, "reason") to AppRefusalResponse("", "reason"), - ExitFormResult(true, ExitFormOption.AppNotWorking, "") to AppRefusalResponse("APP_NOT_WORKING", ""), - ).forEach { (result, expected) -> - val actual = AppRefusalResponse.fromResult(result) - - assertThat(actual).isEqualTo(expected) - } - } -} diff --git a/infra/test-tools/src/main/java/com/simprints/testtools/hilt/HiltExt.kt b/infra/test-tools/src/main/java/com/simprints/testtools/hilt/HiltExt.kt index 02fd14414f..155b7f7c03 100644 --- a/infra/test-tools/src/main/java/com/simprints/testtools/hilt/HiltExt.kt +++ b/infra/test-tools/src/main/java/com/simprints/testtools/hilt/HiltExt.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.commitNow import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelStore import androidx.navigation.NavController import androidx.navigation.Navigation import androidx.navigation.testing.TestNavHostController @@ -79,6 +80,7 @@ fun testNavController( startDestination: Int? = null, ): TestNavHostController { val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) + navController.setViewModelStore(ViewModelStore()) navController.setGraph(graph) startDestination?.also { navController.setCurrentDestination(it) } return navController diff --git a/infra/ui-base/build.gradle.kts b/infra/ui-base/build.gradle.kts index 9980b8e871..7053015455 100644 --- a/infra/ui-base/build.gradle.kts +++ b/infra/ui-base/build.gradle.kts @@ -18,6 +18,8 @@ android { dependencies { implementation(project(":infra:logging")) + api(project(":infra:core")) + api(libs.androidX.core) api(libs.androidX.appcompat) api(libs.androidX.lifecycle) diff --git a/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationParamsDelegate.kt b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationParamsDelegate.kt new file mode 100644 index 0000000000..d83d6f0717 --- /dev/null +++ b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationParamsDelegate.kt @@ -0,0 +1,28 @@ +package com.simprints.infra.uibase.navigation + +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import com.simprints.core.ExcludedFromGeneratedTestCoverageReports +import com.simprints.core.domain.step.StepParams +import java.io.Serializable +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +@ExcludedFromGeneratedTestCoverageReports("UI code") +class NavigationParamsDelegate : ReadOnlyProperty { + override fun getValue( + thisRef: Fragment, + property: KProperty<*>, + ): R = thisRef.arguments + ?.getSerializable(PARAM_KEY) + ?.let { it as? R } + ?: throw IllegalStateException("Fragment does not define serializable argument") +} + +@ExcludedFromGeneratedTestCoverageReports("UI code") +fun navigationParams() = NavigationParamsDelegate() + +private const val PARAM_KEY = "param" + +@ExcludedFromGeneratedTestCoverageReports("UI code") +fun StepParams?.toBundle() = bundleOf(PARAM_KEY to this) diff --git a/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt index 45221139a7..82b96148d8 100644 --- a/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt +++ b/infra/ui-base/src/main/java/com/simprints/infra/uibase/navigation/NavigationResultExt.kt @@ -79,9 +79,15 @@ fun NavController.handleResult( // `getCurrentBackStackEntry` doesn't work in case of recovery from the process death when dialog is opened. val currentEntry = getBackStackEntry(currentDestinationId) + val expectedResultKey = resultName(targetDestinationId) + // After config change, current fragment's NavBackStackEntry is recreated. + // The result, set by the target fragment on the *previous* NavBackStackEntry's SavedStateHandle, + // might be available before ON_RESUME fires for the new entry, or if the initial check for the result was missed. + // Therefore, checking SavedStateHandle immediately when (re)registering listener and on ON_RESUME. + handleResultFromChild(expectedResultKey, currentEntry, handler) val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) { - handleResultFromChild(targetDestinationId, currentEntry, handler) + handleResultFromChild(expectedResultKey, currentEntry, handler) } } currentEntry.lifecycle.addObserver(observer) @@ -95,12 +101,10 @@ fun NavController.handleResult( } private fun handleResultFromChild( - @IdRes childDestinationId: Int, + expectedResultKey: String, currentEntry: NavBackStackEntry, handler: (T) -> Unit, ) { - val expectedResultKey = resultName(childDestinationId) - with(currentEntry.savedStateHandle) { if (contains(expectedResultKey)) { get(expectedResultKey)?.let(handler)