diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TransactionException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TransactionException.kt deleted file mode 100644 index c7dd5daca0247d3e705b6cc4bc0c468be1341f2c..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/exception/TransactionException.kt +++ /dev/null @@ -1,21 +0,0 @@ -package de.rki.coronawarnapp.exception - -import de.rki.coronawarnapp.exception.reporting.ErrorCodes -import de.rki.coronawarnapp.exception.reporting.ReportedException -import java.util.UUID - -/** - * An Exception thrown when an error occurs inside the Transaction - * - * @param transactionId the atomic Transaction ID - * @param state the atomic Transaction state (defined in the Transaction) with a valid ToString - * @param cause the cause of the error - * - * @see de.rki.coronawarnapp.transaction.Transaction - */ -class TransactionException constructor(transactionId: UUID, state: String, cause: Throwable?) : - ReportedException( - ErrorCodes.TRANSACTION_PROBLEM.code, - "An error occurred during execution of transaction $transactionId, State $state", - cause - ) 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 03e2b7cd6e30e82ead765ca2331cc576518a4480..562c87cfc33584fcd0b33e0755fd53d514160cde 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 @@ -7,14 +7,14 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder +import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.databinding.FragmentSubmissionDeletionWarningBinding import de.rki.coronawarnapp.exception.http.BadRequestException import de.rki.coronawarnapp.exception.http.CwaClientError import de.rki.coronawarnapp.exception.http.CwaServerError import de.rki.coronawarnapp.exception.http.CwaWebException -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 @@ -24,6 +24,7 @@ import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted +import timber.log.Timber import javax.inject.Inject class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_deletion_warning), AutoInject { @@ -46,7 +47,6 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ super.onViewCreated(view, savedInstanceState) binding.apply { - when (viewModel.getTestType()) { CoronaTest.Type.PCR -> { headline.text = getString(R.string.submission_deletion_warning_headline_pcr_test) @@ -59,122 +59,63 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_ } } - continueButton.setOnClickListener { - viewModel.deleteExistingAndRegisterNewTest() - } + continueButton.setOnClickListener { viewModel.deleteExistingAndRegisterNewTest() } - toolbar.setNavigationOnClickListener { - viewModel.onCancelButtonClick() - } - } - - viewModel.showRedeemedTokenWarning.observe2(this) { - val dialog = DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_error_dialog_web_tan_redeemed_title, - R.string.submission_error_dialog_web_tan_redeemed_body, - R.string.submission_error_dialog_web_tan_redeemed_button_positive - ) - - DialogHelper.showDialog(dialog) - - navigateToDispatchScreen() + toolbar.setNavigationOnClickListener { viewModel.onCancelButtonClick() } } viewModel.registrationState.observe2(this) { state -> - binding.submissionQrCodeScanSpinner.isVisible = state.apiRequestState == ApiRequestState.STARTED - binding.continueButton.isVisible = state.apiRequestState != ApiRequestState.STARTED - - if (ApiRequestState.SUCCESS == state.apiRequestState) { - - when (viewModel.getRegistrationType()) { - SubmissionDeletionWarningViewModel.RegistrationType.QR -> { - if (state.testResult == CoronaTestResult.PCR_POSITIVE) { - viewModel.triggerNavigationToSubmissionTestResultAvailableFragment() - } else { - viewModel.triggerNavigationToSubmissionTestResultPendingFragment() - } - } - SubmissionDeletionWarningViewModel.RegistrationType.TAN -> { - doNavigate( - SubmissionDeletionWarningFragmentDirections - .actionSubmissionDeletionFragmentToSubmissionTestResultNoConsentFragment( - viewModel.getTestType() - ) - ) - } - } - } + binding.submissionQrCodeScanSpinner.isVisible = state.isFetching + binding.continueButton.isVisible = !state.isFetching && state.coronaTest == null } viewModel.registrationError.observe2(this) { - DialogHelper.showDialog(buildErrorDialog(it)) + showErrorDialog(it) + doNavigate( + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionWarningFragmentToSubmissionDispatcherFragment() + ) } viewModel.routeToScreen.observe2(this) { - when (it) { - SubmissionNavigationEvents.NavigateToConsent -> { - doNavigate( - SubmissionDeletionWarningFragmentDirections - .actionSubmissionDeletionWarningFragmentToSubmissionConsentFragment() - ) - } - is SubmissionNavigationEvents.NavigateToResultAvailableScreen -> { - doNavigate( - SubmissionDeletionWarningFragmentDirections - .actionSubmissionDeletionWarningFragmentToSubmissionTestResultAvailableFragment( - testType = it.coronaTestType - ) - ) - } - is SubmissionNavigationEvents.NavigateToResultPendingScreen -> { - doNavigate( - SubmissionDeletionWarningFragmentDirections - .actionSubmissionDeletionWarningFragmentToSubmissionTestResultPendingFragment( - testType = it.coronaTestType - ) - ) - } - } + Timber.d("Navigating to %s", it) + doNavigate(it) } } - private fun navigateToDispatchScreen() = - doNavigate( - SubmissionDeletionWarningFragmentDirections - .actionSubmissionDeletionWarningFragmentToSubmissionDispatcherFragment() - ) - - private fun buildErrorDialog(exception: CwaWebException): DialogHelper.DialogInstance { - return when (exception) { - is BadRequestException -> DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_qr_code_scan_invalid_dialog_headline, - R.string.submission_qr_code_scan_invalid_dialog_body, - R.string.submission_qr_code_scan_invalid_dialog_button_positive, - R.string.submission_qr_code_scan_invalid_dialog_button_negative, - true, - { /* startDecode() */ }, - ::navigateToDispatchScreen - ) - is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_error_dialog_web_generic_error_title, - R.string.submission_error_dialog_web_generic_network_error_body, - R.string.submission_error_dialog_web_generic_error_button_positive, - null, - true, - ::navigateToDispatchScreen - ) - else -> DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_error_dialog_web_generic_error_title, - R.string.submission_error_dialog_web_generic_error_body, - R.string.submission_error_dialog_web_generic_error_button_positive, - null, - true, - ::navigateToDispatchScreen - ) - } + private fun showErrorDialog(exception: Throwable) = when (exception) { + is InvalidQRCodeException -> DialogHelper.DialogInstance( + context = requireActivity(), + title = R.string.submission_error_dialog_web_tan_redeemed_title, + message = R.string.submission_error_dialog_web_tan_redeemed_body, + cancelable = true, + positiveButton = R.string.submission_error_dialog_web_tan_redeemed_button_positive, + positiveButtonFunction = { /* dismiss */ }, + ).run { DialogHelper.showDialog(this) } + is BadRequestException -> DialogHelper.DialogInstance( + context = requireActivity(), + title = R.string.submission_qr_code_scan_invalid_dialog_headline, + message = R.string.submission_qr_code_scan_invalid_dialog_body, + cancelable = true, + positiveButton = R.string.submission_qr_code_scan_invalid_dialog_button_positive, + positiveButtonFunction = { /* dismiss */ }, + ).run { DialogHelper.showDialog(this) } + is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( + context = requireActivity(), + title = R.string.submission_error_dialog_web_generic_error_title, + message = R.string.submission_error_dialog_web_generic_network_error_body, + cancelable = true, + positiveButton = R.string.submission_error_dialog_web_generic_error_button_positive, + positiveButtonFunction = { /* dismiss */ }, + ).run { DialogHelper.showDialog(this) } + is CwaWebException -> DialogHelper.DialogInstance( + context = requireActivity(), + title = R.string.submission_error_dialog_web_generic_error_title, + message = R.string.submission_error_dialog_web_generic_error_body, + cancelable = true, + positiveButton = R.string.submission_error_dialog_web_generic_error_button_positive, + positiveButtonFunction = { /* dismiss */ }, + ).run { DialogHelper.showDialog(this) } + else -> exception.toErrorDialogBuilder(requireContext()).show() } override fun onResume() { 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 9a46d82aebba0fa406f8dba7e51723aa88e09d19..4315b50048b0d53f0040abb2a11ea20e846eb080 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 @@ -1,24 +1,17 @@ package de.rki.coronawarnapp.ui.submission.deletionwarning -import androidx.annotation.VisibleForTesting import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.navigation.NavDirections import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject 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 -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.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory @@ -26,154 +19,123 @@ import kotlinx.coroutines.flow.first import timber.log.Timber class SubmissionDeletionWarningViewModel @AssistedInject constructor( - private val coronaTestRepository: CoronaTestRepository, - private val submissionRepository: SubmissionRepository, @Assisted private val coronaTestQrCode: CoronaTestQRCode?, @Assisted private val coronaTestQrTan: CoronaTestTAN?, - @Assisted private val isConsentGiven: Boolean, + private val submissionRepository: SubmissionRepository, + private val coronaTestRepository: CoronaTestRepository, ) : CWAViewModel() { - val routeToScreen = SingleLiveEvent<SubmissionNavigationEvents>() - val showRedeemedTokenWarning = SingleLiveEvent<Unit>() - private val mutableRegistrationState = MutableLiveData(RegistrationState(ApiRequestState.IDLE)) + val routeToScreen = SingleLiveEvent<NavDirections>() + private val mutableRegistrationState = MutableLiveData(RegistrationState()) val registrationState: LiveData<RegistrationState> = mutableRegistrationState - val registrationError = SingleLiveEvent<CwaWebException>() + val registrationError = SingleLiveEvent<Throwable>() - fun deleteExistingAndRegisterNewTest() = launch { - when (getRegistrationType()) { - RegistrationType.QR -> deleteExistingAndRegisterNewTestWithQrCode() - RegistrationType.TAN -> deleteExistingAndRegisterNewTestWitTAN() - } + private fun getRegistrationType(): RegistrationType = if (coronaTestQrCode != null) { + RegistrationType.QR + } else { + RegistrationType.TAN } - 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) + // If there is no qrCode, it must be a TAN, and TANs are always PCR + internal fun getTestType(): CoronaTest.Type = coronaTestQrCode?.type ?: CoronaTest.Type.PCR + + fun deleteExistingAndRegisterNewTest() = launch { + when { + coronaTestQrTan != null -> deleteExistingAndRegisterNewTestWitTAN() + else -> deleteExistingAndRegisterNewTestWithQrCode() } } - private suspend fun deleteExistingAndRegisterNewTestWitTAN() { + private suspend fun deleteExistingAndRegisterNewTestWithQrCode() = try { + requireNotNull(coronaTestQrCode) { "QR Code was unavailable" } + + // Remove existing test and wait until that is done + submissionRepository.testForType(coronaTestQrCode.type).first()?.let { + coronaTestRepository.removeTest(it.identifier) + } ?: Timber.w("Test we will replace with QR was already removed?") - try { - val currentTest = submissionRepository.testForType(CoronaTest.Type.PCR).first() - coronaTestRepository.removeTest(currentTest!!.identifier) - onTanSubmit() - } catch (err: Exception) { - Timber.e(err, "Removal of existing test failed with msg: ${err.message}") - err.report(ExceptionCategory.INTERNAL) + mutableRegistrationState.postValue(RegistrationState(isFetching = true)) + + val coronaTest = submissionRepository.registerTest(coronaTestQrCode) + + if (coronaTest.isFinal) { + Timber.d("New test was already final, removing it again: %s", coronaTest) + // This does not wait until the test is removed, + // the exception handling should navigate the user to a new screen anyways + submissionRepository.removeTestFromDevice(type = coronaTest.type) + + throw InvalidQRCodeException() } - } - 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) + if (isConsentGiven) { + submissionRepository.giveConsentToSubmission(type = coronaTestQrCode.type) } - } - data class RegistrationState( - val apiRequestState: ApiRequestState, - val testResult: CoronaTestResult? = null - ) + continueWithNewTest(coronaTest) - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal suspend fun doDeviceRegistration(coronaTestQRCode: CoronaTestQRCode) { - try { - mutableRegistrationState.postValue(RegistrationState(ApiRequestState.STARTED)) - val coronaTest = submissionRepository.registerTest(coronaTestQRCode) - if (isConsentGiven) { - submissionRepository.giveConsentToSubmission(type = coronaTestQRCode.type) - } - checkTestResult(coronaTest.testResult) - mutableRegistrationState.postValue( - RegistrationState( - ApiRequestState.SUCCESS, - coronaTest.testResult - ) - ) - } catch (err: CwaWebException) { - Timber.e(err, "Msg: ${err.message}") - mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) - registrationError.postValue(err) - } catch (err: TransactionException) { - Timber.e(err, "Msg: ${err.message}") - if (err.cause is CwaWebException) { - registrationError.postValue(err.cause) - } else { - err.report(ExceptionCategory.INTERNAL) - } - mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) - } catch (err: InvalidQRCodeException) { - Timber.e(err, "Msg: ${err.message}") - mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) - deregisterTestFromDevice(coronaTestQRCode) - showRedeemedTokenWarning.postValue(Unit) - } catch (err: Exception) { - Timber.e(err, "Msg: ${err.message}") - mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED)) - err.report(ExceptionCategory.INTERNAL) - } + mutableRegistrationState.postValue(RegistrationState(coronaTest = coronaTest)) + } catch (e: Exception) { + Timber.e(e, "Error during test registration via QR code") + mutableRegistrationState.postValue(RegistrationState(isFetching = false)) + registrationError.postValue(e) } - fun onCancelButtonClick() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToConsent) - } + private suspend fun deleteExistingAndRegisterNewTestWitTAN() = try { + requireNotNull(coronaTestQrTan) { "TAN was unavailable" } - fun triggerNavigationToSubmissionTestResultAvailableFragment() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultAvailableScreen(coronaTestQrCode!!.type)) - } + submissionRepository.testForType(CoronaTest.Type.PCR).first()?.let { + coronaTestRepository.removeTest(it.identifier) + } ?: Timber.w("Test we will replace with TAN was already removed?") - fun triggerNavigationToSubmissionTestResultPendingFragment() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPendingScreen(coronaTestQrCode!!.type)) - } + mutableRegistrationState.postValue(RegistrationState(isFetching = true)) - private fun checkTestResult(testResult: CoronaTestResult) { - if (testResult == CoronaTestResult.PCR_REDEEMED) { - throw InvalidQRCodeException() - } - } + val coronaTest = submissionRepository.registerTest(coronaTestQrTan) + continueWithNewTest(coronaTest) - private fun deregisterTestFromDevice(coronaTest: CoronaTestQRCode) { - launch { - Timber.d("deregisterTestFromDevice()") + mutableRegistrationState.postValue(RegistrationState(coronaTest = coronaTest)) + } catch (e: Exception) { + Timber.e(e, "Error during test registration via TAN") + mutableRegistrationState.postValue(RegistrationState(isFetching = false)) + registrationError.postValue(e) + } - submissionRepository.removeTestFromDevice(type = coronaTest.type) - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) - } + fun onCancelButtonClick() { + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionWarningFragmentToSubmissionConsentFragment() + .run { routeToScreen.postValue(this) } } - fun getRegistrationType(): RegistrationType { - return if (coronaTestQrCode != null) { - RegistrationType.QR - } else { - RegistrationType.TAN + private fun continueWithNewTest(coronaTest: CoronaTest) { + Timber.d("Continuing with our new CoronaTest: %s", coronaTest) + when (getRegistrationType()) { + RegistrationType.QR -> { + if (coronaTest.isPositive) { + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionWarningFragmentToSubmissionTestResultAvailableFragment( + testType = coronaTestQrCode!!.type + ) + .run { routeToScreen.postValue(this) } + } else { + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionWarningFragmentToSubmissionTestResultPendingFragment( + testType = coronaTestQrCode!!.type + ) + .run { routeToScreen.postValue(this) } + } + } + RegistrationType.TAN -> { + SubmissionDeletionWarningFragmentDirections + .actionSubmissionDeletionFragmentToSubmissionTestResultNoConsentFragment(getTestType()) + .run { routeToScreen.postValue(this) } + } } } - fun getTestType(): CoronaTest.Type { - return coronaTestQrCode?.type ?: return CoronaTest.Type.PCR - } + data class RegistrationState( + val isFetching: Boolean = false, + val coronaTest: CoronaTest? = null, + ) sealed class RegistrationType { object TAN : RegistrationType() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/QrCodeRegistrationStateProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/QrCodeRegistrationStateProcessor.kt index 0328165d7440c5ef89be43e0a50c0ace31fcafc5..e0e8fd0336cfa0758c00daa7c024f0696b7c08ce 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/QrCodeRegistrationStateProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/QrCodeRegistrationStateProcessor.kt @@ -3,10 +3,8 @@ package de.rki.coronawarnapp.ui.submission.qrcode import androidx.lifecycle.MutableLiveData 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.type.CoronaTest import de.rki.coronawarnapp.exception.ExceptionCategory -import de.rki.coronawarnapp.exception.TransactionException import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.submission.SubmissionRepository @@ -45,13 +43,6 @@ class QrCodeRegistrationStateProcessor @Inject constructor( } catch (err: CwaWebException) { registrationState.postValue(RegistrationState(ApiRequestState.FAILED)) registrationError.postValue(err) - } catch (err: TransactionException) { - if (err.cause is CwaWebException) { - registrationError.postValue(err.cause) - } else { - err.report(ExceptionCategory.INTERNAL) - } - registrationState.postValue(RegistrationState(ApiRequestState.FAILED)) } catch (err: InvalidQRCodeException) { registrationState.postValue(RegistrationState(ApiRequestState.FAILED)) Timber.d("deregisterTestFromDevice()") @@ -63,7 +54,7 @@ class QrCodeRegistrationStateProcessor @Inject constructor( } private fun checkTestResult(request: CoronaTestQRCode, test: CoronaTest) { - if (test.testResult == CoronaTestResult.PCR_REDEEMED) { + if (test.isFinal) { throw InvalidQRCodeException("CoronaTestResult already redeemed ${request.registrationIdentifier}") } } 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 de7b107fc1a7de7c54e045733ad2374e48083bf6..f92627014da54b10669180614e0d2f6847fa5ad1 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 @@ -7,7 +7,6 @@ import dagger.assisted.AssistedInject 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 import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.submission.SubmissionRepository @@ -79,13 +78,6 @@ class SubmissionTanViewModel @AssistedInject constructor( } 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) err.report(ExceptionCategory.INTERNAL) 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 d48804ccba58ce2abd52de0b7ad0e7a1e526a0ee..b5478d44e7bb256e18d7cc6734c67a130f638784 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -728,8 +728,8 @@ app:nullable="true" /> <action android:id="@+id/action_submissionDeletionWarningFragment_to_submissionDispatcherFragment" - app:popUpTo="@id/submissionQRCodeScanFragment" - app:popUpToInclusive="true" /> + app:popUpTo="@id/submissionDispatcherFragment" + app:popUpToInclusive="false" /> <action android:id="@+id/action_submissionDeletionFragment_to_submissionTestResultNoConsentFragment" app:destination="@id/submissionTestResultNoConsentFragment" diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..28353d256d4404c2be68555cb70c25f319d97b0b --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestTest.kt @@ -0,0 +1,25 @@ +package de.rki.coronawarnapp.coronatest.type.pcr + +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import io.kotest.matchers.shouldBe +import org.joda.time.Instant +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class PCRCoronaTestTest : BaseTest() { + + @Test + fun `a test is final if it reaches the REDEEMED state`() { + val instance = PCRCoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = Instant.EPOCH, + registrationToken = "token", + testResult = CoronaTestResult.PCR_REDEEMED, + ) + + instance.isFinal shouldBe true + instance.copy(testResult = CoronaTestResult.PCR_POSITIVE).isFinal shouldBe false + instance.copy(testResult = CoronaTestResult.RAT_POSITIVE).isFinal shouldBe false + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTestTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTestTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..d5f58239c5a8fdd2f666b212bba3593b3044917b --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTestTest.kt @@ -0,0 +1,26 @@ +package de.rki.coronawarnapp.coronatest.type.rapidantigen + +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import io.kotest.matchers.shouldBe +import org.joda.time.Instant +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class RACoronaTestTest : BaseTest() { + + @Test + fun `a test is final if it reaches the REDEEMED state`() { + val instance = RACoronaTest( + identifier = "identifier", + lastUpdatedAt = Instant.EPOCH, + registeredAt = Instant.EPOCH, + registrationToken = "token", + testResult = CoronaTestResult.RAT_REDEEMED, + testedAt = Instant.EPOCH, + ) + + instance.isFinal shouldBe true + instance.copy(testResult = CoronaTestResult.RAT_POSITIVE).isFinal shouldBe false + instance.copy(testResult = CoronaTestResult.PCR_REDEEMED).isFinal shouldBe false + } +}