-
Notifications
You must be signed in to change notification settings - Fork 0
Identification features #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ab6943a
1b259b4
7a75164
7f9d3e2
742640f
73e8192
9c3b9a5
3c276f0
8a699cd
24bd341
00914aa
be18e59
40a7295
4ec7a81
d725805
4ee2168
52ece4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package org.dhis2.simprints | ||
|
|
||
| import android.content.Intent | ||
| import org.dhis2.form.ui.customintent.CustomIntentActivityResultContract | ||
| import org.dhis2.mobile.commons.model.CustomIntentResponseDataModel | ||
|
|
||
| class SimprintsCustomIntentResultMapper( | ||
| private val contract: CustomIntentActivityResultContract = CustomIntentActivityResultContract(), | ||
| ) { | ||
| fun map( | ||
| responseData: List<CustomIntentResponseDataModel>?, | ||
| data: Intent?, | ||
| ): String? = | ||
| contract | ||
| .mapIntentResponseData(responseData, data) | ||
| ?.takeUnless(List<String>::isEmpty) | ||
| ?.joinToString(separator = ",") | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| package org.dhis2.simprints | ||
|
|
||
| import android.app.Activity.RESULT_OK | ||
| import android.content.Intent | ||
| import androidx.lifecycle.ViewModel | ||
| import kotlin.concurrent.atomics.AtomicReference | ||
| import kotlin.concurrent.atomics.ExperimentalAtomicApi | ||
| import org.dhis2.commons.simprints.repository.SimprintsD2Repository | ||
| import org.dhis2.commons.simprints.repository.SimprintsSessionRepository | ||
| import org.dhis2.commons.simprints.usecases.SimprintsResolvePendingEnrollmentActionUseCase | ||
|
|
||
| @OptIn(ExperimentalAtomicApi::class) | ||
| class SimprintsEnrollmentViewModel( | ||
| private val simprintsD2Repository: SimprintsD2Repository, | ||
| private val resolvePendingEnrollmentAction: SimprintsResolvePendingEnrollmentActionUseCase, | ||
| private val sessionRepository: SimprintsSessionRepository, | ||
| private val resultMapper: SimprintsCustomIntentResultMapper, | ||
| ) : ViewModel() { | ||
| enum class RegisterLastResult { | ||
| NONE, | ||
| CONTINUE_FINISH, | ||
| ERROR, | ||
| } | ||
|
|
||
| private val pendingAction = | ||
| AtomicReference<SimprintsResolvePendingEnrollmentActionUseCase.PendingEnrollmentAction?>(null) | ||
|
|
||
| suspend fun onFinishRequested( | ||
| isNewEnrollment: Boolean, | ||
| enrollmentUid: String, | ||
| ): Intent? { | ||
| if (!isNewEnrollment) { | ||
| return null | ||
| } | ||
|
|
||
| val resolvedAction = | ||
| sessionRepository.pendingEnrollmentSessionId()?.let { | ||
| resolvePendingEnrollmentAction( | ||
| enrollmentUid = enrollmentUid, | ||
| sessionId = it, | ||
| ) | ||
| } ?: return null | ||
|
|
||
| pendingAction.store(resolvedAction) | ||
| return resolvedAction.callout.launchIntent | ||
| } | ||
|
|
||
| suspend fun onRegisterLastResult( | ||
| resultCode: Int, | ||
| data: Intent?, | ||
| teiUid: String?, | ||
| ): RegisterLastResult { | ||
| val resolvedAction = pendingAction.exchange(null) ?: return RegisterLastResult.NONE | ||
|
|
||
| val saved = | ||
| if (resultCode == RESULT_OK && teiUid != null) { | ||
| val value = | ||
| resultMapper.map( | ||
| responseData = resolvedAction.callout.responseData, | ||
| data = data, | ||
| ) ?: return RegisterLastResult.ERROR | ||
| simprintsD2Repository.saveTrackedEntityAttributeValue( | ||
| teiUid = teiUid, | ||
| attributeUid = resolvedAction.fieldUid, | ||
| value = value, | ||
| ) | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
|
|
||
| if (saved) { | ||
| sessionRepository.clear() | ||
| return RegisterLastResult.CONTINUE_FINISH | ||
| } | ||
|
|
||
| return RegisterLastResult.ERROR | ||
| } | ||
|
|
||
| fun onRegisterLastLaunchFailed() { | ||
| pendingAction.store(null) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| package org.dhis2.simprints | ||
|
|
||
| import android.app.Activity.RESULT_OK | ||
| import android.content.Intent | ||
| import androidx.lifecycle.ViewModel | ||
| import kotlin.concurrent.atomics.AtomicReference | ||
| import kotlin.concurrent.atomics.ExperimentalAtomicApi | ||
| import org.dhis2.commons.simprints.repository.SimprintsSessionRepository | ||
| import org.dhis2.commons.simprints.usecases.SimprintsResolveConfirmIdentityCalloutUseCase | ||
| import org.dhis2.commons.simprints.utils.SimprintsSearchUtils | ||
|
|
||
| @OptIn(ExperimentalAtomicApi::class) | ||
| class SimprintsSearchViewModel( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As discussed on slack should extend android VM |
||
| private val resolveConfirmIdentityCallout: SimprintsResolveConfirmIdentityCalloutUseCase, | ||
| private val sessionRepository: SimprintsSessionRepository, | ||
| ) : ViewModel() { | ||
| data class PendingDashboardNavigation( | ||
| val teiUid: String, | ||
| val programUid: String?, | ||
| val enrollmentUid: String?, | ||
| ) | ||
|
|
||
| sealed class DashboardAction { | ||
| data class LaunchConfirmIdentity( | ||
| val intent: Intent, | ||
| ) : DashboardAction() | ||
|
|
||
| data class OpenDashboard( | ||
| val navigation: PendingDashboardNavigation, | ||
| ) : DashboardAction() | ||
| } | ||
|
|
||
| private val pendingDashboardNavigation = AtomicReference<PendingDashboardNavigation?>(null) | ||
|
|
||
| suspend fun onDashboardRequested( | ||
| searchFields: List<SimprintsSearchUtils.SearchField>, | ||
| teiUid: String, | ||
| programUid: String?, | ||
| enrollmentUid: String?, | ||
| ): DashboardAction { | ||
| val searchState = SimprintsSearchUtils.searchState(searchFields) | ||
| val sessionId = | ||
| sessionRepository.get()?.takeIf { searchState.hasBiometricIdentificationQuery } | ||
| val confirmIdentityIntent = | ||
| sessionId?.let { | ||
| resolveConfirmIdentityCallout( | ||
| teiUid = teiUid, | ||
| searchFields = searchFields, | ||
| sessionId = it, | ||
| )?.launchIntent | ||
| } | ||
|
|
||
| val navigation = | ||
| PendingDashboardNavigation( | ||
| teiUid = teiUid, | ||
| programUid = programUid, | ||
| enrollmentUid = enrollmentUid, | ||
| ) | ||
| if (confirmIdentityIntent == null) { | ||
| return DashboardAction.OpenDashboard(navigation) | ||
| } | ||
|
|
||
| pendingDashboardNavigation.store(navigation) | ||
| sessionRepository.clear() | ||
| return DashboardAction.LaunchConfirmIdentity(confirmIdentityIntent) | ||
| } | ||
|
|
||
| fun prepareEnrollmentQueryData( | ||
| searchFields: List<SimprintsSearchUtils.SearchField>, | ||
| queryData: Map<String, List<String>?>, | ||
| ): HashMap<String, List<String>> { | ||
| val searchState = SimprintsSearchUtils.searchState(searchFields) | ||
|
|
||
| if (searchState.hasBiometricIdentificationQuery && sessionRepository.hasPendingSession()) { | ||
| sessionRepository.markPendingEnrollment() | ||
| } | ||
|
|
||
| return SimprintsSearchUtils.filterQueryData( | ||
| queryData = queryData, | ||
| fields = searchFields, | ||
| ) | ||
| } | ||
|
|
||
| fun onConfirmIdentityResult(resultCode: Int): PendingDashboardNavigation? = | ||
| pendingDashboardNavigation.exchange(null) | ||
| ?.takeIf { resultCode == RESULT_OK } | ||
|
|
||
| fun onConfirmIdentityLaunchFailed() { | ||
| pendingDashboardNavigation.store(null) | ||
| } | ||
|
|
||
| fun clearPendingSessionIfNeeded(searchFields: List<SimprintsSearchUtils.SearchField>) { | ||
| val searchState = SimprintsSearchUtils.searchState(searchFields) | ||
| if (sessionRepository.hasPendingSession() && searchState.shouldClearPendingSession) { | ||
| sessionRepository.clear() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clearing the session state in a function called ShouldUseLastX is a bit confusing as it feels like a side effect. You call this to find out if you should use the label, but then without knowing it the session is cleared. I'd either change the name to be more explicit, or just split this function into 2: fun clearPendingSessionIfNeeded(searchFields: List<SimprintsSearchUtils.SearchField>) { fun shouldUseLastBiometricsLabel(searchFields: List<SimprintsSearchUtils.SearchField>): Boolean { |
||
| } | ||
| } | ||
|
|
||
| fun shouldUseLastBiometricsLabel(searchFields: List<SimprintsSearchUtils.SearchField>): Boolean { | ||
| val searchState = SimprintsSearchUtils.searchState(searchFields) | ||
| return SimprintsSearchUtils.shouldUseLastBiometricsLabel( | ||
| searchState = searchState, | ||
| hasPendingSession = sessionRepository.hasPendingSession(), | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package org.dhis2.simprints.di | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import androidx.lifecycle.ViewModelProvider | ||
| import org.dhis2.commons.simprints.repository.SimprintsD2Repository | ||
| import org.dhis2.commons.simprints.repository.SimprintsSessionRepository | ||
| import org.dhis2.commons.simprints.usecases.SimprintsResolvePendingEnrollmentActionUseCase | ||
| import org.dhis2.simprints.SimprintsCustomIntentResultMapper | ||
| import org.dhis2.simprints.SimprintsEnrollmentViewModel | ||
|
|
||
| class SimprintsEnrollmentViewModelFactory( | ||
| private val simprintsD2Repository: SimprintsD2Repository, | ||
| private val resolvePendingEnrollmentAction: SimprintsResolvePendingEnrollmentActionUseCase, | ||
| private val sessionRepository: SimprintsSessionRepository, | ||
| private val resultMapper: SimprintsCustomIntentResultMapper, | ||
| ) : ViewModelProvider.Factory { | ||
| override fun <T : ViewModel> create(modelClass: Class<T>): T = | ||
| SimprintsEnrollmentViewModel( | ||
| simprintsD2Repository = simprintsD2Repository, | ||
| resolvePendingEnrollmentAction = resolvePendingEnrollmentAction, | ||
| sessionRepository = sessionRepository, | ||
| resultMapper = resultMapper, | ||
| ) as T | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package org.dhis2.simprints.di | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import androidx.lifecycle.ViewModelProvider | ||
| import org.dhis2.commons.simprints.repository.SimprintsSessionRepository | ||
| import org.dhis2.commons.simprints.usecases.SimprintsResolveConfirmIdentityCalloutUseCase | ||
| import org.dhis2.simprints.SimprintsSearchViewModel | ||
|
|
||
| class SimprintsSearchViewModelFactory( | ||
| private val resolveConfirmIdentityCallout: SimprintsResolveConfirmIdentityCalloutUseCase, | ||
| private val sessionRepository: SimprintsSessionRepository, | ||
| ) : ViewModelProvider.Factory { | ||
| override fun <T : ViewModel> create(modelClass: Class<T>): T = | ||
| SimprintsSearchViewModel( | ||
| resolveConfirmIdentityCallout = resolveConfirmIdentityCallout, | ||
| sessionRepository = sessionRepository, | ||
| ) as T | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on slack should extend android VM