diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSource.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSource.kt index c6a015c9abec1077a8e01c3a17dc3dfd722b868e..d444101396f544a810e39a5b300f420f2c20a137 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSource.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/sources/local/LocalAppConfigSource.kt @@ -19,14 +19,14 @@ class LocalAppConfigSource @Inject constructor( suspend fun getConfigData(): ConfigData? = withContext(dispatcherProvider.IO) { Timber.tag(TAG).v("retrieveConfig()") - val configDownload = storage.getStoredConfig() - if (configDownload == null) { + val internalConfigData = storage.getStoredConfig() + if (internalConfigData == null) { Timber.tag(TAG).d("No stored config available.") return@withContext null } return@withContext try { - configDownload.let { + internalConfigData.let { ConfigDataContainer( mappedConfig = parser.parse(it.rawData), serverTime = it.serverTime, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt index a0f7936a9711fb0a640ff6927b29ebad35845cc7..fb07dcbaef0e0ae6af55d59abc0e4393b00b9f8c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt @@ -16,9 +16,9 @@ class InteroperabilityRepository @Inject constructor( ) { val countryList = appConfigProvider.currentConfig - .map { + .map { configData -> try { - appConfigProvider.getAppConfig() + configData .supportedCountries .mapNotNull { rawCode -> val countryCode = rawCode.toLowerCase(Locale.ROOT) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/TransmissionRiskVectorDeterminator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/TransmissionRiskVectorDeterminator.kt index 20d4eb3aa94da8c8aa225431135cf45848ae6351..f3b45ad55d98aabc9b24a01744c74146c12af42b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/TransmissionRiskVectorDeterminator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/TransmissionRiskVectorDeterminator.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.submission import dagger.Reusable +import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.submission.Symptoms.Indication import de.rki.coronawarnapp.submission.Symptoms.StartOf import de.rki.coronawarnapp.util.TimeAndDateExtensions.ageInDays @@ -47,9 +48,17 @@ class TransmissionRiskVectorDeterminator @Inject constructor( is StartOf.MoreThanTwoWeeks -> intArrayOf(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5) is StartOf.NoInformation -> intArrayOf(5, 6, 8, 8, 8, 7, 5, 3, 2, 1, 1, 1, 1, 1, 1) is StartOf.OneToTwoWeeksAgo -> intArrayOf(1, 1, 1, 1, 2, 3, 4, 5, 6, 6, 7, 7, 6, 6, 4) - else -> intArrayOf(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) + else -> { + IllegalStateException("Positive indication without startDate is not allowed: $symptoms") + .reportProblem( + tag = "TransmissionRiskVectorDeterminator", + info = "Symptoms has an invalid state." + ) + intArrayOf(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) + } } Indication.NEGATIVE -> intArrayOf(4, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) Indication.NO_INFORMATION -> intArrayOf(5, 6, 7, 7, 7, 6, 4, 3, 2, 1, 1, 1, 1, 1, 1) - }) + } + ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/SubmissionCardState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/SubmissionCardState.kt index 71a47304ee2893ef4327836af8212fd1ea105651..5a34ebdb7ea456aff7f40104417e57e42885087f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/SubmissionCardState.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/SubmissionCardState.kt @@ -61,7 +61,7 @@ data class SubmissionCardState( fun isPositiveSubmissionCardVisible(): Boolean = deviceUiState.withSuccess(false) { when (it) { - PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN -> !hasTestResultBeenSeen + PAIRED_POSITIVE, PAIRED_POSITIVE_TELETAN -> hasTestResultBeenSeen else -> false } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarFragment.kt index 16fdcdafac67b7a5136df35a567f0bcfa049154d..ad3e5120ae3c2b2af64e53bf67ce8b57261afb89 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarFragment.kt @@ -4,13 +4,12 @@ import android.content.res.ColorStateList import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment +import androidx.navigation.fragment.navArgs import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionSymptomCalendarBinding import de.rki.coronawarnapp.submission.Symptoms import de.rki.coronawarnapp.ui.submission.SubmissionBlockingDialog import de.rki.coronawarnapp.ui.submission.SubmissionCancelDialog -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents.NavigateToMainActivity -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.formatter.formatCalendarBackgroundButtonStyleByState import de.rki.coronawarnapp.util.formatter.formatCalendarButtonStyleByState @@ -24,12 +23,14 @@ import javax.inject.Inject class SubmissionSymptomCalendarFragment : Fragment(R.layout.fragment_submission_symptom_calendar), AutoInject { + private val navArgs by navArgs<SubmissionSymptomCalendarFragmentArgs>() + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory private val viewModel: SubmissionSymptomCalendarViewModel by cwaViewModelsAssisted( factoryProducer = { viewModelFactory }, constructorCall = { factory, _ -> factory as SubmissionSymptomCalendarViewModel.Factory - factory.create() + factory.create(navArgs.symptomIndication) } ) @@ -54,15 +55,7 @@ class SubmissionSymptomCalendarFragment : Fragment(R.layout.fragment_submission_ } viewModel.routeToScreen.observe2(this) { - when (it) { - is NavigateToResultPositiveOtherWarning -> doNavigate( - SubmissionSymptomCalendarFragmentDirections - .actionSubmissionSymptomCalendarFragmentToSubmissionResultPositiveOtherWarningFragment() - ) - is NavigateToMainActivity -> doNavigate( - SubmissionSymptomCalendarFragmentDirections.actionSubmissionSymptomCalendarFragmentToMainFragment() - ) - } + doNavigate(it) } viewModel.symptomStart.observe2(this) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt index 2e7385ca21ef2bf16508c5d09a183d27aa6dd107..4c99371b8e383d299b4cf03db78bbd16628ffef5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt @@ -1,28 +1,30 @@ package de.rki.coronawarnapp.ui.submission.symptoms.calendar import androidx.lifecycle.asLiveData +import androidx.navigation.NavDirections +import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.AssistedInject +import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.storage.SubmissionRepository import de.rki.coronawarnapp.submission.Symptoms -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.CWAViewModelFactory -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.MutableStateFlow import org.joda.time.LocalDate import timber.log.Timber class SubmissionSymptomCalendarViewModel @AssistedInject constructor( + @Assisted val symptomIndication: Symptoms.Indication, dispatcherProvider: DispatcherProvider, private val submissionRepository: SubmissionRepository ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val symptomStart = submissionRepository.currentSymptoms.flow - .map { it?.startOfSymptoms } - .asLiveData(context = dispatcherProvider.Default) + private val symptomStartInternal = MutableStateFlow<Symptoms.StartOf?>(null) + val symptomStart = symptomStartInternal.asLiveData(context = dispatcherProvider.Default) - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + val routeToScreen = SingleLiveEvent<NavDirections>() val showCancelDialog = SingleLiveEvent<Unit>() val showUploadDialog = submissionRepository.isSubmissionRunning .asLiveData(context = dispatcherProvider.Default) @@ -48,9 +50,7 @@ class SubmissionSymptomCalendarViewModel @AssistedInject constructor( } private fun updateSymptomStart(startOf: Symptoms.StartOf?) { - submissionRepository.currentSymptoms.update { - (it ?: Symptoms.NO_INFO_GIVEN).copy(startOfSymptoms = startOf) - } + symptomStartInternal.value = startOf } fun onCalendarPreviousClicked() { @@ -58,7 +58,18 @@ class SubmissionSymptomCalendarViewModel @AssistedInject constructor( } fun onDone() { - Timber.d("onDone() clicked on calender screen.") + if (symptomStartInternal.value == null) { + IllegalStateException("Can't finish symptom indication without symptomStart value.") + .reportProblem(tag = TAG, "UI should not allow symptom submission without start date.") + return + } + Timber.tag(TAG).d("onDone() clicked on calender screen.") + submissionRepository.currentSymptoms.update { + Symptoms( + symptomIndication = symptomIndication, + startOfSymptoms = symptomStartInternal.value + ).also { Timber.tag(TAG).v("Symptoms updated to %s", it) } + } performSubmission() } @@ -72,9 +83,11 @@ class SubmissionSymptomCalendarViewModel @AssistedInject constructor( try { submissionRepository.startSubmission() } catch (e: Exception) { - Timber.e(e, "performSubmission() failed.") + Timber.tag(TAG).e(e, "performSubmission() failed.") } finally { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) + routeToScreen.postValue( + SubmissionSymptomCalendarFragmentDirections.actionSubmissionSymptomCalendarFragmentToMainFragment() + ) } } } @@ -82,6 +95,10 @@ class SubmissionSymptomCalendarViewModel @AssistedInject constructor( @AssistedInject.Factory interface Factory : CWAViewModelFactory<SubmissionSymptomCalendarViewModel> { - fun create(): SubmissionSymptomCalendarViewModel + fun create(symptomIndication: Symptoms.Indication): SubmissionSymptomCalendarViewModel + } + + companion object { + private const val TAG = "SymptomsCalenderVM" } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt index 077c1167cab46a44db3337b90d325464d37cf123..98c2da6750ff958a15afb504d69234248cc6a874 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt @@ -9,7 +9,6 @@ import de.rki.coronawarnapp.databinding.FragmentSubmissionSymptomIntroBinding import de.rki.coronawarnapp.submission.Symptoms import de.rki.coronawarnapp.ui.submission.SubmissionBlockingDialog import de.rki.coronawarnapp.ui.submission.SubmissionCancelDialog -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.formatter.formatBackgroundButtonStyleByState import de.rki.coronawarnapp.util.formatter.formatButtonStyleByState @@ -37,17 +36,8 @@ class SubmissionSymptomIntroductionFragment : Fragment(R.layout.fragment_submiss super.onViewCreated(view, savedInstanceState) uploadDialog = SubmissionBlockingDialog(requireContext()) - viewModel.routeToScreen.observe2(this) { - when (it) { - is SubmissionNavigationEvents.NavigateToSymptomCalendar -> doNavigate( - SubmissionSymptomIntroductionFragmentDirections - .actionSubmissionSymptomIntroductionFragmentToSubmissionSymptomCalendarFragment() - ) - is SubmissionNavigationEvents.NavigateToMainActivity -> doNavigate( - SubmissionSymptomIntroductionFragmentDirections - .actionSubmissionSymptomIntroductionFragmentToMainFragment() - ) - } + viewModel.navigation.observe2(this) { + doNavigate(it) } viewModel.showCancelDialog.observe2(this) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt index 0ca4d571f47623860bbc9a0ea29504ea9f2c76b7..f7c697b4fe50134f6a06eb5a4127ca23d1587e0d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt @@ -1,17 +1,15 @@ package de.rki.coronawarnapp.ui.submission.symptoms.introduction import androidx.lifecycle.asLiveData +import androidx.navigation.NavDirections import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.storage.SubmissionRepository import de.rki.coronawarnapp.submission.Symptoms -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents.NavigateToMainActivity -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents.NavigateToSymptomCalendar 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.map +import kotlinx.coroutines.flow.MutableStateFlow import timber.log.Timber class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( @@ -19,11 +17,10 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( private val submissionRepository: SubmissionRepository ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - val symptomIndication = submissionRepository.currentSymptoms.flow - .map { it?.symptomIndication } - .asLiveData(context = dispatcherProvider.Default) + private val symptomIndicationInternal = MutableStateFlow<Symptoms.Indication?>(null) + val symptomIndication = symptomIndicationInternal.asLiveData(context = dispatcherProvider.Default) - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + val navigation = SingleLiveEvent<NavDirections>() val showCancelDialog = SingleLiveEvent<Unit>() val showUploadDialog = submissionRepository.isSubmissionRunning @@ -31,8 +28,15 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( fun onNextClicked() { launch { - when (submissionRepository.currentSymptoms.value?.symptomIndication) { - Symptoms.Indication.POSITIVE -> routeToScreen.postValue(NavigateToSymptomCalendar) + when (symptomIndicationInternal.value) { + Symptoms.Indication.POSITIVE -> { + navigation.postValue( + SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToSubmissionSymptomCalendarFragment( + symptomIndication = Symptoms.Indication.POSITIVE + ) + ) + } Symptoms.Indication.NEGATIVE -> doSubmit() Symptoms.Indication.NO_INFORMATION -> showCancelDialog.postValue(Unit) } @@ -57,9 +61,7 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( private fun updateSymptomIndication(indication: Symptoms.Indication) { Timber.d("updateSymptomIndication(indication=$indication)") - submissionRepository.currentSymptoms.update { - (it ?: Symptoms.NO_INFO_GIVEN).copy(symptomIndication = indication) - } + symptomIndicationInternal.value = indication } fun onCancelConfirmed() { @@ -74,7 +76,10 @@ class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( } catch (e: Exception) { Timber.e(e, "doSubmit() failed.") } finally { - routeToScreen.postValue(NavigateToMainActivity) + navigation.postValue( + SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToMainFragment() + ) } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt index a6ef006d25ee2c51a30b666bc7147f1daa634fa7..b9f965dc59eb32a5916193cc0a4a742a1ded42b9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt @@ -44,15 +44,16 @@ class SubmissionTestResultNoConsentFragment : Fragment(R.layout.fragment_submiss .setTestResultSection(it.deviceUiState, it.testResultReceivedDate) } - binding.submissionTestResultConsentGivenHeader.headerButtonBack.buttonIcon.setOnClickListener { - showCancelDialog() - } - - binding.submissionTestResultPositiveNoConsentButtonAbort.setOnClickListener { - showCancelDialog() - } - binding.submissionTestResultPositiveNoConsentButtonWarnOthers.setOnClickListener { - navigateToWarnOthers() + binding.apply { + submissionTestResultConsentGivenHeader.headerButtonBack.buttonIcon.setOnClickListener { + showCancelDialog() + } + submissionTestResultPositiveNoConsentButtonAbort.setOnClickListener { + showCancelDialog() + } + submissionTestResultPositiveNoConsentButtonWarnOthers.setOnClickListener { + navigateToWarnOthers() + } } } @@ -65,10 +66,9 @@ class SubmissionTestResultNoConsentFragment : Fragment(R.layout.fragment_submiss AlertDialog.Builder(requireContext()).apply { setTitle(R.string.submission_test_result_positive_no_consent_dialog_title) setMessage(R.string.submission_test_result_positive_no_consent_dialog_message) - setPositiveButton(R.string.submission_test_result_positive_no_consent_dialog_positive_button) { _, _ -> - navigateToWarnOthers() + setNegativeButton(R.string.submission_test_result_positive_no_consent_dialog_positive_button) { _, _ -> } - setNegativeButton(R.string.submission_test_result_positive_no_consent_dialog_negative_button) { _, _ -> + setPositiveButton(R.string.submission_test_result_positive_no_consent_dialog_negative_button) { _, _ -> navigateToHome() } }.show() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt index b1c7f3e7afc519f20cfba14778e0e4789c7d409e..4e639d566327976e1bf6ac3ec8756e5215093951 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt @@ -5,44 +5,24 @@ import androidx.lifecycle.asLiveData import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.storage.SubmissionRepository import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState -import de.rki.coronawarnapp.util.DeviceUIState -import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess -import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.flow.combine import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.combineTransform -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock class SubmissionTestResultNoConsentViewModel @AssistedInject constructor( val submissionRepository: SubmissionRepository ) : CWAViewModel() { - private val showRedeemedTokenWarning = SingleLiveEvent<Unit>() - private var wasRedeemedTokenErrorShown = false - private val tokenErrorMutex = Mutex() - - val uiState: LiveData<TestResultUIState> = combineTransform( + val uiState: LiveData<TestResultUIState> = combine( submissionRepository.deviceUIStateFlow, submissionRepository.testResultReceivedDateFlow ) { deviceUiState, resultDate -> - tokenErrorMutex.withLock { - if (!wasRedeemedTokenErrorShown) { - deviceUiState.withSuccess { - if (it == DeviceUIState.PAIRED_REDEEMED) { - wasRedeemedTokenErrorShown = true - showRedeemedTokenWarning.postValue(Unit) - } - } - } - } - TestResultUIState( deviceUiState = deviceUiState, testResultReceivedDate = resultDate - ).let { emit(it) } + ) }.asLiveData(context = Dispatchers.Default) fun onTestOpened() { 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 3fa560ce16dcd29622632004a91d0a06d9b3d0ba..7bb0e89b29cba1bb52c6b942728f562065097dec 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 @@ -3,23 +3,11 @@ package de.rki.coronawarnapp.ui.submission.viewmodel sealed class SubmissionNavigationEvents { object NavigateToContact : SubmissionNavigationEvents() object NavigateToDispatcher : SubmissionNavigationEvents() - object NavigateToSubmissionDone : SubmissionNavigationEvents() - object NavigateToSubmissionIntro : SubmissionNavigationEvents() object NavigateToQRCodeScan : SubmissionNavigationEvents() object NavigateToDataPrivacy : SubmissionNavigationEvents() - object NavigateToResultPositiveOtherWarning : SubmissionNavigationEvents() - - object NavigateToResultPositiveOtherWarningNoConsent : SubmissionNavigationEvents() - - object NavigateToSymptomSubmission : SubmissionNavigationEvents() - object NavigateToSymptomCalendar : SubmissionNavigationEvents() - object NavigateToSymptomIntroduction : SubmissionNavigationEvents() object NavigateToTAN : SubmissionNavigationEvents() - object NavigateToTestResult : SubmissionNavigationEvents() object NavigateToConsent : SubmissionNavigationEvents() - object NavigateToYourConsent : SubmissionNavigationEvents() object NavigateToMainActivity : SubmissionNavigationEvents() - object ShowCancelDialog : SubmissionNavigationEvents() } diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_done.xml b/Corona-Warn-App/src/main/res/layout/include_submission_done.xml index e4105a8df62021049fdea92f4c3003686decb6ca..32aa40468b6f1bcf28c287be60737de29f4bca14 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_done.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_done.xml @@ -18,7 +18,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:focusable="true" - android:src="@drawable/ic_submission_illustration_thanks" + android:src="@drawable/ic_illustration_together" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_status_card_done.xml b/Corona-Warn-App/src/main/res/layout/include_submission_status_card_done.xml index f1516fb761529e5b1e79ac139c656fe310af9e0c..55733f0fe60f79fea59baa6130dd2eb7e8ddef34 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_status_card_done.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_status_card_done.xml @@ -30,7 +30,7 @@ android:layout_marginTop="@dimen/spacing_tiny" android:contentDescription="@string/submission_done_illustration_description" android:importantForAccessibility="no" - android:src="@drawable/ic_submission_illustration_thanks" + android:src="@drawable/ic_illustration_together" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/submission_done_card_title" /> 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 47e0e681bf23a70a259dd00dbf86e816d3c9d8fc..77bad0e0c8c7ec0bd6e29f3b6617e77a439e14ca 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -374,6 +374,9 @@ android:id="@+id/submissionSymptomCalendarFragment" android:name="de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarFragment" android:label="SubmissionSymptomCalendarFragment"> + <argument + android:name="symptomIndication" + app:argType="de.rki.coronawarnapp.submission.Symptoms$Indication" /> <action android:id="@+id/action_submissionCalendarFragment_to_submissionSymptomIntroductionFragment" app:destination="@id/submissionSymptomIntroductionFragment" /> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/SubmissionCardStateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/SubmissionCardStateTest.kt index f2dafb9ec006b39c71a9be44175d906d75ca9913..dd2614b4486253bb74ac7787b0e2384121b34396 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/SubmissionCardStateTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/SubmissionCardStateTest.kt @@ -10,7 +10,6 @@ import de.rki.coronawarnapp.util.NetworkRequestWrapper import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.clearAllMocks -import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.verify @@ -42,7 +41,11 @@ class SubmissionCardStateTest : BaseTest() { ) = when (uiStateState) { ApiRequestState.SUCCESS -> - SubmissionCardState(NetworkRequestWrapper.RequestSuccessful(deviceUiState), isDeviceRegistered, hasResultBeenSeen) + SubmissionCardState( + NetworkRequestWrapper.RequestSuccessful(deviceUiState), + isDeviceRegistered, + hasResultBeenSeen + ) ApiRequestState.FAILED -> SubmissionCardState(NetworkRequestWrapper.RequestFailed(mockk()), isDeviceRegistered, hasResultBeenSeen) ApiRequestState.STARTED -> @@ -211,14 +214,14 @@ class SubmissionCardStateTest : BaseTest() { instance( deviceUiState = DeviceUIState.PAIRED_POSITIVE, uiStateState = ApiRequestState.SUCCESS, - hasResultBeenSeen = false + hasResultBeenSeen = true ).apply { isPositiveSubmissionCardVisible() shouldBe true } instance( deviceUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN, uiStateState = ApiRequestState.SUCCESS, - hasResultBeenSeen = false + hasResultBeenSeen = true ).apply { isPositiveSubmissionCardVisible() shouldBe true } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..83478fd77d89e1d06be94240e5a6f96cf5a2b259 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModelTest.kt @@ -0,0 +1,114 @@ +package de.rki.coronawarnapp.ui.submission.symptoms.calendar + +import de.rki.coronawarnapp.storage.SubmissionRepository +import de.rki.coronawarnapp.submission.Symptoms +import de.rki.coronawarnapp.util.preferences.FlowPreference +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.coVerifySequence +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.verify +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import org.joda.time.LocalDate +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider +import testhelpers.extensions.InstantExecutorExtension +import testhelpers.preferences.mockFlowPreference + +@ExtendWith(InstantExecutorExtension::class) +class SubmissionSymptomCalendarViewModelTest : BaseTest() { + + @MockK lateinit var submissionRepository: SubmissionRepository + private lateinit var currentSymptoms: FlowPreference<Symptoms?> + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this, relaxed = true) + + currentSymptoms = mockFlowPreference(null) + + every { submissionRepository.isSubmissionRunning } returns flowOf(false) + coEvery { submissionRepository.startSubmission() } just Runs + every { submissionRepository.currentSymptoms } returns currentSymptoms + } + + @AfterEach + fun tearDown() { + clearAllMocks() + } + + private fun createViewModel(indication: Symptoms.Indication = Symptoms.Indication.POSITIVE) = + SubmissionSymptomCalendarViewModel( + symptomIndication = indication, + dispatcherProvider = TestDispatcherProvider, + submissionRepository = submissionRepository + ) + + @Test + fun `symptom indication is not written to settings`() { + createViewModel().apply { + onLastSevenDaysStart() + onOneToTwoWeeksAgoStart() + onMoreThanTwoWeeksStart() + onNoInformationStart() + onDateSelected(LocalDate.now()) + } + + verify(exactly = 0) { submissionRepository.currentSymptoms } + } + + @Test + fun `submission by symptom completion updates symptom data`() { + createViewModel().apply { + onLastSevenDaysStart() + onDone() + } + + coVerifySequence { + submissionRepository.isSubmissionRunning + submissionRepository.currentSymptoms + submissionRepository.startSubmission() + } + + currentSymptoms.value shouldBe Symptoms( + startOfSymptoms = Symptoms.StartOf.LastSevenDays, + symptomIndication = Symptoms.Indication.POSITIVE + ) + } + + @Test + fun `submission by abort does not write any symptom data`() { + createViewModel().onCancelConfirmed() + + coVerifySequence { + submissionRepository.isSubmissionRunning + submissionRepository.startSubmission() + } + } + + @Test + fun `submission shows upload dialog`() { + val uploadStatus = MutableStateFlow(false) + every { submissionRepository.isSubmissionRunning } returns uploadStatus + createViewModel().apply { + showUploadDialog.observeForever { } + showUploadDialog.value shouldBe false + + uploadStatus.value = true + showUploadDialog.value shouldBe true + + uploadStatus.value = false + showUploadDialog.value shouldBe false + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f54546746acfd013a5e1a799dfe0cbbe38e37670 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModelTest.kt @@ -0,0 +1,124 @@ +package de.rki.coronawarnapp.ui.submission.symptoms.introduction + +import de.rki.coronawarnapp.storage.SubmissionRepository +import de.rki.coronawarnapp.submission.Symptoms +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Runs +import io.mockk.clearAllMocks +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.coVerifySequence +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.verify +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flowOf +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider +import testhelpers.extensions.InstantExecutorExtension + +@ExtendWith(InstantExecutorExtension::class) +class SubmissionSymptomIntroductionViewModelTest : BaseTest() { + + @MockK lateinit var submissionRepository: SubmissionRepository + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this, relaxed = true) + + every { submissionRepository.isSubmissionRunning } returns flowOf(false) + coEvery { submissionRepository.startSubmission() } just Runs + } + + @AfterEach + fun tearDown() { + clearAllMocks() + } + + private fun createViewModel() = SubmissionSymptomIntroductionViewModel( + dispatcherProvider = TestDispatcherProvider, + submissionRepository = submissionRepository + ) + + @Test + fun `symptom indication is not written to settings`() { + createViewModel().apply { + onPositiveSymptomIndication() + onNegativeSymptomIndication() + onNoInformationSymptomIndication() + onNextClicked() + } + + verify(exactly = 0) { submissionRepository.currentSymptoms } + } + + @Test + fun `positive symptom indication is forwarded using navigation arguments`() { + createViewModel().apply { + onPositiveSymptomIndication() + onNextClicked() + navigation.value shouldBe SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToSubmissionSymptomCalendarFragment( + symptomIndication = Symptoms.Indication.POSITIVE + ) + } + + verify(exactly = 0) { submissionRepository.currentSymptoms } + } + + @Test + fun `negative symptom indication leads to submission`() { + createViewModel().apply { + onNegativeSymptomIndication() + onNextClicked() + navigation.value shouldBe SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToMainFragment() + } + + coVerify { submissionRepository.startSubmission() } + } + + @Test + fun `no information symptom indication leads to cancel dialog`() { + createViewModel().apply { + onNoInformationSymptomIndication() + onNextClicked() + navigation.value shouldBe null + showCancelDialog.value shouldBe Unit + } + + verify(exactly = 0) { submissionRepository.currentSymptoms } + } + + @Test + fun `submission by abort does not write any symptom data`() { + createViewModel().onCancelConfirmed() + + coVerifySequence { + submissionRepository.isSubmissionRunning + submissionRepository.startSubmission() + } + } + + @Test + fun `submission shows upload dialog`() { + val uploadStatus = MutableStateFlow(false) + every { submissionRepository.isSubmissionRunning } returns uploadStatus + createViewModel().apply { + showUploadDialog.observeForever { } + showUploadDialog.value shouldBe false + + uploadStatus.value = true + showUploadDialog.value shouldBe true + + uploadStatus.value = false + showUploadDialog.value shouldBe false + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/UpdateCheckerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/UpdateCheckerTest.kt index 5a783eb0516ee09a9aa95233b751035c8b8f149c..e4fe8adbb57d7cf4352a9888ac5266223cddd68b 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/UpdateCheckerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/update/UpdateCheckerTest.kt @@ -29,7 +29,6 @@ class UpdateCheckerTest : BaseTest() { MockKAnnotations.init(this) mockkObject(BuildConfigWrap) - coEvery { appConfigProvider.getAppConfig() } returns configData }