From 1ff2c2eed033b9188466db933f2b9468263f6892 Mon Sep 17 00:00:00 2001 From: axelherbstreith <75120552+axelherbstreith@users.noreply.github.com> Date: Wed, 21 Apr 2021 13:50:51 +0200 Subject: [PATCH] Update TAN Submission Flow (EXPOSUREAPP-6595) (#2885) * updated screen wiring * linting * fix * updated nav args handling --- .../SubmissionDeletionWarningFragment.kt | 29 ++++--- .../SubmissionDeletionWarningViewModel.kt | 81 +++++++++++++++++-- .../scan/SubmissionQRCodeScanFragment.kt | 2 +- .../scan/SubmissionQRCodeScanViewModel.kt | 6 +- .../submission/tan/SubmissionTanFragment.kt | 15 +++- .../submission/tan/SubmissionTanViewModel.kt | 61 +++++++++----- .../viewmodel/SubmissionNavigationEvents.kt | 12 ++- .../src/main/res/navigation/nav_graph.xml | 15 ++++ 8 files changed, 177 insertions(+), 44 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt index b0ceeaeef..e3707942a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt @@ -37,7 +37,7 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ factoryProducer = { viewModelFactory }, constructorCall = { factory, _ -> factory as SubmissionDeletionWarningViewModel.Factory - factory.create(args.coronaTestQrCode, args.isConsentGiven) + factory.create(args.coronaTestQrCode, args.coronaTestTan, args.isConsentGiven) } ) private val binding: FragmentSubmissionDeletionWarningBinding by viewBindingLazy() @@ -47,7 +47,7 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ binding.apply { - when (args.coronaTestQrCode.type) { + when (viewModel.getTestType()) { CoronaTest.Type.PCR -> { headline.text = getString(R.string.submission_deletion_warning_headline_pcr_test) body.text = getString(R.string.submission_deletion_warning_body_pcr_test) @@ -60,6 +60,7 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ } continueButton.setOnClickListener { + viewModel.deleteExistingAndRegisterNewTest() } @@ -85,14 +86,24 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ binding.submissionQrCodeScanSpinner.isVisible = state.apiRequestState == ApiRequestState.STARTED if (ApiRequestState.SUCCESS == state.apiRequestState) { - if (state.testResult == CoronaTestResult.PCR_POSITIVE) { - viewModel.triggerNavigationToSubmissionTestResultAvailableFragment() - } else { - viewModel.triggerNavigationToSubmissionTestResultPendingFragment() + + when (viewModel.getRegistrationType()) { + SubmissionDeletionWarningViewModel.RegistrationType.QR -> { + if (state.testResult == CoronaTestResult.PCR_POSITIVE) { + viewModel.triggerNavigationToSubmissionTestResultAvailableFragment() + } else { + viewModel.triggerNavigationToSubmissionTestResultPendingFragment() + } + } + SubmissionDeletionWarningViewModel.RegistrationType.TAN -> { + doNavigate( + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionFragmentToSubmissionTestResultNoConsentFragment() + ) + } } } } - viewModel.registrationError.observe2(this) { DialogHelper.showDialog(buildErrorDialog(it)) } @@ -109,7 +120,7 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ doNavigate( SubmissionDeletionWarningFragmentDirections .actionSubmissionDeletionWarningFragmentToSubmissionTestResultAvailableFragment( - args.coronaTestQrCode.type + testType = it.coronaTestType ) ) } @@ -117,7 +128,7 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ doNavigate( SubmissionDeletionWarningFragmentDirections .actionSubmissionDeletionWarningFragmentToSubmissionTestResultPendingFragment( - testType = args.coronaTestQrCode.type + testType = it.coronaTestType ) ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt index 1013bc551..7d0738a27 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt @@ -10,6 +10,8 @@ import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN +import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.TransactionException import de.rki.coronawarnapp.exception.http.CwaWebException @@ -26,7 +28,9 @@ import timber.log.Timber class SubmissionDeletionWarningViewModel @AssistedInject constructor( private val coronaTestRepository: CoronaTestRepository, private val submissionRepository: SubmissionRepository, - @Assisted private val coronaTest: CoronaTestQRCode, + @Assisted private val coronaTestQrCode: CoronaTestQRCode?, + @Assisted private val coronaTestQrTan: CoronaTestTAN?, + @Assisted private val isConsentGiven: Boolean, ) : CWAViewModel() { @@ -37,16 +41,60 @@ class SubmissionDeletionWarningViewModel @AssistedInject constructor( val registrationError = SingleLiveEvent<CwaWebException>() fun deleteExistingAndRegisterNewTest() = launch { - val currentTest = submissionRepository.testForType(coronaTest.type).first() + when (getRegistrationType()) { + RegistrationType.QR -> deleteExistingAndRegisterNewTestWithQrCode() + RegistrationType.TAN -> deleteExistingAndRegisterNewTestWitTAN() + } + } + + private suspend fun deleteExistingAndRegisterNewTestWithQrCode() { + try { + val currentTest = submissionRepository.testForType(coronaTestQrCode!!.type).first() + coronaTestRepository.removeTest(currentTest!!.identifier) + doDeviceRegistration(coronaTestQrCode) + } catch (err: Exception) { + Timber.e(err, "Removal of existing test failed with msg: ${err.message}") + err.report(ExceptionCategory.INTERNAL) + } + } + + private suspend fun deleteExistingAndRegisterNewTestWitTAN() { + try { + val currentTest = submissionRepository.testForType(CoronaTest.Type.PCR).first() coronaTestRepository.removeTest(currentTest!!.identifier) - doDeviceRegistration(coronaTest) + onTanSubmit() } catch (err: Exception) { Timber.e(err, "Removal of existing test failed with msg: ${err.message}") err.report(ExceptionCategory.INTERNAL) } } + private suspend fun onTanSubmit() { + + try { + mutableRegistrationState.postValue(RegistrationState(ApiRequestState.STARTED)) + submissionRepository.registerTest(coronaTestQrTan!!) + mutableRegistrationState.postValue(RegistrationState(ApiRequestState.SUCCESS)) + } catch (err: CwaWebException) { + mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) + registrationError.postValue(err) + } catch (err: TransactionException) { + if (err.cause is CwaWebException) { + registrationError.postValue(err.cause) + } else { + err.report(ExceptionCategory.INTERNAL) + } + mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) + } catch (err: Exception) { + mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) + err.report(ExceptionCategory.INTERNAL) + } finally { + // TODO Should not be necessary? What new data would we + submissionRepository.refreshTest(type = CoronaTest.Type.PCR) + } + } + data class RegistrationState( val apiRequestState: ApiRequestState, val testResult: CoronaTestResult? = null @@ -96,11 +144,11 @@ class SubmissionDeletionWarningViewModel @AssistedInject constructor( } fun triggerNavigationToSubmissionTestResultAvailableFragment() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultAvailableScreen) + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultAvailableScreen(coronaTestQrCode!!.type)) } fun triggerNavigationToSubmissionTestResultPendingFragment() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPendingScreen) + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPendingScreen(coronaTestQrCode!!.type)) } private fun checkTestResult(testResult: CoronaTestResult) { @@ -118,8 +166,29 @@ class SubmissionDeletionWarningViewModel @AssistedInject constructor( } } + fun getRegistrationType(): RegistrationType { + return if (coronaTestQrCode != null) { + RegistrationType.QR + } else { + RegistrationType.TAN + } + } + + fun getTestType(): CoronaTest.Type { + return coronaTestQrCode?.type ?: return CoronaTest.Type.PCR + } + + sealed class RegistrationType { + object TAN : RegistrationType() + object QR : RegistrationType() + } + @AssistedFactory interface Factory : CWAViewModelFactory<SubmissionDeletionWarningViewModel> { - fun create(coronaTest: CoronaTestQRCode, isConsentGiven: Boolean): SubmissionDeletionWarningViewModel + fun create( + coronaTestQrCode: CoronaTestQRCode?, + coronaTestTan: CoronaTestTAN?, + isConsentGiven: Boolean + ): SubmissionDeletionWarningViewModel } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt index c2a919a2e..f14ed9c02 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt @@ -72,7 +72,7 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co viewModel.routeToScreen.observe2(this) { when (it) { - is SubmissionNavigationEvents.NavigateToDeletionWarningFragment -> { + is SubmissionNavigationEvents.NavigateToDeletionWarningFragmentFromQrCode -> { doNavigate( SubmissionQRCodeScanFragmentDirections .actionSubmissionQRCodeScanFragmentToSubmissionDeletionWarningFragment( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt index 6466857f9..18b06cf6f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt @@ -49,9 +49,9 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor( if (coronaTest != null) { routeToScreen.postValue( - SubmissionNavigationEvents.NavigateToDeletionWarningFragment( - coronaTestQRCode, - isConsentGiven + SubmissionNavigationEvents.NavigateToDeletionWarningFragmentFromQrCode( + coronaTestQRCode = coronaTestQRCode, + consentGiven = isConsentGiven ) ) } else { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt index 38084f8c5..8e7ec16ac 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt @@ -12,6 +12,7 @@ import de.rki.coronawarnapp.exception.http.CwaServerError import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.doNavigate @@ -48,6 +49,18 @@ class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoIn } } + viewModel.routeToScreen.observe2(this) { + when (it) { + is SubmissionNavigationEvents.NavigateToDeletionWarningFragmentFromTan -> + doNavigate( + SubmissionTanFragmentDirections.actionSubmissionTanFragmentToSubmissionDeletionWarningFragment( + isConsentGiven = it.consentGiven, + coronaTestTan = it.coronaTestTan + ) + ) + } + } + binding.apply { submissionTanContent.submissionTanInput.listener = { tan -> submissionTanContent.submissionTanCharacterError.visibility = View.GONE @@ -57,7 +70,7 @@ class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoIn } submissionTanButtonEnter.setOnClickListener { - viewModel.onTanSubmit() + viewModel.startTanSubmission() } submissionTanHeader.headerButtonBack.buttonIcon.setOnClickListener { goBack() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt index ed42c6abe..b55a94539 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt @@ -12,11 +12,13 @@ import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import timber.log.Timber @@ -27,6 +29,7 @@ class SubmissionTanViewModel @AssistedInject constructor( private val currentTan = MutableStateFlow(Tan("")) + val routeToScreen = SingleLiveEvent<SubmissionNavigationEvents>() val state = currentTan.map { currentTan -> UIState( isTanValid = currentTan.isTanValid, @@ -43,7 +46,7 @@ class SubmissionTanViewModel @AssistedInject constructor( currentTan.value = Tan(tan) } - fun onTanSubmit() { + fun startTanSubmission() { val teletan = currentTan.value if (!teletan.isTanValid) { Timber.w("Tried to set invalid teletan: %s", teletan) @@ -51,28 +54,44 @@ class SubmissionTanViewModel @AssistedInject constructor( } launch { - try { - registrationState.postValue(ApiRequestState.STARTED) - val request = CoronaTestTAN.PCR(tan = teletan.value) - submissionRepository.registerTest(request) - registrationState.postValue(ApiRequestState.SUCCESS) - } catch (err: CwaWebException) { - registrationState.postValue(ApiRequestState.FAILED) - registrationError.postValue(err) - } catch (err: TransactionException) { - if (err.cause is CwaWebException) { - registrationError.postValue(err.cause) - } else { - err.report(ExceptionCategory.INTERNAL) - } - registrationState.postValue(ApiRequestState.FAILED) - } catch (err: Exception) { - registrationState.postValue(ApiRequestState.FAILED) + val pcrTestAlreadyStored = submissionRepository.testForType(CoronaTest.Type.PCR).first() + if (pcrTestAlreadyStored != null) { + val coronaTestTAN = CoronaTestTAN.PCR(tan = teletan.value) + routeToScreen.postValue( + SubmissionNavigationEvents.NavigateToDeletionWarningFragmentFromTan( + consentGiven = false, + coronaTestTan = coronaTestTAN + ) + ) + } else { + onTanSubmit(teletan) + } + } + } + + private suspend fun onTanSubmit(teletan: Tan) { + + try { + registrationState.postValue(ApiRequestState.STARTED) + val request = CoronaTestTAN.PCR(tan = teletan.value) + submissionRepository.registerTest(request) + registrationState.postValue(ApiRequestState.SUCCESS) + } catch (err: CwaWebException) { + registrationState.postValue(ApiRequestState.FAILED) + registrationError.postValue(err) + } catch (err: TransactionException) { + if (err.cause is CwaWebException) { + registrationError.postValue(err.cause) + } else { err.report(ExceptionCategory.INTERNAL) - } finally { - // TODO Should not be necessary? What new data would we - submissionRepository.refreshTest(type = CoronaTest.Type.PCR) } + registrationState.postValue(ApiRequestState.FAILED) + } catch (err: Exception) { + registrationState.postValue(ApiRequestState.FAILED) + err.report(ExceptionCategory.INTERNAL) + } finally { + // TODO Should not be necessary? What new data would we + submissionRepository.refreshTest(type = CoronaTest.Type.PCR) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt index 838118d13..1d329dd9e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionNavigationEvents.kt @@ -2,6 +2,8 @@ package de.rki.coronawarnapp.ui.submission.viewmodel import com.google.android.gms.common.api.ApiException import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode +import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN +import de.rki.coronawarnapp.coronatest.type.CoronaTest sealed class SubmissionNavigationEvents { object NavigateToContact : SubmissionNavigationEvents() @@ -13,9 +15,13 @@ sealed class SubmissionNavigationEvents { object NavigateToTAN : SubmissionNavigationEvents() object NavigateToConsent : SubmissionNavigationEvents() object NavigateToMainActivity : SubmissionNavigationEvents() - object NavigateToResultPendingScreen : SubmissionNavigationEvents() - object NavigateToResultAvailableScreen : SubmissionNavigationEvents() - data class NavigateToDeletionWarningFragment(val coronaTestQRCode: CoronaTestQRCode, val consentGiven: Boolean) : + data class NavigateToResultPendingScreen(var coronaTestType: CoronaTest.Type) : SubmissionNavigationEvents() + data class NavigateToResultAvailableScreen(var coronaTestType: CoronaTest.Type) : SubmissionNavigationEvents() + data class NavigateToDeletionWarningFragmentFromQrCode( + val coronaTestQRCode: CoronaTestQRCode, + val consentGiven: Boolean + ) : SubmissionNavigationEvents() + data class NavigateToDeletionWarningFragmentFromTan(val coronaTestTan: CoronaTestTAN, val consentGiven: Boolean) : SubmissionNavigationEvents() data class ResolvePlayServicesException(val exception: ApiException) : SubmissionNavigationEvents() } diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index d4ff1223b..b1a3f0bb1 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -342,6 +342,9 @@ android:defaultValue="true" app:argType="boolean" /> </action> + <action + android:id="@+id/action_submissionTanFragment_to_submissionDeletionWarningFragment" + app:destination="@id/submissionDeletionWarningFragment" /> <action android:id="@+id/action_submissionTanFragment_to_submissionTestResultNoConsentFragment" app:destination="@id/submissionTestResultNoConsentFragment" @@ -662,11 +665,23 @@ app:argType="boolean" /> <argument android:name="coronaTestQrCode" + app:nullable="true" + android:defaultValue="@null" app:argType="de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode" /> + <argument + android:name="coronaTestTan" + app:nullable="true" + android:defaultValue="@null" + app:argType="de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN" /> <action android:id="@+id/action_submissionDeletionWarningFragment_to_submissionDispatcherFragment" app:popUpTo="@id/submissionQRCodeScanFragment" app:popUpToInclusive="true" /> + <action + android:id="@+id/action_submissionDeletionFragment_to_submissionTestResultNoConsentFragment" + app:destination="@id/submissionTestResultNoConsentFragment" + app:popUpTo="@id/mainFragment" + app:popUpToInclusive="false" /> <action android:id="@+id/action_submissionDeletionWarningFragment_to_submissionTestResultPendingFragment" app:destination="@id/submissionTestResultPendingFragment" -- GitLab