diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 5ed563edf22fbc1e6d163ad4feb0e33cf3814a5a..b27fde297bd9296ae225904b1597a36d2f16630e 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -293,6 +293,12 @@ task jacocoTestReportDeviceRelease(type: JacocoReport, dependsOn: 'testDeviceRel getExecutionData().from(fileTree(dir: "$buildDir", includes: ["jacoco/testDeviceReleaseUnitTest.exec"])) } +configurations.all { + resolutionStrategy { + force "androidx.test:monitor:1.3.0" + } +} + dependencies { // KOTLIN def coroutineVersion = "1.4.0-M1" @@ -381,6 +387,8 @@ dependencies { androidTestImplementation "io.mockk:mockk-android:1.10.2" debugImplementation 'androidx.fragment:fragment-testing:1.2.5' + androidTestImplementation 'tools.fastlane:screengrab:2.0.0' + // Play Services implementation 'com.google.android.play:core:1.7.3' implementation 'com.google.android.gms:play-services-base:17.3.0' diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt index 289a23b9b886e81a787ecd8486ccbf0fd39f50e5..d109adabf7c87a4a5a87aa9f4bb5ab519277d698 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentTest.kt @@ -3,7 +3,6 @@ package de.rki.coronawarnapp.ui.main.home import androidx.fragment.app.testing.launchFragment import androidx.lifecycle.Lifecycle import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.SavedStateHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector @@ -34,7 +33,8 @@ class HomeFragmentTest : BaseUITest() { every { viewModel.refreshRequiredData() } just Runs setupMockViewModel(object : HomeFragmentViewModel.Factory { - override fun create(handle: SavedStateHandle): HomeFragmentViewModel = viewModel + // override fun create(handle: SavedStateHandle){} HomeFragmentViewModel = viewModel} + override fun create(): HomeFragmentViewModel = viewModel }) } diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e262d08eb3ddcf76f2b7b40760a1ab8fa91dc08 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionContactFragmentTest.kt @@ -0,0 +1,66 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.fragment.SubmissionContactFragment +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionContactViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionContactFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionContactViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionContactViewModel.Factory { + override fun create(): SubmissionContactViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionContactFragment>() + } + + @Test fun testContactCallClicked() { + val scenario = launchFragmentInContainer<SubmissionContactFragment>() + onView(withId(R.id.submission_contact_button_call)) + .perform(click()) + + // TODO verify result + } + + @Test fun testContactEnterTanClicked() { + val scenario = launchFragmentInContainer<SubmissionContactFragment>() + onView(withId(R.id.submission_contact_button_enter)) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionContactTestModule { + @ContributesAndroidInjector + abstract fun submissionContactScreen(): SubmissionContactFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..fc417a990b5c53a98167039c839d67bbee6fcb07 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDispatcherFragmentTest.kt @@ -0,0 +1,78 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDispatcherFragment +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionDispatcherViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionDispatcherFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionDispatcherViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionDispatcherViewModel.Factory { + override fun create(): SubmissionDispatcherViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionDispatcherFragment>() + } + + @Test fun testEventQRClicked() { + val scenario = launchFragmentInContainer<SubmissionDispatcherFragment>() + onView(withId(R.id.submission_dispatcher_qr)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } + + @Test fun testEventTeleClicked() { + val scenario = launchFragmentInContainer<SubmissionDispatcherFragment>() + onView(withId(R.id.submission_dispatcher_tan_tele)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } + + @Test fun testEventTanClicked() { + val scenario = launchFragmentInContainer<SubmissionDispatcherFragment>() + onView(withId(R.id.submission_dispatcher_tan_code)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionDispatcherTestModule { + @ContributesAndroidInjector + abstract fun submissionDispatcherScreen(): SubmissionDispatcherFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDoneFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDoneFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..989bbcdcbeb17fcc04c3e0df15b236c11da33df8 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionDoneFragmentTest.kt @@ -0,0 +1,58 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDoneFragment +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionDoneViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionDoneFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionDoneViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionDoneViewModel.Factory { + override fun create(): SubmissionDoneViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionDoneFragment>() + } + + @Test fun testDoneClicked() { + val scenario = launchFragmentInContainer<SubmissionDoneFragment>() + onView(withId(R.id.submission_done_button_done)) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionDoneTestModule { + @ContributesAndroidInjector + abstract fun submissionDoneScreen(): SubmissionDoneFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionIntroFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionIntroFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..5f3cd05665c1a9e4b49d18ecd496c21a488ce46c --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionIntroFragmentTest.kt @@ -0,0 +1,69 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.fragment.SubmissionIntroFragment +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionIntroViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest +import tools.fastlane.screengrab.Screengrab +import tools.fastlane.screengrab.locale.LocaleTestRule + +@RunWith(AndroidJUnit4::class) +class SubmissionIntroFragmentTest : BaseUITest() { + + /*@get:Rule + var activityRule = ActivityTestRule(MainActivity::class.java)*/ + + @Rule @JvmField + val localeTestRule = LocaleTestRule() + + @MockK lateinit var viewModel: SubmissionIntroViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : SubmissionIntroViewModel.Factory { + override fun create(): SubmissionIntroViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionIntroFragment>() + Screengrab.screenshot("submission_Intro_fragment_opened") + } + + @Test fun testEventNextClicked() { + val scenario = launchFragmentInContainer<SubmissionIntroFragment>() + onView(withId(R.id.submission_intro_button_next)) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionIntroTestModule { + @ContributesAndroidInjector + abstract fun submissionIntroScreen(): SubmissionIntroFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionOtherWarningFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionOtherWarningFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..eb4a3dba7d25d21d9461208ff1d5604a5cf1f794 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionOtherWarningFragmentTest.kt @@ -0,0 +1,83 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.submission.Symptoms +import de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarViewModel +import de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionViewModel +import de.rki.coronawarnapp.ui.submission.warnothers.SubmissionResultPositiveOtherWarningFragment +import de.rki.coronawarnapp.ui.submission.warnothers.SubmissionResultPositiveOtherWarningViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionOtherWarningFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionResultPositiveOtherWarningViewModel + @MockK lateinit var symptomIntroViewModel: SubmissionSymptomIntroductionViewModel + @MockK lateinit var symptomCalendarViewModel: SubmissionSymptomCalendarViewModel + + // @MockK lateinit var symptoms: Symptoms + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionSymptomIntroductionViewModel.Factory { + override fun create(): SubmissionSymptomIntroductionViewModel = symptomIntroViewModel + }) + + /* every { symptomIntroViewModel.symptomIndication } returns MutableLiveData() + every { symptomIntroViewModel. } returns MutableLiveData() + + symptomIntroViewModel.onPositiveSymptomIndication() + symptomIntroViewModel.symptomIndication.postValue("tesasdf") */ + + setupMockViewModel(object : SubmissionSymptomCalendarViewModel.Factory { + override fun create(symptomIndication: Symptoms.Indication): SubmissionSymptomCalendarViewModel = + symptomCalendarViewModel + }) + + setupMockViewModel(object : SubmissionResultPositiveOtherWarningViewModel.Factory { + override fun create(symptoms: Symptoms): SubmissionResultPositiveOtherWarningViewModel = + viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionResultPositiveOtherWarningFragment>() + } + + @Test fun testOtherWarningNextClicked() { + val scenario = launchFragmentInContainer<SubmissionResultPositiveOtherWarningFragment>() + onView(withId(R.id.submission_positive_other_warning_button_next)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionOtherWarningTestModule { + @ContributesAndroidInjector + abstract fun submissionOtherWarningScreen(): SubmissionResultPositiveOtherWarningFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeInfoFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeInfoFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e2b2ceaffa72d225586eb2b612a42f985372f14 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeInfoFragmentTest.kt @@ -0,0 +1,58 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.qrcode.info.SubmissionQRCodeInfoFragment +import de.rki.coronawarnapp.ui.submission.qrcode.info.SubmissionQRCodeInfoFragmentViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionQrCodeInfoFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionQRCodeInfoFragmentViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionQRCodeInfoFragmentViewModel.Factory { + override fun create(): SubmissionQRCodeInfoFragmentViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionQRCodeInfoFragment>() + } + + @Test fun testQRInfoNextClicked() { + val scenario = launchFragmentInContainer<SubmissionQRCodeInfoFragment>() + onView(withId(R.id.submission_qr_info_button_next)) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionQRInfoFragmentModule { + @ContributesAndroidInjector + abstract fun submissionQRInfoScreen(): SubmissionQRCodeInfoFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeScanFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeScanFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..245374a2a1013fd21b244be791f6cd459e5355bf --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionQrCodeScanFragmentTest.kt @@ -0,0 +1,45 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanFragment +import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionQrCodeScanFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionQRCodeScanViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionQRCodeScanViewModel.Factory { + override fun create(): SubmissionQRCodeScanViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionQRCodeScanFragment>() + } +} + +@Module +abstract class SubmissionQRScanFragmentModule { + @ContributesAndroidInjector + abstract fun submissionQRScanScreen(): SubmissionQRCodeScanFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..b592443c863b3a5d634d83696455ae1b42252418 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomCalendarFragmentTest.kt @@ -0,0 +1,68 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.submission.Symptoms +import de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarFragment +import de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionSymptomCalendarFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionSymptomCalendarViewModel + + /* private val symptoms = Symptoms(Symptoms.StartOf.OneToTwoWeeksAgo, POSITIVE) + private val positiveSymptomIndication = POSITIVE; + private val negativeSymptomIndication = Symptoms.Indication.NEGATIVE; + private val noSymptomIndication = Symptoms.Indication.NO_INFORMATION;*/ + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : SubmissionSymptomCalendarViewModel.Factory { + override fun create(symptomIndication: Symptoms.Indication): SubmissionSymptomCalendarViewModel = + viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionSymptomCalendarFragment>() + } + + @Test fun testSymptomCalendarNextClicked() { + val scenario = launchFragmentInContainer<SubmissionSymptomCalendarFragment>() + onView(withId(R.id.symptom_button_next)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionSymptomCalendarFragmentTestModule { + @ContributesAndroidInjector + abstract fun submissionSymptomCalendarScreen(): SubmissionSymptomCalendarFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ed959a210f9b3b33f3821b245fc5b0af3900cf1f --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionSymptomIntroFragmentTest.kt @@ -0,0 +1,60 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionFragment +import de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionSymptomIntroFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionSymptomIntroductionViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + setupMockViewModel(object : SubmissionSymptomIntroductionViewModel.Factory { + override fun create(): SubmissionSymptomIntroductionViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionSymptomIntroductionFragment>() + } + + @Test fun testSymptomNextClicked() { + val scenario = launchFragmentInContainer<SubmissionSymptomIntroductionFragment>() + onView(withId(R.id.symptom_button_next)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionSymptomIntroFragmentTestModule { + @ContributesAndroidInjector + abstract fun submissionSymptomIntroScreen(): SubmissionSymptomIntroductionFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..b2dc3033d936ec5bcbb81754816dcbf80498e5c5 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTanFragmentTest.kt @@ -0,0 +1,63 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.tan.SubmissionTanFragment +import de.rki.coronawarnapp.ui.submission.tan.SubmissionTanViewModel +import io.mockk.MockKAnnotations +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionTanFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionTanViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + setupMockViewModel(object : SubmissionTanViewModel.Factory { + override fun create(): SubmissionTanViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionTanFragment>() + } + + @Test fun testEventTanNextClicked() { + val scenario = launchFragmentInContainer<SubmissionTanFragment>() + ViewActions.closeSoftKeyboard() + onView(withId(R.id.submission_tan_button_enter)) + .perform(scrollTo()) + .perform(click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionTanTestModule { + @ContributesAndroidInjector + abstract fun submissionTanScreen(): SubmissionTanFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..ba62622e5fcd3f294fd3036257653624d5ad27b3 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultFragmentTest.kt @@ -0,0 +1,117 @@ +package de.rki.coronawarnapp.ui.submission + +import androidx.fragment.app.testing.launchFragment +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.lifecycle.MutableLiveData +import androidx.test.espresso.Espresso +import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ui.submission.testresult.SubmissionTestResultFragment +import de.rki.coronawarnapp.ui.submission.testresult.SubmissionTestResultViewModel +import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest + +@RunWith(AndroidJUnit4::class) +class SubmissionTestResultFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: SubmissionTestResultViewModel + @MockK lateinit var uiState: TestResultUIState + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + every { viewModel.uiState } returns MutableLiveData() + + setupMockViewModel(object : SubmissionTestResultViewModel.Factory { + override fun create(): SubmissionTestResultViewModel = viewModel + }) + } + + @After + fun teardown() { + clearAllViewModels() + } + + @Test + fun launch_fragment() { + launchFragment<SubmissionTestResultFragment>() + } + + @Test + fun testEventPendingRefreshClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_pending_refresh)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } + + @Test + fun testEventPendingRemoveClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_pending_remove_test)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } + + @Test + fun testEventInvalidRemoveClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_invalid_remove_test)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } + + @Test + fun testEventPositiveContinueWithSymptomsClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_positive_continue)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } + + @Test + fun testEventPositiveContinueWithoutSymptomsClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_positive_continue_without_symptoms)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } + + @Test + fun testEventNegativeRemoveClicked() { + val scenario = launchFragmentInContainer<SubmissionTestResultFragment>() + Espresso.onView(ViewMatchers.withId(R.id.submission_test_result_button_negative_remove_test)) + .perform(ViewActions.scrollTo()) + .perform(ViewActions.click()) + + // TODO verify result + } +} + +@Module +abstract class SubmissionTestResultTestModule { + @ContributesAndroidInjector + abstract fun submissionTestResultScreen(): SubmissionTestResultFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt index da725b563e82176894ab4217cd2ae37f699ad42e..e50180f6ccf93867a659830bf1b655a25e592831 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt @@ -8,16 +8,40 @@ import de.rki.coronawarnapp.ui.onboarding.OnboardingNotificationsTestModule import de.rki.coronawarnapp.ui.onboarding.OnboardingPrivacyTestModule import de.rki.coronawarnapp.ui.onboarding.OnboardingTestFragmentModule import de.rki.coronawarnapp.ui.onboarding.OnboardingTracingFragmentTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionContactTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionDispatcherTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionDoneTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionIntroTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionOtherWarningTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionQRInfoFragmentModule +import de.rki.coronawarnapp.ui.submission.SubmissionQRScanFragmentModule +import de.rki.coronawarnapp.ui.submission.SubmissionSymptomCalendarFragmentTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionSymptomIntroFragmentTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionTanTestModule +import de.rki.coronawarnapp.ui.submission.SubmissionTestResultTestModule @Module( includes = [ HomeFragmentTestModule::class, + // Onboarding OnboardingFragmentTestModule::class, OnboardingDeltaInteroperabilityFragmentTestModule::class, OnboardingNotificationsTestModule::class, OnboardingPrivacyTestModule::class, OnboardingTestFragmentModule::class, - OnboardingTracingFragmentTestModule::class + OnboardingTracingFragmentTestModule::class, + // Submission + SubmissionIntroTestModule::class, + SubmissionDispatcherTestModule::class, + SubmissionTanTestModule::class, + SubmissionTestResultTestModule::class, + SubmissionOtherWarningTestModule::class, + SubmissionSymptomIntroFragmentTestModule::class, + SubmissionSymptomCalendarFragmentTestModule::class, + SubmissionContactTestModule::class, + SubmissionDoneTestModule::class, + SubmissionQRInfoFragmentModule::class, + SubmissionQRScanFragmentModule::class ] ) class FragmentTestModuleRegistrar diff --git a/Corona-Warn-App/src/debug/AndroidManifest.xml b/Corona-Warn-App/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..ba24510e29ef4b6b35dd0c3ec856f1decd626ca4 --- /dev/null +++ b/Corona-Warn-App/src/debug/AndroidManifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <!-- Allows unlocking your device and activating its screen so UI tests can succeed --> + <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <!-- Allows for storing and retrieving screenshots --> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + + <!-- Allows changing locales --> + <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> + +</manifest> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt index eff4ebae82a41cb2f9d9d032d2f70a520ef5c2bf..ed9100cda73ba3dad4bf70ede1eda68443f3dd14 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt @@ -32,11 +32,9 @@ object SubmissionRepository { private val testResultReceivedDateFlowInternal = MutableStateFlow(Date()) val testResultReceivedDateFlow: Flow<Date> = testResultReceivedDateFlowInternal - val testResultReceivedDate = testResultReceivedDateFlow.asLiveData() private val deviceUIStateFlowInternal = MutableStateFlow(DeviceUIState.UNPAIRED) val deviceUIStateFlow: Flow<DeviceUIState> = deviceUIStateFlowInternal - val deviceUIState = deviceUIStateFlow.asLiveData() private val testResultFlow = MutableStateFlow<TestResult?>(null) 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 81327c8e1182373949179b771c87b4e3936e01dd..b29b2469819cf00badd319ae1a24ef7c3a6fafbf 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 @@ -1,11 +1,12 @@ package de.rki.coronawarnapp.storage.interoperability import android.text.TextUtils -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations +import androidx.lifecycle.asLiveData import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.storage.LocalData import de.rki.coronawarnapp.ui.Country +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.runBlocking import timber.log.Timber import java.util.Locale @@ -21,8 +22,11 @@ class InteroperabilityRepository @Inject constructor( LocalData.isInteroperabilityShownAtLeastOnce = true } - private val _countryList: MutableLiveData<List<Country>> = MutableLiveData(listOf()) - val countryList = Transformations.distinctUntilChanged(_countryList) + private val countryListFlowInternal = MutableStateFlow(listOf<Country>()) + val countryListFlow: Flow<List<Country>> = countryListFlowInternal + + @Deprecated("Use countryListFlow") + val countryList = countryListFlow.asLiveData() init { getAllCountries() @@ -44,16 +48,16 @@ class InteroperabilityRepository @Inject constructor( if (mappedCountry == null) Timber.e("Unknown countrycode: %s", rawCode) mappedCountry } - _countryList.postValue(countries) + countryListFlowInternal.value = countries Timber.d("Country list: ${TextUtils.join(System.lineSeparator(), countries)}") } catch (e: Exception) { Timber.e(e) - _countryList.postValue(listOf()) + countryListFlowInternal.value = emptyList() } } } fun clear() { - _countryList.postValue(emptyList()) + countryListFlowInternal.value = emptyList() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/DaysSinceOnsetOfSymptomsVectorDeterminator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/DaysSinceOnsetOfSymptomsVectorDeterminator.kt index e7a6c4179315a9f031c59ae5d97094ed5f7894b4..037dfc69052e4aa8674fde07ce589b8b2ce04f70 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/DaysSinceOnsetOfSymptomsVectorDeterminator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/DaysSinceOnsetOfSymptomsVectorDeterminator.kt @@ -31,10 +31,9 @@ class DaysSinceOnsetOfSymptomsVectorDeterminator @Inject constructor( @Suppress("MagicNumber") private fun determinePositiveIndication(symptoms: Symptoms): DaysSinceOnsetOfSymptomsVector { return when (symptoms.startOfSymptoms) { - is Symptoms.StartOf.Date -> - createDaysSinceOnsetOfSymptomsVectorWith( - symptoms.startOfSymptoms.date.ageInDays(timeStamper.nowUTC.toLocalDate()) - ) + is Symptoms.StartOf.Date -> createDaysSinceOnsetOfSymptomsVectorWith( + symptoms.startOfSymptoms.date.ageInDays(timeStamper.nowUTC.toLocalDate()) + ) is Symptoms.StartOf.LastSevenDays -> createDaysSinceOnsetOfSymptomsVectorWith(701) is Symptoms.StartOf.OneToTwoWeeksAgo -> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/Symptoms.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/Symptoms.kt index 13e80e20a5f75dd4014b4f7597914124338ed71f..769df6264d8d24e3abd186fd2acfd57e110e30e4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/Symptoms.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/Symptoms.kt @@ -1,23 +1,43 @@ package de.rki.coronawarnapp.submission +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize import org.joda.time.LocalDate +@Parcelize data class Symptoms( val startOfSymptoms: StartOf?, val symptomIndication: Indication -) { - sealed class StartOf { +) : Parcelable { + sealed class StartOf : Parcelable { + @Parcelize data class Date(val date: LocalDate) : StartOf() + + @Parcelize object LastSevenDays : StartOf() + + @Parcelize object OneToTwoWeeksAgo : StartOf() + + @Parcelize object MoreThanTwoWeeks : StartOf() + + @Parcelize object NoInformation : StartOf() } - enum class Indication { + @Parcelize + enum class Indication : Parcelable { POSITIVE, NEGATIVE, NO_INFORMATION } + + companion object { + val NO_INFO_GIVEN = Symptoms( + startOfSymptoms = null, // FIXME should this be null? + symptomIndication = Indication.NO_INFORMATION + ) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt index af1a59dfd887c171a66c9e901a55a21e049d5e61..647f333c86bf16cc897fd14aad17495fe12ebe38 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt @@ -9,7 +9,7 @@ import de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaInteroperabilityModule import de.rki.coronawarnapp.ui.settings.SettingFragmentsModule import de.rki.coronawarnapp.ui.settings.SettingsResetFragment import de.rki.coronawarnapp.ui.settings.SettingsResetModule -import de.rki.coronawarnapp.ui.submission.SubmissionFragmentModule +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionFragmentModule import de.rki.coronawarnapp.ui.tracing.details.RiskDetailsFragmentModule @Module( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt deleted file mode 100644 index 644cf1ee0f3aca6bb52e87b0fcca899b24065220..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/TanConstants.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.ui.submission - -object TanConstants { - const val MAX_LENGTH = 10 - val ALPHA_NUMERIC_CHARS = ('a'..'z').plus('A'..'Z').plus('0'..'9') -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomCalendarFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomCalendarFragment.kt deleted file mode 100644 index 1c3184ab813f7b2bb8a2cf3537b55ca4b05605a4..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomCalendarFragment.kt +++ /dev/null @@ -1,161 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.fragment - -import android.content.res.ColorStateList -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentSubmissionSymptomCalendarBinding -import de.rki.coronawarnapp.submission.Symptoms -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionSymptomCalendarViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel -import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.formatter.formatCalendarBackgroundButtonStyleByState -import de.rki.coronawarnapp.util.formatter.formatCalendarButtonStyleByState -import de.rki.coronawarnapp.util.formatter.isEnableSymptomCalendarButtonByState -import de.rki.coronawarnapp.util.ui.doNavigate -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider -import de.rki.coronawarnapp.util.viewmodel.cwaViewModels -import javax.inject.Inject - -class SubmissionSymptomCalendarFragment : Fragment(), AutoInject { - - @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: SubmissionSymptomCalendarViewModel by cwaViewModels { viewModelFactory } - private var _binding: FragmentSubmissionSymptomCalendarBinding? = null - private val binding: FragmentSubmissionSymptomCalendarBinding get() = _binding!! - private val submissionViewModel: SubmissionViewModel by activityViewModels() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - _binding = FragmentSubmissionSymptomCalendarBinding.inflate(inflater) - binding.submissionViewModel = submissionViewModel - binding.lifecycleOwner = this - return binding.root - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - setButtonOnClickListener() - - binding.symptomCalendarContainer.setDateSelectedListener(submissionViewModel::onDateSelected) - - viewModel.routeToScreen.observe(viewLifecycleOwner, Observer { - when (it) { - is SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning -> navigateToSymptomFinish() - is SubmissionNavigationEvents.NavigateToSymptomIntroduction -> navigateToPreviousScreen() - } - }) - - submissionViewModel.symptomStart.observe(viewLifecycleOwner, Observer { - updateButtons(it) - if (it !is Symptoms.StartOf.Date) { - binding.symptomCalendarContainer.unsetSelection() - } - }) - - submissionViewModel.initSymptomStart() - } - - private fun updateButtons(symptomStart: Symptoms.StartOf?) { - binding.symptomCalendarChoiceSelection.calendarButtonSevenDays - .findViewById<Button>(R.id.calendar_button_seven_days) - .setTextColor(formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.LastSevenDays)) - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.calendar_button_seven_days).backgroundTintList = - ColorStateList.valueOf( - formatCalendarBackgroundButtonStyleByState( - symptomStart, Symptoms.StartOf.LastSevenDays - ) - ) - - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.calendar_button_one_two_weeks) - .setTextColor(formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.OneToTwoWeeksAgo)) - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.calendar_button_one_two_weeks).backgroundTintList = - ColorStateList.valueOf( - formatCalendarBackgroundButtonStyleByState( - symptomStart, Symptoms.StartOf.OneToTwoWeeksAgo - ) - ) - - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.calendar_button_more_than_two_weeks) - .setTextColor(formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.MoreThanTwoWeeks)) - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.calendar_button_more_than_two_weeks).backgroundTintList = - ColorStateList.valueOf( - formatCalendarBackgroundButtonStyleByState( - symptomStart, Symptoms.StartOf.MoreThanTwoWeeks - ) - ) - - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.target_button_verify) - .setTextColor(formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.NoInformation)) - binding.symptomCalendarChoiceSelection.targetLayout - .findViewById<Button>(R.id.target_button_verify).backgroundTintList = - ColorStateList.valueOf( - formatCalendarBackgroundButtonStyleByState( - symptomStart, Symptoms.StartOf.NoInformation - ) - ) - - binding - .symptomButtonNext.findViewById<Button>(R.id.symptom_button_next).isEnabled = - isEnableSymptomCalendarButtonByState( - symptomStart - ) - } - - private fun navigateToSymptomFinish() { - doNavigate(SubmissionSymptomCalendarFragmentDirections - .actionSubmissionSymptomCalendarFragmentToSubmissionResultPositiveOtherWarningFragment()) - } - - private fun navigateToPreviousScreen() { - doNavigate(SubmissionSymptomCalendarFragmentDirections - .actionSubmissionCalendarFragmentToSubmissionSymptomIntroductionFragment()) - } - - private fun setButtonOnClickListener() { - binding - .submissionSymptomCalendarHeader.headerButtonBack.buttonIcon - .setOnClickListener { viewModel.onCalendarPreviousClicked() } - - binding - .symptomButtonNext - .setOnClickListener { viewModel.onCalendarNextClicked() } - - binding.symptomCalendarChoiceSelection - .calendarButtonSevenDays - .setOnClickListener { submissionViewModel.onLastSevenDaysStart() } - - binding.symptomCalendarChoiceSelection - .calendarButtonOneTwoWeeks - .setOnClickListener { submissionViewModel.onOneToTwoWeeksAgoStart() } - - binding.symptomCalendarChoiceSelection - .calendarButtonMoreThanTwoWeeks - .setOnClickListener { submissionViewModel.onMoreThanTwoWeeksStart() } - - binding.symptomCalendarChoiceSelection - .targetButtonVerify - .setOnClickListener { submissionViewModel.onNoInformationStart() } - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragment.kt similarity index 78% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragment.kt index 1f37ca3e03ea3d3829a9492919609c4ae2864333..c98d506a032d742cec2cc6f8a1749d03717f10b1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.qrcode.info import android.os.Bundle import android.view.View @@ -6,7 +6,6 @@ import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionQrCodeInfoBinding -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionQRCodeInfoFragmentViewModel import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.doNavigate import de.rki.coronawarnapp.util.ui.observe2 @@ -24,11 +23,11 @@ class SubmissionQRCodeInfoFragment : Fragment(R.layout.fragment_submission_qr_co override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.submissionQrCodeInfoHeader.headerButtonBack.buttonIcon.setOnClickListener() { + binding.submissionQrCodeInfoHeader.headerButtonBack.buttonIcon.setOnClickListener { viewModel.onBackPressed() } - binding.submissionQrInfoButtonNext.setOnClickListener() { + binding.submissionQrInfoButtonNext.setOnClickListener { viewModel.onNextPressed() } @@ -38,8 +37,9 @@ class SubmissionQRCodeInfoFragment : Fragment(R.layout.fragment_submission_qr_co viewModel.navigateToQRScan.observe2(this) { doNavigate( - SubmissionQRCodeInfoFragmentDirections - .actionSubmissionQRCodeInfoFragmentToSubmissionQRCodeScanFragment()) + SubmissionQRCodeInfoFragmentDirections + .actionSubmissionQRCodeInfoFragmentToSubmissionQRCodeScanFragment() + ) } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt index cde1a6246b98b1f4ed7321526b2f4b1f86bb6312..f90824a82de035cbc09cc81fec9986e3e05ff305 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModel.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.qrcode.info import com.squareup.inject.assisted.AssistedInject import de.rki.coronawarnapp.util.ui.SingleLiveEvent diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoModule.kt similarity index 79% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoModule.kt index fe135026e5823adaf0be18a9283913b193ff3410..437fc64b2353848b749819925a9187cf4fd956cb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeInfoModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoModule.kt @@ -1,9 +1,8 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.qrcode.info import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionQRCodeInfoFragmentViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeScanFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt index c09f7a54bcd6321ea83d2d00f1ff39eb3d52485e..1ecb64ba822eb0f531f8cf37d4fc769e464f1925 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionQRCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.qrcode.scan import android.Manifest import android.content.pm.PackageManager @@ -6,9 +6,7 @@ import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import com.google.zxing.BarcodeFormat -import com.journeyapps.barcodescanner.BarcodeResult import com.journeyapps.barcodescanner.DefaultDecoderFactory import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionQrCodeScanBinding @@ -20,8 +18,6 @@ import de.rki.coronawarnapp.ui.main.MainActivity import de.rki.coronawarnapp.ui.submission.ApiRequestState import de.rki.coronawarnapp.ui.submission.ScanStatus import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionQRCodeScanViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.util.CameraPermissionHelper import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.di.AutoInject @@ -40,54 +36,10 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory private val viewModel: SubmissionQRCodeScanViewModel by cwaViewModels { viewModelFactory } - private val submissionViewModel: SubmissionViewModel by activityViewModels() + private val binding: FragmentSubmissionQrCodeScanBinding by viewBindingLazy() private var showsPermissionDialog = false - private fun decodeCallback(result: BarcodeResult) { - submissionViewModel.validateAndStoreTestGUID(result.text) - } - - private fun startDecode() { - binding.submissionQrCodeScanPreview.decodeSingle { decodeCallback(it) } - } - - 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, - getString( - R.string.submission_error_dialog_web_generic_network_error_body, - exception.statusCode - ), - 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 - ) - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -106,9 +58,9 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co binding.submissionQrCodeScanViewfinderView.setCameraPreview(binding.submissionQrCodeScanPreview) - submissionViewModel.scanStatus.observeEvent(viewLifecycleOwner) { + viewModel.scanStatus.observeEvent(viewLifecycleOwner) { if (ScanStatus.SUCCESS == it) { - submissionViewModel.doDeviceRegistration() + viewModel.doDeviceRegistration() } if (ScanStatus.INVALID == it) { @@ -116,7 +68,7 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co } } - submissionViewModel.registrationState.observeEvent(viewLifecycleOwner) { + viewModel.registrationState.observe2(this) { binding.submissionQrCodeScanSpinner.visibility = when (it) { ApiRequestState.STARTED -> View.VISIBLE else -> View.GONE @@ -130,7 +82,7 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co } } - submissionViewModel.registrationError.observeEvent(viewLifecycleOwner) { + viewModel.registrationError.observe2(this) { DialogHelper.showDialog(buildErrorDialog(it)) } @@ -144,10 +96,51 @@ class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_co } } + private fun startDecode() { + binding.submissionQrCodeScanPreview.decodeSingle { + viewModel.validateAndStoreTestGUID(it.text) + } + } + + 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, + getString( + R.string.submission_error_dialog_web_generic_network_error_body, + exception.statusCode + ), + 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 navigateToDispatchScreen() = doNavigate( - SubmissionQRCodeScanFragmentDirections - .actionSubmissionQRCodeScanFragmentToSubmissionDispatcherFragment() + SubmissionQRCodeScanFragmentDirections.actionSubmissionQRCodeScanFragmentToSubmissionDispatcherFragment() ) private fun showInvalidScanDialog() { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanModule.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanModule.kt index 55c737607dd9de8d75bf7054e5137b53daaad770..7a435572f5371e5e76a6a49482e6bc80386e803b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.qrcode.scan import dagger.Binds import dagger.Module 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 new file mode 100644 index 0000000000000000000000000000000000000000..b4330c73b3d8596cfcd44f708d814e6e9e786ede --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt @@ -0,0 +1,71 @@ +package de.rki.coronawarnapp.ui.submission.qrcode.scan + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.squareup.inject.assisted.AssistedInject +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.service.submission.QRScanResult +import de.rki.coronawarnapp.service.submission.SubmissionService +import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.ui.submission.ScanStatus +import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents +import de.rki.coronawarnapp.util.Event +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory + +class SubmissionQRCodeScanViewModel @AssistedInject constructor() : CWAViewModel() { + + val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + private val _scanStatus = MutableLiveData(Event(ScanStatus.STARTED)) + + val scanStatus: LiveData<Event<ScanStatus>> = _scanStatus + + fun validateAndStoreTestGUID(rawResult: String) { + val scanResult = QRScanResult(rawResult) + if (scanResult.isValid) { + SubmissionService.storeTestGUID(scanResult.guid!!) + _scanStatus.value = Event(ScanStatus.SUCCESS) + } else { + _scanStatus.value = Event(ScanStatus.INVALID) + } + } + + val registrationState = MutableLiveData(ApiRequestState.IDLE) + val registrationError = SingleLiveEvent<CwaWebException>() + + fun doDeviceRegistration() = launch { + try { + registrationState.postValue(ApiRequestState.STARTED) + SubmissionService.asyncRegisterDevice() + 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) + err.report(ExceptionCategory.INTERNAL) + } + } + + fun onBackPressed() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToQRInfo) + } + + fun onClosePressed() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToDispatcher) + } + + @AssistedInject.Factory + interface Factory : SimpleCWAViewModelFactory<SubmissionQRCodeScanViewModel> +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..2c1653eedd26f0d6df583642e4edd1fbb6eb3263 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarFragment.kt @@ -0,0 +1,148 @@ +package de.rki.coronawarnapp.ui.submission.symptoms.calendar + +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.viewmodel.SubmissionNavigationEvents +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.formatter.formatCalendarBackgroundButtonStyleByState +import de.rki.coronawarnapp.util.formatter.formatCalendarButtonStyleByState +import de.rki.coronawarnapp.util.formatter.isEnableSymptomCalendarButtonByState +import de.rki.coronawarnapp.util.ui.doNavigate +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 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(navArgs.symptomIndication) + } + ) + + private val binding: FragmentSubmissionSymptomCalendarBinding by viewBindingLazy() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + binding.symptomCalendarContainer.setDateSelectedListener { + viewModel.onDateSelected(it) + } + + viewModel.routeToScreen.observe2(this) { + when (it) { + is SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning -> doNavigate( + SubmissionSymptomCalendarFragmentDirections + .actionSubmissionSymptomCalendarFragmentToSubmissionResultPositiveOtherWarningFragment( + it.symptoms + ) + ) + is SubmissionNavigationEvents.NavigateToSymptomIntroduction -> doNavigate( + SubmissionSymptomCalendarFragmentDirections + .actionSubmissionCalendarFragmentToSubmissionSymptomIntroductionFragment() + ) + } + } + + viewModel.symptomStart.observe2(this) { + updateButtons(it) + if (it !is Symptoms.StartOf.Date) { + binding.symptomCalendarContainer.unsetSelection() + } + } + + binding.apply { + submissionSymptomCalendarHeader.headerButtonBack.buttonIcon + .setOnClickListener { viewModel.onCalendarPreviousClicked() } + + symptomButtonNext + .setOnClickListener { viewModel.onCalendarNextClicked() } + + symptomCalendarChoiceSelection + .calendarButtonSevenDays + .setOnClickListener { viewModel.onLastSevenDaysStart() } + + symptomCalendarChoiceSelection + .calendarButtonOneTwoWeeks + .setOnClickListener { viewModel.onOneToTwoWeeksAgoStart() } + + symptomCalendarChoiceSelection + .calendarButtonMoreThanTwoWeeks + .setOnClickListener { viewModel.onMoreThanTwoWeeksStart() } + + symptomCalendarChoiceSelection + .targetButtonVerify + .setOnClickListener { viewModel.onNoInformationStart() } + } + } + + private fun updateButtons(symptomStart: Symptoms.StartOf?) { + binding.symptomCalendarChoiceSelection.apply { + calendarButtonSevenDays.apply { + setTextColor( + formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.LastSevenDays) + ) + backgroundTintList = ColorStateList.valueOf( + formatCalendarBackgroundButtonStyleByState( + symptomStart, Symptoms.StartOf.LastSevenDays + ) + ) + } + + calendarButtonOneTwoWeeks.apply { + setTextColor( + formatCalendarButtonStyleByState( + symptomStart, + Symptoms.StartOf.OneToTwoWeeksAgo + ) + ) + backgroundTintList = ColorStateList.valueOf( + formatCalendarBackgroundButtonStyleByState( + symptomStart, Symptoms.StartOf.OneToTwoWeeksAgo + ) + ) + } + + calendarButtonMoreThanTwoWeeks.apply { + setTextColor( + formatCalendarButtonStyleByState( + symptomStart, + Symptoms.StartOf.MoreThanTwoWeeks + ) + ) + backgroundTintList = ColorStateList.valueOf( + formatCalendarBackgroundButtonStyleByState( + symptomStart, Symptoms.StartOf.MoreThanTwoWeeks + ) + ) + } + targetButtonVerify.apply { + setTextColor( + formatCalendarButtonStyleByState(symptomStart, Symptoms.StartOf.NoInformation) + ) + backgroundTintList = ColorStateList.valueOf( + formatCalendarBackgroundButtonStyleByState( + symptomStart, Symptoms.StartOf.NoInformation + ) + ) + } + } + + binding.symptomButtonNext.isEnabled = isEnableSymptomCalendarButtonByState( + symptomStart + ) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarModule.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarModule.kt index d359621f48f15cce986628fcea9dec82e2eb64f3..82edb4598b871084eb556494657d61a2e6e0f505 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.symptoms.calendar import dagger.Binds import dagger.Module 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 new file mode 100644 index 0000000000000000000000000000000000000000..e79975890a20c3d5572801fd7ecf5481f246e367 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/calendar/SubmissionSymptomCalendarViewModel.kt @@ -0,0 +1,68 @@ +package de.rki.coronawarnapp.ui.submission.symptoms.calendar + +import androidx.lifecycle.asLiveData +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +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.MutableStateFlow +import kotlinx.coroutines.flow.first +import org.joda.time.LocalDate + +class SubmissionSymptomCalendarViewModel @AssistedInject constructor( + @Assisted private val symptomIndication: Symptoms.Indication, + dispatcherProvider: DispatcherProvider +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + private val symptomStartInternal = MutableStateFlow<Symptoms.StartOf?>(null) + val symptomStart = symptomStartInternal + .asLiveData(context = dispatcherProvider.Default) + + val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + + fun onCalendarNextClicked() { + launch { + val symptoms = Symptoms( + startOfSymptoms = symptomStartInternal.first(), + symptomIndication = symptomIndication + ) + routeToScreen.postValue( + SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning(symptoms) + ) + } + } + + fun onCalendarPreviousClicked() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomIntroduction) + } + + fun onLastSevenDaysStart() { + symptomStartInternal.value = Symptoms.StartOf.LastSevenDays + } + + fun onOneToTwoWeeksAgoStart() { + symptomStartInternal.value = Symptoms.StartOf.OneToTwoWeeksAgo + } + + fun onMoreThanTwoWeeksStart() { + symptomStartInternal.value = Symptoms.StartOf.MoreThanTwoWeeks + } + + fun onNoInformationStart() { + symptomStartInternal.value = Symptoms.StartOf.NoInformation + } + + fun onDateSelected(localDate: LocalDate?) { + symptomStartInternal.value = localDate?.let { Symptoms.StartOf.Date(it) } + } + + @AssistedInject.Factory + interface Factory : CWAViewModelFactory<SubmissionSymptomCalendarViewModel> { + + fun create(symptomIndication: Symptoms.Indication): SubmissionSymptomCalendarViewModel + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomIntroductionFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt similarity index 60% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomIntroductionFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt index a7895306fc34a66757f801c3b52c7cce97f77ec7..c2af32534fb433369929cc714279aed588a3a4f1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionSymptomIntroductionFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionFragment.kt @@ -1,20 +1,15 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.symptoms.introduction import android.content.res.ColorStateList import android.os.Bundle -import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.widget.Button import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionSymptomIntroBinding import de.rki.coronawarnapp.submission.Symptoms import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionSymptomIntroductionViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.util.DialogHelper import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.formatter.formatBackgroundButtonStyleByState @@ -22,52 +17,62 @@ import de.rki.coronawarnapp.util.formatter.formatButtonStyleByState import de.rki.coronawarnapp.util.formatter.isEnableSymptomIntroButtonByState import de.rki.coronawarnapp.util.ui.doNavigate 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.cwaViewModels import javax.inject.Inject -class SubmissionSymptomIntroductionFragment : Fragment(), AutoInject { +class SubmissionSymptomIntroductionFragment : Fragment(R.layout.fragment_submission_symptom_intro), + AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory private val viewModel: SubmissionSymptomIntroductionViewModel by cwaViewModels { viewModelFactory } - private var _binding: FragmentSubmissionSymptomIntroBinding? = null - private val binding: FragmentSubmissionSymptomIntroBinding get() = _binding!! - private val submissionViewModel: SubmissionViewModel by activityViewModels() - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - _binding = FragmentSubmissionSymptomIntroBinding.inflate(inflater) - binding.submissionViewModel = submissionViewModel - binding.lifecycleOwner = this - return binding.root - } - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } + private val binding: FragmentSubmissionSymptomIntroBinding by viewBindingLazy() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setButtonOnClickListener() viewModel.routeToScreen.observe2(this) { when (it) { - is SubmissionNavigationEvents.NavigateToSymptomCalendar -> navigateToNext() + is SubmissionNavigationEvents.NavigateToSymptomCalendar -> doNavigate( + SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToSubmissionSymptomCalendarFragment( + symptomIndication = it.symptomIndication + ) + ) + is SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning -> doNavigate( + SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToSubmissionResultPositiveOtherWarningFragment( + it.symptoms + ) + ) is SubmissionNavigationEvents.NavigateToTestResult -> handleSubmissionCancellation() } } - submissionViewModel.symptomIndication.observe(viewLifecycleOwner, { + viewModel.symptomIndication.observe2(this) { updateButtons(it) - }) + } requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backCallback) - submissionViewModel.initSymptoms() + binding.apply { + submissionSymptomHeader.headerButtonBack.buttonIcon + .setOnClickListener { viewModel.onPreviousClicked() } + + symptomButtonNext + .setOnClickListener { viewModel.onNextClicked() } + + symptomChoiceSelection.targetButtonApply + .setOnClickListener { viewModel.onPositiveSymptomIndication() } + + symptomChoiceSelection.targetButtonReject + .setOnClickListener { viewModel.onNegativeSymptomIndication() } + + symptomChoiceSelection.targetButtonVerify + .setOnClickListener { viewModel.onNoInformationSymptomIndication() } + } } private val backCallback: OnBackPressedCallback = @@ -117,26 +122,9 @@ class SubmissionSymptomIntroductionFragment : Fragment(), AutoInject { ) } - private fun navigateToNext() { - - if (submissionViewModel.symptomIndication.value!! == Symptoms.Indication.POSITIVE) { - doNavigate( - SubmissionSymptomIntroductionFragmentDirections - .actionSubmissionSymptomIntroductionFragmentToSubmissionSymptomCalendarFragment() - ) - } else { - doNavigate( - SubmissionSymptomIntroductionFragmentDirections - .actionSubmissionSymptomIntroductionFragmentToSubmissionResultPositiveOtherWarningFragment() - ) - } - } - /** * Opens a Dialog that warns user * when they're about to cancel the submission flow - * @see DialogHelper - * @see navigateToPreviousScreen */ private fun handleSubmissionCancellation() { DialogHelper.showDialog( @@ -147,37 +135,13 @@ class SubmissionSymptomIntroductionFragment : Fragment(), AutoInject { R.string.submission_error_dialog_confirm_cancellation_button_positive, R.string.submission_error_dialog_confirm_cancellation_button_negative, true, - ::navigateToPreviousScreen + { + doNavigate( + SubmissionSymptomIntroductionFragmentDirections + .actionSubmissionSymptomIntroductionFragmentToSubmissionResultFragment() + ) + } ) ) } - - private fun navigateToPreviousScreen() { - doNavigate( - SubmissionSymptomIntroductionFragmentDirections - .actionSubmissionSymptomIntroductionFragmentToSubmissionResultFragment() - ) - } - - private fun setButtonOnClickListener() { - binding - .submissionSymptomHeader.headerButtonBack.buttonIcon - .setOnClickListener { viewModel.onPreviousClicked() } - - binding - .symptomButtonNext - .setOnClickListener { viewModel.onNextClicked() } - - binding - .symptomChoiceSelection.targetButtonApply - .setOnClickListener { submissionViewModel.onPositiveSymptomIndication() } - - binding - .symptomChoiceSelection.targetButtonReject - .setOnClickListener { submissionViewModel.onNegativeSymptomIndication() } - - binding - .symptomChoiceSelection.targetButtonVerify - .setOnClickListener { submissionViewModel.onNoInformationSymptomIndication() } - } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionModule.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionModule.kt index 53792300528498a2ff95abcfe6d4d2fa69c10628..40b49ba8e839ffc6d3cc687d375632e93678ec79 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.symptoms.introduction import dagger.Binds import dagger.Module 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 new file mode 100644 index 0000000000000000000000000000000000000000..43f7550b426a28cfee0e10c8360906c685c4aaf2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/symptoms/introduction/SubmissionSymptomIntroductionViewModel.kt @@ -0,0 +1,61 @@ +package de.rki.coronawarnapp.ui.submission.symptoms.introduction + +import androidx.lifecycle.asLiveData +import com.squareup.inject.assisted.AssistedInject +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.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first + +class SubmissionSymptomIntroductionViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + private val symptomIndicationInternal = MutableStateFlow<Symptoms.Indication?>(null) + val symptomIndication = symptomIndicationInternal + .asLiveData(context = dispatcherProvider.Default) + + val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + + fun onNextClicked() { + launch { + when (symptomIndicationInternal.first()) { + Symptoms.Indication.POSITIVE -> SubmissionNavigationEvents.NavigateToSymptomCalendar( + Symptoms.Indication.POSITIVE + ) + Symptoms.Indication.NEGATIVE -> SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning( + symptoms = Symptoms( + startOfSymptoms = null, + symptomIndication = Symptoms.Indication.NEGATIVE + ) + ) + else -> SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning( + symptoms = Symptoms.NO_INFO_GIVEN + ) + }.let { routeToScreen.postValue(it) } + } + } + + fun onPreviousClicked() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToTestResult) + } + + fun onPositiveSymptomIndication() { + symptomIndicationInternal.value = Symptoms.Indication.POSITIVE + } + + fun onNegativeSymptomIndication() { + symptomIndicationInternal.value = Symptoms.Indication.NEGATIVE + } + + fun onNoInformationSymptomIndication() { + symptomIndicationInternal.value = Symptoms.Indication.NO_INFORMATION + } + + @AssistedInject.Factory + interface Factory : SimpleCWAViewModelFactory<SubmissionSymptomIntroductionViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt similarity index 69% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTanFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt index f021bdc4d7b675c3c40cff9513773999cab8d873..a9c953118a6983ab44db4e83d669d41185e25c00 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanFragment.kt @@ -1,10 +1,9 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.tan import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionTanBinding import de.rki.coronawarnapp.exception.http.BadRequestException @@ -13,14 +12,11 @@ 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.TanConstants -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTanViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.util.DialogHelper -import de.rki.coronawarnapp.util.TanHelper import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.observeEvent import de.rki.coronawarnapp.util.ui.doNavigate +import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.setGone import de.rki.coronawarnapp.util.ui.viewBindingLazy import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModels @@ -33,67 +29,33 @@ import javax.inject.Inject class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val submissionViewModel: SubmissionViewModel by viewModels() private val viewModel: SubmissionTanViewModel by cwaViewModels { viewModelFactory } - private val binding: FragmentSubmissionTanBinding by viewBindingLazy() - private fun buildErrorDialog(exception: CwaWebException): DialogHelper.DialogInstance { - return when (exception) { - is BadRequestException -> DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_error_dialog_web_test_paired_title_tan, - R.string.submission_error_dialog_web_test_paired_body_tan, - R.string.submission_error_dialog_web_test_paired_button_positive, - null, - true, - ::goBack - ) - is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_error_dialog_web_generic_error_title, - getString( - R.string.submission_error_dialog_web_generic_network_error_body, - exception.statusCode - ), - R.string.submission_error_dialog_web_generic_error_button_positive, - null, - true, - ::goBack - ) - 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, - ::goBack - ) - } - } + private val binding: FragmentSubmissionTanBinding by viewBindingLazy() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.viewmodel = viewModel - binding.submissionTanContent.submissionTanInput.listener = { tan -> - resetError() + viewModel.state.observe2(this) { + binding.uiState = it - viewModel.tan.value = tan + submission_tan_character_error.setGone(it.areCharactersCorrect) + submission_tan_error.setGone(it.isTanValidFormat) + } - if (tan != null) { - if (!TanHelper.allCharactersValid(tan)) - showCharacterError() + binding.submissionTanContent.submissionTanInput.listener = { tan -> + submission_tan_character_error.visibility = View.GONE + submission_tan_error.visibility = View.GONE - if (tan.length == TanConstants.MAX_LENGTH && !TanHelper.isChecksumValid(tan)) - showTanError() - } + viewModel.onTanChanged(tan) } - binding.submissionTanButtonEnter.setOnClickListener { storeTanAndContinue() } + binding.submissionTanButtonEnter.setOnClickListener { + viewModel.onTanSubmit() + } binding.submissionTanHeader.headerButtonBack.buttonIcon.setOnClickListener { goBack() } - submissionViewModel.registrationState.observeEvent(viewLifecycleOwner) { + viewModel.registrationState.observe2(this) { binding.submissionTanSpinner.visibility = when (it) { ApiRequestState.STARTED -> View.VISIBLE else -> View.GONE @@ -106,24 +68,11 @@ class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoIn } } - submissionViewModel.registrationError.observeEvent(viewLifecycleOwner) { + viewModel.registrationError.observe2(this) { DialogHelper.showDialog(buildErrorDialog(it)) } } - private fun resetError() { - submission_tan_character_error.visibility = View.GONE - submission_tan_error.visibility = View.GONE - } - - private fun showCharacterError() { - submission_tan_character_error.visibility = View.VISIBLE - } - - private fun showTanError() { - submission_tan_error.visibility = View.VISIBLE - } - override fun onResume() { super.onResume() binding.submissionTanRoot.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) @@ -131,14 +80,38 @@ class SubmissionTanFragment : Fragment(R.layout.fragment_submission_tan), AutoIn private fun goBack() = (activity as MainActivity).goBack() - private fun storeTanAndContinue() { - // verify input format - if (viewModel.isValidTanFormat.value != true) - return - - // store locally - viewModel.storeTeletan() - - submissionViewModel.doDeviceRegistration() + private fun buildErrorDialog(exception: CwaWebException): DialogHelper.DialogInstance { + return when (exception) { + is BadRequestException -> DialogHelper.DialogInstance( + requireActivity(), + R.string.submission_error_dialog_web_test_paired_title_tan, + R.string.submission_error_dialog_web_test_paired_body_tan, + R.string.submission_error_dialog_web_test_paired_button_positive, + null, + true, + ::goBack + ) + is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance( + requireActivity(), + R.string.submission_error_dialog_web_generic_error_title, + getString( + R.string.submission_error_dialog_web_generic_network_error_body, + exception.statusCode + ), + R.string.submission_error_dialog_web_generic_error_button_positive, + null, + true, + ::goBack + ) + 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, + ::goBack + ) + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanModule.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanModule.kt index 5a05697d71229959b5716f7ba14c7c24ab095391..a33e0f4ca9d23dd81adf95d3eb6a3719a187d5a6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.tan import dagger.Binds import dagger.Module 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 new file mode 100644 index 0000000000000000000000000000000000000000..b2fcfe37df70cdd766efa70050c84b350cba7336 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModel.kt @@ -0,0 +1,81 @@ +package de.rki.coronawarnapp.ui.submission.tan + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData +import com.squareup.inject.assisted.AssistedInject +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.service.submission.SubmissionService +import de.rki.coronawarnapp.storage.SubmissionRepository +import de.rki.coronawarnapp.ui.submission.ApiRequestState +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.map +import timber.log.Timber + +class SubmissionTanViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider +) : CWAViewModel() { + + private val currentTan = MutableStateFlow(Tan("")) + + val state = currentTan.map { currentTan -> + UIState( + isTanValid = currentTan.isTanValid, + isTanValidFormat = currentTan.isTanValidFormat, + areCharactersCorrect = currentTan.areCharactersValid + ) + }.asLiveData(context = dispatcherProvider.Default) + + val registrationState = MutableLiveData(ApiRequestState.IDLE) + val registrationError = SingleLiveEvent<CwaWebException>() + + fun onTanChanged(tan: String) { + currentTan.value = Tan(tan) + } + + fun onTanSubmit() { + val teletan = currentTan.value + if (!teletan.isTanValid) { + Timber.w("Tried to set invalid teletan: %s", teletan) + return + } + Timber.d("Storing teletan $teletan") + SubmissionRepository.setTeletan(teletan.value) + + launch { + try { + registrationState.postValue(ApiRequestState.STARTED) + SubmissionService.asyncRegisterDevice() + 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) + err.report(ExceptionCategory.INTERNAL) + } + } + } + + data class UIState( + val isTanValid: Boolean = false, + val areCharactersCorrect: Boolean = false, + val isTanValidFormat: Boolean = false + ) + + @AssistedInject.Factory + interface Factory : SimpleCWAViewModelFactory<SubmissionTanViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/Tan.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/Tan.kt new file mode 100644 index 0000000000000000000000000000000000000000..a4d883605477fbc1295047d10155bb8bb5305b22 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/Tan.kt @@ -0,0 +1,46 @@ +package de.rki.coronawarnapp.ui.submission.tan + +import java.nio.charset.StandardCharsets +import java.security.MessageDigest +import java.util.Locale + +data class Tan( + val value: String +) { + + val areCharactersValid = allCharactersValid(value) + val isTanValidFormat = value.length == MAX_LENGTH && isChecksumValid(value) + val isTanValid = areCharactersValid && isTanValidFormat + + companion object { + const val MAX_LENGTH = 10 + internal val ALPHA_NUMERIC_CHARS = ('a'..'z').plus('A'..'Z').plus('0'..'9') + + private const val VALID_CHARACTERS = "23456789ABCDEFGHJKMNPQRSTUVWXYZ" + + fun isChecksumValid(tan: String): Boolean { + if (tan.trim().length != MAX_LENGTH) + return false + val subTan = tan.substring(0, MAX_LENGTH - 1).toUpperCase(Locale.ROOT) + val tanDigest = MessageDigest.getInstance("SHA-256") + .digest(subTan.toByteArray(StandardCharsets.US_ASCII)) + var checkChar = "%02x".format(tanDigest[0])[0] + if (checkChar == '0') checkChar = 'G' + if (checkChar == '1') checkChar = 'H' + + return checkChar.toUpperCase() == tan.last().toUpperCase() + } + + fun allCharactersValid(tan: String): Boolean { + for (character in tan) { + if (!isTanCharacterValid(character.toString())) + return false + } + return true + } + + fun isTanCharacterValid(character: String): Boolean { + return VALID_CHARACTERS.contains(character) + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TanInput.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TanInput.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt index 91a3e227e730b652d2a98bdb9cf2b9f915358106..04c1e7a8f0040f8bff6199ad7d1e752fc46751e6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TanInput.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/tan/TanInput.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.view +package de.rki.coronawarnapp.ui.submission.tan import android.content.Context import android.os.Handler @@ -13,9 +13,7 @@ import androidx.annotation.DimenRes import androidx.core.view.children import androidx.core.widget.doOnTextChanged import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.ui.submission.TanConstants -import de.rki.coronawarnapp.util.TanHelper -import kotlinx.android.synthetic.main.view_tan_input_edittext.view.tan_input_edittext +import kotlinx.android.synthetic.main.view_tan_input_edittext.view.* import java.util.Locale import kotlin.math.max @@ -36,14 +34,14 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs InputFilter { source, _, _, _, _, _ -> source.filter { !it.isWhitespace() } } private val alphaNumericFilter = InputFilter { source, _, _, _, _, _ -> source.filter { - TanConstants.ALPHA_NUMERIC_CHARS.contains(it) + Tan.ALPHA_NUMERIC_CHARS.contains(it) } } - private var lengthFilter = InputFilter.LengthFilter(TanConstants.MAX_LENGTH) + private var lengthFilter = InputFilter.LengthFilter(Tan.MAX_LENGTH) - var listener: ((String?) -> Unit)? = null + var listener: ((String) -> Unit)? = null - private var tan: String? = null + private var tan: String = "" private val lineSpacing: Int @@ -61,11 +59,12 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs tan_input_edittext.filters = arrayOf(whitespaceFilter, alphaNumericFilter, lengthFilter) // register listener - tan_input_edittext.doOnTextChanged { text, _, _, _ -> updateTan(text) } + tan_input_edittext.doOnTextChanged { text, _, _, _ -> updateTan(text ?: "") } setOnClickListener { showKeyboard() } // initially show the keyboard - Handler().postDelayed({ showKeyboard() }, + Handler().postDelayed( + { showKeyboard() }, KEYBOARD_TRIGGER_DELAY ) } @@ -77,8 +76,8 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs } } - private fun updateTan(text: CharSequence?) { - this.tan = text?.toString()?.toUpperCase(Locale.ROOT) + private fun updateTan(text: CharSequence) { + this.tan = text.toString().toUpperCase(Locale.ROOT) updateDigits() notifyListener() } @@ -98,7 +97,7 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs tanDigit.text = text tanDigit.background = when { text == EMPTY_STRING -> resources.getDrawable(R.drawable.tan_input_digit, null) - TanHelper.isTanCharacterValid(text) -> resources.getDrawable( + Tan.isTanCharacterValid(text) -> resources.getDrawable( R.drawable.tan_input_digit_entered, null ) @@ -106,14 +105,14 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs } tanDigit.setTextColor( - if (TanHelper.isTanCharacterValid(text)) + if (Tan.isTanCharacterValid(text)) resources.getColor(R.color.colorTextPrimary1, null) else resources.getColor(R.color.colorTextSemanticRed, null) ) } - private fun digitAtIndex(index: Int): String = tan?.getOrNull(index)?.toString() ?: EMPTY_STRING + private fun digitAtIndex(index: Int): String = tan.getOrNull(index)?.toString() ?: EMPTY_STRING override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) @@ -206,7 +205,7 @@ class TanInput(context: Context, attrs: AttributeSet) : ViewGroup(context, attrs private fun calculateDigitDimension(availableWith: Int, textSize: Int): Pair<Int, Int> { val widthRequiredForSpacing = (DIGIT_SPACING_COUNT * getDimension(R.dimen.submission_tan_total_digit_spacing)) + - (GROUP_SPACING_COUNT * getDimension(R.dimen.submission_tan_total_group_spacing)) + (GROUP_SPACING_COUNT * getDimension(R.dimen.submission_tan_total_group_spacing)) val remainingWidthForDigits = availableWith - widthRequiredForSpacing diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTestResultFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultFragment.kt similarity index 70% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTestResultFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultFragment.kt index c0da337c77a6c1373d2090684ec976173f218194..0655e3e5893e889f563f5b0b7361e3f9e4efcf0d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionTestResultFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.testresult import android.app.AlertDialog import android.os.Bundle @@ -6,7 +6,6 @@ import android.view.View import android.view.accessibility.AccessibilityEvent import androidx.activity.OnBackPressedCallback import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionTestResultBinding import de.rki.coronawarnapp.exception.http.CwaClientError @@ -14,11 +13,7 @@ import de.rki.coronawarnapp.exception.http.CwaServerError import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.storage.SubmissionRepository import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTestResultViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel -import de.rki.coronawarnapp.util.DeviceUIState import de.rki.coronawarnapp.util.DialogHelper -import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.observeEvent import de.rki.coronawarnapp.util.ui.doNavigate @@ -26,20 +21,13 @@ 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.cwaViewModels -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withContext import javax.inject.Inject -/** - * A simple [Fragment] subclass. - */ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_result), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory private val viewModel: SubmissionTestResultViewModel by cwaViewModels { viewModelFactory } - private val submissionViewModel: SubmissionViewModel by activityViewModels() private val binding: FragmentSubmissionTestResultBinding by viewBindingLazy() @@ -86,7 +74,11 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.submissionViewModel = submissionViewModel + + viewModel.uiState.observe2(this) { + binding.uiState = it + } + // registers callback when the os level back is pressed requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backCallback) @@ -95,14 +87,29 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ setButtonOnClickListener() - submissionViewModel.uiStateError.observeEvent(viewLifecycleOwner) { + viewModel.showTracingRequiredScreen.observe2(this) { + val tracingRequiredDialog = DialogHelper.DialogInstance( + requireActivity(), + R.string.submission_test_result_dialog_tracing_required_title, + R.string.submission_test_result_dialog_tracing_required_message, + R.string.submission_test_result_dialog_tracing_required_button + ) + DialogHelper.showDialog(tracingRequiredDialog) + } + + viewModel.uiStateError.observeEvent(viewLifecycleOwner) { DialogHelper.showDialog(buildErrorDialog(it)) } - submissionViewModel.deviceUiState.observe2(this) { uiState -> - if (uiState == DeviceUIState.PAIRED_REDEEMED) { - showRedeemedTokenWarningDialog() - } + 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) } viewModel.routeToScreen.observe2(this) { @@ -115,7 +122,9 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ is SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning -> doNavigate( SubmissionTestResultFragmentDirections - .actionSubmissionResultFragmentToSubmissionResultPositiveOtherWarningFragment() + .actionSubmissionResultFragmentToSubmissionResultPositiveOtherWarningFragment( + it.symptoms + ) ) is SubmissionNavigationEvents.NavigateToMainActivity -> doNavigate( @@ -125,17 +134,6 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ } } - private fun showRedeemedTokenWarningDialog() { - 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) - } - override fun onResume() { super.onResume() binding.submissionTestResultContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) @@ -160,12 +158,11 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ } binding.submissionTestResultButtonPositiveContinue.setOnClickListener { - continueIfTracingEnabled(false) + viewModel.onContinuePressed() } binding.submissionTestResultButtonPositiveContinueWithoutSymptoms.setOnClickListener { - submissionViewModel.onNoInformationSymptomIndication() - continueIfTracingEnabled(true) + viewModel.onContinueWithoutSymptoms() } binding.submissionTestResultButtonInvalidRemoveTest.setOnClickListener { @@ -177,31 +174,6 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ } } - private fun continueIfTracingEnabled(skipSymptomSubmission: Boolean) { - // TODO Workaround until we have a VM injected that can handle this - submissionViewModel.launch { - val isTracingEnabled = AppInjector.component.enfClient.isTracingEnabled.first() - withContext(Dispatchers.Main) { - if (!isTracingEnabled) { - val tracingRequiredDialog = DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_test_result_dialog_tracing_required_title, - R.string.submission_test_result_dialog_tracing_required_message, - R.string.submission_test_result_dialog_tracing_required_button - ) - DialogHelper.showDialog(tracingRequiredDialog) - return@withContext - } - - if (skipSymptomSubmission) { - viewModel.onContinueNoSymptomsPressed() - } else { - viewModel.onContinuePressed() - } - } - } - } - private fun removeTestAfterConfirmation() { val removeTestDialog = DialogHelper.DialogInstance( requireActivity(), @@ -210,8 +182,7 @@ class SubmissionTestResultFragment : Fragment(R.layout.fragment_submission_test_ R.string.submission_test_result_dialog_remove_test_button_positive, R.string.submission_test_result_dialog_remove_test_button_negative, positiveButtonFunction = { - submissionViewModel.deregisterTestFromDevice() - viewModel.onNavigateTestRemoved() + viewModel.deregisterTestFromDevice() } ) DialogHelper.showDialog(removeTestDialog).apply { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultModule.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultModule.kt index a10ad1086c01cfb63f2c70f7566f94db5e95f854..a81b3bf8e6f3c2eab36f14ed34f9807f902222a7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.testresult import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..96766c26904d12ab16c85b2809fe68477248cb0d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultViewModel.kt @@ -0,0 +1,101 @@ +package de.rki.coronawarnapp.ui.submission.testresult + +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import com.squareup.inject.assisted.AssistedInject +import de.rki.coronawarnapp.exception.http.CwaWebException +import de.rki.coronawarnapp.nearby.ENFClient +import de.rki.coronawarnapp.service.submission.SubmissionService +import de.rki.coronawarnapp.storage.LocalData +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.DeviceUIState +import de.rki.coronawarnapp.util.Event +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.combineTransform +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import timber.log.Timber + +class SubmissionTestResultViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider, + private val enfClient: ENFClient +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + val showTracingRequiredScreen = SingleLiveEvent<Unit>() + val showRedeemedTokenWarning = SingleLiveEvent<Unit>() + + private var wasRedeemedTokenErrorShown = false + private val tokenErrorMutex = Mutex() + + val uiState: LiveData<TestResultUIState> = combineTransform( + SubmissionRepository.uiStateStateFlow, + SubmissionRepository.deviceUIStateFlow, + SubmissionRepository.testResultReceivedDateFlow + ) { apiRequestState, deviceUiState, resultDate -> + + tokenErrorMutex.withLock { + if (!wasRedeemedTokenErrorShown && deviceUiState == DeviceUIState.PAIRED_REDEEMED) { + wasRedeemedTokenErrorShown = true + showRedeemedTokenWarning.postValue(Unit) + } + } + + TestResultUIState( + apiRequestState = apiRequestState, + deviceUiState = deviceUiState, + testResultReceivedDate = resultDate + ).let { emit(it) } + }.asLiveData(context = dispatcherProvider.Default) + + val uiStateError: LiveData<Event<CwaWebException>> = SubmissionRepository.uiStateError + + fun onBackPressed() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) + } + + fun onContinuePressed() { + Timber.d("onContinuePressed()") + requireTracingOrShowError { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomIntroduction) + } + } + + fun onContinueWithoutSymptoms() { + Timber.d("onContinueWithoutSymptoms()") + requireTracingOrShowError { + Symptoms.NO_INFO_GIVEN + .let { SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning(it) } + .let { routeToScreen.postValue(it) } + } + } + + private fun requireTracingOrShowError(action: () -> Unit) = launch { + if (enfClient.isTracingEnabled.first()) { + action() + } else { + showTracingRequiredScreen.postValue(Unit) + } + } + + fun deregisterTestFromDevice() { + launch { + Timber.d("deregisterTestFromDevice()") + SubmissionService.deleteTestGUID() + SubmissionService.deleteRegistrationToken() + LocalData.isAllowedToSubmitDiagnosisKeys(false) + LocalData.initialTestResultReceivedTimestamp(0L) + + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) + } + } + + @AssistedInject.Factory + interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/TestResultUIState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/TestResultUIState.kt new file mode 100644 index 0000000000000000000000000000000000000000..b1909eb805d6cbaa923b41336f14d60cb30aa45b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/TestResultUIState.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.ui.submission.testresult + +import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.util.DeviceUIState +import java.util.Date + +data class TestResultUIState( + val apiRequestState: ApiRequestState, + val deviceUiState: DeviceUIState, + val testResultReceivedDate: Date? +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt similarity index 61% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt index 0378f1666f6cad28665bc0026c3f48174860b0f1..07cdfe2f9d8fc00919ab4de58fb2b4d8c4af0592 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/SubmissionFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission +package de.rki.coronawarnapp.ui.submission.viewmodel import dagger.Module import dagger.android.ContributesAndroidInjector @@ -6,24 +6,20 @@ import de.rki.coronawarnapp.ui.submission.fragment.SubmissionContactFragment import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDispatcherFragment import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDoneFragment import de.rki.coronawarnapp.ui.submission.fragment.SubmissionIntroFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionQRCodeInfoFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionQRCodeInfoModule -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionQRCodeScanFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionResultPositiveOtherWarningFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionSymptomCalendarFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionSymptomIntroductionFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionTanFragment -import de.rki.coronawarnapp.ui.submission.fragment.SubmissionTestResultFragment -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionContactModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionDispatcherModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionDoneModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionIntroModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionQRCodeScanModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionResultPositiveOtherWarningModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionSymptomCalendarModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionSymptomIntroductionModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTanModule -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTestResultModule +import de.rki.coronawarnapp.ui.submission.qrcode.info.SubmissionQRCodeInfoFragment +import de.rki.coronawarnapp.ui.submission.qrcode.info.SubmissionQRCodeInfoModule +import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanFragment +import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanModule +import de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarFragment +import de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarModule +import de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionFragment +import de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionModule +import de.rki.coronawarnapp.ui.submission.tan.SubmissionTanFragment +import de.rki.coronawarnapp.ui.submission.tan.SubmissionTanModule +import de.rki.coronawarnapp.ui.submission.testresult.SubmissionTestResultFragment +import de.rki.coronawarnapp.ui.submission.testresult.SubmissionTestResultModule +import de.rki.coronawarnapp.ui.submission.warnothers.SubmissionResultPositiveOtherWarningFragment +import de.rki.coronawarnapp.ui.submission.warnothers.SubmissionResultPositiveOtherWarningModule @Module internal abstract class SubmissionFragmentModule { 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 347cb1c18a335a8a9d72926b04e4721c36bf23be..5a0fe235fefec008d7d167cfe47144d5fd755f95 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 @@ -1,14 +1,23 @@ package de.rki.coronawarnapp.ui.submission.viewmodel +import de.rki.coronawarnapp.submission.Symptoms + sealed class SubmissionNavigationEvents { object NavigateToContact : SubmissionNavigationEvents() object NavigateToDispatcher : SubmissionNavigationEvents() object NavigateToSubmissionDone : SubmissionNavigationEvents() object NavigateToSubmissionIntro : SubmissionNavigationEvents() object NavigateToQRCodeScan : SubmissionNavigationEvents() - object NavigateToResultPositiveOtherWarning : SubmissionNavigationEvents() + + data class NavigateToResultPositiveOtherWarning( + val symptoms: Symptoms + ) : SubmissionNavigationEvents() + object NavigateToSymptomSubmission : SubmissionNavigationEvents() - object NavigateToSymptomCalendar : SubmissionNavigationEvents() + data class NavigateToSymptomCalendar( + val symptomIndication: Symptoms.Indication + ) : SubmissionNavigationEvents() + object NavigateToSymptomIntroduction : SubmissionNavigationEvents() object NavigateToTAN : SubmissionNavigationEvents() object NavigateToTestResult : SubmissionNavigationEvents() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanViewModel.kt deleted file mode 100644 index ae575ce54fc6d2cc6cd8ab3c3d2a7018f955b345..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeScanViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class SubmissionQRCodeScanViewModel @AssistedInject constructor() : CWAViewModel() { - - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() - - fun onBackPressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToQRInfo) - } - - fun onClosePressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToDispatcher) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionQRCodeScanViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningViewModel.kt deleted file mode 100644 index a2b242ceaca511028fdbfadfdff686813b416f4c..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningViewModel.kt +++ /dev/null @@ -1,26 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class SubmissionResultPositiveOtherWarningViewModel @AssistedInject constructor() : CWAViewModel() { - - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() - - fun onBackPressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToTestResult) - } - - fun onWarnOthersPressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomIntroduction) - } - - fun onSubmissionComplete() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSubmissionDone) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionResultPositiveOtherWarningViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarViewModel.kt deleted file mode 100644 index 38bd530b33e363ebaebad3d0c1e11eb3ee2af652..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomCalendarViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class SubmissionSymptomCalendarViewModel @AssistedInject constructor() : CWAViewModel() { - - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() - - fun onCalendarNextClicked() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning) - } - - fun onCalendarPreviousClicked() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomIntroduction) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionSymptomCalendarViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionViewModel.kt deleted file mode 100644 index a79f4a1b3204e61c841e0b1419f9a59d03acefc3..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionSymptomIntroductionViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class SubmissionSymptomIntroductionViewModel @AssistedInject constructor() : CWAViewModel() { - - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() - - fun onNextClicked() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomCalendar) - } - - fun onPreviousClicked() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToTestResult) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionSymptomIntroductionViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanViewModel.kt deleted file mode 100644 index 2a33f7239397b9bb01df36b8608c8b23516c07bc..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTanViewModel.kt +++ /dev/null @@ -1,37 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.storage.SubmissionRepository -import de.rki.coronawarnapp.ui.submission.TanConstants -import de.rki.coronawarnapp.util.TanHelper -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import timber.log.Timber - -class SubmissionTanViewModel @AssistedInject constructor() : CWAViewModel() { - - companion object { - private val TAG: String? = SubmissionTanViewModel::class.simpleName - } - - val tan = MutableLiveData<String?>(null) - - val isValidTanFormat = - Transformations.map(tan) { - it != null && - it.length == TanConstants.MAX_LENGTH && - TanHelper.isChecksumValid(it) && - TanHelper.allCharactersValid(it) - } - - fun storeTeletan() { - val teletan = tan.value!! - Timber.d("Storing teletan $teletan") - SubmissionRepository.setTeletan(teletan) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionTanViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultViewModel.kt deleted file mode 100644 index 5c96c86ecfc27250f9499765a8e9a58a96536cf8..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionTestResultViewModel.kt +++ /dev/null @@ -1,30 +0,0 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel - -import com.squareup.inject.assisted.AssistedInject -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class SubmissionTestResultViewModel @AssistedInject constructor() : CWAViewModel() { - - val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() - - fun onBackPressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) - } - - fun onNavigateTestRemoved() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity) - } - - fun onContinuePressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSymptomIntroduction) - } - - fun onContinueNoSymptomsPressed() { - routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPositiveOtherWarning) - } - - @AssistedInject.Factory - interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionResultPositiveOtherWarningFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningFragment.kt similarity index 62% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionResultPositiveOtherWarningFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningFragment.kt index 1d96f4804519e3e36d919cecfd46aa072190729f..65ad86643463f269fa71867d72bc81fc98e638c8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/fragment/SubmissionResultPositiveOtherWarningFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningFragment.kt @@ -1,12 +1,12 @@ -package de.rki.coronawarnapp.ui.submission.fragment +package de.rki.coronawarnapp.ui.submission.warnothers import android.content.Intent import android.os.Bundle import android.view.View import android.view.accessibility.AccessibilityEvent import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentSubmissionPositiveOtherWarningBinding @@ -16,41 +16,111 @@ import de.rki.coronawarnapp.exception.http.CwaServerError import de.rki.coronawarnapp.exception.http.CwaWebException import de.rki.coronawarnapp.exception.http.ForbiddenException import de.rki.coronawarnapp.nearby.InternalExposureNotificationPermissionHelper -import de.rki.coronawarnapp.ui.submission.ApiRequestState import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionResultPositiveOtherWarningViewModel -import de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel import de.rki.coronawarnapp.util.DialogHelper -import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.observeEvent import de.rki.coronawarnapp.util.ui.doNavigate 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.cwaViewModels -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withContext +import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted import javax.inject.Inject class SubmissionResultPositiveOtherWarningFragment : Fragment(R.layout.fragment_submission_positive_other_warning), InternalExposureNotificationPermissionHelper.Callback, AutoInject { + private val navArgs by navArgs<SubmissionResultPositiveOtherWarningFragmentArgs>() + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: SubmissionResultPositiveOtherWarningViewModel by cwaViewModels { viewModelFactory } - private val submissionViewModel: SubmissionViewModel by activityViewModels() + private val viewModel: SubmissionResultPositiveOtherWarningViewModel by cwaViewModelsAssisted( + factoryProducer = { viewModelFactory }, + constructorCall = { factory, _ -> + factory as SubmissionResultPositiveOtherWarningViewModel.Factory + factory.create(navArgs.symptoms) + } + ) private val binding: FragmentSubmissionPositiveOtherWarningBinding by viewBindingLazy() - private lateinit var internalExposureNotificationPermissionHelper: - InternalExposureNotificationPermissionHelper + private lateinit var internalExposureNotificationPermissionHelper: InternalExposureNotificationPermissionHelper + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.uiState.observe2(this) { + binding.uiState = it + } + + internalExposureNotificationPermissionHelper = + InternalExposureNotificationPermissionHelper(this, this) + + binding.submissionPositiveOtherWarningButtonNext.setOnClickListener { + viewModel.onWarnOthersPressed() + } + binding.submissionPositiveOtherWarningHeader.headerButtonBack.buttonIcon.setOnClickListener { + viewModel.onBackPressed() + } + + viewModel.routeToScreen.observe2(this) { + when (it) { + is SubmissionNavigationEvents.NavigateToSubmissionIntro -> doNavigate( + SubmissionResultPositiveOtherWarningFragmentDirections + .actionSubmissionResultPositiveOtherWarningFragmentToSubmissionDoneFragment() + ) + is SubmissionNavigationEvents.NavigateToSubmissionDone -> doNavigate( + SubmissionResultPositiveOtherWarningFragmentDirections + .actionSubmissionResultPositiveOtherWarningFragmentToSubmissionDoneFragment() + ) + is SubmissionNavigationEvents.NavigateToTestResult -> findNavController().popBackStack() + } + } + + viewModel.submissionError.observe2(this) { + DialogHelper.showDialog(buildErrorDialog(it)) + } + + viewModel.requestKeySharing.observe2(this) { + internalExposureNotificationPermissionHelper.requestPermissionToShareKeys() + } + + viewModel.showEnableTracingEvent.observe2(this) { + val tracingRequiredDialog = DialogHelper.DialogInstance( + requireActivity(), + R.string.submission_test_result_dialog_tracing_required_title, + R.string.submission_test_result_dialog_tracing_required_message, + R.string.submission_test_result_dialog_tracing_required_button + ) + DialogHelper.showDialog(tracingRequiredDialog) + } + } override fun onResume() { super.onResume() binding.submissionPositiveOtherPrivacyContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) } + private fun navigateToSubmissionResultFragment() = doNavigate( + SubmissionResultPositiveOtherWarningFragmentDirections + .actionSubmissionResultPositiveOtherWarningFragmentToSubmissionResultFragment() + ) + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + this.internalExposureNotificationPermissionHelper.onResolutionComplete( + requestCode, + resultCode + ) + } + + // InternalExposureNotificationPermissionHelper - callbacks + override fun onKeySharePermissionGranted(keys: List<TemporaryExposureKey>) { + super.onKeySharePermissionGranted(keys) + viewModel.onKeysShared(keys) + } + + override fun onFailure(exception: Exception?) { + // NOOP + } + private fun buildErrorDialog(exception: CwaWebException): DialogHelper.DialogInstance { return when (exception) { is BadRequestException -> DialogHelper.DialogInstance( @@ -94,104 +164,4 @@ class SubmissionResultPositiveOtherWarningFragment : ) } } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding.submissionViewModel = submissionViewModel - - internalExposureNotificationPermissionHelper = - InternalExposureNotificationPermissionHelper(this, this) - - setButtonOnClickListener() - - submissionViewModel.submissionError.observeEvent(viewLifecycleOwner) { - DialogHelper.showDialog(buildErrorDialog(it)) - } - - submissionViewModel.submissionState.observe2(this) { - if (it == ApiRequestState.SUCCESS) { - viewModel.onSubmissionComplete() - } - } - } - - private fun setButtonOnClickListener() { - binding.submissionPositiveOtherWarningButtonNext.setOnClickListener { - initiateWarningOthers() - viewModel.onWarnOthersPressed() - } - binding.submissionPositiveOtherWarningHeader.headerButtonBack.buttonIcon.setOnClickListener { - findNavController().popBackStack() - viewModel.onBackPressed() - } - - viewModel.routeToScreen.observe2(this) { - when (it) { - is SubmissionNavigationEvents.NavigateToSubmissionIntro -> - initiateWarningOthers() - is SubmissionNavigationEvents.NavigateToSubmissionDone -> - navigateToSubmissionDoneFragment() - is SubmissionNavigationEvents.NavigateToTestResult -> - findNavController().popBackStack() - } - } - } - - private fun navigateToSubmissionResultFragment() = - doNavigate( - SubmissionResultPositiveOtherWarningFragmentDirections - .actionSubmissionResultPositiveOtherWarningFragmentToSubmissionResultFragment() - ) - - /** - * Navigate to submission done Fragment - * @see SubmissionDoneFragment - */ - private fun navigateToSubmissionDoneFragment() = - doNavigate( - SubmissionResultPositiveOtherWarningFragmentDirections - .actionSubmissionResultPositiveOtherWarningFragmentToSubmissionDoneFragment() - ) - - private fun initiateWarningOthers() { - // TODO remove after VM Injection, workaround, should not happen in the fragment - submissionViewModel.launch { - val isTracingEnabled = AppInjector.component.enfClient.isTracingEnabled.first() - withContext(Dispatchers.Main) { - if (!isTracingEnabled) { - val tracingRequiredDialog = DialogHelper.DialogInstance( - requireActivity(), - R.string.submission_test_result_dialog_tracing_required_title, - R.string.submission_test_result_dialog_tracing_required_message, - R.string.submission_test_result_dialog_tracing_required_button - ) - DialogHelper.showDialog(tracingRequiredDialog) - } else { - internalExposureNotificationPermissionHelper.requestPermissionToShareKeys() - } - } - } - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - this.internalExposureNotificationPermissionHelper.onResolutionComplete( - requestCode, - resultCode - ) - } - - // InternalExposureNotificationPermissionHelper - callbacks - override fun onKeySharePermissionGranted(keys: List<TemporaryExposureKey>) { - super.onKeySharePermissionGranted(keys) - if (keys.isNotEmpty()) { - submissionViewModel.submitDiagnosisKeys(keys) - } else { - submissionViewModel.submitWithNoDiagnosisKeys() - viewModel.onSubmissionComplete() - } - } - - override fun onFailure(exception: Exception?) { - // NOOP - } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningModule.kt similarity index 91% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningModule.kt index 41a0322ceeb2edc24cbd7d66d4eec75fec795688..3ff8fc4cf5f20ecee8060a6f925b5f16cb43bf13 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionResultPositiveOtherWarningModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.warnothers import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..e4e381a7e08386c46f6d43c9faaf1fb5ed4a850e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/SubmissionResultPositiveOtherWarningViewModel.kt @@ -0,0 +1,110 @@ +package de.rki.coronawarnapp.ui.submission.warnothers + +import androidx.lifecycle.asLiveData +import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject +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.nearby.ENFClient +import de.rki.coronawarnapp.service.submission.SubmissionService +import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository +import de.rki.coronawarnapp.submission.Symptoms +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.CWAViewModelFactory +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combineTransform +import kotlinx.coroutines.flow.first +import timber.log.Timber + +class SubmissionResultPositiveOtherWarningViewModel @AssistedInject constructor( + @Assisted private val symptoms: Symptoms, + dispatcherProvider: DispatcherProvider, + private val enfClient: ENFClient, + interoperabilityRepository: InteroperabilityRepository +) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + + private val submissionState = MutableStateFlow(ApiRequestState.IDLE) + val submissionError = SingleLiveEvent<CwaWebException>() + + val uiState = combineTransform( + submissionState, + interoperabilityRepository.countryListFlow + ) { state, countries -> + WarnOthersState( + apiRequestState = state, + countryList = countries + ).also { emit(it) } + }.asLiveData(context = dispatcherProvider.Default) + + val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent() + + val requestKeySharing = SingleLiveEvent<Unit>() + val showEnableTracingEvent = SingleLiveEvent<Unit>() + + fun onBackPressed() { + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToTestResult) + } + + fun onWarnOthersPressed() { + launch { + if (enfClient.isTracingEnabled.first()) { + requestKeySharing.postValue(Unit) + } else { + showEnableTracingEvent.postValue(Unit) + } + } + } + + fun onKeysShared(keys: List<TemporaryExposureKey>) { + if (keys.isNotEmpty()) { + submitDiagnosisKeys(keys) + } else { + submitWithNoDiagnosisKeys() + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSubmissionDone) + } + } + + private fun submitDiagnosisKeys(keys: List<TemporaryExposureKey>) { + Timber.d("submitDiagnosisKeys(keys=%s, symptoms=%s)", keys, symptoms) + + submissionState.value = ApiRequestState.STARTED + launch { + try { + SubmissionService.asyncSubmitExposureKeys(keys, symptoms) + routeToScreen.postValue(SubmissionNavigationEvents.NavigateToSubmissionDone) + + submissionState.value = ApiRequestState.SUCCESS + } catch (err: CwaWebException) { + submissionError.postValue(err) + submissionState.value = ApiRequestState.FAILED + } catch (err: TransactionException) { + if (err.cause is CwaWebException) { + submissionError.postValue(err.cause) + } else { + err.report(ExceptionCategory.INTERNAL) + } + submissionState.value = ApiRequestState.FAILED + } catch (err: Exception) { + submissionState.value = ApiRequestState.FAILED + err.report(ExceptionCategory.INTERNAL) + } + } + } + + private fun submitWithNoDiagnosisKeys() { + Timber.d("submitWithNoDiagnosisKeys()") + SubmissionService.submissionSuccessful() + } + + @AssistedInject.Factory + interface Factory : CWAViewModelFactory<SubmissionResultPositiveOtherWarningViewModel> { + fun create(symptoms: Symptoms): SubmissionResultPositiveOtherWarningViewModel + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/WarnOthersState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/WarnOthersState.kt new file mode 100644 index 0000000000000000000000000000000000000000..33809f6b7780c42c014d3c0bfb0698deee4aab2b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/warnothers/WarnOthersState.kt @@ -0,0 +1,17 @@ +package de.rki.coronawarnapp.ui.submission.warnothers + +import de.rki.coronawarnapp.ui.Country +import de.rki.coronawarnapp.ui.submission.ApiRequestState + +data class WarnOthersState( + val apiRequestState: ApiRequestState, + val countryList: List<Country> +) { + + fun isSubmitButtonEnabled(): Boolean = + apiRequestState == ApiRequestState.IDLE || apiRequestState == ApiRequestState.FAILED + + fun isSubmitSpinnerVisible(): Boolean { + return apiRequestState == ApiRequestState.STARTED + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModel.kt index c1079bbdd87eef234f6a31dc0a3cac320cd83265..6b668e6c420a52ee06796f5a4365794fd96452b6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModel.kt @@ -1,175 +1,12 @@ package de.rki.coronawarnapp.ui.viewmodel import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.viewModelScope -import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey -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.service.submission.QRScanResult -import de.rki.coronawarnapp.service.submission.SubmissionService -import de.rki.coronawarnapp.storage.LocalData +import androidx.lifecycle.asLiveData import de.rki.coronawarnapp.storage.SubmissionRepository -import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository -import de.rki.coronawarnapp.submission.Symptoms -import de.rki.coronawarnapp.ui.submission.ApiRequestState -import de.rki.coronawarnapp.ui.submission.ScanStatus import de.rki.coronawarnapp.util.DeviceUIState -import de.rki.coronawarnapp.util.Event -import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import kotlinx.coroutines.launch -import org.joda.time.LocalDate -import timber.log.Timber -import java.util.Date class SubmissionViewModel : CWAViewModel() { - private val _scanStatus = MutableLiveData(Event(ScanStatus.STARTED)) - private val _registrationState = MutableLiveData(Event(ApiRequestState.IDLE)) - private val _registrationError = MutableLiveData<Event<CwaWebException>>(null) - - private val _submissionState = MutableLiveData(ApiRequestState.IDLE) - private val _submissionError = MutableLiveData<Event<CwaWebException>>(null) - private val interoperabilityRepository: InteroperabilityRepository - get() = AppInjector.component.interoperabilityRepository - - val scanStatus: LiveData<Event<ScanStatus>> = _scanStatus - - val registrationState: LiveData<Event<ApiRequestState>> = _registrationState - val registrationError: LiveData<Event<CwaWebException>> = _registrationError - - val uiStateState: LiveData<ApiRequestState> = SubmissionRepository.uiStateState - val uiStateError: LiveData<Event<CwaWebException>> = SubmissionRepository.uiStateError - - val submissionState: LiveData<ApiRequestState> = _submissionState - val submissionError: LiveData<Event<CwaWebException>> = _submissionError - - val testResultReceivedDate: LiveData<Date> = SubmissionRepository.testResultReceivedDate - val deviceUiState: LiveData<DeviceUIState> = SubmissionRepository.deviceUIState - - val symptomIndication = MutableLiveData<Symptoms.Indication?>() - val symptomStart = MutableLiveData<Symptoms.StartOf?>() - - val countryList by lazy { - MutableLiveData(interoperabilityRepository.countryList) - } - - fun initSymptoms() { - symptomIndication.postValue(null) - } - - fun initSymptomStart() { - symptomStart.postValue(null) - } - - fun submitDiagnosisKeys(keys: List<TemporaryExposureKey>) { - val indication = symptomIndication.value - if (indication == null) { - Timber.w("symptoms indicator is null") - return - } - Symptoms(symptomStart.value, indication).also { - viewModelScope.launch { - try { - _submissionState.value = ApiRequestState.STARTED - SubmissionService.asyncSubmitExposureKeys(keys, it) - _submissionState.value = ApiRequestState.SUCCESS - } catch (err: CwaWebException) { - _submissionError.value = Event(err) - _submissionState.value = ApiRequestState.FAILED - } catch (err: TransactionException) { - if (err.cause is CwaWebException) { - _submissionError.value = Event(err.cause) - } else { - err.report(ExceptionCategory.INTERNAL) - } - _submissionState.value = ApiRequestState.FAILED - } catch (err: Exception) { - _submissionState.value = ApiRequestState.FAILED - err.report(ExceptionCategory.INTERNAL) - } - } - } - } - - fun doDeviceRegistration() = viewModelScope.launch { - try { - _registrationState.value = Event(ApiRequestState.STARTED) - SubmissionService.asyncRegisterDevice() - _registrationState.value = Event(ApiRequestState.SUCCESS) - } catch (err: CwaWebException) { - _registrationError.value = Event(err) - _registrationState.value = Event(ApiRequestState.FAILED) - } catch (err: TransactionException) { - if (err.cause is CwaWebException) { - _registrationError.value = Event(err.cause) - } else { - err.report(ExceptionCategory.INTERNAL) - } - _registrationState.value = Event(ApiRequestState.FAILED) - } catch (err: Exception) { - _registrationState.value = Event(ApiRequestState.FAILED) - err.report(ExceptionCategory.INTERNAL) - } - } - - fun validateAndStoreTestGUID(rawResult: String) { - val scanResult = QRScanResult(rawResult) - if (scanResult.isValid) { - SubmissionService.storeTestGUID(scanResult.guid!!) - _scanStatus.value = Event(ScanStatus.SUCCESS) - } else { - _scanStatus.value = Event(ScanStatus.INVALID) - } - } - - fun deleteTestGUID() { - SubmissionService.deleteTestGUID() - } - - fun submitWithNoDiagnosisKeys() { - SubmissionService.submissionSuccessful() - } - - fun deregisterTestFromDevice() { - deleteTestGUID() - SubmissionService.deleteRegistrationToken() - LocalData.isAllowedToSubmitDiagnosisKeys(false) - LocalData.initialTestResultReceivedTimestamp(0L) - } - - fun onPositiveSymptomIndication() { - symptomIndication.postValue(Symptoms.Indication.POSITIVE) - } - - fun onNegativeSymptomIndication() { - symptomIndication.postValue(Symptoms.Indication.NEGATIVE) - } - - fun onNoInformationSymptomIndication() { - symptomIndication.postValue(Symptoms.Indication.NO_INFORMATION) - } - - fun onLastSevenDaysStart() { - symptomStart.postValue(Symptoms.StartOf.LastSevenDays) - } - - fun onOneToTwoWeeksAgoStart() { - symptomStart.postValue(Symptoms.StartOf.OneToTwoWeeksAgo) - } - - fun onMoreThanTwoWeeksStart() { - symptomStart.postValue(Symptoms.StartOf.MoreThanTwoWeeks) - } - - fun onNoInformationStart() { - symptomStart.postValue(Symptoms.StartOf.NoInformation) - } - - fun onDateSelected(localDate: LocalDate?) { - symptomStart.postValue(if (localDate == null) null else Symptoms.StartOf.Date(localDate)) - } + val deviceUiState: LiveData<DeviceUIState> = SubmissionRepository.deviceUIStateFlow.asLiveData() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt deleted file mode 100644 index 5327b0f0073f1ced096a9a3ac329b6f750ec2981..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TanHelper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package de.rki.coronawarnapp.util - -import de.rki.coronawarnapp.ui.submission.TanConstants.MAX_LENGTH -import java.nio.charset.StandardCharsets -import java.security.MessageDigest -import java.util.Locale - -object TanHelper { - private const val VALID_CHARACTERS = "23456789ABCDEFGHJKMNPQRSTUVWXYZ" - - fun isChecksumValid(tan: String): Boolean { - if (tan.trim().length != MAX_LENGTH) - return false - val subTan = tan.substring(0, MAX_LENGTH - 1).toUpperCase(Locale.ROOT) - val tanDigest = MessageDigest.getInstance("SHA-256") - .digest(subTan.toByteArray(StandardCharsets.US_ASCII)) - var checkChar = "%02x".format(tanDigest[0])[0] - if (checkChar == '0') checkChar = 'G' - if (checkChar == '1') checkChar = 'H' - - return checkChar.toUpperCase() == tan.last().toUpperCase() - } - - fun allCharactersValid(tan: String): Boolean { - for (character in tan) { - if (!isTanCharacterValid(character.toString())) - return false - } - return true - } - - fun isTanCharacterValid(character: String): Boolean { - return VALID_CHARACTERS.contains(character) - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt index df3c1283f4104a8323e413dd185f55b76abe23fb..156f8efee0289c727be6c85d0aa6e4a96b116674 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelper.kt @@ -55,12 +55,6 @@ fun formatTestResultSpinnerVisible(uiStateState: ApiRequestState?): Int = fun formatTestResultVisible(uiStateState: ApiRequestState?): Int = formatVisibility(uiStateState == ApiRequestState.SUCCESS) -fun formatSubmitButtonEnabled(apiRequestState: ApiRequestState) = - apiRequestState == ApiRequestState.IDLE || apiRequestState == ApiRequestState.FAILED - -fun formatSubmitSpinnerVisible(apiRequestState: ApiRequestState) = - formatVisibility(apiRequestState == ApiRequestState.STARTED) - fun formatTestResultStatusText(uiState: DeviceUIState?): String { val appContext = CoronaWarnApplication.getAppContext() return when (uiState) { diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_positive_other_warning.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_positive_other_warning.xml index 555709a0ab052805a88e12fc87c3421347548227..fcf4c4a8300f153e0b8e3adc5bf5ee8544990ede 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_positive_other_warning.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_positive_other_warning.xml @@ -4,13 +4,9 @@ xmlns:tools="http://schemas.android.com/tools"> <data> - - <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> - <variable - name="submissionViewModel" - type="de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel" /> - + name="uiState" + type="de.rki.coronawarnapp.ui.submission.warnothers.WarnOthersState" /> </data> @@ -20,7 +16,7 @@ android:layout_height="match_parent" android:contentDescription="@string/submission_positive_other_warning_title" android:fillViewport="true" - tools:context=".ui.submission.fragment.SubmissionResultPositiveOtherWarningFragment"> + tools:context=".ui.submission.warnothers.SubmissionResultPositiveOtherWarningFragment"> <include android:id="@+id/submission_positive_other_warning_header" @@ -37,7 +33,7 @@ layout="@layout/include_submission_positive_other_warning" android:layout_width="@dimen/match_constraint" android:layout_height="@dimen/match_constraint" - app:countryData="@{submissionViewModel.countryList}" + app:countryData="@{uiState.countryList}" app:layout_constraintBottom_toTopOf="@+id/guideline_action" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" @@ -50,7 +46,7 @@ style="@style/buttonPrimary" android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" - android:enabled="@{FormatterSubmissionHelper.formatSubmitButtonEnabled(submissionViewModel.submissionState)}" + android:enabled="@{uiState != null && uiState.isSubmitButtonEnabled()}" android:text="@string/submission_positive_other_warning_button" android:textAllCaps="true" app:layout_constraintBottom_toBottomOf="parent" @@ -65,7 +61,7 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_large" android:indeterminate="true" - android:visibility="@{FormatterSubmissionHelper.formatSubmitSpinnerVisible(submissionViewModel.submissionState)}" + gone="@{uiState == null || !uiState.isSubmitSpinnerVisible()}" app:layout_constraintBottom_toBottomOf="@+id/submission_positive_other_warning_button_next" app:layout_constraintEnd_toEndOf="@+id/submission_positive_other_warning_button_next" app:layout_constraintStart_toStartOf="@+id/submission_positive_other_warning_button_next" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml index eb19ee84f777439eb95616fd29cd758dfc369bc5..a34166dd59df62849ed4df4572518c4ef5ea3e24 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_symptom_calendar.xml @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:fillViewport="true" android:focusable="true" - tools:context=".ui.submission.fragment.SubmissionSymptomCalendarFragment"> + tools:context=".ui.submission.symptoms.calendar.SubmissionSymptomCalendarFragment"> <include android:id="@+id/submission_symptom_calendar_header" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml index 3ef4b9f79ef0c89881003cd9e7f1ec963a4416c4..e499b7cef4937c65ecc526611b20d3eddeef6d98 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_tan.xml @@ -3,11 +3,11 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> - <data> + <data> <variable - name="viewmodel" - type="de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTanViewModel" /> + name="uiState" + type="de.rki.coronawarnapp.ui.submission.tan.SubmissionTanViewModel.UIState" /> </data> <androidx.constraintlayout.widget.ConstraintLayout @@ -16,7 +16,7 @@ android:layout_height="match_parent" android:contentDescription="@string/submission_tan_accessibility_title" android:fillViewport="true" - tools:context=".ui.submission.fragment.SubmissionTanFragment"> + tools:context=".ui.submission.tan.SubmissionTanFragment"> <include android:id="@+id/submission_tan_header" @@ -38,8 +38,7 @@ app:layout_constraintBottom_toTopOf="@id/guideline_action" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/submission_tan_header" - app:viewmodel="@{viewmodel}" /> + app:layout_constraintTop_toBottomOf="@+id/submission_tan_header" /> <ProgressBar android:id="@+id/submission_tan_spinner" @@ -57,7 +56,7 @@ style="@style/buttonPrimary" android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" - android:enabled="@{viewmodel.isValidTanFormat}" + android:enabled="@{uiState.tanValid}" android:text="@string/submission_tan_button_text" android:textAllCaps="true" app:layout_constraintBottom_toBottomOf="parent" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result.xml index 2f7825389e55c461017cc670b808d92fd318c09f..3521faa0f64126875ffb9317c6782159fa998fff 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result.xml @@ -7,8 +7,8 @@ <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> <variable - name="submissionViewModel" - type="de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel" /> + name="uiState" + type="de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState" /> </data> <androidx.constraintlayout.widget.ConstraintLayout @@ -34,7 +34,7 @@ style="?android:attr/progressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:visibility="@{FormatterSubmissionHelper.formatTestResultSpinnerVisible(submissionViewModel.uiStateState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultSpinnerVisible(uiState.apiRequestState)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -48,18 +48,17 @@ android:layout_width="@dimen/match_constraint" android:layout_height="@dimen/match_constraint" android:layout_marginBottom="@dimen/button_padding_top_bottom" - android:visibility="@{FormatterSubmissionHelper.formatTestResultVisible(submissionViewModel.uiStateState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultVisible(uiState.apiRequestState)}" app:layout_constraintBottom_toTopOf="@+id/include_submission_test_result_buttons" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_header" - app:submissionViewModel="@{submissionViewModel}" /> + app:uiState="@{uiState}" /> <androidx.constraintlayout.widget.Barrier android:id="@+id/include_submission_test_result_buttons" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:barrierAllowsGoneWidgets="false" app:barrierDirection="top" app:constraint_referenced_ids="submission_test_result_button_pending_refresh,submission_test_result_button_invalid_remove_test,submission_test_result_button_positive_continue,submission_test_result_button_negative_remove_test" /> @@ -70,7 +69,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_pending_refresh_button" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toTopOf="@+id/submission_test_result_button_pending_remove_test" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" @@ -82,7 +81,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_pending_remove_test_button" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" @@ -94,7 +93,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_invalid_remove_test_button" - android:visibility="@{FormatterSubmissionHelper.formatTestResultInvalidStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultInvalidStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" @@ -106,7 +105,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_positive_continue_button_with_symptoms" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toTopOf="@+id/submission_test_result_button_positive_continue_without_symptoms" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" @@ -118,7 +117,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_positive_continue_button_wo_symptoms" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" @@ -130,7 +129,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:text="@string/submission_test_result_negative_remove_test_button" - android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(uiState.deviceUiState)}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@id/guideline_start" diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_tan.xml b/Corona-Warn-App/src/main/res/layout/include_submission_tan.xml index 795e2414432a623b48ec39c9887c2a07db4e690d..5d31152d6d36f837475f713187a17be0acc85746 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_tan.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_tan.xml @@ -2,18 +2,6 @@ <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> - - <data> - - <import type="de.rki.coronawarnapp.util.formatter.FormatterHelper" /> - - <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> - - <variable - name="viewmodel" - type="de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTanViewModel" /> - </data> - <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> @@ -35,7 +23,7 @@ app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toTopOf="parent" /> - <de.rki.coronawarnapp.ui.view.TanInput + <de.rki.coronawarnapp.ui.submission.tan.TanInput android:id="@+id/submission_tan_input" android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" diff --git a/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml b/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml index b6b15d563c317d0d90058b10c1f10e64fd260c5b..a564e5c9632c893c096db4b4829bc72603ab5550 100644 --- a/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml +++ b/Corona-Warn-App/src/main/res/layout/include_submission_test_result.xml @@ -7,8 +7,8 @@ <import type="de.rki.coronawarnapp.util.formatter.FormatterSubmissionHelper" /> <variable - name="submissionViewModel" - type="de.rki.coronawarnapp.ui.viewmodel.SubmissionViewModel" /> + name="uiState" + type="de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState" /> </data> @@ -28,11 +28,11 @@ android:layout_marginTop="@dimen/spacing_small" android:focusable="true" android:importantForAccessibility="yes" - app:deviceUIState="@{submissionViewModel.deviceUiState}" + app:deviceUIState="@{uiState.deviceUiState}" app:layout_constraintEnd_toEndOf="@+id/guideline_card_end" app:layout_constraintStart_toStartOf="@+id/guideline_card_start" app:layout_constraintTop_toTopOf="parent" - app:registerDate="@{submissionViewModel.testResultReceivedDate}" /> + app:registerDate="@{uiState.testResultReceivedDate}" /> <TextView android:id="@+id/submission_test_result_subtitle" @@ -52,7 +52,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPendingStepsVisible(uiState.deviceUiState)}" app:layout_constraintEnd_toEndOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_subtitle" /> @@ -63,7 +63,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(uiState.deviceUiState)}" app:layout_constraintEnd_toEndOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_subtitle" /> @@ -74,7 +74,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultNegativeStepsVisible(uiState.deviceUiState)}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_negative_steps" /> @@ -85,7 +85,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultPositiveStepsVisible(uiState.deviceUiState)}" app:layout_constraintEnd_toEndOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_subtitle" /> @@ -96,7 +96,7 @@ android:layout_width="@dimen/match_constraint" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacing_normal" - android:visibility="@{FormatterSubmissionHelper.formatTestResultInvalidStepsVisible(submissionViewModel.deviceUiState)}" + android:visibility="@{FormatterSubmissionHelper.formatTestResultInvalidStepsVisible(uiState.deviceUiState)}" app:layout_constraintEnd_toEndOf="@+id/guideline_end" app:layout_constraintStart_toStartOf="@+id/guideline_start" app:layout_constraintTop_toBottomOf="@+id/submission_test_result_subtitle" /> 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 6d734cbc2904ca373f763df1ffa89578a6648666..44aea7e8c24b13c4aedcd91e535e6309c8f828b1 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -224,7 +224,7 @@ </fragment> <fragment android:id="@+id/submissionResultPositiveOtherWarningFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionResultPositiveOtherWarningFragment" + android:name="de.rki.coronawarnapp.ui.submission.warnothers.SubmissionResultPositiveOtherWarningFragment" android:label="fragment_submission_result_positive_other_warning" tools:layout="@layout/fragment_submission_positive_other_warning"> <action @@ -243,10 +243,13 @@ <action android:id="@+id/action_submissionResultPositiveOtherWarningFragment_to_submissionSymptomIntroductionFragment" app:destination="@id/submissionSymptomIntroductionFragment" /> + <argument + android:name="symptoms" + app:argType="de.rki.coronawarnapp.submission.Symptoms" /> </fragment> <fragment android:id="@+id/submissionResultFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionTestResultFragment" + android:name="de.rki.coronawarnapp.ui.submission.testresult.SubmissionTestResultFragment" android:label="fragment_submission_result" tools:layout="@layout/fragment_submission_test_result"> <argument @@ -268,7 +271,7 @@ <fragment android:id="@+id/submissionTanFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionTanFragment" + android:name="de.rki.coronawarnapp.ui.submission.tan.SubmissionTanFragment" android:label="fragment_submission_tan" tools:layout="@layout/fragment_submission_tan"> <action @@ -311,7 +314,7 @@ </activity> <fragment android:id="@+id/submissionQRCodeInfoFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionQRCodeInfoFragment" + android:name="de.rki.coronawarnapp.ui.submission.qrcode.info.SubmissionQRCodeInfoFragment" android:label="SubmissionQRCodeInfoFragment"> <action android:id="@+id/action_submissionQRCodeInfoFragment_to_submissionQRCodeScanFragment" @@ -320,7 +323,7 @@ <fragment android:id="@+id/submissionQRCodeScanFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionQRCodeScanFragment" + android:name="de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanFragment" android:label="SubmissionQRCodeScanFragment"> <action android:id="@+id/action_submissionQRCodeScanFragment_to_submissionDispatcherFragment" @@ -357,7 +360,7 @@ </fragment> <fragment android:id="@+id/submissionSymptomIntroductionFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionSymptomIntroductionFragment" + android:name="de.rki.coronawarnapp.ui.submission.symptoms.introduction.SubmissionSymptomIntroductionFragment" android:label="SubmissionSymptomIntroductionFragment" > <action @@ -372,14 +375,17 @@ </fragment> <fragment android:id="@+id/submissionSymptomCalendarFragment" - android:name="de.rki.coronawarnapp.ui.submission.fragment.SubmissionSymptomCalendarFragment" - android:label="SubmissionSymptomCalendarFragment" > + android:name="de.rki.coronawarnapp.ui.submission.symptoms.calendar.SubmissionSymptomCalendarFragment" + android:label="SubmissionSymptomCalendarFragment"> <action android:id="@+id/action_submissionCalendarFragment_to_submissionSymptomIntroductionFragment" app:destination="@id/submissionSymptomIntroductionFragment" /> <action android:id="@+id/action_submissionSymptomCalendarFragment_to_submissionResultPositiveOtherWarningFragment" app:destination="@id/submissionResultPositiveOtherWarningFragment" /> + <argument + android:name="symptomIndication" + app:argType="de.rki.coronawarnapp.submission.Symptoms$Indication" /> </fragment> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt index 2e2d2cb5a31ecd7bfd98cc95dc61793c22341a88..671fd3ab13b4be98186c78f0e730204372e60efa 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt @@ -8,7 +8,6 @@ import io.mockk.MockKAnnotations import io.mockk.clearAllMocks import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.mockk import kotlinx.coroutines.runBlocking import okhttp3.ConnectionSpec import okhttp3.mockwebserver.MockResponse @@ -32,7 +31,6 @@ class AppConfigApiTest : BaseIOTest() { private val cacheFiles = File(testDir, "cache") private val cacheDir = File(cacheFiles, "http_app-config") - @BeforeEach fun setup() { MockKAnnotations.init(this) @@ -69,7 +67,7 @@ class AppConfigApiTest : BaseIOTest() { cache = cache ) } - + @Test fun `application config download`() { val api = createAPI() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt index c9ecb45bccb3930a85efa6bdd454be17d4459bfd..541eedf627670fbf71a60a819a15cf71e661771d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/ENFClientTest.kt @@ -158,7 +158,7 @@ class ENFClientTest : BaseTest() { mapOf( "1" to Calculation( identifier = "1", - startedAt = Instant.EPOCH.plus(5), + startedAt = Instant.EPOCH.plus(5) ), "2" to Calculation( identifier = "2", @@ -176,7 +176,7 @@ class ENFClientTest : BaseTest() { mapOf( "1" to Calculation( identifier = "1", - startedAt = Instant.EPOCH, + startedAt = Instant.EPOCH ), "2" to Calculation( identifier = "2", @@ -251,7 +251,7 @@ class ENFClientTest : BaseTest() { "2" to Calculation( identifier = "2", result = Calculation.Result.UPDATED_STATE, - startedAt = Instant.EPOCH, + startedAt = Instant.EPOCH ), "3" to Calculation( identifier = "3", @@ -266,4 +266,3 @@ class ENFClientTest : BaseTest() { } } } - diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/calculationtracker/DefaultCalculationTrackerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/calculationtracker/DefaultCalculationTrackerTest.kt index 5ae6cad6afb8e39c1389f8f668d1556c34f29ed1..067680cac59871576df6c49a6acb9eec6426e300 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/calculationtracker/DefaultCalculationTrackerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/calculationtracker/DefaultCalculationTrackerTest.kt @@ -116,7 +116,6 @@ class DefaultCalculationTrackerTest : BaseTest() { ) } - every { timeStamper.nowUTC } returns Instant.EPOCH.plus(1) createInstance(scope = this).apply { @@ -233,7 +232,7 @@ class DefaultCalculationTrackerTest : BaseTest() { timeoutIgnoresFinishedCalcs.identifier to timeoutIgnoresFinishedCalcs, timeoutRunningOnEdge.identifier to timeoutRunningOnEdge, noTimeoutCalcRunning.identifier to noTimeoutCalcRunning, - noTimeOutCalcFinished.identifier to noTimeOutCalcFinished, + noTimeOutCalcFinished.identifier to noTimeOutCalcFinished ) coEvery { storage.load() } returns calcData @@ -256,7 +255,6 @@ class DefaultCalculationTrackerTest : BaseTest() { this["3"] shouldBe timeoutRunningOnEdge - this["4"] shouldBe noTimeoutCalcRunning this["5"] shouldBe noTimeOutCalcFinished } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/task/testtasks/SkippingTask.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/task/testtasks/SkippingTask.kt index 3c832ad293951d1072960f4ffbefaee9fe1b0333..f04714e342280f267dfd7a55d7f34365b6b43c90 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/task/testtasks/SkippingTask.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/task/testtasks/SkippingTask.kt @@ -18,7 +18,7 @@ class SkippingTask : QueueingTask() { } class Factory @Inject constructor( - private val taskByDagger: Provider<QueueingTask>, + private val taskByDagger: Provider<QueueingTask> ) : TaskFactory<DefaultProgress, Result> { override val config: TaskFactory.Config = diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt index ea8c5d2192fd484c46e862fea33580b70bf2d66b..5a6d942050d2262b7dd676d8a77b74ee07077b7b 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt @@ -17,7 +17,6 @@ import de.rki.coronawarnapp.risk.RiskScoreAnalysis import de.rki.coronawarnapp.risk.TimeVariables import de.rki.coronawarnapp.server.protocols.internal.AppConfig import de.rki.coronawarnapp.server.protocols.internal.AttenuationDurationOuterClass -import de.rki.coronawarnapp.server.protocols.internal.RiskScoreClassificationOuterClass import de.rki.coronawarnapp.server.protocols.internal.RiskScoreClassificationOuterClass.RiskScoreClass import de.rki.coronawarnapp.server.protocols.internal.RiskScoreClassificationOuterClass.RiskScoreClassification import de.rki.coronawarnapp.service.applicationconfiguration.ApplicationConfigurationService diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt deleted file mode 100644 index 8f49bcb3f001e37e3e3012895d795f3217c9485b..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/SubmissionTanViewModelTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package de.rki.coronawarnapp.ui.submission - -import de.rki.coronawarnapp.storage.SubmissionRepository -import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionTanViewModel -import io.mockk.Runs -import io.mockk.every -import io.mockk.just -import io.mockk.mockk -import io.mockk.verify -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test - -class SubmissionTanViewModelTest { - private var viewModel: SubmissionTanViewModel = SubmissionTanViewModel() - - @Test - fun tanFormatValid() { - viewModel.tan.postValue("ZWFPC7NG47") - viewModel.isValidTanFormat.value?.let { assertTrue(it) } - - viewModel.tan.postValue("ABC") - viewModel.isValidTanFormat.value?.let { assertFalse(it) } - - viewModel.tan.postValue("ZWFPC7NG48") - viewModel.isValidTanFormat.value?.let { assertFalse(it) } - - viewModel.tan.postValue("ZWFPC7NG4A") - viewModel.isValidTanFormat.value?.let { assertFalse(it) } - } - - @Test - fun testTanStorage() { - val sr = mockk<SubmissionRepository> { - every { setTeletan(any()) } just Runs - } - val tan = "ZWFPC7NG47" - sr.setTeletan(tan) - - verify(exactly = 1) { - sr.setTeletan( - withArg { - assertEquals(it, tan) - }) - } - } -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModelTest.kt similarity index 93% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModelTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModelTest.kt index 44023d6cd1cc8c3e8b8efe8b3846df0cbd45e1ea..d42ae7c1d4307f20c66c0c795901e657fb8a97a9 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionQRCodeInfoFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/info/SubmissionQRCodeInfoFragmentViewModelTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.viewmodel +package de.rki.coronawarnapp.ui.submission.qrcode.info import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt similarity index 88% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModelTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt index cbc54ad5d3a78b2dddbe89f2a525c0576ca03672..b518ae31a0ba845187e3889b415605b4507da462 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/viewmodel/SubmissionViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.viewmodel +package de.rki.coronawarnapp.ui.submission.qrcode.scan import de.rki.coronawarnapp.playbook.BackgroundNoise import de.rki.coronawarnapp.storage.LocalData @@ -14,10 +14,11 @@ import org.junit.Assert import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import testhelpers.BaseTest import testhelpers.extensions.InstantExecutorExtension @ExtendWith(InstantExecutorExtension::class) -class SubmissionViewModelTest { +class SubmissionQRCodeScanViewModelTest : BaseTest() { @MockK lateinit var backgroundNoise: BackgroundNoise @@ -32,7 +33,7 @@ class SubmissionViewModelTest { every { BackgroundNoise.getInstance() } returns backgroundNoise } - private fun createViewModel() = SubmissionViewModel() + private fun createViewModel() = SubmissionQRCodeScanViewModel() @Test fun scanStatusValid() { diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe2bd9043d442472d3a2b8739ffa07a583545f85 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/SubmissionTanViewModelTest.kt @@ -0,0 +1,58 @@ +package de.rki.coronawarnapp.ui.submission.tan + +import de.rki.coronawarnapp.storage.SubmissionRepository +import io.kotest.matchers.shouldBe +import io.mockk.Runs +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.verify +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider +import testhelpers.extensions.CoroutinesTestExtension +import testhelpers.extensions.InstantExecutorExtension + +@ExtendWith(InstantExecutorExtension::class, CoroutinesTestExtension::class) +class SubmissionTanViewModelTest : BaseTest() { + + private fun createInstance() = SubmissionTanViewModel( + dispatcherProvider = TestDispatcherProvider + ) + + @Test + fun tanFormatValid() { + val viewModel = createInstance() + viewModel.state.observeForever { } + + viewModel.onTanChanged("ZWFPC7NG47") + viewModel.state.value!!.isTanValid shouldBe true + + viewModel.onTanChanged("ABC") + viewModel.state.value!!.isTanValid shouldBe false + + viewModel.onTanChanged("ZWFPC7NG48") + viewModel.state.value!!.isTanValid shouldBe false + + viewModel.onTanChanged("ZWFPC7NG4A") + viewModel.state.value!!.isTanValid shouldBe false + } + + @Test + fun testTanStorage() { + val sr = mockk<SubmissionRepository> { + every { setTeletan(any()) } just Runs + } + val tan = "ZWFPC7NG47" + sr.setTeletan(tan) + + verify(exactly = 1) { + sr.setTeletan( + withArg { + it shouldBe tan + } + ) + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/TanTest.kt similarity index 63% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/TanTest.kt index 7a70f8c5493057354b1953815cd5869051bb252d..4a011b972587c5bc14a74e2b0bf002a1e70259ce 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/TanHelperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/tan/TanTest.kt @@ -1,10 +1,10 @@ -package de.rki.coronawarnapp.util +package de.rki.coronawarnapp.ui.submission.tan -import org.hamcrest.CoreMatchers -import org.hamcrest.MatcherAssert -import org.junit.Test +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import testhelpers.BaseTest -class TanHelperTest { +class TanTest : BaseTest() { @Test fun isValidCharacter() { @@ -14,10 +14,7 @@ class TanHelperTest { "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ) for (character in validCharacters) { - MatcherAssert.assertThat( - TanHelper.isTanCharacterValid(character), - CoreMatchers.equalTo(true) - ) + Tan.isTanCharacterValid(character) shouldBe true } // invalid @@ -26,10 +23,7 @@ class TanHelperTest { "c", "ö", "ß", "é", ".", " ", "€", "(", ")", ";", "," ) for (character in invalidCharacters) { - MatcherAssert.assertThat( - TanHelper.isTanCharacterValid(character), - CoreMatchers.equalTo(false) - ) + Tan.isTanCharacterValid(character) shouldBe false } } @@ -40,10 +34,7 @@ class TanHelperTest { "ABCD", "2345", "PTPHM35RP4", "AAAAAAAAAA", "BBBBB" ) for (text in validStrings) { - MatcherAssert.assertThat( - TanHelper.allCharactersValid(text), - CoreMatchers.equalTo(true) - ) + Tan.allCharactersValid(text) shouldBe true } // invalid input strings @@ -51,10 +42,7 @@ class TanHelperTest { "ABCDÖ", "01234", "PTPHM15RP4", "AAAAAA AAA", "BB.BBB" ) for (text in invalidStrings) { - MatcherAssert.assertThat( - TanHelper.allCharactersValid(text), - CoreMatchers.equalTo(false) - ) + Tan.allCharactersValid(text) shouldBe false } } @@ -65,10 +53,7 @@ class TanHelperTest { "9A3B578UMG", "DEU7TKSV3H", "PTPHM35RP4", "V923D59AT8", "H9NC5CQ34E" ) for (tan in validTans) { - MatcherAssert.assertThat( - TanHelper.isChecksumValid(tan), - CoreMatchers.equalTo(true) - ) + Tan.isChecksumValid(tan) shouldBe true } // invalid @@ -81,10 +66,7 @@ class TanHelperTest { "9A3B578UM0", "DEU7TKSV31", "Q4XBJCB43", "929B96CA8" ) for (tan in invalidTans) { - MatcherAssert.assertThat( - TanHelper.isChecksumValid(tan), - CoreMatchers.equalTo(false) - ) + Tan.isChecksumValid(tan) shouldBe false } } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/details/TracingDetailsStateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/details/TracingDetailsStateTest.kt index 9a22c516dbff73ac424f0d72bb83f95d219ab459..93154d75926eaf91c65b80950faeac0735a74072 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/details/TracingDetailsStateTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/details/TracingDetailsStateTest.kt @@ -249,61 +249,61 @@ class TracingDetailsStateTest : BaseTest() { fun `risk details buttons visibility`() { createInstance( riskLevelScore = RiskLevelConstants.INCREASED_RISK, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { areRiskDetailsButtonsVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.NO_CALCULATION_POSSIBLE_TRACING_OFF, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { areRiskDetailsButtonsVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { areRiskDetailsButtonsVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.INCREASED_RISK, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.NO_CALCULATION_POSSIBLE_TRACING_OFF, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { areRiskDetailsButtonsVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { areRiskDetailsButtonsVisible() shouldBe true } @@ -338,62 +338,62 @@ class TracingDetailsStateTest : BaseTest() { fun `risk details update button visibility`() { createInstance( riskLevelScore = RiskLevelConstants.INCREASED_RISK, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.NO_CALCULATION_POSSIBLE_TRACING_OFF, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, - isBackgroundJobEnabled = true, + isBackgroundJobEnabled = true ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.INCREASED_RISK, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { isRiskDetailsUpdateButtonVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_OUTDATED_RESULTS, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.NO_CALCULATION_POSSIBLE_TRACING_OFF, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { isRiskDetailsUpdateButtonVisible() shouldBe false } createInstance( riskLevelScore = RiskLevelConstants.LOW_LEVEL_RISK, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { isRiskDetailsUpdateButtonVisible() shouldBe true } createInstance( riskLevelScore = RiskLevelConstants.UNKNOWN_RISK_INITIAL, - isBackgroundJobEnabled = false, + isBackgroundJobEnabled = false ).apply { isRiskDetailsUpdateButtonVisible() shouldBe true } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/flow/HotDataFlowTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/flow/HotDataFlowTest.kt index 0a5702aa451a11d0064f51a1d9f97fa5e54ab376..e5bbbc32bf1b36461320d765152b9771a9a65607 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/flow/HotDataFlowTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/flow/HotDataFlowTest.kt @@ -141,7 +141,6 @@ class HotDataFlowTest : BaseTest() { hotData.updateSafely { "2" } hotData.updateSafely { "1" } - runBlocking { testCollector.await { list, l -> list.size == 3 } testCollector.latestValues shouldBe listOf("1", "2", "1") @@ -161,7 +160,6 @@ class HotDataFlowTest : BaseTest() { sharingBehavior = SharingStarted.Lazily ) - testScope.runBlockingTest2(permanentJobs = true) { val sub1 = hotData.data.test().start(scope = this) val sub2 = hotData.data.test().start(scope = this) diff --git a/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt b/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt index bcf45781d2a11ea590622c2f157acee224bd5e02..0fc5027bee9cc61a1545b2cab0ad52bdde4b1488 100644 --- a/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt +++ b/Corona-Warn-App/src/test/java/testhelpers/coroutines/TestExtensions.kt @@ -40,5 +40,3 @@ fun runBlockingTest2( } } } - -