diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/QrCodeDetailFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/QrCodeDetailFragmentTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9986448a35777f646a5b4b694ce54a39f8fbde0d
--- /dev/null
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/QrCodeDetailFragmentTest.kt
@@ -0,0 +1,98 @@
+package de.rki.coronawarnapp.ui.eventregistration.organizer
+
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
+import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository
+import de.rki.coronawarnapp.ui.presencetracing.organizer.details.QrCodeDetailFragment
+import de.rki.coronawarnapp.ui.presencetracing.organizer.details.QrCodeDetailFragmentArgs
+import de.rki.coronawarnapp.ui.presencetracing.organizer.details.QrCodeDetailViewModel
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import org.joda.time.DateTimeZone
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import testhelpers.BaseUITest
+import testhelpers.TestDispatcherProvider
+import testhelpers.launchFragmentInContainer2
+import java.util.TimeZone
+
+@RunWith(AndroidJUnit4::class)
+class QrCodeDetailFragmentTest : BaseUITest() {
+
+    @MockK private lateinit var qrCodeGenerator: QrCodeGenerator
+    @MockK private lateinit var traceLocationRepository: TraceLocationRepository
+
+    private val timeZone = TimeZone.getTimeZone("Europe/Berlin")
+
+    @Before
+    fun setup() {
+        TimeZone.setDefault(timeZone)
+        DateTimeZone.setDefault(DateTimeZone.forTimeZone(timeZone))
+        MockKAnnotations.init(this, relaxed = true)
+
+        coEvery { traceLocationRepository.traceLocationForId(1) } returns TraceLocationData.traceLocationSameDate
+        coEvery { traceLocationRepository.traceLocationForId(2) } returns TraceLocationData.traceLocationDifferentDate
+
+        setupMockViewModel(
+            object : QrCodeDetailViewModel.Factory {
+                override fun create(traceLocationId: Long): QrCodeDetailViewModel {
+                    return createViewModel(traceLocationId)
+                }
+            }
+        )
+    }
+
+    @After
+    fun teardown() {
+        clearAllViewModels()
+    }
+
+    @Test
+    fun eventDetailForSameDatesTest() {
+        launchFragmentInContainer2<QrCodeDetailFragment>(
+            fragmentArgs = QrCodeDetailFragmentArgs(
+                traceLocationId = 1
+            ).toBundle()
+        )
+
+        onView(withId(R.id.title)).check(matches(withText("My Birthday Party")))
+        onView(withId(R.id.subtitle)).check(matches(withText("at my place")))
+        onView(withId(R.id.eventDate)).check(matches(withText("19.04.2021, 06:12 - 22:52 Uhr")))
+    }
+
+    @Test
+    fun eventDetailForDifferentDatesTest() {
+        launchFragmentInContainer2<QrCodeDetailFragment>(
+            fragmentArgs = QrCodeDetailFragmentArgs(
+                traceLocationId = 2
+            ).toBundle()
+        )
+        onView(withId(R.id.title)).check(matches(withText("Your Birthday Party")))
+        onView(withId(R.id.subtitle)).check(matches(withText("at your place")))
+        onView(withId(R.id.eventDate)).check(matches(withText("18.04.2021, 12:00 - 19.04.2021, 22:52 Uhr")))
+    }
+
+    private fun createViewModel(traceLocationId: Long) =
+        QrCodeDetailViewModel(
+            traceLocationId = traceLocationId,
+            qrCodeGenerator = qrCodeGenerator,
+            traceLocationRepository = traceLocationRepository,
+            dispatcher = TestDispatcherProvider()
+        )
+}
+
+@Module
+abstract class QrCodeDetailFragmentTestModule {
+    @ContributesAndroidInjector
+    abstract fun qrCodeDetailFragment(): QrCodeDetailFragment
+}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationCreateFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationCreateFragmentTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2685049bd5b53fbcc0887d35c53494f417ef6fda
--- /dev/null
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationCreateFragmentTest.kt
@@ -0,0 +1,122 @@
+package de.rki.coronawarnapp.ui.eventregistration.organizer
+
+import androidx.navigation.Navigation
+import androidx.navigation.testing.TestNavHostController
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.click
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.presencetracing.locations.TraceLocationCreator
+import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository
+import de.rki.coronawarnapp.ui.presencetracing.organizer.category.adapter.category.TraceLocationCategory
+import de.rki.coronawarnapp.ui.presencetracing.organizer.create.TraceLocationCreateFragment
+import de.rki.coronawarnapp.ui.presencetracing.organizer.create.TraceLocationCreateFragmentArgs
+import de.rki.coronawarnapp.ui.presencetracing.organizer.create.TraceLocationCreateViewModel
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import org.joda.time.DateTimeZone
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import testhelpers.BaseUITest
+import testhelpers.TestDispatcherProvider
+import testhelpers.launchFragmentInContainer2
+import java.util.TimeZone
+
+@RunWith(AndroidJUnit4::class)
+class TraceLocationCreateFragmentTest : BaseUITest() {
+
+    @MockK private lateinit var traceLocationRepository: TraceLocationRepository
+    @MockK private lateinit var traceLocationCreator: TraceLocationCreator
+
+    private val timeZone = TimeZone.getTimeZone("Europe/Berlin")
+
+    private val navController = TestNavHostController(
+        ApplicationProvider.getApplicationContext()
+    ).apply {
+        UiThreadStatement.runOnUiThread { setGraph(R.navigation.trace_location_organizer_nav_graph) }
+    }
+
+    @Before
+    fun setup() {
+        TimeZone.setDefault(timeZone)
+        DateTimeZone.setDefault(DateTimeZone.forTimeZone(timeZone))
+        MockKAnnotations.init(this, relaxed = true)
+
+        coEvery { traceLocationRepository.addTraceLocation(any()) } returns TraceLocationData.traceLocationSameDate
+
+        setupMockViewModel(
+            object : TraceLocationCreateViewModel.Factory {
+                override fun create(category: TraceLocationCategory): TraceLocationCreateViewModel {
+                    return createViewModel(category)
+                }
+            }
+        )
+    }
+
+    @After
+    fun teardown() {
+        clearAllViewModels()
+    }
+
+    @Test
+    fun duplicateEventWithSameDatesTest() {
+        launchFragmentInContainer2<TraceLocationCreateFragment>(
+            fragmentArgs = TraceLocationCreateFragmentArgs(
+                category = TraceLocationData.categoryEvent,
+                originalItem = TraceLocationData.traceLocationSameDate
+            ).toBundle()
+        ).onFragment { fragment -> Navigation.setViewNavController(fragment.requireView(), navController) }
+
+        onView(withId(R.id.description_input_edit)).check(matches(withText("My Birthday Party")))
+        onView(withId(R.id.place_input_edit)).check(matches(withText("at my place")))
+        onView(withId(R.id.value_start)).check(matches(withText("Mo., 19.04.21   06:12")))
+        onView(withId(R.id.value_end)).check(matches(withText("Mo., 19.04.21   22:52")))
+
+        onView(withId(R.id.button_submit)).perform(click())
+
+        navController.currentDestination?.id shouldBe R.id.traceLocationInfoFragment
+    }
+
+    @Test
+    fun duplicateEventWithDifferentDatesTest() {
+        launchFragmentInContainer2<TraceLocationCreateFragment>(
+            fragmentArgs = TraceLocationCreateFragmentArgs(
+                category = TraceLocationData.categoryEvent,
+                originalItem = TraceLocationData.traceLocationDifferentDate
+            ).toBundle()
+        ).onFragment { fragment -> Navigation.setViewNavController(fragment.requireView(), navController) }
+
+        onView(withId(R.id.description_input_edit)).check(matches(withText("Your Birthday Party")))
+        onView(withId(R.id.place_input_edit)).check(matches(withText("at your place")))
+        onView(withId(R.id.value_start)).check(matches(withText("So., 18.04.21   12:00")))
+        onView(withId(R.id.value_end)).check(matches(withText("Mo., 19.04.21   22:52")))
+
+        onView(withId(R.id.button_submit)).perform(click())
+
+        navController.currentDestination?.id shouldBe R.id.traceLocationInfoFragment
+    }
+
+    private fun createViewModel(category: TraceLocationCategory) =
+        TraceLocationCreateViewModel(
+            category = category,
+            traceLocationCreator = traceLocationCreator,
+            dispatcherProvider = TestDispatcherProvider()
+        )
+}
+
+@Module
+abstract class CreateEventTestModule {
+    @ContributesAndroidInjector
+    abstract fun traceLocationCreateFragment(): TraceLocationCreateFragment
+}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9bf3382fff76dde0a8faf0f26d21008da1ab301f
--- /dev/null
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationData.kt
@@ -0,0 +1,51 @@
+package de.rki.coronawarnapp.ui.eventregistration.organizer
+
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation
+import de.rki.coronawarnapp.server.protocols.internal.pt.TraceLocationOuterClass
+import de.rki.coronawarnapp.ui.presencetracing.organizer.category.adapter.category.TraceLocationCategory
+import de.rki.coronawarnapp.ui.presencetracing.organizer.category.adapter.category.TraceLocationUIType
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.secondsToInstant
+import okio.ByteString.Companion.decodeBase64
+
+object TraceLocationData {
+
+    private const val CRYPTOGRAPHIC_SEED = "MTIzNA=="
+
+    private const val PUB_KEY =
+        "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEc7DEstcUIRcyk35OYDJ95/hTg3UVhsaDXKT0z" +
+            "K7NhHPXoyzipEnOp3GyNXDVpaPi3cAfQmxeuFMZAIX2+6A5Xg=="
+
+    val traceLocationSameDate = TraceLocation(
+        id = 1,
+        type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER,
+        description = "My Birthday Party",
+        address = "at my place",
+        startDate = 1618805545L.secondsToInstant(),
+        endDate = 1618865545L.secondsToInstant(),
+        defaultCheckInLengthInMinutes = null,
+        cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!,
+        cnPublicKey = PUB_KEY,
+        version = TraceLocation.VERSION
+    )
+
+    val traceLocationDifferentDate = TraceLocation(
+        id = 2,
+        type = TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_OTHER,
+        description = "Your Birthday Party",
+        address = "at your place",
+        startDate = 1618740005L.secondsToInstant(),
+        endDate = 1618865545L.secondsToInstant(),
+        defaultCheckInLengthInMinutes = null,
+        cryptographicSeed = CRYPTOGRAPHIC_SEED.decodeBase64()!!,
+        cnPublicKey = PUB_KEY,
+        version = TraceLocation.VERSION
+    )
+
+    val categoryEvent = TraceLocationCategory(
+        TraceLocationOuterClass.TraceLocationType.LOCATION_TYPE_TEMPORARY_CULTURAL_EVENT,
+        TraceLocationUIType.EVENT,
+        R.string.tracelocation_organizer_category_cultural_event_title,
+        R.string.tracelocation_organizer_category_cultural_event_subtitle
+    )
+}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationsFragmentTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e478d51bc5b0709902117966277aae8f58014ace
--- /dev/null
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/eventregistration/organizer/TraceLocationsFragmentTest.kt
@@ -0,0 +1,94 @@
+package de.rki.coronawarnapp.ui.eventregistration.organizer
+
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.withId
+import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository
+import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository
+import de.rki.coronawarnapp.ui.presencetracing.organizer.list.TraceLocationsFragment
+import de.rki.coronawarnapp.ui.presencetracing.organizer.list.TraceLocationsViewModel
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import kotlinx.coroutines.flow.flowOf
+import org.joda.time.DateTimeZone
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import testhelpers.BaseUITest
+import testhelpers.TestDispatcherProvider
+import testhelpers.launchFragmentInContainer2
+import java.util.TimeZone
+
+@RunWith(AndroidJUnit4::class)
+class TraceLocationsFragmentTest : BaseUITest() {
+
+    @MockK private lateinit var checkInsRepository: CheckInRepository
+    @MockK private lateinit var traceLocationRepository: TraceLocationRepository
+
+    private val timeZone = TimeZone.getTimeZone("Europe/Berlin")
+
+    @Before
+    fun setup() {
+        TimeZone.setDefault(timeZone)
+        DateTimeZone.setDefault(DateTimeZone.forTimeZone(timeZone))
+        MockKAnnotations.init(this, relaxed = true)
+
+        every { checkInsRepository.allCheckIns } returns flowOf(listOf())
+
+        setupMockViewModel(
+            object : TraceLocationsViewModel.Factory {
+                override fun create(): TraceLocationsViewModel {
+                    return createViewModel()
+                }
+            }
+        )
+    }
+
+    @After
+    fun teardown() {
+        clearAllViewModels()
+    }
+
+    @Test
+    fun itemWithSameDatesTest() {
+        every { traceLocationRepository.traceLocationsWithinRetention } returns
+            flowOf(listOf(TraceLocationData.traceLocationSameDate))
+
+        launchFragmentInContainer2<TraceLocationsFragment>()
+
+        onView(withId(R.id.description)).check(matches(withText("My Birthday Party")))
+        onView(withId(R.id.address)).check(matches(withText("at my place")))
+        onView(withId(R.id.duration)).check(matches(withText("19.04.21 06:12 - 22:52 Uhr")))
+    }
+
+    @Test
+    fun itemWithDifferentDatesTest() {
+        every { traceLocationRepository.traceLocationsWithinRetention } returns
+            flowOf(listOf(TraceLocationData.traceLocationDifferentDate))
+
+        launchFragmentInContainer2<TraceLocationsFragment>()
+
+        onView(withId(R.id.description)).check(matches(withText("Your Birthday Party")))
+        onView(withId(R.id.address)).check(matches(withText("at your place")))
+        onView(withId(R.id.duration)).check(matches(withText("18.04.21 12:00 - 19.04.21 22:52 Uhr")))
+    }
+
+    private fun createViewModel() = TraceLocationsViewModel(
+        checkInsRepository = checkInsRepository,
+        traceLocationRepository = traceLocationRepository,
+        dispatcherProvider = TestDispatcherProvider()
+    )
+}
+
+@Module
+abstract class TraceLocationsFragmentTestModule {
+    @ContributesAndroidInjector
+    abstract fun traceLocationsFragment(): TraceLocationsFragment
+}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
index d610fb4905df02d3f210878cee1c6ad16b09b225..6a90f9e2132e68c155bbe1691ea4d197f793da50 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
@@ -134,12 +134,17 @@ object HomeData {
         )
 
         val TEST_POSITIVE_ITEM = PcrTestPositiveCard.Item(
-            state = TestPositive,
+            state = TestPositive(
+                testRegisteredAt = Instant.now()
+            ),
             onClickAction = {}
         )
 
         val TEST_NEGATIVE_ITEM = PcrTestNegativeCard.Item(
-            state = TestNegative
+            state = TestNegative(
+                testRegisteredAt = Instant.now()
+            ),
+            onClickAction = {}
         )
 
         val TEST_INVALID_ITEM = PcrTestInvalidCard.Item(
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 db6e15d1b787f3a1ff5f73150390ecd0b5c85096..4dc5e974943a1f3b8b2744823285e49af166a10a 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
@@ -12,9 +12,9 @@ import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
 import de.rki.coronawarnapp.main.CWASettings
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.statistics.source.StatisticsProvider
 import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard
 import de.rki.coronawarnapp.storage.TracingRepository
@@ -84,7 +84,6 @@ class HomeFragmentTest : BaseUITest() {
         MockKAnnotations.init(this, relaxed = true)
         homeFragmentViewModel = homeFragmentViewModelSpy()
         with(homeFragmentViewModel) {
-            every { observeTestResultToSchedulePositiveTestResultReminder() } just Runs
             every { refreshRequiredData() } just Runs
             every { tracingHeaderState } returns MutableLiveData(TracingHeaderState.TracingActive)
             every { showLoweredRiskLevelDialog } returns MutableLiveData()
@@ -266,7 +265,6 @@ class HomeFragmentTest : BaseUITest() {
             errorResetTool = errorResetTool,
             tracingRepository = tracingRepository,
             tracingStateProviderFactory = tracingStateProviderFactory,
-            shareTestResultNotificationService = shareTestResultNotificationService,
             appConfigProvider = appConfigProvider,
             tracingStatus = tracingStatus,
             submissionRepository = submissionRepository,
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt
index 7748ab27cde652375f87387997882dea0eec59a9..27bc93242d927d3caea09fffa5fafb3a18dde9df 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionConsentFragmentTest.kt
@@ -48,11 +48,9 @@ class SubmissionConsentFragmentTest : BaseUITest() {
         every { interoperabilityRepository.countryList } returns flowOf()
         viewModel =
             SubmissionConsentViewModel(
-                submissionRepository,
                 interoperabilityRepository,
                 TestDispatcherProvider(),
                 tekHistoryProvider,
-                analyticsKeySubmissionCollector
             )
         setupMockViewModel(
             object : SubmissionConsentViewModel.Factory {
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
index 06ab62aa36eba132f077b81be1b302b906204f0a..e516c0b18e863220d12b08eb4ed205ff83fb7468 100644
--- 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
@@ -4,6 +4,7 @@ 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.SubmissionQRCodeScanFragmentArgs
 import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanViewModel
 import io.mockk.MockKAnnotations
 import io.mockk.impl.annotations.MockK
@@ -19,12 +20,14 @@ class SubmissionQrCodeScanFragmentTest : BaseUITest() {
 
     @MockK lateinit var viewModel: SubmissionQRCodeScanViewModel
 
+    private var fragmentArgs = SubmissionQRCodeScanFragmentArgs(isConsentGiven = true).toBundle()
+
     @Before
     fun setup() {
         MockKAnnotations.init(this, relaxed = true)
         setupMockViewModel(
             object : SubmissionQRCodeScanViewModel.Factory {
-                override fun create(): SubmissionQRCodeScanViewModel = viewModel
+                override fun create(isConsentGiven: Boolean): SubmissionQRCodeScanViewModel = viewModel
             }
         )
     }
@@ -36,7 +39,9 @@ class SubmissionQrCodeScanFragmentTest : BaseUITest() {
 
     @Test
     fun launch_fragment() {
-        launchFragment2<SubmissionQRCodeScanFragment>()
+        launchFragment2<SubmissionQRCodeScanFragment>(
+            fragmentArgs = fragmentArgs
+        )
     }
 }
 
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt
index 5e48790a6dbfd987b0a88beb50c7607a5cf8d63b..97f06b6023be71680d732e9bc1dcb791831f1674 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultConsentGivenFragmentTest.kt
@@ -15,7 +15,7 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.submission.auto.AutoSubmission
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
@@ -46,7 +46,7 @@ class SubmissionTestResultConsentGivenFragmentTest : BaseUITest() {
 
     @MockK lateinit var submissionRepository: SubmissionRepository
     @MockK lateinit var autoSubmission: AutoSubmission
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
     @MockK lateinit var analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
 
     @Rule
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
index 4b970b3a999263b3f99687dc97b6d4355939cfa7..849a4e22013f7d00d36f500edb61df251c3d62bb 100644
--- 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
@@ -10,16 +10,14 @@ import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResultPendingFragment
+import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResultPendingFragmentArgs
 import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResultPendingViewModel
 import io.mockk.MockKAnnotations
-import io.mockk.Runs
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
-import io.mockk.just
 import io.mockk.mockk
 import io.mockk.spyk
 import kotlinx.coroutines.flow.flowOf
@@ -43,7 +41,8 @@ class SubmissionTestResultFragmentTest : BaseUITest() {
 
     lateinit var viewModel: SubmissionTestResultPendingViewModel
     @MockK lateinit var submissionRepository: SubmissionRepository
-    @MockK lateinit var shareTestResultNotificationService: ShareTestResultNotificationService
+
+    private val pendingFragmentArgs = SubmissionTestResultPendingFragmentArgs(testType = CoronaTest.Type.PCR).toBundle()
 
     @Rule
     @JvmField
@@ -61,13 +60,12 @@ class SubmissionTestResultFragmentTest : BaseUITest() {
         viewModel = spyk(
             SubmissionTestResultPendingViewModel(
                 TestDispatcherProvider(),
-                shareTestResultNotificationService,
-                submissionRepository
+                submissionRepository,
+                testType = CoronaTest.Type.PCR
             )
         )
 
         with(viewModel) {
-            every { observeTestResultToSchedulePositiveTestResultReminder() } just Runs
             every { consentGiven } returns MutableLiveData(true)
             every { testState } returns MutableLiveData(
                 TestResultUIState(
@@ -82,7 +80,7 @@ class SubmissionTestResultFragmentTest : BaseUITest() {
 
         setupMockViewModel(
             object : SubmissionTestResultPendingViewModel.Factory {
-                override fun create(): SubmissionTestResultPendingViewModel = viewModel
+                override fun create(testType: CoronaTest.Type): SubmissionTestResultPendingViewModel = viewModel
             }
         )
     }
@@ -94,19 +92,19 @@ class SubmissionTestResultFragmentTest : BaseUITest() {
 
     @Test
     fun launch_fragment() {
-        launchFragment2<SubmissionTestResultPendingFragment>()
+        launchFragment2<SubmissionTestResultPendingFragment>(pendingFragmentArgs)
     }
 
     @Test
     fun testEventPendingRefreshClicked() {
-        launchFragmentInContainer2<SubmissionTestResultPendingFragment>()
+        launchFragmentInContainer2<SubmissionTestResultPendingFragment>(pendingFragmentArgs)
         onView(withId(R.id.submission_test_result_button_pending_refresh))
             .perform(click())
     }
 
     @Test
     fun testEventPendingRemoveClicked() {
-        launchFragmentInContainer2<SubmissionTestResultPendingFragment>()
+        launchFragmentInContainer2<SubmissionTestResultPendingFragment>(pendingFragmentArgs)
         onView(withId(R.id.submission_test_result_button_pending_remove_test))
             .perform(click())
     }
@@ -123,7 +121,7 @@ class SubmissionTestResultFragmentTest : BaseUITest() {
                 }
             )
         )
-        captureScreenshot<SubmissionTestResultPendingFragment>()
+        captureScreenshot<SubmissionTestResultPendingFragment>(fragmentArgs = pendingFragmentArgs)
     }
 }
 
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt
index 5eba253d54bb986fbd8c95f225c8c3e7eeb4876e..53df826ee0150196d647da355c51c091e58f43bf 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNegativeFragmentTest.kt
@@ -6,7 +6,7 @@ import dagger.Module
 import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.ui.submission.testresult.negative.SubmissionTestResultNegativeFragment
@@ -35,7 +35,7 @@ class SubmissionTestResultNegativeFragmentTest : BaseUITest() {
 
     lateinit var viewModel: SubmissionTestResultNegativeViewModel
     @MockK lateinit var submissionRepository: SubmissionRepository
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
 
     @Rule
     @JvmField
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNoConsentGivenFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNoConsentGivenFragmentTest.kt
index 55d8144158bfc191c60d729b26af79204b5ea397..3ec1a885ea9f0fef9e4443cda94977da3a276781 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNoConsentGivenFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/SubmissionTestResultNoConsentGivenFragmentTest.kt
@@ -7,7 +7,7 @@ import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.ui.submission.testresult.positive.SubmissionTestResultNoConsentFragment
@@ -33,7 +33,7 @@ import tools.fastlane.screengrab.locale.LocaleTestRule
 class SubmissionTestResultNoConsentGivenFragmentTest : BaseUITest() {
 
     @MockK lateinit var submissionRepository: SubmissionRepository
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
     @MockK lateinit var analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
 
     @Rule
diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt
index ecb1c406efdc4d80d26d484c1e55fa286e48da5d..5f3cabbfcacfc6f5c44078480bc4dfcff0651fde 100644
--- a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt
+++ b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt
@@ -10,6 +10,9 @@ import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryLocationListFragmentTest
 import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryOnboardingFragmentTestModule
 import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryOverviewFragmentTestModule
 import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryPersonListFragmentTestModule
+import de.rki.coronawarnapp.ui.eventregistration.organizer.CreateEventTestModule
+import de.rki.coronawarnapp.ui.eventregistration.organizer.QrCodeDetailFragmentTestModule
+import de.rki.coronawarnapp.ui.eventregistration.organizer.TraceLocationsFragmentTestModule
 import de.rki.coronawarnapp.ui.main.home.HomeFragmentTestModule
 import de.rki.coronawarnapp.ui.onboarding.OnboardingAnalyticsFragmentTestModule
 import de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaInteroperabilityFragmentTestModule
@@ -74,7 +77,11 @@ import de.rki.coronawarnapp.ui.tracing.TracingDetailsFragmentTestTestModule
         StatisticsExplanationFragmentTestModule::class,
         // Bugreporting
         DebugLogUploadTestModule::class,
-        DebugLogTestModule::class
+        DebugLogTestModule::class,
+        // Event Registration
+        CreateEventTestModule::class,
+        TraceLocationsFragmentTestModule::class,
+        QrCodeDetailFragmentTestModule::class
     ]
 )
 class FragmentTestModuleRegistrar
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/hometestcards/ui/HomeTestCardsFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/hometestcards/ui/HomeTestCardsFragmentViewModel.kt
index 8d43b3595cdcbfd7dda641e9bc9d043f7fb9dc70..2def4a1e835ee1167eea8a8eb61272f82a1c2e91 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/hometestcards/ui/HomeTestCardsFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/hometestcards/ui/HomeTestCardsFragmentViewModel.kt
@@ -44,16 +44,16 @@ class HomeTestCardsFragmentViewModel @AssistedInject constructor(
             PcrTestReadyCard.Item(SubmissionStatePCR.TestResultReady) {},
             PcrTestInvalidCard.Item(SubmissionStatePCR.TestInvalid) {},
             PcrTestErrorCard.Item(SubmissionStatePCR.TestError) {},
-            PcrTestNegativeCard.Item(SubmissionStatePCR.TestNegative),
-            PcrTestPositiveCard.Item(SubmissionStatePCR.TestPositive) {},
+            PcrTestNegativeCard.Item(SubmissionStatePCR.TestNegative(Instant.now())) {},
+            PcrTestPositiveCard.Item(SubmissionStatePCR.TestPositive(Instant.now())) {},
             PcrTestSubmissionDoneCard.Item(SubmissionStatePCR.SubmissionDone(Instant.now())),
             RapidTestPendingCard.Item(SubmissionStateRAT.TestPending) {},
             RapidTestReadyCard.Item(SubmissionStateRAT.TestResultReady) {},
             RapidTestInvalidCard.Item(SubmissionStateRAT.TestInvalid) {},
             RapidTestOutdatedCard.Item(SubmissionStateRAT.TestInvalid) {},
             RapidTestErrorCard.Item(SubmissionStateRAT.TestError) {},
-            RapidTestNegativeCard.Item(SubmissionStateRAT.TestNegative),
-            RapidTestPositiveCard.Item(SubmissionStateRAT.TestPositive) {},
+            RapidTestNegativeCard.Item(SubmissionStateRAT.TestNegative(Instant.now())) {},
+            RapidTestPositiveCard.Item(SubmissionStateRAT.TestPositive(Instant.now())) {},
             RapidTestSubmissionDoneCard.Item(SubmissionStateRAT.SubmissionDone(Instant.now()))
         )
     )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
index d10c520260fb53b3cc45d2457c95d168d37a8329..b2d67700ee839cee11b6e2e8dabbe454500ea6ab 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt
@@ -16,13 +16,14 @@ import de.rki.coronawarnapp.appconfig.devicetime.DeviceTimeHandler
 import de.rki.coronawarnapp.bugreporting.loghistory.LogHistoryTree
 import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryWorkScheduler
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
-import de.rki.coronawarnapp.presencetracing.storage.retention.TraceLocationDbCleanUpScheduler
 import de.rki.coronawarnapp.exception.reporting.ErrorReportReceiver
 import de.rki.coronawarnapp.exception.reporting.ReportingConstants.ERROR_REPORT_LOCAL_BROADCAST_CHANNEL
 import de.rki.coronawarnapp.notification.GeneralNotifications
 import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut
+import de.rki.coronawarnapp.presencetracing.storage.retention.TraceLocationDbCleanUpScheduler
 import de.rki.coronawarnapp.risk.RiskLevelChangeDetector
 import de.rki.coronawarnapp.storage.OnboardingSettings
 import de.rki.coronawarnapp.submission.auto.AutoSubmission
@@ -68,6 +69,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector {
     @Inject lateinit var autoCheckOut: AutoCheckOut
     @Inject lateinit var traceLocationDbCleanupScheduler: TraceLocationDbCleanUpScheduler
     @Inject lateinit var backgroundWorkScheduler: BackgroundWorkScheduler
+    @Inject lateinit var shareTestResultNotificationService: ShareTestResultNotificationService
 
     @LogHistoryTree @Inject lateinit var rollingLogHistory: Timber.Tree
 
@@ -119,6 +121,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector {
         autoSubmission.setup()
         autoCheckOut.setupMonitor()
         traceLocationDbCleanupScheduler.scheduleDaily()
+        shareTestResultNotificationService.setup()
     }
 
     private val activityLifecycleCallback = object : ActivityLifecycleCallbacks {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt
index b3c47171c9024dcd36ff61c6ff86d1d5417bd0ec..c8769d23087ff67a5d92401421161273d206e739 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/RegistrationTokenCensor.kt
@@ -6,7 +6,6 @@ import de.rki.coronawarnapp.bugreporting.debuglog.LogLine
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
 import de.rki.coronawarnapp.util.CWADebug
 import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.flow.map
 import javax.inject.Inject
 
 @Reusable
@@ -14,9 +13,7 @@ class RegistrationTokenCensor @Inject constructor(
     private val coronaTestRepository: CoronaTestRepository,
 ) : BugCensor {
     override suspend fun checkLog(entry: LogLine): LogLine? {
-        val tokens = coronaTestRepository.coronaTests.map { tests ->
-            tests.map { it.registrationToken }
-        }.first()
+        val tokens = coronaTestRepository.coronaTests.first().map { it.registrationToken }
 
         if (tokens.isEmpty()) return null
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3cebace1c18fa97124d78c0cb557b9120764a2d8
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotification.kt
@@ -0,0 +1,57 @@
+package de.rki.coronawarnapp.coronatest.notification
+
+import android.content.Context
+import androidx.navigation.NavDeepLinkBuilder
+import dagger.Reusable
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.notification.GeneralNotifications
+import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_ID
+import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_INITIAL_OFFSET
+import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_INTERVAL
+import de.rki.coronawarnapp.ui.main.MainActivity
+import de.rki.coronawarnapp.util.TimeStamper
+import de.rki.coronawarnapp.util.di.AppContext
+import de.rki.coronawarnapp.util.notifications.setContentTextExpandable
+import timber.log.Timber
+import javax.inject.Inject
+
+@Reusable
+class ShareTestResultNotification @Inject constructor(
+    @AppContext private val context: Context,
+    private val timeStamper: TimeStamper,
+    private val notificationHelper: GeneralNotifications,
+) {
+
+    fun scheduleSharePositiveTestResultReminder() {
+        notificationHelper.scheduleRepeatingNotification(
+            timeStamper.nowUTC.plus(POSITIVE_RESULT_NOTIFICATION_INITIAL_OFFSET),
+            POSITIVE_RESULT_NOTIFICATION_INTERVAL,
+            POSITIVE_RESULT_NOTIFICATION_ID
+        )
+    }
+
+    fun showSharePositiveTestResultNotification(notificationId: Int) {
+        Timber.d("showSharePositiveTestResultNotification(notificationId=$notificationId)")
+        val pendingIntent = NavDeepLinkBuilder(context)
+            .setGraph(R.navigation.nav_graph)
+            .setComponentName(MainActivity::class.java)
+            .setDestination(R.id.submissionTestResultAvailableFragment)
+            .createPendingIntent()
+
+        val notification = notificationHelper.newBaseBuilder().apply {
+            setContentTitle(context.getString(R.string.notification_headline_share_positive_result))
+            setContentTextExpandable(context.getString(R.string.notification_body_share_positive_result))
+            setContentIntent(pendingIntent)
+        }.build()
+
+        notificationHelper.sendNotification(
+            notificationId = notificationId,
+            notification = notification,
+        )
+    }
+
+    fun cancelSharePositiveTestResultNotification() {
+        notificationHelper.cancelFutureNotifications(POSITIVE_RESULT_NOTIFICATION_ID)
+        Timber.v("Future positive test result notifications have been canceled")
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dddb09c4b7b328f9304001911108288c0bfd907f
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationService.kt
@@ -0,0 +1,68 @@
+package de.rki.coronawarnapp.coronatest.notification
+
+import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.main.CWASettings
+import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT
+import de.rki.coronawarnapp.util.coroutine.AppScope
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class ShareTestResultNotificationService @Inject constructor(
+    @AppScope private val appScope: CoroutineScope,
+    private val cwaSettings: CWASettings,
+    private val coronaTestRepository: CoronaTestRepository,
+    private val notification: ShareTestResultNotification
+) {
+
+    fun setup() {
+        Timber.d("setup()")
+        coronaTestRepository.coronaTests
+            .onEach { tests ->
+                when {
+                    tests.any { it.isSubmissionAllowed } -> {
+                        maybeScheduleSharePositiveTestResultReminder()
+                    }
+                    tests.isNotEmpty() -> {
+                        notification.cancelSharePositiveTestResultNotification()
+                    }
+                    tests.isEmpty() -> {
+                        resetSharePositiveTestResultNotification()
+                    }
+                }
+            }
+            .catch { Timber.e(it, "Failed to schedule positive test result reminder.") }
+            .launchIn(appScope)
+    }
+
+    fun maybeShowSharePositiveTestResultNotification(notificationId: Int) {
+        Timber.d("maybeShowSharePositiveTestResultNotification(notificationId=$notificationId)")
+        if (cwaSettings.numberOfRemainingSharePositiveTestResultReminders > 0) {
+            cwaSettings.numberOfRemainingSharePositiveTestResultReminders -= 1
+            notification.showSharePositiveTestResultNotification(notificationId)
+        } else {
+            notification.cancelSharePositiveTestResultNotification()
+        }
+    }
+
+    private fun maybeScheduleSharePositiveTestResultReminder() {
+        if (cwaSettings.numberOfRemainingSharePositiveTestResultReminders < 0) {
+            Timber.v("Schedule positive test result notification")
+            cwaSettings.numberOfRemainingSharePositiveTestResultReminders = POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT
+            notification.scheduleSharePositiveTestResultReminder()
+        } else {
+            Timber.v("Positive test result notification has already been scheduled")
+        }
+    }
+
+    private fun resetSharePositiveTestResultNotification() {
+        notification.cancelSharePositiveTestResultNotification()
+        cwaSettings.numberOfRemainingSharePositiveTestResultReminders = Int.MIN_VALUE
+        Timber.v("Positive test result notification counter has been reset")
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CommonSubmissionStates.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CommonSubmissionStates.kt
index 1867629ee401e40b7d8ed2f2ec6c62d74b40465b..190189a114a6575b56d6b0cfd0a35020f3bb94af 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CommonSubmissionStates.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CommonSubmissionStates.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.coronatest.type
 
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone
 import org.joda.time.Instant
 
 interface CommonSubmissionStates {
@@ -7,7 +8,16 @@ interface CommonSubmissionStates {
 
     interface TestFetching : CommonSubmissionStates
 
-    interface SubmissionDone : CommonSubmissionStates {
+    interface PositiveTest : HasTestRegistrationDate, CommonSubmissionStates
+
+    interface NegativeTest : HasTestRegistrationDate, CommonSubmissionStates
+
+    interface SubmissionDone : HasTestRegistrationDate, CommonSubmissionStates
+
+    interface HasTestRegistrationDate {
         val testRegisteredAt: Instant
+
+        fun getFormattedRegistrationDate(): String =
+            testRegisteredAt.toUserTimeZone().toLocalDate().toString("dd.MM.yy")
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt
index 6a3e9b1142e5c68ea4bc995906d3a9b66565b1d1..4fac9bbf2040b9143baeda36bab432a472185e9f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt
@@ -18,6 +18,8 @@ interface CoronaTest {
     val isSubmitted: Boolean
     val isViewed: Boolean
 
+    val isPositive: Boolean
+
     val testResultReceivedAt: Instant?
     val testResult: CoronaTestResult
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt
index ed6b4d56af89d29120f12ed7c1cf1f007be6d500..77957e449c932e055af49ad2dd75d9782488eab3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTest.kt
@@ -44,7 +44,9 @@ data class PCRCoronaTest(
 
     override val type: CoronaTest.Type = CoronaTest.Type.PCR
 
-    override val isSubmissionAllowed: Boolean = testResult == CoronaTestResult.PCR_POSITIVE
+    override val isPositive: Boolean = testResult == CoronaTestResult.PCR_POSITIVE
+
+    override val isSubmissionAllowed: Boolean = isPositive && !isSubmitted
 
     val state: State = when (testResult) {
         CoronaTestResult.PCR_OR_RAT_PENDING -> State.PENDING
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestExtensions.kt
index 5ce1c65829e0f85eff22df70bb980e40dd264d1f..7ea0ce6d7a53ffae6f3e3b4790cc7c5c084bc3fd 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRCoronaTestExtensions.kt
@@ -1,121 +1,33 @@
 package de.rki.coronawarnapp.coronatest.type.pcr
 
+import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest.State.INVALID
+import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest.State.NEGATIVE
+import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest.State.PENDING
+import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest.State.POSITIVE
+import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest.State.REDEEMED
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.FetchingResult
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.NoTest
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestError
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestInvalid
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestNegative
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestPending
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestPositive
+import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestResultReady
 import de.rki.coronawarnapp.exception.http.CwaServerError
-import de.rki.coronawarnapp.util.CWADebug
-import de.rki.coronawarnapp.util.DeviceUIState
-import de.rki.coronawarnapp.util.NetworkRequestWrapper
-import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess
-import timber.log.Timber
 
-fun PCRCoronaTest?.toSubmissionState(): SubmissionStatePCR {
-    if (this == null) return SubmissionStatePCR.NoTest
-
-    val uiState: DeviceUIState = when (state) {
-        PCRCoronaTest.State.PENDING -> DeviceUIState.PAIRED_NO_RESULT
-        PCRCoronaTest.State.INVALID -> DeviceUIState.PAIRED_ERROR
-        PCRCoronaTest.State.POSITIVE -> DeviceUIState.PAIRED_POSITIVE
-        PCRCoronaTest.State.NEGATIVE -> DeviceUIState.PAIRED_NEGATIVE
-        PCRCoronaTest.State.REDEEMED -> DeviceUIState.PAIRED_REDEEMED
-    }
-
-    val networkWrapper = when {
-        isProcessing -> NetworkRequestWrapper.RequestStarted
-        lastError != null -> NetworkRequestWrapper.RequestFailed(lastError)
-        else -> NetworkRequestWrapper.RequestSuccessful(uiState)
+fun PCRCoronaTest?.toSubmissionState() = when {
+    this == null -> NoTest
+    isSubmitted -> SubmissionStatePCR.SubmissionDone(testRegisteredAt = registeredAt)
+    isProcessing -> FetchingResult
+    lastError != null -> if (lastError is CwaServerError) TestPending else TestInvalid
+    else -> when (state) {
+        INVALID -> TestError
+        POSITIVE -> when {
+            isViewed -> TestPositive(testRegisteredAt = registeredAt)
+            else -> TestResultReady
+        }
+        NEGATIVE -> TestNegative(testRegisteredAt = registeredAt)
+        REDEEMED -> TestInvalid
+        PENDING -> TestPending
     }
-
-    val eval = Evaluation(
-        deviceUiState = networkWrapper,
-        isDeviceRegistered = registrationToken != null,
-        hasTestResultBeenSeen = this.isViewed
-    )
-    Timber.d("eval: %s", eval)
-    return when {
-        eval.isUnregistered() -> SubmissionStatePCR.NoTest
-        eval.isFetching() -> SubmissionStatePCR.FetchingResult
-        eval.isTestResultReady() -> SubmissionStatePCR.TestResultReady
-        eval.isResultPositive() -> SubmissionStatePCR.TestPositive
-        eval.isInvalid() -> SubmissionStatePCR.TestInvalid
-        eval.isError() -> SubmissionStatePCR.TestError
-        eval.isResultNegative() -> SubmissionStatePCR.TestNegative
-        eval.isSubmissionDone() -> SubmissionStatePCR.SubmissionDone(testRegisteredAt = registeredAt)
-        eval.isPending() -> SubmissionStatePCR.TestPending
-        else -> {
-            if (CWADebug.isDeviceForTestersBuild) throw IllegalStateException(eval.toString())
-            else SubmissionStatePCR.TestPending
-        }
-    }
-}
-
-// TODO Refactor this to be easier to understand, probably remove the "withSuccess" logic.
-private data class Evaluation(
-    val deviceUiState: NetworkRequestWrapper<DeviceUIState, Throwable>,
-    val isDeviceRegistered: Boolean,
-    val hasTestResultBeenSeen: Boolean
-) {
-
-    fun isUnregistered(): Boolean = !isDeviceRegistered
-
-    fun isTestResultReady(): Boolean = deviceUiState.withSuccess(false) {
-        when (it) {
-            DeviceUIState.PAIRED_POSITIVE,
-            DeviceUIState.PAIRED_POSITIVE_TELETAN -> !hasTestResultBeenSeen
-            else -> false
-        }
-    }
-
-    fun isFetching(): Boolean =
-        isDeviceRegistered && when (deviceUiState) {
-            is NetworkRequestWrapper.RequestFailed -> false
-            is NetworkRequestWrapper.RequestStarted -> true
-            is NetworkRequestWrapper.RequestIdle -> true
-            else -> false
-        }
-
-    fun isResultPositive(): Boolean =
-        deviceUiState.withSuccess(false) {
-            when (it) {
-                DeviceUIState.PAIRED_POSITIVE, DeviceUIState.PAIRED_POSITIVE_TELETAN -> hasTestResultBeenSeen
-                else -> false
-            }
-        }
-
-    fun isResultNegative(): Boolean =
-        deviceUiState.withSuccess(false) {
-            when (it) {
-                DeviceUIState.PAIRED_NEGATIVE -> true
-                else -> false
-            }
-        }
-
-    fun isSubmissionDone(): Boolean =
-        when (deviceUiState) {
-            is NetworkRequestWrapper.RequestSuccessful -> deviceUiState.data == DeviceUIState.SUBMITTED_FINAL
-            else -> false
-        }
-
-    fun isInvalid(): Boolean =
-        isDeviceRegistered && when (deviceUiState) {
-            is NetworkRequestWrapper.RequestFailed -> deviceUiState.error !is CwaServerError
-            is NetworkRequestWrapper.RequestSuccessful -> deviceUiState.data == DeviceUIState.PAIRED_REDEEMED
-            else -> false
-        }
-
-    fun isError(): Boolean =
-        deviceUiState.withSuccess(false) {
-            when (it) {
-                DeviceUIState.PAIRED_ERROR -> true
-                else -> false
-            }
-        }
-
-    fun isPending(): Boolean =
-        when (deviceUiState) {
-            is NetworkRequestWrapper.RequestFailed -> true
-            is NetworkRequestWrapper.RequestSuccessful -> {
-                deviceUiState.data == DeviceUIState.PAIRED_ERROR ||
-                    deviceUiState.data == DeviceUIState.PAIRED_NO_RESULT
-            }
-            else -> false
-        }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt
index 90eee46fc2409ca41f3290abe4ff144c455e91b9..f7e353433494fd402f0efd26667a55172fc41dbb 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt
@@ -5,6 +5,16 @@ import de.rki.coronawarnapp.coronatest.TestRegistrationRequest
 import de.rki.coronawarnapp.coronatest.execution.TestResultScheduler
 import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_INVALID
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_NEGATIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_OR_RAT_PENDING
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_POSITIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_REDEEMED
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_INVALID
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_NEGATIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_PENDING
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED
 import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor
@@ -16,6 +26,7 @@ import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.http.CwaWebException
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.util.TimeStamper
+import org.joda.time.Instant
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -58,23 +69,19 @@ class PCRProcessor @Inject constructor(
         response: CoronaTestService.RegistrationData
     ): PCRCoronaTest {
         analyticsKeySubmissionCollector.reset()
-        response.testResult.validOrThrow()
 
-        testResultDataCollector.updatePendingTestResultReceivedTime(response.testResult)
+        val testResult = response.testResult.validOrThrow()
 
-        if (response.testResult == CoronaTestResult.PCR_POSITIVE) {
+        testResultDataCollector.updatePendingTestResultReceivedTime(testResult)
+
+        if (testResult == PCR_POSITIVE) {
             analyticsKeySubmissionCollector.reportPositiveTestResultReceived()
             deadmanNotificationScheduler.cancelScheduledWork()
         }
 
         analyticsKeySubmissionCollector.reportTestRegistered()
 
-//        val currentTime = timeStamper.nowUTC
-//        submissionSettings.initialTestResultReceivedAt = currentTime
-//        testResultReceivedDateFlowInternal.value = currentTime.toDate()
-        if (response.testResult == CoronaTestResult.PCR_OR_RAT_PENDING) {
-//            riskWorkScheduler.setPeriodicRiskCalculation(enabled = true)
-
+        if (testResult == PCR_OR_RAT_PENDING) {
             testResultScheduler.setPeriodicTestPolling(enabled = true)
         }
 
@@ -82,7 +89,8 @@ class PCRProcessor @Inject constructor(
             identifier = request.identifier,
             registeredAt = timeStamper.nowUTC,
             registrationToken = response.registrationToken,
-            testResult = response.testResult,
+            testResult = testResult,
+            testResultReceivedAt = determineReceivedDate(null, testResult),
         )
     }
 
@@ -91,25 +99,26 @@ class PCRProcessor @Inject constructor(
             Timber.tag(TAG).v("pollServer(test=%s)", test)
             test as PCRCoronaTest
 
-            if (test.isSubmitted || test.isSubmissionAllowed) {
-                Timber.tag(TAG).w("Not refreshing already final test.")
+            if (test.isSubmitted) {
+                Timber.tag(TAG).w("Not refreshing, we have already submitted.")
                 return test
             }
 
-            val testResult = submissionService.asyncRequestTestResult(test.registrationToken)
-            Timber.tag(TAG).d("Test result was %s", testResult)
+            val newTestResult = submissionService.asyncRequestTestResult(test.registrationToken)
+            Timber.tag(TAG).d("Test result was %s", newTestResult)
 
-            testResult.validOrThrow()
+            newTestResult.validOrThrow()
 
-            testResultDataCollector.updatePendingTestResultReceivedTime(testResult)
+            testResultDataCollector.updatePendingTestResultReceivedTime(newTestResult)
 
-            if (testResult == CoronaTestResult.PCR_POSITIVE) {
+            if (newTestResult == PCR_POSITIVE) {
                 analyticsKeySubmissionCollector.reportPositiveTestResultReceived()
                 deadmanNotificationScheduler.cancelScheduledWork()
             }
 
             test.copy(
-                testResult = testResult,
+                testResult = newTestResult,
+                testResultReceivedAt = determineReceivedDate(test, newTestResult),
                 lastError = null
             )
         } catch (e: Exception) {
@@ -121,6 +130,12 @@ class PCRProcessor @Inject constructor(
         }
     }
 
+    private fun determineReceivedDate(oldTest: PCRCoronaTest?, newTestResult: CoronaTestResult): Instant? = when {
+        oldTest != null && FINAL_STATES.contains(oldTest.testResult) -> oldTest.testResultReceivedAt
+        FINAL_STATES.contains(newTestResult) -> timeStamper.nowUTC
+        else -> null
+    }
+
     override suspend fun onRemove(toBeRemoved: CoronaTest) {
         Timber.tag(TAG).v("onRemove(toBeRemoved=%s)", toBeRemoved)
         testResultDataCollector.clear()
@@ -162,24 +177,26 @@ class PCRProcessor @Inject constructor(
     }
 
     companion object {
+        private val FINAL_STATES = setOf(PCR_POSITIVE, PCR_NEGATIVE, PCR_REDEEMED)
         private const val TAG = "PCRProcessor"
     }
 }
 
-private fun CoronaTestResult.validOrThrow() {
+private fun CoronaTestResult.validOrThrow(): CoronaTestResult {
     val isValid = when (this) {
-        CoronaTestResult.PCR_OR_RAT_PENDING,
-        CoronaTestResult.PCR_NEGATIVE,
-        CoronaTestResult.PCR_POSITIVE,
-        CoronaTestResult.PCR_INVALID,
-        CoronaTestResult.PCR_REDEEMED -> true
-
-        CoronaTestResult.RAT_PENDING,
-        CoronaTestResult.RAT_NEGATIVE,
-        CoronaTestResult.RAT_POSITIVE,
-        CoronaTestResult.RAT_INVALID,
-        CoronaTestResult.RAT_REDEEMED -> false
+        PCR_OR_RAT_PENDING,
+        PCR_NEGATIVE,
+        PCR_POSITIVE,
+        PCR_INVALID,
+        PCR_REDEEMED -> true
+
+        RAT_PENDING,
+        RAT_NEGATIVE,
+        RAT_POSITIVE,
+        RAT_INVALID,
+        RAT_REDEEMED -> false
     }
 
     if (!isValid) throw IllegalArgumentException("Invalid testResult $this")
+    return this
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/SubmissionStatePCR.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/SubmissionStatePCR.kt
index 2a45ebb8a01fbd08b4ef2e0568163986539c28f0..3e1335c76d5b33f87fb212e7b7b451ee8b3b53b5 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/SubmissionStatePCR.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/SubmissionStatePCR.kt
@@ -6,13 +6,25 @@ import org.joda.time.Instant
 sealed class SubmissionStatePCR {
 
     object NoTest : SubmissionStatePCR(), CommonSubmissionStates.TestUnregistered
+
     object FetchingResult : SubmissionStatePCR(), CommonSubmissionStates.TestFetching
+
     object TestResultReady : SubmissionStatePCR()
-    object TestPositive : SubmissionStatePCR()
-    object TestNegative : SubmissionStatePCR()
+
+    data class TestPositive(
+        override val testRegisteredAt: Instant
+    ) : SubmissionStatePCR(), CommonSubmissionStates.PositiveTest
+
+    data class TestNegative(
+        override val testRegisteredAt: Instant
+    ) : SubmissionStatePCR(), CommonSubmissionStates.NegativeTest
+
     object TestError : SubmissionStatePCR()
+
     object TestInvalid : SubmissionStatePCR()
+
     object TestPending : SubmissionStatePCR()
+
     data class SubmissionDone(
         override val testRegisteredAt: Instant
     ) : SubmissionStatePCR(), CommonSubmissionStates.SubmissionDone
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt
index 7cdf2cf5bcb094d3c11235626e7b2288a1737d3b..75216e43738f04e2cc2ad2f2943aca55b7cc5b51 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RACoronaTest.kt
@@ -57,12 +57,18 @@ data class RACoronaTest(
 
     override val type: CoronaTest.Type = CoronaTest.Type.RAPID_ANTIGEN
 
-    fun getState(nowUTC: Instant): State {
-        // TODO
-        return State.PENDING
+    fun getState(nowUTC: Instant) = when (testResult) {
+        CoronaTestResult.PCR_OR_RAT_PENDING -> State.PENDING
+        CoronaTestResult.RAT_NEGATIVE -> State.NEGATIVE
+        CoronaTestResult.RAT_POSITIVE -> State.POSITIVE
+        CoronaTestResult.RAT_INVALID -> State.INVALID
+        CoronaTestResult.RAT_REDEEMED -> State.REDEEMED
+        else -> throw IllegalArgumentException("Invalid RAT test state $testResult")
     }
 
-    override val isSubmissionAllowed: Boolean = testResult == CoronaTestResult.RAT_POSITIVE
+    override val isPositive: Boolean = testResult == CoronaTestResult.RAT_POSITIVE
+
+    override val isSubmissionAllowed: Boolean = isPositive && !isSubmitted
 
     enum class State {
         PENDING,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenCoronaTestExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenCoronaTestExtensions.kt
index fa8ad42e982d75d0e766afd6ac74c2c7fe7adca5..e11e53071d35d16b9de90e79122d566464b5cd75 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenCoronaTestExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenCoronaTestExtensions.kt
@@ -1,7 +1,36 @@
 package de.rki.coronawarnapp.coronatest.type.rapidantigen
 
-fun RACoronaTest?.toSubmissionState(): SubmissionStateRAT {
-    if (this == null) return SubmissionStateRAT.NoTest
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.INVALID
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.NEGATIVE
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.OUTDATED
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.PENDING
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.POSITIVE
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest.State.REDEEMED
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.FetchingResult
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.NoTest
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestError
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestInvalid
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestNegative
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestPending
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestPositive
+import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT.TestResultReady
+import de.rki.coronawarnapp.exception.http.CwaServerError
+import org.joda.time.Instant
 
-    return SubmissionStateRAT.FetchingResult
+fun RACoronaTest?.toSubmissionState(nowUTC: Instant = Instant.now()) = when {
+    this == null -> NoTest
+    isProcessing -> FetchingResult
+    lastError != null -> if (lastError is CwaServerError) TestPending else TestInvalid
+    else -> when (getState(nowUTC)) {
+        INVALID -> TestError
+        POSITIVE -> {
+            if (isViewed) TestPositive(testRegisteredAt = registeredAt)
+            else TestResultReady
+        }
+        NEGATIVE -> TestNegative(testRegisteredAt = registeredAt)
+        REDEEMED -> TestInvalid
+        PENDING -> TestPending
+        // TODO: Should be updated once the logic for OUTDATED tests is in
+        OUTDATED -> TestNegative(testRegisteredAt = registeredAt)
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt
index 6052fa0a06cc18df4e07161d807c579f3247792e..482f6af98084cf7c6ec736116954d8955a8c3d9d 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessor.kt
@@ -3,6 +3,16 @@ package de.rki.coronawarnapp.coronatest.type.rapidantigen
 import dagger.Reusable
 import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_INVALID
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_NEGATIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_OR_RAT_PENDING
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_POSITIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.PCR_REDEEMED
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_INVALID
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_NEGATIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_PENDING
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_POSITIVE
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.RAT_REDEEMED
 import de.rki.coronawarnapp.coronatest.tan.CoronaTestTAN
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor
@@ -11,6 +21,7 @@ import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.http.CwaWebException
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.util.TimeStamper
+import org.joda.time.Instant
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -28,13 +39,14 @@ class RapidAntigenProcessor @Inject constructor(
 
         val registrationData = submissionService.asyncRegisterDeviceViaGUID(request.registrationIdentifier)
 
-        registrationData.testResult.validOrThrow()
+        val testResult = registrationData.testResult.validOrThrow()
 
         return RACoronaTest(
             identifier = request.identifier,
             registeredAt = timeStamper.nowUTC,
             registrationToken = registrationData.registrationToken,
-            testResult = registrationData.testResult,
+            testResult = testResult,
+            testResultReceivedAt = determineReceivedDate(null, testResult),
             testedAt = request.createdAt,
             firstName = request.firstName,
             lastName = request.lastName,
@@ -48,13 +60,19 @@ class RapidAntigenProcessor @Inject constructor(
         throw UnsupportedOperationException("There are no TAN based RATs")
     }
 
+    private fun determineReceivedDate(oldTest: RACoronaTest?, newTestResult: CoronaTestResult): Instant? = when {
+        oldTest != null && FINAL_STATES.contains(oldTest.testResult) -> oldTest.testResultReceivedAt
+        FINAL_STATES.contains(newTestResult) -> timeStamper.nowUTC
+        else -> null
+    }
+
     override suspend fun pollServer(test: CoronaTest): CoronaTest {
         return try {
             Timber.tag(TAG).v("pollServer(test=%s)", test)
             test as RACoronaTest
 
-            if (test.isSubmitted || test.isSubmissionAllowed) {
-                Timber.tag(TAG).w("Not refreshing already final test.")
+            if (test.isSubmitted) {
+                Timber.tag(TAG).w("Not refreshing, we have already submitted.")
                 return test
             }
 
@@ -115,24 +133,26 @@ class RapidAntigenProcessor @Inject constructor(
     }
 
     companion object {
+        private val FINAL_STATES = setOf(RAT_POSITIVE, RAT_NEGATIVE, RAT_REDEEMED)
         private const val TAG = "RapidAntigenProcessor"
     }
 }
 
-private fun CoronaTestResult.validOrThrow() {
+private fun CoronaTestResult.validOrThrow(): CoronaTestResult {
     val isValid = when (this) {
-        CoronaTestResult.PCR_OR_RAT_PENDING,
-        CoronaTestResult.RAT_PENDING,
-        CoronaTestResult.RAT_NEGATIVE,
-        CoronaTestResult.RAT_POSITIVE,
-        CoronaTestResult.RAT_INVALID,
-        CoronaTestResult.RAT_REDEEMED -> true
-
-        CoronaTestResult.PCR_NEGATIVE,
-        CoronaTestResult.PCR_POSITIVE,
-        CoronaTestResult.PCR_INVALID,
-        CoronaTestResult.PCR_REDEEMED -> false
+        PCR_OR_RAT_PENDING,
+        RAT_PENDING,
+        RAT_NEGATIVE,
+        RAT_POSITIVE,
+        RAT_INVALID,
+        RAT_REDEEMED -> true
+
+        PCR_NEGATIVE,
+        PCR_POSITIVE,
+        PCR_INVALID,
+        PCR_REDEEMED -> false
     }
 
     if (!isValid) throw IllegalArgumentException("Invalid testResult $this")
+    return this
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/SubmissionStateRAT.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/SubmissionStateRAT.kt
index bbb6cd6474fb89385c762be591e47a486df038e3..8069a5d4d963f954333d0b22034c1204cacf5f68 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/SubmissionStateRAT.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/SubmissionStateRAT.kt
@@ -6,13 +6,25 @@ import org.joda.time.Instant
 sealed class SubmissionStateRAT {
 
     object NoTest : SubmissionStateRAT(), CommonSubmissionStates.TestUnregistered
+
     object FetchingResult : SubmissionStateRAT(), CommonSubmissionStates.TestFetching
+
     object TestResultReady : SubmissionStateRAT()
-    object TestPositive : SubmissionStateRAT()
-    object TestNegative : SubmissionStateRAT()
+
+    data class TestPositive(
+        override val testRegisteredAt: Instant
+    ) : SubmissionStateRAT(), CommonSubmissionStates.PositiveTest
+
+    data class TestNegative(
+        override val testRegisteredAt: Instant
+    ) : SubmissionStateRAT(), CommonSubmissionStates.NegativeTest
+
     object TestError : SubmissionStateRAT()
+
     object TestInvalid : SubmissionStateRAT()
+
     object TestPending : SubmissionStateRAT()
+
     data class SubmissionDone(
         override val testRegisteredAt: Instant
     ) : SubmissionStateRAT(), CommonSubmissionStates.SubmissionDone
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
index 0375e974226341dae587918b107e4e51374622d0..bea984361be38b2b0ce3e29aa119d48f5f46766e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
@@ -47,7 +47,7 @@ class DefaultExposureDetectionTracker @Inject constructor(
         val setupTimeoutEnforcer: (HotDataFlow<Map<String, TrackedExposureDetection>>) -> Unit = { hd ->
             flow<Unit> {
                 while (true) {
-                    hd.updateSafely {
+                    hd.updateBlocking {
                         val timeNow = timeStamper.nowUTC
                         Timber.v("Running timeout check (now=%s): %s", timeNow, values)
                         val timeoutLimit = appConfigProvider.currentConfig.first().overallDetectionTimeout
@@ -84,7 +84,7 @@ class DefaultExposureDetectionTracker @Inject constructor(
 
     override fun trackNewExposureDetection(identifier: String) {
         Timber.i("trackNewExposureDetection(token=%s)", identifier)
-        detectionStates.updateSafely {
+        detectionStates.updateAsync {
             mutate {
                 this[identifier] = TrackedExposureDetection(
                     identifier = identifier,
@@ -97,7 +97,7 @@ class DefaultExposureDetectionTracker @Inject constructor(
 
     override fun finishExposureDetection(identifier: String?, result: Result) {
         Timber.i("finishExposureDetection(token=%s, result=%s)", identifier, result)
-        detectionStates.updateSafely {
+        detectionStates.updateAsync {
             mutate {
                 if (identifier == null) {
                     val id = this.findUnfinishedOrCreateIdentifier()
@@ -164,7 +164,7 @@ class DefaultExposureDetectionTracker @Inject constructor(
 
     override fun clear() {
         Timber.i("clear()")
-        detectionStates.updateSafely {
+        detectionStates.updateAsync {
             emptyMap()
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt
index 9c2f731aa42d867ba995c9c0576c63c5aff6d449..5e89ce916308b96724c92ce00219e9c5806a1813 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationConstants.kt
@@ -18,7 +18,8 @@ object NotificationConstants {
 
     const val DEADMAN_NOTIFICATION_ID: NotificationId = 3
     const val NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID: NotificationId = 110
-    const val TEST_RESULT_AVAILABLE_NOTIFICATION_ID: NotificationId = 130
+    const val PCR_TEST_RESULT_AVAILABLE_NOTIFICATION_ID: NotificationId = 130
     const val INCORRECT_DEVICE_TIME_NOTIFICATION_ID: NotificationId = 140
+    const val RAT_TEST_RESULT_AVAILABLE_NOTIFICATION_ID: NotificationId = 150
     const val TRACELOCATION_AUTOCHECKOUT_NOTIFICATION_ID: NotificationId = 1001
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationReceiver.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationReceiver.kt
index ea0021a012cb782da316a10a98716e48d98512ce..b11c004a67e76b40da477cd2382af14a64750e8b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationReceiver.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/NotificationReceiver.kt
@@ -4,6 +4,7 @@ import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import dagger.android.AndroidInjection
+import de.rki.coronawarnapp.coronatest.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.notification.NotificationConstants.NOTIFICATION_ID
 import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_ID
 import timber.log.Timber
@@ -20,7 +21,7 @@ class NotificationReceiver : BroadcastReceiver() {
         when (val notificationId = intent.getIntExtra(NOTIFICATION_ID, Int.MIN_VALUE)) {
             POSITIVE_RESULT_NOTIFICATION_ID -> {
                 Timber.tag(TAG).v("NotificationReceiver received intent to show a positive test result notification")
-                shareTestResultNotificationService.showSharePositiveTestResultNotification(notificationId)
+                shareTestResultNotificationService.maybeShowSharePositiveTestResultNotification(notificationId)
             }
             else ->
                 Timber.tag(TAG).d("NotificationReceiver received an undefined notificationId: %s", notificationId)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/PCRTestResultAvailableNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/PCRTestResultAvailableNotificationService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..15eb8d656cc17073b7ed87bc969b192878fe3c0a
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/PCRTestResultAvailableNotificationService.kt
@@ -0,0 +1,26 @@
+package de.rki.coronawarnapp.notification
+
+import android.content.Context
+import androidx.navigation.NavDeepLinkBuilder
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.main.CWASettings
+import de.rki.coronawarnapp.util.device.ForegroundState
+import de.rki.coronawarnapp.util.di.AppContext
+import javax.inject.Inject
+import javax.inject.Provider
+
+class PCRTestResultAvailableNotificationService @Inject constructor(
+    @AppContext context: Context,
+    foregroundState: ForegroundState,
+    navDeepLinkBuilderProvider: Provider<NavDeepLinkBuilder>,
+    notificationHelper: GeneralNotifications,
+    cwaSettings: CWASettings
+) : TestResultAvailableNotificationService(
+    context,
+    foregroundState,
+    navDeepLinkBuilderProvider,
+    notificationHelper,
+    cwaSettings,
+    NotificationConstants.PCR_TEST_RESULT_AVAILABLE_NOTIFICATION_ID,
+    R.id.submissionTestResultPendingFragment
+)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/RATTestResultAvailableNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/RATTestResultAvailableNotificationService.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2d85badd449cee0bd2c4968fb89e110fec6f82f5
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/RATTestResultAvailableNotificationService.kt
@@ -0,0 +1,26 @@
+package de.rki.coronawarnapp.notification
+
+import android.content.Context
+import androidx.navigation.NavDeepLinkBuilder
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.main.CWASettings
+import de.rki.coronawarnapp.util.device.ForegroundState
+import de.rki.coronawarnapp.util.di.AppContext
+import javax.inject.Inject
+import javax.inject.Provider
+
+class RATTestResultAvailableNotificationService @Inject constructor(
+    @AppContext context: Context,
+    foregroundState: ForegroundState,
+    navDeepLinkBuilderProvider: Provider<NavDeepLinkBuilder>,
+    notificationHelper: GeneralNotifications,
+    cwaSettings: CWASettings
+) : TestResultAvailableNotificationService(
+    context,
+    foregroundState,
+    navDeepLinkBuilderProvider,
+    notificationHelper,
+    cwaSettings,
+    NotificationConstants.RAT_TEST_RESULT_AVAILABLE_NOTIFICATION_ID,
+    R.id.submissionTestResultPendingFragment // TODO check nav args, after other PRs are merged
+)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/ShareTestResultNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/ShareTestResultNotificationService.kt
deleted file mode 100644
index fb02d7f334100194376287286f30f75d19b52938..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/ShareTestResultNotificationService.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-package de.rki.coronawarnapp.notification
-
-import android.content.Context
-import androidx.navigation.NavDeepLinkBuilder
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.main.CWASettings
-import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_ID
-import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_INITIAL_OFFSET
-import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_INTERVAL
-import de.rki.coronawarnapp.notification.NotificationConstants.POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT
-import de.rki.coronawarnapp.ui.main.MainActivity
-import de.rki.coronawarnapp.util.TimeStamper
-import de.rki.coronawarnapp.util.di.AppContext
-import de.rki.coronawarnapp.util.notifications.setContentTextExpandable
-import timber.log.Timber
-import javax.inject.Inject
-
-class ShareTestResultNotificationService @Inject constructor(
-    @AppContext private val context: Context,
-    private val timeStamper: TimeStamper,
-    private val notificationHelper: GeneralNotifications,
-    private val cwaSettings: CWASettings
-) {
-
-    fun scheduleSharePositiveTestResultReminder() {
-        if (cwaSettings.numberOfRemainingSharePositiveTestResultReminders < 0) {
-            Timber.v("Schedule positive test result notification")
-            cwaSettings.numberOfRemainingSharePositiveTestResultReminders = POSITIVE_RESULT_NOTIFICATION_TOTAL_COUNT
-            notificationHelper.scheduleRepeatingNotification(
-                timeStamper.nowUTC.plus(POSITIVE_RESULT_NOTIFICATION_INITIAL_OFFSET),
-                POSITIVE_RESULT_NOTIFICATION_INTERVAL,
-                POSITIVE_RESULT_NOTIFICATION_ID
-            )
-        } else {
-            Timber.v("Positive test result notification has already been scheduled")
-        }
-    }
-
-    fun showSharePositiveTestResultNotification(notificationId: Int) {
-        if (cwaSettings.numberOfRemainingSharePositiveTestResultReminders > 0) {
-            cwaSettings.numberOfRemainingSharePositiveTestResultReminders -= 1
-
-            val pendingIntent = NavDeepLinkBuilder(context)
-                .setGraph(R.navigation.nav_graph)
-                .setComponentName(MainActivity::class.java)
-                .setDestination(R.id.submissionTestResultAvailableFragment)
-                .createPendingIntent()
-
-            val notification = notificationHelper.newBaseBuilder().apply {
-                setContentTitle(context.getString(R.string.notification_headline_share_positive_result))
-                setContentTextExpandable(context.getString(R.string.notification_body_share_positive_result))
-                setContentIntent(pendingIntent)
-            }.build()
-
-            notificationHelper.sendNotification(
-                notificationId = notificationId,
-                notification = notification,
-            )
-        } else {
-            notificationHelper.cancelFutureNotifications(notificationId)
-        }
-    }
-
-    fun cancelSharePositiveTestResultNotification() {
-        notificationHelper.cancelFutureNotifications(POSITIVE_RESULT_NOTIFICATION_ID)
-        Timber.v("Future positive test result notifications have been canceled")
-    }
-
-    fun resetSharePositiveTestResultNotification() {
-        cancelSharePositiveTestResultNotification()
-        cwaSettings.numberOfRemainingSharePositiveTestResultReminders = Int.MIN_VALUE
-        Timber.v("Positive test result notification counter has been reset")
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationService.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationService.kt
index c0b7a2943500af8bcc434d11c042a15bfb5f7de5..db751afd8659e75c4e6c9619f7b86ba60692742a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationService.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationService.kt
@@ -1,25 +1,26 @@
 package de.rki.coronawarnapp.notification
 
 import android.content.Context
+import androidx.annotation.IdRes
 import androidx.navigation.NavDeepLinkBuilder
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.util.device.ForegroundState
-import de.rki.coronawarnapp.util.di.AppContext
 import de.rki.coronawarnapp.util.notifications.setContentTextExpandable
 import kotlinx.coroutines.flow.first
 import timber.log.Timber
-import javax.inject.Inject
 import javax.inject.Provider
 
-class TestResultAvailableNotificationService @Inject constructor(
-    @AppContext private val context: Context,
+open class TestResultAvailableNotificationService(
+    private val context: Context,
     private val foregroundState: ForegroundState,
     private val navDeepLinkBuilderProvider: Provider<NavDeepLinkBuilder>,
     private val notificationHelper: GeneralNotifications,
-    private val cwaSettings: CWASettings
+    private val cwaSettings: CWASettings,
+    private val notificationId: NotificationId,
+    @IdRes private val destination: Int
 ) {
 
     suspend fun showTestResultAvailableNotification(testResult: CoronaTestResult) {
@@ -38,7 +39,16 @@ class TestResultAvailableNotificationService @Inject constructor(
         val pendingIntent = navDeepLinkBuilderProvider.get().apply {
             setGraph(R.navigation.nav_graph)
             setComponentName(MainActivity::class.java)
-            setDestination(getNotificationDestination(testResult))
+            /*
+             * The pending result fragment will forward to the correct screen
+             * Because we can't save the test result at the moment (legal),
+             * it needs to be reloaded each time.
+             * If we navigate directly to the positive/negative result screen,
+             * then we also need to add explicit test loading logic there.
+             * By letting the forwarding happen via the PendingResultFragment,
+             * we have a common location to retrieve the test result.
+             */
+            setDestination(destination)
         }.createPendingIntent()
 
         val notification = notificationHelper.newBaseBuilder().apply {
@@ -49,23 +59,12 @@ class TestResultAvailableNotificationService @Inject constructor(
 
         Timber.i("Showing TestResultAvailable notification!")
         notificationHelper.sendNotification(
-            notificationId = NotificationConstants.TEST_RESULT_AVAILABLE_NOTIFICATION_ID,
+            notificationId = notificationId,
             notification = notification,
         )
     }
 
     fun cancelTestResultAvailableNotification() {
-        notificationHelper.cancelCurrentNotification(NotificationConstants.TEST_RESULT_AVAILABLE_NOTIFICATION_ID)
+        notificationHelper.cancelCurrentNotification(notificationId)
     }
-
-    /**
-     * The pending result fragment will forward to the correct screen
-     * Because we can't save the test result at the moment (legal),
-     * it needs to be reloaded each time.
-     * If we navigate directly to the positive/negative result screen,
-     * then we also need to add explicit test loading logic there.
-     * By letting the forwarding happen via the PendingResultFragment,
-     * we have a common location to retrieve the test result.
-     */
-    fun getNotificationDestination(testResult: CoronaTestResult): Int = R.id.submissionTestResultPendingFragment
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt
index 3d254d25e69e67155345cda31c084c9238453d27..87736b0e50e144c7a1daa9f2cb11600f3c0a9620 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/statistics/source/StatisticsProvider.kt
@@ -78,14 +78,10 @@ class StatisticsProvider @Inject constructor(
 
     fun triggerUpdate() {
         Timber.tag(TAG).d("triggerUpdate()")
-        statisticsData.updateSafely {
-            try {
-                fromServer()
-            } catch (e: Exception) {
-                Timber.tag(TAG).e(e, "Failed to update statistics.")
-                this@updateSafely // return previous data
-            }
-        }
+        statisticsData.updateAsync(
+            onUpdate = { fromServer() },
+            onError = { Timber.tag(TAG).e(it, "Failed to update statistics.") }
+        )
     }
 
     fun clear() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/task/SubmissionTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/task/SubmissionTask.kt
index 49c162c39401af971dd5e0135121b419c30f55f2..85f17678359c8ef09b11ae6427017d2ff3c819a6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/task/SubmissionTask.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/task/SubmissionTask.kt
@@ -4,10 +4,10 @@ import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.bugreporting.reportProblem
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.playbook.Playbook
 import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository
 import de.rki.coronawarnapp.presencetracing.checkins.CheckInsTransformer
@@ -41,7 +41,7 @@ class SubmissionTask @Inject constructor(
     private val autoSubmission: AutoSubmission,
     private val timeStamper: TimeStamper,
     private val shareTestResultNotificationService: ShareTestResultNotificationService,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService,
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService,
     private val checkInsRepository: CheckInRepository,
     private val checkInsTransformer: CheckInsTransformer,
     private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector,
@@ -129,7 +129,7 @@ class SubmissionTask @Inject constructor(
     private suspend fun performSubmission(): Result {
         val availableTests = coronaTestRepository.coronaTests.first()
         Timber.tag(TAG).v("Available tests: %s", availableTests)
-        val coronaTest = availableTests.firstOrNull { it.isSubmissionAllowed && !it.isSubmitted }
+        val coronaTest = availableTests.firstOrNull { it.isSubmissionAllowed }
             ?: throw IllegalStateException("No valid test available to authorize submission")
 
         Timber.tag(TAG).d("Submission is authorized by coronaTest=%s", coronaTest)
@@ -199,7 +199,6 @@ class SubmissionTask @Inject constructor(
         coronaTestRepository.markAsSubmitted(coronaTest.identifier)
         backgroundWorkScheduler.startWorkScheduler()
 
-        shareTestResultNotificationService.cancelSharePositiveTestResultNotification()
         testResultAvailableNotificationService.cancelTestResultAvailableNotification()
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestNegativeCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestNegativeCard.kt
index b9ae26372a44fea01df80384b7debbe5777396ab..2441c9d54171e06dc09832f61f17fa54bce6de3f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestNegativeCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestNegativeCard.kt
@@ -26,10 +26,18 @@ class PcrTestNegativeCard(
     override val onBindData: HomeSubmissionPcrStatusCardNegativeBinding.(
         item: Item,
         payloads: List<Any>
-    ) -> Unit = { _, _ -> }
+    ) -> Unit = { item, payloads ->
+        val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_pcr_body_result_date, userDate)
+
+        itemView.setOnClickListener { curItem.onClickAction(item) }
+    }
 
     data class Item(
-        val state: SubmissionStatePCR.TestNegative
+        val state: SubmissionStatePCR.TestNegative,
+        val onClickAction: (Item) -> Unit
     ) : TestResultItem.PCR, HasPayloadDiffer {
         override fun diffPayload(old: Any, new: Any): Any? = if (old::class == new::class) new else null
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestPositiveCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestPositiveCard.kt
index 327ea83f7ae04c1f08fd1b950e8950beca79e9d7..5d52d9acdaaf1c2ebe84b0f9a60e2380357cbef9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestPositiveCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestPositiveCard.kt
@@ -28,6 +28,10 @@ class PcrTestPositiveCard(
         payloads: List<Any>
     ) -> Unit = { item, payloads ->
         val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_pcr_body_result_date, userDate)
+
         itemView.setOnClickListener { curItem.onClickAction(item) }
         submissionStatusCardPositiveButton.setOnClickListener { itemView.performClick() }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestSubmissionDoneCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestSubmissionDoneCard.kt
index 34ecfef89a27bc2c69769620493dc956f9eb07d6..9cb260b99330b0006922b70d69f3d6ec14cd3960 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestSubmissionDoneCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/PcrTestSubmissionDoneCard.kt
@@ -22,7 +22,12 @@ class PcrTestSubmissionDoneCard(
     override val onBindData: HomeSubmissionPcrStatusCardPositiveSharedBinding.(
         item: Item,
         payloads: List<Any>
-    ) -> Unit = { _, _ -> }
+    ) -> Unit = { item, payloads ->
+        val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_pcr_body_result_date, userDate)
+    }
 
     data class Item(
         val state: SubmissionStatePCR.SubmissionDone
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestNegativeCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestNegativeCard.kt
index f8ae43ad4edb42a2d070f5cb21f3e250c36c8490..930aeef2b4789426a8de1809b6fa1f701aaf15b3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestNegativeCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestNegativeCard.kt
@@ -26,10 +26,18 @@ class RapidTestNegativeCard(
     override val onBindData: HomeSubmissionRapidStatusCardNegativeBinding.(
         item: Item,
         payloads: List<Any>
-    ) -> Unit = { _, _ -> }
+    ) -> Unit = { item, payloads ->
+        val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_rapid_body_result_date, userDate)
+
+        itemView.setOnClickListener { curItem.onClickAction(item) }
+    }
 
     data class Item(
-        val state: SubmissionStateRAT.TestNegative
+        val state: SubmissionStateRAT.TestNegative,
+        val onClickAction: (Item) -> Unit
     ) : TestResultItem.RA, HasPayloadDiffer {
         override fun diffPayload(old: Any, new: Any): Any? = if (old::class == new::class) new else null
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestPositiveCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestPositiveCard.kt
index e9c021499f4ee97962892bbbd762a18e92d7d77e..4b244824e66be08097bea31ec4f26035307f8c82 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestPositiveCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestPositiveCard.kt
@@ -28,6 +28,10 @@ class RapidTestPositiveCard(
         payloads: List<Any>
     ) -> Unit = { item, payloads ->
         val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_rapid_body_result_date, userDate)
+
         itemView.setOnClickListener { curItem.onClickAction(item) }
         submissionStatusCardPositiveButton.setOnClickListener { itemView.performClick() }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestSubmissionDoneCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestSubmissionDoneCard.kt
index a96d2a531c24f998838019bf19bfa1cbfe1f5f09..c6e3866a09b30c10f864d7b732150a7f7c16c0fe 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestSubmissionDoneCard.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/RapidTestSubmissionDoneCard.kt
@@ -22,7 +22,12 @@ class RapidTestSubmissionDoneCard(
     override val onBindData: HomeSubmissionRapidStatusCardPositiveSharedBinding.(
         item: Item,
         payloads: List<Any>
-    ) -> Unit = { _, _ -> }
+    ) -> Unit = { item, payloads ->
+        val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item
+
+        val userDate = curItem.state.getFormattedRegistrationDate()
+        date.text = resources.getString(R.string.ag_homescreen_card_rapid_body_result_date, userDate)
+    }
 
     data class Item(
         val state: SubmissionStateRAT.SubmissionDone
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/TestResultItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/TestResultItem.kt
index 2271042d492467547db54f49f2825fb3e875f328..6d7e4ec9f88c0db0a88e0bc240db90fef47bf0a0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/TestResultItem.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/homecards/TestResultItem.kt
@@ -1,6 +1,8 @@
 package de.rki.coronawarnapp.submission.ui.homecards
 
 import de.rki.coronawarnapp.ui.main.home.items.HomeItem
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone
+import org.joda.time.Instant
 
 interface TestResultItem : HomeItem {
     override val stableId: Long
@@ -23,4 +25,6 @@ interface TestResultItem : HomeItem {
             val LIST_ID = PCR::class.java.name.hashCode().toLong()
         }
     }
+
+    fun Instant.formatAsUserTestRegisteredAt(): String = toUserTimeZone().toLocalDate().toString("dd.MM.yy")
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..082b63819b5f409c2ec72fd4a650f0621c337545
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeFragment.kt
@@ -0,0 +1,24 @@
+package de.rki.coronawarnapp.submission.ui.testresults.negative
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.databinding.FragmentSubmissionAntigenTestResultNegativeBinding
+import de.rki.coronawarnapp.util.di.AutoInject
+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 RATResultNegativeFragment : Fragment(R.layout.fragment_submission_antigen_test_result_negative), AutoInject {
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: RATResultNegativeViewModel by cwaViewModels { viewModelFactory }
+
+    private val binding: FragmentSubmissionAntigenTestResultNegativeBinding by viewBindingLazy()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        // TODO: Add fragment logic and databinding.
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8476430efdc7413e96120d25db842fc5db33dad1
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeModule.kt
@@ -0,0 +1,18 @@
+package de.rki.coronawarnapp.submission.ui.testresults.negative
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
+
+@Module
+abstract class RATResultNegativeModule {
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(RATResultNegativeViewModel::class)
+    abstract fun ratResultNegativeFragment(
+        factory: RATResultNegativeViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ee69cae57e7ddec41d54d482494044998780a738
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/submission/ui/testresults/negative/RATResultNegativeViewModel.kt
@@ -0,0 +1,15 @@
+package de.rki.coronawarnapp.submission.ui.testresults.negative
+
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+
+class RATResultNegativeViewModel @AssistedInject constructor(
+    dispatcherProvider: DispatcherProvider
+) : CWAViewModel(dispatcherProvider) {
+
+    @AssistedFactory
+    interface Factory : SimpleCWAViewModelFactory<RATResultNegativeViewModel>
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/TracingExplanationDialog.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/TracingExplanationDialog.kt
index 7ebebc73a22e6aef5ae2753807fa505df2e42540..e5f992ed1a0b94b0c3bed9104ba16104e3c139d9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/TracingExplanationDialog.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/TracingExplanationDialog.kt
@@ -26,7 +26,7 @@ class TracingExplanationDialog @Inject constructor(
             message = "$infoPeriodLogged\n$infoPeriodLoggedAssessment\n\n$infoFAQ",
             positiveButton = context.getString(R.string.errors_generic_button_positive),
             negativeButton = null,
-            cancelable = null,
+            cancelable = false,
             positiveButtonFunction = onPositive,
             negativeButtonFunction = {}
         )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
index f2596fefb4c970957ce572717bbec87fc3df2cb2..ad760fe105b7e6761951322bf58af0442cdbf7da 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
@@ -121,8 +121,6 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject {
             if (!showDialog) return@observe2
             deviceTimeIncorrectDialog.show { viewModel.userHasAcknowledgedIncorrectDeviceTime() }
         }
-
-        viewModel.observeTestResultToSchedulePositiveTestResultReminder()
     }
 
     override fun onResume() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
index 6a761f77735f46cb759bc32d52cf1881e27c17da..ff35f3915795045b9d37d746d8a55c58e1f90ef8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt
@@ -19,13 +19,11 @@ import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT
 import de.rki.coronawarnapp.coronatest.type.rapidantigen.toSubmissionState
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
 import de.rki.coronawarnapp.main.CWASettings
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.statistics.source.StatisticsProvider
 import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard
 import de.rki.coronawarnapp.storage.TracingRepository
 import de.rki.coronawarnapp.storage.TracingSettings
 import de.rki.coronawarnapp.submission.SubmissionRepository
-import de.rki.coronawarnapp.submission.toDeviceUIState
 import de.rki.coronawarnapp.submission.ui.homecards.PcrTestErrorCard
 import de.rki.coronawarnapp.submission.ui.homecards.PcrTestInvalidCard
 import de.rki.coronawarnapp.submission.ui.homecards.PcrTestNegativeCard
@@ -63,7 +61,6 @@ import de.rki.coronawarnapp.ui.main.home.items.FAQCard
 import de.rki.coronawarnapp.ui.main.home.items.HomeItem
 import de.rki.coronawarnapp.ui.main.home.items.ReenableRiskCard
 import de.rki.coronawarnapp.ui.presencetracing.organizer.TraceLocationOrganizerSettings
-import de.rki.coronawarnapp.util.DeviceUIState
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.encryptionmigration.EncryptionErrorResetTool
 import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
@@ -72,7 +69,6 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 
@@ -84,7 +80,6 @@ class HomeFragmentViewModel @AssistedInject constructor(
     tracingStateProviderFactory: TracingStateProvider.Factory,
     private val coronaTestRepository: CoronaTestRepository,
     private val tracingRepository: TracingRepository,
-    private val shareTestResultNotificationService: ShareTestResultNotificationService,
     private val submissionRepository: SubmissionRepository,
     private val cwaSettings: CWASettings,
     appConfigProvider: AppConfigProvider,
@@ -181,29 +176,33 @@ class HomeFragmentViewModel @AssistedInject constructor(
         is SubmissionStatePCR.FetchingResult -> TestFetchingCard.Item(state)
         is SubmissionStatePCR.TestResultReady -> PcrTestReadyCard.Item(state) {
             routeToScreen.postValue(
-                HomeFragmentDirections.actionMainFragmentToSubmissionTestResultAvailableFragment()
+                HomeFragmentDirections.actionMainFragmentToSubmissionTestResultAvailableFragment(CoronaTest.Type.PCR)
             )
         }
         is SubmissionStatePCR.TestPositive -> PcrTestPositiveCard.Item(state) {
             routeToScreen.postValue(
                 HomeFragmentDirections
-                    .actionMainFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment()
+                    .actionMainFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment(CoronaTest.Type.PCR)
+            )
+        }
+        is SubmissionStatePCR.TestNegative -> PcrTestNegativeCard.Item(state) {
+            routeToScreen.postValue(
+                HomeFragmentDirections.actionMainFragmentToSubmissionTestResultNegativeFragment()
             )
         }
-        is SubmissionStatePCR.TestNegative -> PcrTestNegativeCard.Item(state)
         is SubmissionStatePCR.TestInvalid -> PcrTestInvalidCard.Item(state) {
             popupEvents.postValue(HomeFragmentEvents.ShowDeleteTestDialog)
         }
         is SubmissionStatePCR.TestError -> PcrTestErrorCard.Item(state) {
             routeToScreen.postValue(
                 HomeFragmentDirections
-                    .actionMainFragmentToSubmissionTestResultPendingFragment()
+                    .actionMainFragmentToSubmissionTestResultPendingFragment(testType = CoronaTest.Type.PCR)
             )
         }
         is SubmissionStatePCR.TestPending -> PcrTestPendingCard.Item(state) {
             routeToScreen.postValue(
                 HomeFragmentDirections
-                    .actionMainFragmentToSubmissionTestResultPendingFragment()
+                    .actionMainFragmentToSubmissionTestResultPendingFragment(testType = CoronaTest.Type.PCR)
             )
         }
         is SubmissionStatePCR.SubmissionDone -> PcrTestSubmissionDoneCard.Item(state)
@@ -211,41 +210,43 @@ class HomeFragmentViewModel @AssistedInject constructor(
 
     private fun RACoronaTest?.toTestCardItem() = when (val state = this.toSubmissionState()) {
         is SubmissionStateRAT.NoTest -> TestUnregisteredCard.Item(state) {
-            // TODO
-//            routeToScreen.postValue(HomeFragmentDirections.actionMainFragmentToSubmissionDispatcher())
+            routeToScreen.postValue(HomeFragmentDirections.actionMainFragmentToSubmissionDispatcher())
         }
         is SubmissionStateRAT.FetchingResult -> TestFetchingCard.Item(state)
         is SubmissionStateRAT.TestResultReady -> RapidTestReadyCard.Item(state) {
-            // TODO
-//            routeToScreen.postValue(
-//                HomeFragmentDirections.actionMainFragmentToSubmissionTestResultAvailableFragment()
-//            )
+            routeToScreen.postValue(
+                HomeFragmentDirections
+                    .actionMainFragmentToSubmissionTestResultAvailableFragment(CoronaTest.Type.RAPID_ANTIGEN)
+            )
         }
         is SubmissionStateRAT.TestPositive -> RapidTestPositiveCard.Item(state) {
-            // TODO
-//            routeToScreen.postValue(
-//                HomeFragmentDirections
-//                    .actionMainFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment()
-//            )
+            routeToScreen.postValue(
+                HomeFragmentDirections
+                    .actionMainFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment(
+                        CoronaTest.Type.RAPID_ANTIGEN
+                    )
+            )
+        }
+        is SubmissionStateRAT.TestNegative -> RapidTestNegativeCard.Item(state) {
+            routeToScreen.postValue(
+                HomeFragmentDirections
+                    .actionMainFragmentToSubmissionNegativeAntigenTestResultFragment()
+            )
         }
-        is SubmissionStateRAT.TestNegative -> RapidTestNegativeCard.Item(state)
         is SubmissionStateRAT.TestInvalid -> RapidTestInvalidCard.Item(state) {
-            // TODO
-//            popupEvents.postValue(HomeFragmentEvents.ShowDeleteTestDialog)
+            popupEvents.postValue(HomeFragmentEvents.ShowDeleteTestDialog)
         }
         is SubmissionStateRAT.TestError -> RapidTestErrorCard.Item(state) {
-            // TODO
-//            routeToScreen.postValue(
-//                HomeFragmentDirections
-//                    .actionMainFragmentToSubmissionTestResultPendingFragment()
-//            )
+            routeToScreen.postValue(
+                HomeFragmentDirections
+                    .actionMainFragmentToSubmissionTestResultPendingFragment(testType = CoronaTest.Type.RAPID_ANTIGEN)
+            )
         }
         is SubmissionStateRAT.TestPending -> RapidTestPendingCard.Item(state) {
-            // TODO
-//            routeToScreen.postValue(
-//                HomeFragmentDirections
-//                    .actionMainFragmentToSubmissionTestResultPendingFragment()
-//            )
+            routeToScreen.postValue(
+                HomeFragmentDirections
+                    .actionMainFragmentToSubmissionTestResultPendingFragment(testType = CoronaTest.Type.RAPID_ANTIGEN)
+            )
         }
         is SubmissionStateRAT.SubmissionDone -> RapidTestSubmissionDoneCard.Item(state)
     }
@@ -270,10 +271,29 @@ class HomeFragmentViewModel @AssistedInject constructor(
                 else -> add(tracingItem)
             }
 
-            add(testPCR.toTestCardItem())
-
-            if (stateRAT != SubmissionStateRAT.NoTest || statePCR != SubmissionStatePCR.NoTest) {
-                add(testRAT.toTestCardItem())
+            // TODO: Would be nice to have a more elegant solution of displaying the result cards in the right order
+            when (statePCR) {
+                SubmissionStatePCR.NoTest -> {
+                    if (stateRAT == SubmissionStateRAT.NoTest) {
+                        add(testPCR.toTestCardItem())
+                    } else {
+                        add(testRAT.toTestCardItem())
+                        add(testPCR.toTestCardItem())
+                    }
+                }
+                else -> {
+                    add(testPCR.toTestCardItem())
+                    if (stateRAT != SubmissionStateRAT.NoTest) {
+                        add(testRAT.toTestCardItem())
+                        add(
+                            TestUnregisteredCard.Item(SubmissionStatePCR.NoTest) {
+                                routeToScreen.postValue(
+                                    HomeFragmentDirections.actionMainFragmentToSubmissionDispatcher()
+                                )
+                            }
+                        )
+                    } else add(testRAT.toTestCardItem())
+                }
             }
 
             bothTestStates.firstOrNull { it is CommonSubmissionStates.SubmissionDone }?.let {
@@ -305,27 +325,14 @@ class HomeFragmentViewModel @AssistedInject constructor(
         .distinctUntilChanged()
         .asLiveData(dispatcherProvider.Default)
 
-    private var isLoweredRiskLevelDialogBeingShown = false
-    fun observeTestResultToSchedulePositiveTestResultReminder() = launch {
-        submissionRepository.pcrTest
-            .first { test ->
-                when {
-                    test == null -> false
-                    test.lastError != null -> false
-                    test.testResult.toDeviceUIState() == DeviceUIState.PAIRED_POSITIVE -> true
-                    test.testResult.toDeviceUIState() == DeviceUIState.PAIRED_POSITIVE_TELETAN -> true
-                    else -> false
-                }
-            }
-            .also { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() }
-    }
-
     fun reenableRiskCalculation() {
         deregisterWarningAccepted()
         deadmanNotificationScheduler.schedulePeriodic()
         refreshRiskResult()
     }
 
+    private var isLoweredRiskLevelDialogBeingShown = false
+
     // TODO only lazy to keep tests going which would break because of LocalData access
     val showLoweredRiskLevelDialog: LiveData<Boolean> by lazy {
         tracingSettings
@@ -364,7 +371,6 @@ class HomeFragmentViewModel @AssistedInject constructor(
 
     fun deregisterWarningAccepted() {
         submissionRepository.removeTestFromDevice(type = CoronaTest.Type.PCR)
-        submissionRepository.refreshTest(type = CoronaTest.Type.PCR)
     }
 
     fun userHasAcknowledgedTheLoweredRiskLevel() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/common/TraceLocationCardHighlightView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/common/TraceLocationCardHighlightView.kt
index 9db690c9c246b6562782953810ff91f623127f4a..83f5d258b41d7733d0295bbb95fa5da39cd9d1e5 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/common/TraceLocationCardHighlightView.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/common/TraceLocationCardHighlightView.kt
@@ -25,8 +25,6 @@ class TraceLocationCardHighlightView @JvmOverloads constructor(
     init {
         LayoutInflater.from(context).inflate(R.layout.trace_location_view_cardhighlight, this, true)
 
-        background = ContextCompat.getDrawable(context, R.drawable.trace_location_view_cardhighlight_background)
-
         context.withStyledAttributes(attrs, R.styleable.TraceLocationHighlightView) {
             val captionText = getText(R.styleable.TraceLocationHighlightView_android_text) ?: ""
             setCaption(captionText.toString())
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateFragment.kt
index 4843c35ccac9c53e05cce240cd9357bdc2e5e88d..64348cc5adb780b0d342e0caeaf8ab0d0c79c9b8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateFragment.kt
@@ -25,9 +25,9 @@ import de.rki.coronawarnapp.util.ui.popBackStack
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
+import org.joda.time.DateTime
 import org.joda.time.Duration
 import org.joda.time.LocalDate
-import org.joda.time.LocalDateTime
 import org.joda.time.LocalTime
 import javax.inject.Inject
 
@@ -121,8 +121,8 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
                     placeInputEdit.setText(it.address)
                 }
                 viewModel.apply {
-                    begin = it.startDate?.let { time -> LocalDateTime(time) }
-                    end = it.endDate?.let { time -> LocalDateTime(time) }
+                    begin = it.startDate?.toDateTime()
+                    end = it.endDate?.toDateTime()
                     checkInLength = Duration.standardMinutes(it.defaultCheckInLengthInMinutes?.toLong() ?: 0L)
                 }
             }
@@ -152,9 +152,9 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
     }
 
     private fun showDatePicker(
-        defaultValue: LocalDateTime?,
-        minConstraint: LocalDateTime? = null,
-        callback: (LocalDateTime) -> Unit
+        defaultValue: DateTime?,
+        minConstraint: DateTime? = null,
+        callback: (DateTime) -> Unit
     ) {
         MaterialDatePicker
             .Builder
@@ -181,7 +181,7 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
             .show(childFragmentManager, DATE_PICKER_TAG)
     }
 
-    private fun showTimePicker(date: LocalDate, hours: Int?, minutes: Int?, callback: (LocalDateTime) -> Unit) {
+    private fun showTimePicker(date: LocalDate, hours: Int?, minutes: Int?, callback: (DateTime) -> Unit) {
         MaterialTimePicker
             .Builder()
             .setTimeFormat(if (is24HourFormat(requireContext())) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H)
@@ -194,7 +194,7 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
             .build()
             .apply {
                 addOnPositiveButtonClickListener {
-                    callback(date.toLocalDateTime(LocalTime(this.hour, this.minute)))
+                    callback(date.toDateTime(LocalTime(this.hour, this.minute)))
                 }
             }
             .show(childFragmentManager, TIME_PICKER_TAG)
@@ -228,8 +228,8 @@ class TraceLocationCreateFragment : Fragment(R.layout.trace_location_create_frag
         savedInstanceState?.getLong(LENGTH_OF_STAY)?.let {
             viewModel.checkInLength = Duration.standardMinutes(it)
         }
-        viewModel.begin = savedInstanceState?.getSerializable(BEGIN) as LocalDateTime?
-        viewModel.end = savedInstanceState?.getSerializable(END) as LocalDateTime?
+        viewModel.begin = savedInstanceState?.getSerializable(BEGIN) as DateTime?
+        viewModel.end = savedInstanceState?.getSerializable(END) as DateTime?
     }
 
     companion object {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt
index 4e862e265e57d3cb0d9cd7c41bb3a8f8405fa179..29976a36cb93c1afb1b495e72df06335aca4aab1 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModel.kt
@@ -19,8 +19,8 @@ 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 org.joda.time.DateTime
 import org.joda.time.Duration
-import org.joda.time.LocalDateTime
 import timber.log.Timber
 import java.util.Locale
 import kotlin.properties.ReadWriteProperty
@@ -42,8 +42,8 @@ class TraceLocationCreateViewModel @AssistedInject constructor(
     var description: String by UpdateDelegateWithDefaultValue("")
     var address: String by UpdateDelegateWithDefaultValue("")
     var checkInLength: Duration by UpdateDelegateWithDefaultValue(Duration.ZERO)
-    var begin: LocalDateTime? by UpdateDelegate()
-    var end: LocalDateTime? by UpdateDelegate()
+    var begin: DateTime? by UpdateDelegate()
+    var end: DateTime? by UpdateDelegate()
 
     init {
         checkInLength = when (category.uiType) {
@@ -111,8 +111,8 @@ class TraceLocationCreateViewModel @AssistedInject constructor(
     private fun String.isTextFormattedCorrectly() = trim().length in 1..100 && !contains('\n')
 
     data class UIState(
-        private val begin: LocalDateTime? = null,
-        private val end: LocalDateTime? = null,
+        private val begin: DateTime? = null,
+        private val end: DateTime? = null,
         private val checkInLength: Duration? = null,
         @StringRes val title: Int,
         val isRequestInProgress: Boolean,
@@ -129,7 +129,7 @@ class TraceLocationCreateViewModel @AssistedInject constructor(
             )
         }
 
-        private fun getFormattedTime(value: LocalDateTime?, locale: Locale) =
+        private fun getFormattedTime(value: DateTime?, locale: Locale) =
             value?.toString("E, ${locale.shortDatePattern()}   HH:mm", locale)
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailFragment.kt
index 2607499099a7fe0790bb63e57acfb8b70dd3a318..3dc3137e1e4bb77feb965150d3a7d7b7a4bd4883 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailFragment.kt
@@ -125,7 +125,7 @@ class QrCodeDetailFragment : Fragment(R.layout.trace_location_organizer_qr_code_
                         requireContext().getString(
                             R.string.trace_location_organizer_detail_item_duration_multiple_days,
                             startTime.toLocalDate().toString("dd.MM.yyyy"),
-                            endTime.toLocalTime().toString("HH:mm"),
+                            startTime.toLocalTime().toString("HH:mm"),
                             endTime.toLocalDate().toString("dd.MM.yyyy"),
                             endTime.toLocalTime().toString("HH:mm")
                         )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailViewModel.kt
index 0a33ec89e6e88a184b0da4018dc37af1e500f67e..e69e39631b58c76d47ac1b3ebe9780bc544c7210 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/presencetracing/organizer/details/QrCodeDetailViewModel.kt
@@ -8,7 +8,7 @@ import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
 import de.rki.coronawarnapp.presencetracing.checkins.qrcode.TraceLocation
-import de.rki.coronawarnapp.presencetracing.storage.repo.DefaultTraceLocationRepository
+import de.rki.coronawarnapp.presencetracing.storage.repo.TraceLocationRepository
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.ui.presencetracing.organizer.category.adapter.category.traceLocationCategories
@@ -24,7 +24,7 @@ class QrCodeDetailViewModel @AssistedInject constructor(
     @Assisted private val traceLocationId: Long,
     private val dispatcher: DispatcherProvider,
     private val qrCodeGenerator: QrCodeGenerator,
-    private val traceLocationRepository: DefaultTraceLocationRepository
+    private val traceLocationRepository: TraceLocationRepository
 ) : CWAViewModel() {
 
     private var traceLocation: TraceLocation? = null
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt
index 14459385664ce333363ac8b4bf4a86769f0f920b..aac4f54a8698348da4a06949c581e571c607289f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt
@@ -6,7 +6,6 @@ import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.util.DataReset
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
@@ -18,7 +17,6 @@ import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
 class SettingsResetViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
     private val dataReset: DataReset,
-    private val shareTestResultNotificationService: ShareTestResultNotificationService,
     private val shortcutsHelper: AppShortcutsHelper,
     private val backgroundWorkScheduler: BackgroundWorkScheduler,
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
@@ -49,7 +47,6 @@ class SettingsResetViewModel @AssistedInject constructor(
                     null
                 )
             }
-            shareTestResultNotificationService.resetSharePositiveTestResultNotification()
 
             dataReset.clearAllLocalData()
             shortcutsHelper.removeAppShortcut()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt
index 8a918bfad256615ff081bfd5d0748bb8a06e4d43..b0ceeaeef6310f2e1ea69c84be527c4c1d6107ad 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragment.kt
@@ -3,19 +3,43 @@ package de.rki.coronawarnapp.ui.submission.deletionwarning
 import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityEvent
+import androidx.core.view.isVisible
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
 import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
+import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.databinding.FragmentSubmissionDeletionWarningBinding
+import de.rki.coronawarnapp.exception.http.BadRequestException
+import de.rki.coronawarnapp.exception.http.CwaClientError
+import de.rki.coronawarnapp.exception.http.CwaServerError
+import de.rki.coronawarnapp.exception.http.CwaWebException
+import de.rki.coronawarnapp.ui.submission.ApiRequestState
+import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents
+import de.rki.coronawarnapp.util.DialogHelper
 import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.SingleLiveEvent
+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 de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
 import javax.inject.Inject
 
 class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_deletion_warning), AutoInject {
 
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val viewModel: SubmissionDeletionWarningFragmentViewModel by cwaViewModels { viewModelFactory }
+
+    private val args by navArgs<SubmissionDeletionWarningFragmentArgs>()
+    val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent()
+
+    private val viewModel: SubmissionDeletionWarningViewModel by cwaViewModelsAssisted(
+        factoryProducer = { viewModelFactory },
+        constructorCall = { factory, _ ->
+            factory as SubmissionDeletionWarningViewModel.Factory
+            factory.create(args.coronaTestQrCode, args.isConsentGiven)
+        }
+    )
     private val binding: FragmentSubmissionDeletionWarningBinding by viewBindingLazy()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -23,14 +47,121 @@ class SubmissionDeletionWarningFragment : Fragment(R.layout.fragment_submission_
 
         binding.apply {
 
-            cancelButton.setOnClickListener { /* TODO */ }
+            when (args.coronaTestQrCode.type) {
+                CoronaTest.Type.PCR -> {
+                    headline.text = getString(R.string.submission_deletion_warning_headline_pcr_test)
+                    body.text = getString(R.string.submission_deletion_warning_body_pcr_test)
+                }
 
-            continueButton.setOnClickListener { /* TODO */ }
+                CoronaTest.Type.RAPID_ANTIGEN -> {
+                    headline.text = getString(R.string.submission_deletion_warning_headline_antigen_test)
+                    body.text = getString(R.string.submission_deletion_warning_body_antigen_test)
+                }
+            }
+
+            continueButton.setOnClickListener {
+                viewModel.deleteExistingAndRegisterNewTest()
+            }
 
-            toolbar.apply {
-                setNavigationOnClickListener { /* TODO */ }
+            toolbar.setNavigationOnClickListener {
+                viewModel.onCancelButtonClick()
             }
         }
+
+        viewModel.showRedeemedTokenWarning.observe2(this) {
+            val dialog = DialogHelper.DialogInstance(
+                requireActivity(),
+                R.string.submission_error_dialog_web_tan_redeemed_title,
+                R.string.submission_error_dialog_web_tan_redeemed_body,
+                R.string.submission_error_dialog_web_tan_redeemed_button_positive
+            )
+
+            DialogHelper.showDialog(dialog)
+
+            navigateToDispatchScreen()
+        }
+
+        viewModel.registrationState.observe2(this) { state ->
+            binding.submissionQrCodeScanSpinner.isVisible = state.apiRequestState == ApiRequestState.STARTED
+
+            if (ApiRequestState.SUCCESS == state.apiRequestState) {
+                if (state.testResult == CoronaTestResult.PCR_POSITIVE) {
+                    viewModel.triggerNavigationToSubmissionTestResultAvailableFragment()
+                } else {
+                    viewModel.triggerNavigationToSubmissionTestResultPendingFragment()
+                }
+            }
+        }
+
+        viewModel.registrationError.observe2(this) {
+            DialogHelper.showDialog(buildErrorDialog(it))
+        }
+
+        viewModel.routeToScreen.observe2(this) {
+            when (it) {
+                SubmissionNavigationEvents.NavigateToConsent -> {
+                    doNavigate(
+                        SubmissionDeletionWarningFragmentDirections
+                            .actionSubmissionDeletionWarningFragmentToSubmissionConsentFragment()
+                    )
+                }
+                is SubmissionNavigationEvents.NavigateToResultAvailableScreen -> {
+                    doNavigate(
+                        SubmissionDeletionWarningFragmentDirections
+                            .actionSubmissionDeletionWarningFragmentToSubmissionTestResultAvailableFragment(
+                                args.coronaTestQrCode.type
+                            )
+                    )
+                }
+                is SubmissionNavigationEvents.NavigateToResultPendingScreen -> {
+                    doNavigate(
+                        SubmissionDeletionWarningFragmentDirections
+                            .actionSubmissionDeletionWarningFragmentToSubmissionTestResultPendingFragment(
+                                testType = args.coronaTestQrCode.type
+                            )
+                    )
+                }
+            }
+        }
+    }
+
+    private fun navigateToDispatchScreen() =
+        doNavigate(
+            SubmissionDeletionWarningFragmentDirections
+                .actionSubmissionDeletionWarningFragmentToSubmissionDispatcherFragment()
+        )
+
+    private fun buildErrorDialog(exception: CwaWebException): DialogHelper.DialogInstance {
+        return when (exception) {
+            is BadRequestException -> DialogHelper.DialogInstance(
+                requireActivity(),
+                R.string.submission_qr_code_scan_invalid_dialog_headline,
+                R.string.submission_qr_code_scan_invalid_dialog_body,
+                R.string.submission_qr_code_scan_invalid_dialog_button_positive,
+                R.string.submission_qr_code_scan_invalid_dialog_button_negative,
+                true,
+                { /* startDecode() */ },
+                ::navigateToDispatchScreen
+            )
+            is CwaClientError, is CwaServerError -> DialogHelper.DialogInstance(
+                requireActivity(),
+                R.string.submission_error_dialog_web_generic_error_title,
+                R.string.submission_error_dialog_web_generic_network_error_body,
+                R.string.submission_error_dialog_web_generic_error_button_positive,
+                null,
+                true,
+                ::navigateToDispatchScreen
+            )
+            else -> DialogHelper.DialogInstance(
+                requireActivity(),
+                R.string.submission_error_dialog_web_generic_error_title,
+                R.string.submission_error_dialog_web_generic_error_body,
+                R.string.submission_error_dialog_web_generic_error_button_positive,
+                null,
+                true,
+                ::navigateToDispatchScreen
+            )
+        }
     }
 
     override fun onResume() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragmentViewModel.kt
deleted file mode 100644
index a2e1e24371ff9b72a431b46e7077baf17205aeb3..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningFragmentViewModel.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package de.rki.coronawarnapp.ui.submission.deletionwarning
-
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
-import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
-
-class SubmissionDeletionWarningFragmentViewModel @AssistedInject constructor() : CWAViewModel() {
-
-    @AssistedFactory
-    interface Factory : SimpleCWAViewModelFactory<SubmissionDeletionWarningFragmentViewModel>
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningModule.kt
index ac2010cd75d8413c47987c5c1533d2294831b4a6..2f95413150901adf0f4b61e246110a459c6cc0b7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningModule.kt
@@ -12,8 +12,8 @@ abstract class SubmissionDeletionWarningModule {
 
     @Binds
     @IntoMap
-    @CWAViewModelKey(SubmissionDeletionWarningFragmentViewModel::class)
+    @CWAViewModelKey(SubmissionDeletionWarningViewModel::class)
     abstract fun submissionDeletionWarningFragmentVM(
-        factory: SubmissionDeletionWarningFragmentViewModel.Factory
+        factory: SubmissionDeletionWarningViewModel.Factory
     ): CWAViewModelFactory<out CWAViewModel>
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1013bc5517fda4dcebaa1ca3ea74a68b8c1e4c6c
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/deletionwarning/SubmissionDeletionWarningViewModel.kt
@@ -0,0 +1,125 @@
+package de.rki.coronawarnapp.ui.submission.deletionwarning
+
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
+import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
+import de.rki.coronawarnapp.exception.ExceptionCategory
+import de.rki.coronawarnapp.exception.TransactionException
+import de.rki.coronawarnapp.exception.http.CwaWebException
+import de.rki.coronawarnapp.exception.reporting.report
+import de.rki.coronawarnapp.submission.SubmissionRepository
+import de.rki.coronawarnapp.ui.submission.ApiRequestState
+import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents
+import de.rki.coronawarnapp.util.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
+import kotlinx.coroutines.flow.first
+import timber.log.Timber
+
+class SubmissionDeletionWarningViewModel @AssistedInject constructor(
+    private val coronaTestRepository: CoronaTestRepository,
+    private val submissionRepository: SubmissionRepository,
+    @Assisted private val coronaTest: CoronaTestQRCode,
+    @Assisted private val isConsentGiven: Boolean,
+) : CWAViewModel() {
+
+    val routeToScreen = SingleLiveEvent<SubmissionNavigationEvents>()
+    val showRedeemedTokenWarning = SingleLiveEvent<Unit>()
+    private val mutableRegistrationState = MutableLiveData(RegistrationState(ApiRequestState.IDLE))
+    val registrationState: LiveData<RegistrationState> = mutableRegistrationState
+    val registrationError = SingleLiveEvent<CwaWebException>()
+
+    fun deleteExistingAndRegisterNewTest() = launch {
+        val currentTest = submissionRepository.testForType(coronaTest.type).first()
+        try {
+            coronaTestRepository.removeTest(currentTest!!.identifier)
+            doDeviceRegistration(coronaTest)
+        } catch (err: Exception) {
+            Timber.e(err, "Removal of existing test failed with msg: ${err.message}")
+            err.report(ExceptionCategory.INTERNAL)
+        }
+    }
+
+    data class RegistrationState(
+        val apiRequestState: ApiRequestState,
+        val testResult: CoronaTestResult? = null
+    )
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    internal suspend fun doDeviceRegistration(coronaTestQRCode: CoronaTestQRCode) {
+        try {
+            mutableRegistrationState.postValue(RegistrationState(ApiRequestState.STARTED))
+            val coronaTest = submissionRepository.registerTest(coronaTestQRCode)
+            if (isConsentGiven) {
+                submissionRepository.giveConsentToSubmission(type = coronaTestQRCode.type)
+            }
+            checkTestResult(coronaTest.testResult)
+            mutableRegistrationState.postValue(
+                RegistrationState(
+                    ApiRequestState.SUCCESS,
+                    coronaTest.testResult
+                )
+            )
+        } catch (err: CwaWebException) {
+            Timber.e(err, "Msg: ${err.message}")
+            mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED))
+            registrationError.postValue(err)
+        } catch (err: TransactionException) {
+            Timber.e(err, "Msg: ${err.message}")
+            if (err.cause is CwaWebException) {
+                registrationError.postValue(err.cause)
+            } else {
+                err.report(ExceptionCategory.INTERNAL)
+            }
+            mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED))
+        } catch (err: InvalidQRCodeException) {
+            Timber.e(err, "Msg: ${err.message}")
+            mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED))
+            deregisterTestFromDevice(coronaTestQRCode)
+            showRedeemedTokenWarning.postValue(Unit)
+        } catch (err: Exception) {
+            Timber.e(err, "Msg: ${err.message}")
+            mutableRegistrationState.postValue(RegistrationState(ApiRequestState.FAILED))
+            err.report(ExceptionCategory.INTERNAL)
+        }
+    }
+
+    fun onCancelButtonClick() {
+        routeToScreen.postValue(SubmissionNavigationEvents.NavigateToConsent)
+    }
+
+    fun triggerNavigationToSubmissionTestResultAvailableFragment() {
+        routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultAvailableScreen)
+    }
+
+    fun triggerNavigationToSubmissionTestResultPendingFragment() {
+        routeToScreen.postValue(SubmissionNavigationEvents.NavigateToResultPendingScreen)
+    }
+
+    private fun checkTestResult(testResult: CoronaTestResult) {
+        if (testResult == CoronaTestResult.PCR_REDEEMED) {
+            throw InvalidQRCodeException()
+        }
+    }
+
+    private fun deregisterTestFromDevice(coronaTest: CoronaTestQRCode) {
+        launch {
+            Timber.d("deregisterTestFromDevice()")
+
+            submissionRepository.removeTestFromDevice(type = coronaTest.type)
+            routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity)
+        }
+    }
+
+    @AssistedFactory
+    interface Factory : CWAViewModelFactory<SubmissionDeletionWarningViewModel> {
+        fun create(coronaTest: CoronaTestQRCode, isConsentGiven: Boolean): SubmissionDeletionWarningViewModel
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentFragment.kt
index 6458a16da1f8c82708ed7bfdc92a3a7e6db23193..89a9a4a17f525b7b027377aae1072e22fdef179d 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentFragment.kt
@@ -32,9 +32,11 @@ class SubmissionConsentFragment : Fragment(R.layout.fragment_submission_consent)
         }
         viewModel.routeToScreen.observe2(this) {
             when (it) {
-                is SubmissionNavigationEvents.NavigateToQRCodeScan -> doNavigate(
-                    SubmissionConsentFragmentDirections.actionSubmissionConsentFragmentToSubmissionQRCodeScanFragment()
-                )
+                is SubmissionNavigationEvents.NavigateToQRCodeScan ->
+                    doNavigate(
+                        SubmissionConsentFragmentDirections
+                            .actionSubmissionConsentFragmentToSubmissionQRCodeScanFragment(isConsentGiven = true)
+                    )
                 is SubmissionNavigationEvents.NavigateToDispatcher -> popBackStack()
                 is SubmissionNavigationEvents.NavigateToDataPrivacy -> doNavigate(
                     SubmissionConsentFragmentDirections.actionSubmissionConsentFragmentToInformationPrivacyFragment()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt
index 459ea67f482258a9e58aa664e74dec82f37587d0..e3387e7dc8426e7b8e5fac3908eabbe9dbb4a81c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModel.kt
@@ -4,10 +4,8 @@ import androidx.lifecycle.asLiveData
 import com.google.android.gms.common.api.ApiException
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
 import de.rki.coronawarnapp.nearby.modules.tekhistory.TEKHistoryProvider
 import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository
-import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
@@ -16,11 +14,10 @@ import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import timber.log.Timber
 
 class SubmissionConsentViewModel @AssistedInject constructor(
-    private val submissionRepository: SubmissionRepository,
     interoperabilityRepository: InteroperabilityRepository,
     dispatcherProvider: DispatcherProvider,
     private val tekHistoryProvider: TEKHistoryProvider,
-    private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
+    // private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
 
     val routeToScreen: SingleLiveEvent<SubmissionNavigationEvents> = SingleLiveEvent()
@@ -30,8 +27,7 @@ class SubmissionConsentViewModel @AssistedInject constructor(
 
     fun onConsentButtonClick() {
         // TODO Do we have a Test registered at this time? We need to forward the decission with navargs?
-//        submissionRepository.giveConsentToSubmission(type = CoronaTest.Type.PCR)
-        analyticsKeySubmissionCollector.reportAdvancedConsentGiven()
+        // analyticsKeySubmissionCollector.reportAdvancedConsentGiven()
         launch {
             try {
                 val preAuthorized = tekHistoryProvider.preAuthorizeExposureKeyHistory()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
index f19296ecba2b0a05766d8ddd40b6ae8d4546ee30..c2a919a2e22ad3fdd5f967ef434383efef26cf07 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
@@ -6,10 +6,12 @@ import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityEvent
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
 import com.google.zxing.BarcodeFormat
 import com.journeyapps.barcodescanner.DefaultDecoderFactory
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
+import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.databinding.FragmentSubmissionQrCodeScanBinding
 import de.rki.coronawarnapp.exception.http.BadRequestException
 import de.rki.coronawarnapp.exception.http.CwaClientError
@@ -26,18 +28,24 @@ 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 de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
 import javax.inject.Inject
 
 /**
  * A simple [Fragment] subclass.
  */
-class SubmissionQRCodeScanFragment :
-    Fragment(R.layout.fragment_submission_qr_code_scan),
-    AutoInject {
-
+class SubmissionQRCodeScanFragment : Fragment(R.layout.fragment_submission_qr_code_scan), AutoInject {
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val viewModel: SubmissionQRCodeScanViewModel by cwaViewModels { viewModelFactory }
+
+    private val args by navArgs<SubmissionQRCodeScanFragmentArgs>()
+
+    private val viewModel: SubmissionQRCodeScanViewModel by cwaViewModelsAssisted(
+        factoryProducer = { viewModelFactory },
+        constructorCall = { factory, _ ->
+            factory as SubmissionQRCodeScanViewModel.Factory
+            factory.create(args.isConsentGiven)
+        }
+    )
 
     private val binding: FragmentSubmissionQrCodeScanBinding by viewBindingLazy()
     private var showsPermissionDialog = false
@@ -62,6 +70,20 @@ class SubmissionQRCodeScanFragment :
             submissionQrCodeScanViewfinderView.setCameraPreview(binding.submissionQrCodeScanPreview)
         }
 
+        viewModel.routeToScreen.observe2(this) {
+            when (it) {
+                is SubmissionNavigationEvents.NavigateToDeletionWarningFragment -> {
+                    doNavigate(
+                        SubmissionQRCodeScanFragmentDirections
+                            .actionSubmissionQRCodeScanFragmentToSubmissionDeletionWarningFragment(
+                                it.consentGiven,
+                                it.coronaTestQRCode
+                            )
+                    )
+                }
+            }
+        }
+
         viewModel.scanStatusValue.observe2(this) {
             if (ScanStatus.INVALID == it) {
                 showInvalidScanDialog()
@@ -86,16 +108,57 @@ class SubmissionQRCodeScanFragment :
                 else -> View.GONE
             }
             if (ApiRequestState.SUCCESS == state.apiRequestState) {
-                if (state.testResult == CoronaTestResult.PCR_POSITIVE) {
-                    doNavigate(
-                        SubmissionQRCodeScanFragmentDirections
-                            .actionSubmissionQRCodeScanFragmentToSubmissionTestResultAvailableFragment()
-                    )
-                } else {
-                    doNavigate(
-                        SubmissionQRCodeScanFragmentDirections
-                            .actionSubmissionQRCodeScanFragmentToSubmissionTestResultPendingFragment()
-                    )
+                when (state.testResult) {
+                    CoronaTestResult.PCR_POSITIVE ->
+                        doNavigate(
+                            SubmissionQRCodeScanFragmentDirections
+                                .actionSubmissionQRCodeScanFragmentToSubmissionTestResultAvailableFragment(
+                                    testType = CoronaTest.Type.PCR
+                                )
+                        )
+                    CoronaTestResult.PCR_OR_RAT_PENDING -> {
+                        if (state.testType == CoronaTest.Type.RAPID_ANTIGEN) {
+                            doNavigate(
+                                SubmissionQRCodeScanFragmentDirections
+                                    .actionSubmissionQRCodeScanFragmentToSubmissionTestResultPendingFragment(
+                                        testType = CoronaTest.Type.RAPID_ANTIGEN
+                                    )
+                            )
+                        } else {
+                            doNavigate(
+                                SubmissionQRCodeScanFragmentDirections
+                                    .actionSubmissionQRCodeScanFragmentToSubmissionTestResultPendingFragment(
+                                        testType = CoronaTest.Type.PCR
+                                    )
+                            )
+                        }
+                    }
+                    CoronaTestResult.PCR_NEGATIVE,
+                    CoronaTestResult.PCR_INVALID,
+                    CoronaTestResult.PCR_REDEEMED ->
+                        doNavigate(
+                            SubmissionQRCodeScanFragmentDirections
+                                .actionSubmissionQRCodeScanFragmentToSubmissionTestResultPendingFragment(
+                                    testType = CoronaTest.Type.PCR
+                                )
+                        )
+                    CoronaTestResult.RAT_POSITIVE ->
+                        doNavigate(
+                            SubmissionQRCodeScanFragmentDirections
+                                .actionSubmissionQRCodeScanFragmentToSubmissionTestResultAvailableFragment(
+                                    testType = CoronaTest.Type.RAPID_ANTIGEN
+                                )
+                        )
+                    CoronaTestResult.RAT_NEGATIVE,
+                    CoronaTestResult.RAT_INVALID,
+                    CoronaTestResult.RAT_PENDING,
+                    CoronaTestResult.RAT_REDEEMED ->
+                        doNavigate(
+                            SubmissionQRCodeScanFragmentDirections
+                                .actionSubmissionQRCodeScanFragmentToSubmissionTestResultPendingFragment(
+                                    testType = CoronaTest.Type.RAPID_ANTIGEN
+                                )
+                        )
                 }
             }
         }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
index ff300dd094c8610d70ccd2ec42bc7231d4114c37..6466857f9a768b2f05a9ef27aa1922814ac94508 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModel.kt
@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.ui.submission.qrcode.scan
 
 import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.MutableLiveData
+import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.bugreporting.censors.QRCodeCensor
@@ -18,29 +19,46 @@ import de.rki.coronawarnapp.submission.SubmissionRepository
 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.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.permission.CameraSettings
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
-import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
+import kotlinx.coroutines.flow.first
 import timber.log.Timber
 
 class SubmissionQRCodeScanViewModel @AssistedInject constructor(
+    dispatcherProvider: DispatcherProvider,
     private val submissionRepository: SubmissionRepository,
     private val cameraSettings: CameraSettings,
+    @Assisted private val isConsentGiven: Boolean,
     private val qrCodeValidator: CoronaTestQrCodeValidator
-) : CWAViewModel() {
+) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
     val routeToScreen = SingleLiveEvent<SubmissionNavigationEvents>()
     val showRedeemedTokenWarning = SingleLiveEvent<Unit>()
     val scanStatusValue = SingleLiveEvent<ScanStatus>()
 
-    fun validateTestGUID(rawResult: String) {
+    fun validateTestGUID(rawResult: String) = launch {
         try {
             val coronaTestQRCode = qrCodeValidator.validate(rawResult)
             // TODO this needs to be adapted to work for different types
             QRCodeCensor.lastGUID = coronaTestQRCode.registrationIdentifier
             scanStatusValue.postValue(ScanStatus.SUCCESS)
-            doDeviceRegistration(coronaTestQRCode)
+
+            val coronaTest = submissionRepository.testForType(coronaTestQRCode.type).first()
+
+            if (coronaTest != null) {
+                routeToScreen.postValue(
+                    SubmissionNavigationEvents.NavigateToDeletionWarningFragment(
+                        coronaTestQRCode,
+                        isConsentGiven
+                    )
+                )
+            } else {
+                doDeviceRegistration(coronaTestQRCode)
+            }
         } catch (err: InvalidQRCodeException) {
+            Timber.e(err, "Failed to validate GUID")
             scanStatusValue.postValue(ScanStatus.INVALID)
         }
     }
@@ -50,17 +68,26 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor(
 
     data class RegistrationState(
         val apiRequestState: ApiRequestState,
-        val testResult: CoronaTestResult? = null
+        val testResult: CoronaTestResult? = null,
+        val testType: CoronaTest.Type? = null
     )
 
     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
-    internal fun doDeviceRegistration(coronaTestQRCode: CoronaTestQRCode) = launch {
+    internal suspend fun doDeviceRegistration(coronaTestQRCode: CoronaTestQRCode) {
         try {
             registrationState.postValue(RegistrationState(ApiRequestState.STARTED))
             val coronaTest = submissionRepository.registerTest(coronaTestQRCode)
-            submissionRepository.giveConsentToSubmission(type = coronaTestQRCode.type)
+            if (isConsentGiven) {
+                submissionRepository.giveConsentToSubmission(type = coronaTestQRCode.type)
+            }
             checkTestResult(coronaTest.testResult)
-            registrationState.postValue(RegistrationState(ApiRequestState.SUCCESS, coronaTest.testResult))
+            registrationState.postValue(
+                RegistrationState(
+                    ApiRequestState.SUCCESS,
+                    coronaTest.testResult,
+                    coronaTestQRCode.type
+                )
+            )
         } catch (err: CwaWebException) {
             registrationState.postValue(RegistrationState(ApiRequestState.FAILED))
             registrationError.postValue(err)
@@ -73,7 +100,7 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor(
             registrationState.postValue(RegistrationState(ApiRequestState.FAILED))
         } catch (err: InvalidQRCodeException) {
             registrationState.postValue(RegistrationState(ApiRequestState.FAILED))
-            deregisterTestFromDevice()
+            deregisterTestFromDevice(coronaTestQRCode)
             showRedeemedTokenWarning.postValue(Unit)
         } catch (err: Exception) {
             registrationState.postValue(RegistrationState(ApiRequestState.FAILED))
@@ -87,12 +114,11 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor(
         }
     }
 
-    private fun deregisterTestFromDevice() {
+    private fun deregisterTestFromDevice(coronaTest: CoronaTestQRCode) {
         launch {
             Timber.d("deregisterTestFromDevice()")
 
-            submissionRepository.removeTestFromDevice(type = CoronaTest.Type.PCR)
-
+            submissionRepository.removeTestFromDevice(type = coronaTest.type)
             routeToScreen.postValue(SubmissionNavigationEvents.NavigateToMainActivity)
         }
     }
@@ -111,5 +137,7 @@ class SubmissionQRCodeScanViewModel @AssistedInject constructor(
     }
 
     @AssistedFactory
-    interface Factory : SimpleCWAViewModelFactory<SubmissionQRCodeScanViewModel>
+    interface Factory : CWAViewModelFactory<SubmissionQRCodeScanViewModel> {
+        fun create(isConsentGiven: Boolean): SubmissionQRCodeScanViewModel
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt
index 81a4ebbc057e1effeed28b8b0f1505ef35b8cbfc..d6e40ed1b55061fac0d811365af65631c574f058 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/invalid/SubmissionTestResultInvalidViewModel.kt
@@ -6,7 +6,7 @@ import androidx.navigation.NavDirections
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
@@ -20,7 +20,7 @@ import timber.log.Timber
 class SubmissionTestResultInvalidViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
     private val submissionRepository: SubmissionRepository,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService,
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService,
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
     // TODO Use navargs to supply this
     private val coronaTestType: CoronaTest.Type = CoronaTest.Type.PCR
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt
index e7f6ebec332e3ad125f57f9dc7164cebff9ae569..8640c2bb8dd493c5648255ae79253800ac1ce543 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/negative/SubmissionTestResultNegativeViewModel.kt
@@ -6,7 +6,7 @@ import androidx.navigation.NavDirections
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
@@ -20,7 +20,7 @@ import timber.log.Timber
 class SubmissionTestResultNegativeViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
     private val submissionRepository: SubmissionRepository,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
     // TODO Use navargs to supply this
     private val coronaTestType: CoronaTest.Type = CoronaTest.Type.PCR
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt
index 2c2ed4a3ca78d308b9dfefdea4cd66dcc85e4fec..096b1c81a56c7455a3f398ea0f8683c87a1d676a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingFragment.kt
@@ -5,6 +5,7 @@ import android.view.View
 import android.view.accessibility.AccessibilityEvent
 import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentSubmissionTestResultPendingBinding
 import de.rki.coronawarnapp.exception.http.CwaClientError
@@ -19,20 +20,28 @@ import de.rki.coronawarnapp.util.ui.popBackStack
 import de.rki.coronawarnapp.util.ui.setInvisible
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
-import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
+import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
 import javax.inject.Inject
 
 class SubmissionTestResultPendingFragment : Fragment(R.layout.fragment_submission_test_result_pending), AutoInject {
 
-    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val pendingViewModel: SubmissionTestResultPendingViewModel by cwaViewModels { viewModelFactory }
-
     private val binding: FragmentSubmissionTestResultPendingBinding by viewBindingLazy()
 
     private var skipInitialTestResultRefresh = false
 
     private var errorDialog: AlertDialog? = null
 
+    private val navArgs by navArgs<SubmissionTestResultPendingFragmentArgs>()
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val pendingViewModel: SubmissionTestResultPendingViewModel by cwaViewModelsAssisted(
+        factoryProducer = { viewModelFactory },
+        constructorCall = { factory, _ ->
+            factory as SubmissionTestResultPendingViewModel.Factory
+            factory.create(navArgs.testType)
+        }
+    )
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
@@ -81,8 +90,6 @@ class SubmissionTestResultPendingFragment : Fragment(R.layout.fragment_submissio
         pendingViewModel.routeToScreen.observe2(this) {
             it?.let { doNavigate(it) } ?: navigateToMainScreen()
         }
-
-        pendingViewModel.observeTestResultToSchedulePositiveTestResultReminder()
     }
 
     override fun onResume() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt
index b01b89606ed2a5b22e16efeacf5164f9b077abf5..d04817c085031fc1f430c95619ae7f28b7d9ab57 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/pending/SubmissionTestResultPendingViewModel.kt
@@ -3,10 +3,11 @@ package de.rki.coronawarnapp.ui.submission.testresult.pending
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
 import androidx.navigation.NavDirections
+import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
+import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.submission.toDeviceUIState
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
@@ -14,10 +15,9 @@ import de.rki.coronawarnapp.util.DeviceUIState
 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 de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.sync.Mutex
@@ -26,27 +26,25 @@ import timber.log.Timber
 
 class SubmissionTestResultPendingViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
-    private val shareTestResultNotificationService: ShareTestResultNotificationService,
-    private val submissionRepository: SubmissionRepository
+    private val submissionRepository: SubmissionRepository,
+    @Assisted private val testType: CoronaTest.Type
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
-    // TODO Use navargs to supply this
-    private val coronaTestType: CoronaTest.Type = CoronaTest.Type.PCR
 
     init {
-        Timber.v("init() coronaTestType=%s", coronaTestType)
+        Timber.v("init() coronaTestType=%s", testType)
     }
 
     val routeToScreen = SingleLiveEvent<NavDirections?>()
 
     val showRedeemedTokenWarning = SingleLiveEvent<Unit>()
-    val consentGiven = submissionRepository.testForType(type = coronaTestType).map {
+    val consentGiven = submissionRepository.testForType(type = testType).map {
         it?.isAdvancedConsentGiven ?: false
     }.asLiveData()
 
     private var wasRedeemedTokenErrorShown = false
     private val tokenErrorMutex = Mutex()
 
-    private val testResultFlow = submissionRepository.testForType(type = coronaTestType)
+    private val testResultFlow = submissionRepository.testForType(type = testType)
         .filterNotNull()
         .map { test ->
             tokenErrorMutex.withLock {
@@ -62,17 +60,33 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor(
 
     val testState: LiveData<TestResultUIState> = testResultFlow
         .onEach { testResultUIState ->
-            when (val deviceState = testResultUIState.coronaTest.testResult.toDeviceUIState()) {
-                DeviceUIState.PAIRED_POSITIVE ->
+            when (val deviceState = testResultUIState.coronaTest.testResult) {
+                CoronaTestResult.PCR_POSITIVE ->
                     SubmissionTestResultPendingFragmentDirections
-                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultAvailableFragment()
-                DeviceUIState.PAIRED_NEGATIVE ->
+                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultAvailableFragment(
+                            testType = CoronaTest.Type.PCR
+                        )
+                CoronaTestResult.RAT_POSITIVE ->
+                    SubmissionTestResultPendingFragmentDirections
+                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultAvailableFragment(
+                            testType = CoronaTest.Type.RAPID_ANTIGEN
+                        )
+                CoronaTestResult.PCR_NEGATIVE ->
                     SubmissionTestResultPendingFragmentDirections
                         .actionSubmissionTestResultPendingFragmentToSubmissionTestResultNegativeFragment()
-                DeviceUIState.PAIRED_REDEEMED,
-                DeviceUIState.PAIRED_ERROR ->
+                CoronaTestResult.RAT_NEGATIVE ->
+                    SubmissionTestResultPendingFragmentDirections
+                        .actionSubmissionTestResultPendingFragmentToSubmissionNegativeAntigenTestResultFragment()
+                CoronaTestResult.PCR_REDEEMED, CoronaTestResult.PCR_INVALID ->
+                    SubmissionTestResultPendingFragmentDirections
+                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultInvalidFragment(
+                            CoronaTest.Type.PCR
+                        )
+                CoronaTestResult.RAT_REDEEMED, CoronaTestResult.RAT_INVALID ->
                     SubmissionTestResultPendingFragmentDirections
-                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultInvalidFragment()
+                        .actionSubmissionTestResultPendingFragmentToSubmissionTestResultInvalidFragment(
+                            CoronaTest.Type.RAPID_ANTIGEN
+                        )
                 else -> {
                     Timber.w("Unknown success state: %s", deviceState)
                     null
@@ -80,7 +94,7 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor(
             }?.let { routeToScreen.postValue(it) }
         }
         .filter { testResultUIState ->
-            val isPositiveTest = testResultUIState.coronaTest.isSubmissionAllowed
+            val isPositiveTest = testResultUIState.coronaTest.isPositive
             if (isPositiveTest) {
                 Timber.w("Filtering out positive test emission as we don't display this here.")
             }
@@ -88,27 +102,21 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor(
         }
         .asLiveData(context = dispatcherProvider.Default)
 
-    val cwaWebExceptionLiveData = submissionRepository.testForType(type = coronaTestType)
+    val cwaWebExceptionLiveData = submissionRepository.testForType(type = testType)
         .filterNotNull()
         .filter { it.lastError != null }
         .map { it.lastError!! }
         .asLiveData()
 
-    fun observeTestResultToSchedulePositiveTestResultReminder() = launch {
-        submissionRepository.testForType(type = coronaTestType)
-            .first { request -> request?.isSubmissionAllowed ?: false }
-            .also { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() }
-    }
-
     fun deregisterTestFromDevice() = launch {
         Timber.d("deregisterTestFromDevice()")
-        submissionRepository.removeTestFromDevice(type = coronaTestType)
+        submissionRepository.removeTestFromDevice(type = testType)
         routeToScreen.postValue(null)
     }
 
     fun refreshDeviceUIState() = launch {
         Timber.v("refreshDeviceUIState()")
-        submissionRepository.refreshTest(type = coronaTestType)
+        submissionRepository.refreshTest(type = testType)
     }
 
     fun onConsentClicked() {
@@ -119,5 +127,7 @@ class SubmissionTestResultPendingViewModel @AssistedInject constructor(
     }
 
     @AssistedFactory
-    interface Factory : SimpleCWAViewModelFactory<SubmissionTestResultPendingViewModel>
+    interface Factory : CWAViewModelFactory<SubmissionTestResultPendingViewModel> {
+        fun create(testType: CoronaTest.Type): SubmissionTestResultPendingViewModel
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt
index 053baa300837faac283f575edda2cd5633f30e78..e217fa4c4e8d4f7ebe94cfaac97819063bb3579f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultConsentGivenViewModel.kt
@@ -7,7 +7,7 @@ import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.Screen
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.submission.auto.AutoSubmission
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
@@ -24,7 +24,7 @@ import timber.log.Timber
 class SubmissionTestResultConsentGivenViewModel @AssistedInject constructor(
     private val submissionRepository: SubmissionRepository,
     private val autoSubmission: AutoSubmission,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService,
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService,
     private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector,
     dispatcherProvider: DispatcherProvider
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt
index 04374d1b4d688bfe8e66003f303e30dd16312d45..091025361fa654a41a92b4b07a04aa87f6a5c07b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentFragment.kt
@@ -6,8 +6,10 @@ import android.view.View
 import android.view.accessibility.AccessibilityEvent
 import androidx.activity.OnBackPressedCallback
 import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentSubmissionTestResultPositiveNoConsentBinding
+import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResultPendingFragmentArgs
 import de.rki.coronawarnapp.util.di.AutoInject
 import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper
 import de.rki.coronawarnapp.util.ui.doNavigate
@@ -30,6 +32,8 @@ class SubmissionTestResultNoConsentFragment :
     private val viewModel: SubmissionTestResultNoConsentViewModel by cwaViewModels { viewModelFactory }
     private val binding: FragmentSubmissionTestResultPositiveNoConsentBinding by viewBindingLazy()
 
+    private val navArgs by navArgs<SubmissionTestResultPendingFragmentArgs>()
+
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
@@ -85,7 +89,9 @@ class SubmissionTestResultNoConsentFragment :
     private fun navigateToWarnOthers() {
         doNavigate(
             SubmissionTestResultNoConsentFragmentDirections
-                .actionSubmissionTestResultNoConsentFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment()
+                .actionSubmissionTestResultNoConsentFragmentToSubmissionResultPositiveOtherWarningNoConsentFragment(
+                    navArgs.testType
+                )
         )
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt
index 8161c5e817c77b091843d54556f7425ec76fa7c8..80afcd0a31a65f2e1b14817fe8db78a4e399ebb4 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/testresult/positive/SubmissionTestResultNoConsentViewModel.kt
@@ -7,7 +7,7 @@ import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.Screen
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.TestResultUIState
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
@@ -19,7 +19,7 @@ import timber.log.Timber
 
 class SubmissionTestResultNoConsentViewModel @AssistedInject constructor(
     private val submissionRepository: SubmissionRepository,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService,
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService,
     private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
 ) : CWAViewModel() {
     // TODO Use navargs to supply this
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt
index e89a06d0dabe6cc67283b8f1c0cd657c3e8b384e..93186ab86ac5b3655f3b99668d21d719adbaa0d8 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt
@@ -2,6 +2,8 @@ package de.rki.coronawarnapp.ui.submission.viewmodel
 
 import dagger.Module
 import dagger.android.ContributesAndroidInjector
+import de.rki.coronawarnapp.submission.ui.testresults.negative.RATResultNegativeFragment
+import de.rki.coronawarnapp.submission.ui.testresults.negative.RATResultNegativeModule
 import de.rki.coronawarnapp.ui.submission.fragment.SubmissionContactFragment
 import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDispatcherFragment
 import de.rki.coronawarnapp.ui.submission.deletionwarning.SubmissionDeletionWarningFragment
@@ -95,4 +97,7 @@ internal abstract class SubmissionFragmentModule {
 
     @ContributesAndroidInjector(modules = [SubmissionDeletionWarningModule::class])
     abstract fun submissionDeletionWarningScreen(): SubmissionDeletionWarningFragment
+
+    @ContributesAndroidInjector(modules = [RATResultNegativeModule::class])
+    abstract fun submissionNegativeRATResultScreen(): RATResultNegativeFragment
 }
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 de51d34bff749610e87ed4dd9aa3a9fdc8261440..838118d1308a928e03ed67551321bbabac459587 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,6 +1,7 @@
 package de.rki.coronawarnapp.ui.submission.viewmodel
 
 import com.google.android.gms.common.api.ApiException
+import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode
 
 sealed class SubmissionNavigationEvents {
     object NavigateToContact : SubmissionNavigationEvents()
@@ -12,5 +13,9 @@ sealed class SubmissionNavigationEvents {
     object NavigateToTAN : SubmissionNavigationEvents()
     object NavigateToConsent : SubmissionNavigationEvents()
     object NavigateToMainActivity : SubmissionNavigationEvents()
+    object NavigateToResultPendingScreen : SubmissionNavigationEvents()
+    object NavigateToResultAvailableScreen : SubmissionNavigationEvents()
+    data class NavigateToDeletionWarningFragment(val coronaTestQRCode: CoronaTestQRCode, val consentGiven: Boolean) :
+        SubmissionNavigationEvents()
     data class ResolvePlayServicesException(val exception: ApiException) : SubmissionNavigationEvents()
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TestResultSectionView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TestResultSectionView.kt
index 11c140a9ff906b736dbafec4afda1f2c1aeddbc3..acdb4ca5d7edbbdc25ecf6efe2aaa83496b1960b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TestResultSectionView.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/view/TestResultSectionView.kt
@@ -60,7 +60,6 @@ constructor(
     private fun formatTestStatusIcon(coronaTest: CoronaTest?): Drawable? {
         val drawable = when (coronaTest?.testResult.toDeviceUIState()) {
             DeviceUIState.PAIRED_NO_RESULT -> R.drawable.ic_test_result_illustration_pending
-            DeviceUIState.PAIRED_POSITIVE_TELETAN,
             DeviceUIState.PAIRED_POSITIVE -> R.drawable.ic_test_result_illustration_positive
             DeviceUIState.PAIRED_NEGATIVE -> R.drawable.ic_test_result_illustration_negative
             DeviceUIState.PAIRED_ERROR,
@@ -84,7 +83,6 @@ constructor(
                 SpannableString(context.getString(R.string.test_result_card_status_invalid))
 
             DeviceUIState.PAIRED_POSITIVE,
-            DeviceUIState.PAIRED_POSITIVE_TELETAN,
             DeviceUIState.PAIRED_NEGATIVE -> SpannableString(formatTestResult(context, uiState))
             else -> SpannableString("")
         }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DeviceUIState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DeviceUIState.kt
index bc304ab27c4a7eadac06adb6decadd14c4c97fac..8d4ecd55a44631262b3e84738a50cc7076c9fecf 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DeviceUIState.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DeviceUIState.kt
@@ -4,10 +4,8 @@ enum class DeviceUIState {
     UNPAIRED,
     PAIRED_NO_RESULT,
     PAIRED_POSITIVE,
-    PAIRED_POSITIVE_TELETAN,
     PAIRED_NEGATIVE,
     PAIRED_ERROR,
     PAIRED_REDEEMED,
-    SUBMITTED_INITIAL,
     SUBMITTED_FINAL
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/NetworkRequestWrapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/NetworkRequestWrapper.kt
deleted file mode 100644
index ddef4e3140281c5ddf59dd4ef68bffc640cd0537..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/NetworkRequestWrapper.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.rki.coronawarnapp.util
-
-sealed class NetworkRequestWrapper<out T : Any, out U : Any> {
-    object RequestIdle : NetworkRequestWrapper<Nothing, Nothing>()
-    object RequestStarted : NetworkRequestWrapper<Nothing, Nothing>()
-    data class RequestSuccessful<T : Any, U : Any>(val data: T) : NetworkRequestWrapper<T, U>()
-    data class RequestFailed<T : Any, U : Throwable>(val error: U) : NetworkRequestWrapper<T, U>()
-
-    companion object {
-        fun <T : Any, U : Any, W> NetworkRequestWrapper<T, U>?.withSuccess(without: W, block: (data: T) -> W): W {
-            return if (this is RequestSuccessful) {
-                block(this.data)
-            } else {
-                without
-            }
-        }
-
-        fun <T : Any, U : Any> NetworkRequestWrapper<T, U>?.withSuccess(block: (data: T) -> Unit) {
-            if (this is RequestSuccessful) {
-                block(this.data)
-            }
-        }
-
-        fun <T : Any, U : Any> NetworkRequestWrapper<T, U>?.withFailure(block: (error: U) -> Unit) {
-            if (this is RequestFailed) {
-                block(this.error)
-            }
-        }
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/flow/HotDataFlow.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/flow/HotDataFlow.kt
index adb705b8853b16d296567070479d47f4cc762cc0..b28c701d272a760bc57de284b18789ff15c1e5f3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/flow/HotDataFlow.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/flow/HotDataFlow.kt
@@ -6,7 +6,6 @@ import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -21,12 +20,20 @@ import kotlinx.coroutines.sync.withLock
 import timber.log.Timber
 import kotlin.coroutines.CoroutineContext
 
+/**
+ * A thread safe flow that can be updated blockingly and async, with way to provide an initial (lazy) value.
+ *
+ * @param loggingTag will be prepended to logging tag, i.e. "$loggingTag:HD"
+ * @param scope on which the update operations and callbacks will be executed on
+ * @param coroutineContext used in combination with [scope]
+ * @param sharingBehavior see [Flow.shareIn]
+ * @param startValueProvider provides the first value, errors will be rethrown on [scope]
+ */
 class HotDataFlow<T : Any>(
     loggingTag: String,
     scope: CoroutineScope,
     coroutineContext: CoroutineContext = scope.coroutineContext,
     sharingBehavior: SharingStarted = SharingStarted.WhileSubscribed(),
-    forwardException: Boolean = true,
     private val startValueProvider: suspend CoroutineScope.() -> T
 ) {
     private val tag = "$loggingTag:HD"
@@ -35,19 +42,21 @@ class HotDataFlow<T : Any>(
         Timber.tag(tag).v("init()")
     }
 
-    private val updateActions = MutableSharedFlow<suspend (T) -> T>(
+    private val updateActions = MutableSharedFlow<Update<T>>(
         replay = Int.MAX_VALUE,
         extraBufferCapacity = Int.MAX_VALUE,
         onBufferOverflow = BufferOverflow.SUSPEND
     )
     private val valueGuard = Mutex()
 
-    private val internalProducer: Flow<Holder<T>> = channelFlow {
+    private val internalProducer: Flow<State<T>> = channelFlow {
         var currentValue = valueGuard.withLock {
             startValueProvider().also {
                 Timber.tag(tag).v("startValue=%s", it)
-                val updatedBy: suspend T.() -> T = { it }
-                send(Holder.Data(value = it, updatedBy = updatedBy))
+
+                val initializer = Update<T>(onError = null, onModify = { it })
+
+                send(State(value = it, updatedBy = initializer))
             }
         }
         Timber.tag(tag).v("startValue=%s", currentValue)
@@ -57,10 +66,20 @@ class HotDataFlow<T : Any>(
                 Timber.tag(tag).v("updateActions onCompletion -> resetReplayCache()")
                 updateActions.resetReplayCache()
             }
-            .collect { updateAction ->
+            .collect { update ->
                 currentValue = valueGuard.withLock {
-                    updateAction(currentValue).also {
-                        send(Holder.Data(value = it, updatedBy = updateAction))
+                    try {
+                        update.onModify(currentValue).also {
+                            send(State(value = it, updatedBy = update))
+                        }
+                    } catch (e: Exception) {
+                        Timber.tag(tag).v(e, "Data modifying failed (hasErrorHandler=${update.onError != null})")
+                        if (update.onError != null) {
+                            update.onError.invoke(e)
+                        } else {
+                            send(State(value = currentValue, error = e, updatedBy = update))
+                        }
+                        currentValue
                     }
                 }
             }
@@ -70,16 +89,6 @@ class HotDataFlow<T : Any>(
 
     private val internalFlow = internalProducer
         .onStart { Timber.tag(tag).v("Internal onStart") }
-        .catch {
-            if (forwardException) {
-                Timber.tag(tag).w(it, "Forwarding internal Error")
-                // Wrap the error to get it past `sharedIn`
-                emit(Holder.Error(error = it))
-            } else {
-                Timber.tag(tag).e(it, "Throwing internal Error")
-                throw it
-            }
-        }
         .onCompletion { err ->
             when {
                 err is CancellationException -> Timber.tag(tag).d("internal onCompletion due to cancelation")
@@ -92,31 +101,55 @@ class HotDataFlow<T : Any>(
             replay = 1,
             started = sharingBehavior
         )
-        .map {
-            when (it) {
-                is Holder.Data<T> -> it
-                is Holder.Error<T> -> throw it.error
-            }
-        }
 
-    val data: Flow<T> = internalFlow.map { it.value }.distinctUntilChanged()
+    val data: Flow<T> = internalFlow
+        .map { it.value }
+        .distinctUntilChanged()
 
-    fun updateSafely(update: suspend T.() -> T) = updateActions.tryEmit(update)
+    /**
+     * Non blocking update method.
+     * Gets executed on the scope and context this instance was initialized with.
+     *
+     * @param onError if you don't provide this, and exception in [onUpdate] will the scope passed to this class
+     */
+    fun updateAsync(
+        onError: (suspend (Exception) -> Unit) = { throw it },
+        onUpdate: suspend T.() -> T,
+    ): Boolean {
+        val update: Update<T> = Update(
+            onModify = onUpdate,
+            onError = onError
+        )
+        return updateActions.tryEmit(update)
+    }
 
-    suspend fun updateBlocking(update: suspend T.() -> T): T {
+    /**
+     * Blocking update method
+     * Gets executed on the scope and context this instance was initialized with.
+     * Waiting will happen on the callers scope.
+     *
+     * Any errors that occured during [action] will be rethrown by this method.
+     */
+    suspend fun updateBlocking(action: suspend T.() -> T): T {
+        val update: Update<T> = Update(onModify = action)
         updateActions.tryEmit(update)
+
         Timber.tag(tag).v("Waiting for update.")
-        return internalFlow.first { it.updatedBy == update }.value
-    }
+        val ourUpdate = internalFlow.first { it.updatedBy == update }
 
-    internal sealed class Holder<T> {
-        data class Data<T>(
-            val value: T,
-            val updatedBy: suspend T.() -> T
-        ) : Holder<T>()
+        ourUpdate.error?.let { throw it }
 
-        data class Error<T>(
-            val error: Throwable
-        ) : Holder<T>()
+        return ourUpdate.value
     }
+
+    private data class Update<T>(
+        val onModify: suspend T.() -> T,
+        val onError: (suspend (Exception) -> Unit)? = null,
+    )
+
+    private data class State<T>(
+        val value: T,
+        val error: Exception? = null,
+        val updatedBy: Update<T>,
+    )
 }
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 4983861085b09dbad197b772a0847b2a331a4faf..41caecdaa6de6fef99dc10bafa80934fd6c8b4be 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
@@ -67,15 +67,13 @@ fun formatSymptomBackgroundButtonStyleByState(
 
 fun formatTestResultStatusText(context: Context, uiState: DeviceUIState): String = when (uiState) {
     DeviceUIState.PAIRED_NEGATIVE -> R.string.test_result_card_status_negative
-    DeviceUIState.PAIRED_POSITIVE,
-    DeviceUIState.PAIRED_POSITIVE_TELETAN -> R.string.test_result_card_status_positive
+    DeviceUIState.PAIRED_POSITIVE -> R.string.test_result_card_status_positive
     else -> R.string.test_result_card_status_invalid
 }.let { context.getString(it) }
 
 fun formatTestResultStatusColor(context: Context, uiState: DeviceUIState): Int = when (uiState) {
     DeviceUIState.PAIRED_NEGATIVE -> R.color.colorTextSemanticGreen
-    DeviceUIState.PAIRED_POSITIVE,
-    DeviceUIState.PAIRED_POSITIVE_TELETAN -> R.color.colorTextSemanticRed
+    DeviceUIState.PAIRED_POSITIVE -> R.color.colorTextSemanticRed
     else -> R.color.colorTextSemanticRed
 }.let { context.getColorCompat(it) }
 
@@ -105,7 +103,6 @@ fun formatTestResultCardContent(
             SpannableString(context.getString(R.string.test_result_card_status_invalid))
 
         DeviceUIState.PAIRED_POSITIVE,
-        DeviceUIState.PAIRED_POSITIVE_TELETAN,
         DeviceUIState.PAIRED_NEGATIVE -> SpannableString(formatTestResult(context, uiState))
         else -> SpannableString("")
     }
@@ -113,7 +110,6 @@ fun formatTestResultCardContent(
 
 fun formatTestStatusIcon(context: Context, uiState: DeviceUIState): Drawable? = when (uiState) {
     DeviceUIState.PAIRED_NO_RESULT -> R.drawable.ic_test_result_illustration_pending
-    DeviceUIState.PAIRED_POSITIVE_TELETAN,
     DeviceUIState.PAIRED_POSITIVE -> R.drawable.ic_test_result_illustration_positive
     DeviceUIState.PAIRED_NEGATIVE -> R.drawable.ic_test_result_illustration_negative
     DeviceUIState.PAIRED_ERROR,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt
index 0d2b9c43a0cfa7841f4cfed0583b117d72075c71..dd9ce5a954aa810bf83c2b47ed8d8691bb087294 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt
@@ -14,7 +14,7 @@ import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest
 import de.rki.coronawarnapp.notification.GeneralNotifications
 import de.rki.coronawarnapp.notification.NotificationConstants
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory
 import kotlinx.coroutines.flow.first
@@ -30,7 +30,7 @@ import timber.log.Timber
 class DiagnosisTestResultRetrievalPeriodicWorker @AssistedInject constructor(
     @Assisted val context: Context,
     @Assisted workerParams: WorkerParameters,
-    private val testResultAvailableNotificationService: TestResultAvailableNotificationService,
+    private val testResultAvailableNotificationService: PCRTestResultAvailableNotificationService,
     private val notificationHelper: GeneralNotifications,
     private val coronaTestRepository: CoronaTestRepository,
     private val timeStamper: TimeStamper,
diff --git a/Corona-Warn-App/src/main/res/drawable/card_counter_green.xml b/Corona-Warn-App/src/main/res/drawable/card_counter_green.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b1ca09ca6dab9577b41d1d11b7dc5067887a1b1b
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/card_counter_green.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:topLeftRadius="@dimen/radius_card" android:topRightRadius="@dimen/radius_card" />
+            <solid android:color="@color/colorSemanticLowRisk" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/drawable/card_white_bottom_rounded_corners.xml b/Corona-Warn-App/src/main/res/drawable/card_white_bottom_rounded_corners.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d61e81a7a7997d073551032a356f1f06a40b7f93
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/card_white_bottom_rounded_corners.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:bottomLeftRadius="@dimen/radius_card" android:bottomRightRadius="@dimen/radius_card" />
+            <solid android:color="@color/colorSurface1" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_test_result_delete_test.xml b/Corona-Warn-App/src/main/res/drawable/ic_test_result_delete_test.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7d97911443a1fc4bea6e7faa67c2286c19ac4709
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_test_result_delete_test.xml
@@ -0,0 +1,12 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40">
+  <path
+      android:pathData="M20,40C31.0457,40 40,31.0457 40,20C40,8.9543 31.0457,0 20,0C8.9543,0 0,8.9543 0,20C0,31.0457 8.9543,40 20,40Z"
+      android:fillColor="#F5F5F5"/>
+  <path
+      android:pathData="M24,17V27H16V17H24ZM22.5,11H17.5L16.5,12H13V14H27V12H23.5L22.5,11ZM26,15H14V27C14,28.1 14.9,29 16,29H24C25.1,29 26,28.1 26,27V15Z"
+      android:fillColor="#979797"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_antigen_test_result_negative.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_antigen_test_result_negative.xml
new file mode 100644
index 0000000000000000000000000000000000000000..19676144452838395000ff8d518611be495c4265
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_antigen_test_result_negative.xml
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.material.appbar.MaterialToolbar
+        android:id="@+id/toolbar"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:navigationIcon="@drawable/ic_close"
+        app:title="@string/submission_test_result_toolbar_text" />
+
+    <ScrollView
+        android:id="@+id/scrollview"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginBottom="@dimen/button_padding_top_bottom"
+        app:layout_constraintBottom_toTopOf="@id/coronatest_negative_antigen_result_button"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/toolbar">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:id="@+id/rapid_test_card"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/spacing_small"
+                android:background="@drawable/card_dark"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent">
+
+                <TextView
+                    android:id="@+id/rapid_test_card_title"
+                    style="@style/headline5"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="@dimen/spacing_normal"
+                    android:layout_marginTop="@dimen/spacing_medium"
+                    android:text="@string/submission_test_result_antigen_title"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_subtitle"
+                    style="@style/subtitleMedium"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_normal"
+                    android:text="@string/submission_result_subtitle"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_title"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_title" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_virus_name"
+                    style="@style/headline5Bold"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:text="@string/test_result_card_virus_name_text"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_subtitle"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_subtitle" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_diagnosis"
+                    style="@style/headline5Bold"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:text="@string/submission_test_result_negative"
+                    android:textColor="@color/colorTextSemanticGreen"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_virus_name"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_virus_name"
+                    tools:text="@string/submission_test_result_negative" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_patient_name"
+                    style="@style/body2"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_small"
+                    android:layout_marginEnd="@dimen/spacing_normal"
+                    android:textStyle="bold"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_diagnosis"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_diagnosis"
+                    tools:text="@string/submission_test_result_antigen_patient_name_placeholder" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_patient_birthdate"
+                    style="@style/body2"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toEndOf="@id/rapid_test_card_patient_name"
+                    app:layout_constraintTop_toTopOf="@id/rapid_test_card_patient_name"
+                    tools:text="@string/submission_test_result_antigen_patient_birth_date_placeholder" />
+
+                <TextView
+                    android:id="@+id/rapid_test_card_negative_result_message"
+                    style="@style/body2"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_tiny"
+                    android:layout_marginEnd="@dimen/spacing_normal"
+                    android:text="@string/submission_test_result_negative_message"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_patient_name"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_patient_birthdate" />
+
+                <include
+                    android:id="@+id/result_received_counter"
+                    layout="@layout/time_counter"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_normal"
+                    app:layout_constraintEnd_toEndOf="@id/rapid_test_card_negative_result_message"
+                    app:layout_constraintStart_toStartOf="@id/rapid_test_card_negative_result_message"
+                    app:layout_constraintTop_toBottomOf="@id/rapid_test_card_negative_result_message" />
+
+                <androidx.constraintlayout.widget.ConstraintLayout
+                    android:id="@+id/result_received_counter_bottom_part"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_marginBottom="@dimen/spacing_normal"
+                    android:background="@drawable/card_white_bottom_rounded_corners"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    app:layout_constraintEnd_toEndOf="@id/result_received_counter"
+                    app:layout_constraintStart_toStartOf="@id/result_received_counter"
+                    app:layout_constraintTop_toBottomOf="@id/result_received_counter">
+
+                    <TextView
+                        android:id="@+id/result_received_time_and_date"
+                        style="@style/body2"
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="@dimen/guideline_card"
+                        android:layout_marginBottom="@dimen/guideline_card"
+                        android:gravity="center"
+                        app:layout_constraintBottom_toBottomOf="parent"
+                        app:layout_constraintEnd_toEndOf="parent"
+                        app:layout_constraintStart_toStartOf="parent"
+                        app:layout_constraintTop_toTopOf="parent"
+                        tools:text="@string/coronatest_negative_antigen_result_time_date_placeholder" />
+
+                </androidx.constraintlayout.widget.ConstraintLayout>
+
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <TextView
+                android:id="@+id/negative_test_proof_title"
+                style="@style/headline5"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/spacing_small"
+                android:accessibilityHeading="true"
+                android:text="@string/submission_test_result_antigen_negative_proof_title"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/rapid_test_card" />
+
+            <TextView
+                android:id="@+id/negative_test_proof_body"
+                style="@style/subtitle"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_small"
+                android:text="@string/submission_test_result_antigen_negative_proof_body"
+                app:layout_constraintEnd_toEndOf="@id/negative_test_proof_title"
+                app:layout_constraintStart_toStartOf="@id/negative_test_proof_title"
+                app:layout_constraintTop_toBottomOf="@id/negative_test_proof_title" />
+
+            <TextView
+                android:id="@+id/submission_test_result_subtitle"
+                style="@style/headline5"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/spacing_small"
+                android:accessibilityHeading="true"
+                android:text="@string/submission_test_result_subtitle"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/negative_test_proof_body" />
+
+            <de.rki.coronawarnapp.ui.view.SimpleStepEntry
+                android:id="@+id/test_result_negative_steps_added"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_normal"
+                android:layout_marginEnd="@dimen/spacing_normal"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="@id/submission_test_result_subtitle"
+                app:layout_constraintTop_toBottomOf="@id/submission_test_result_subtitle"
+                app:simple_step_entry_text="@string/coronatest_negative_antigen_result_first_info_body"
+                app:simple_step_entry_title="@string/coronatest_negative_antigen_result_first_info_title"
+                app:step_entry_final="false"
+                app:step_entry_icon="@drawable/ic_test_result_step_done" />
+
+            <de.rki.coronawarnapp.ui.view.SimpleStepEntry
+                android:id="@+id/test_result_negative_steps_negative_result"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="@dimen/spacing_normal"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="@id/test_result_negative_steps_added"
+                app:layout_constraintTop_toBottomOf="@+id/test_result_negative_steps_added"
+                app:simple_step_entry_text="@string/coronatest_negative_antigen_result_second_info_body"
+                app:simple_step_entry_title="@string/coronatest_negative_antigen_result_second_info_title"
+                app:step_entry_final="true"
+                app:step_entry_icon="@drawable/ic_test_result_step_invalid" />
+
+            <de.rki.coronawarnapp.ui.view.SimpleStepEntry
+                android:id="@+id/coronatest_negative_antigen_result_third_info"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="@dimen/spacing_normal"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="@id/test_result_negative_steps_negative_result"
+                app:layout_constraintTop_toBottomOf="@+id/test_result_negative_steps_negative_result"
+                app:simple_step_entry_text="@string/coronatest_negative_antigen_restul_third_info_body"
+                app:simple_step_entry_title="@string/coronatest_negative_antigen_result_third_info_title"
+                app:step_entry_final="true"
+                app:step_entry_icon="@drawable/ic_test_result_delete_test" />
+
+            <LinearLayout
+                android:id="@+id/further_info"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="@color/colorSurface2"
+                android:orientation="vertical"
+                android:padding="@dimen/spacing_normal"
+                app:layout_constraintTop_toBottomOf="@id/test_result_negative_steps_negative_result">
+
+                <TextView
+                    android:id="@+id/further_info_title"
+                    style="@style/headline5"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:accessibilityHeading="true"
+                    android:text="@string/test_result_card_negative_further_info_title" />
+
+                <de.rki.coronawarnapp.ui.view.BulletPointList
+                    android:id="@+id/further_info_text"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_normal"
+                    app:entries="@array/test_result_card_negative_further_info_bullet_points" />
+
+            </LinearLayout>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </ScrollView>
+
+    <Button
+        android:id="@+id/coronatest_negative_antigen_result_button"
+        style="@style/buttonPrimary"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/spacing_normal"
+        android:text="@string/submission_test_result_negative_remove_test_button"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_deletion_warning.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_deletion_warning.xml
index 56a83c008188580c8de15fb748a69241c9a55ba3..27fdf7bca4367c65b6311fa1732c57cf836dd3b2 100644
--- a/Corona-Warn-App/src/main/res/layout/fragment_submission_deletion_warning.xml
+++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_deletion_warning.xml
@@ -18,6 +18,19 @@
         app:navigationIcon="@drawable/ic_close"
         app:title="@string/submission_deletion_warning_title" />
 
+    <ProgressBar
+        android:id="@+id/submission_qr_code_scan_spinner"
+        style="?android:attr/progressBarStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_normal"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible" />
+
     <ScrollView
         android:layout_width="0dp"
         android:layout_height="0dp"
@@ -94,26 +107,12 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/spacing_normal"
         android:layout_marginEnd="@dimen/spacing_normal"
-        android:layout_marginBottom="8dp"
+        android:layout_marginBottom="@dimen/spacing_normal"
         android:text="@string/submission_deletion_warning_continue_button"
-        app:layout_constraintBottom_toTopOf="@id/cancel_button"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        tools:text="@string/submission_deletion_warning_continue_button" />
-
-    <Button
-        android:id="@+id/cancel_button"
-        style="@style/buttonLight"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/spacing_normal"
-        android:layout_marginEnd="@dimen/spacing_normal"
-        android:layout_marginBottom="4dp"
-        android:text="@string/submission_deletion_warning_cancel_button"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        tools:text="@string/submission_deletion_warning_cancel_button" />
+        tools:text="@string/submission_deletion_warning_continue_button" />
 
 </androidx.constraintlayout.widget.ConstraintLayout>
 
diff --git a/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result_negative.xml b/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result_negative.xml
index f87e108477b6c97139a44c18bc1a19fde7cfd683..49d5aab4c16290b5dbfae8b3908674b1f6f69173 100644
--- a/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result_negative.xml
+++ b/Corona-Warn-App/src/main/res/layout/fragment_submission_test_result_negative.xml
@@ -48,6 +48,7 @@
                     app:layout_constraintTop_toTopOf="parent"
                     app:test_result_section_headline="@string/test_result_card_headline" />
 
+
                 <TextView
                     android:id="@+id/submission_test_result_subtitle"
                     style="@style/headline5"
@@ -58,7 +59,7 @@
                     android:text="@string/submission_test_result_subtitle"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintStart_toStartOf="parent"
-                    app:layout_constraintTop_toBottomOf="@+id/submission_test_result_section" />
+                    app:layout_constraintTop_toBottomOf="@id/submission_test_result_section" />
 
 
                 <de.rki.coronawarnapp.ui.view.SimpleStepEntry
diff --git a/Corona-Warn-App/src/main/res/layout/time_counter.xml b/Corona-Warn-App/src/main/res/layout/time_counter.xml
new file mode 100644
index 0000000000000000000000000000000000000000..07a4f52748469fb0ab0edddf20139fa3e8f49fa1
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/time_counter.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<androidx.constraintlayout.widget.ConstraintLayout 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"
+    android:id="@+id/upper_green_part"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/card_counter_green"
+    app:layout_constraintEnd_toEndOf="parent"
+    app:layout_constraintStart_toStartOf="parent"
+    app:layout_constraintTop_toTopOf="parent">
+
+    <TextView
+        android:id="@+id/result_timer"
+        style="@style/body2"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/guideline_card"
+        android:gravity="center"
+        android:text="@string/submission_test_result_antigen_negative_counter_title"
+        android:textColor="@color/colorTextPrimary1InvertedStable"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <Chronometer
+        android:id="@+id/chronometer"
+        style="@style/headline4Bold"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_tiny"
+        android:format="00:00:00"
+        android:gravity="center"
+        android:textColor="@color/colorTextPrimary1InvertedStable"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/result_timer"
+        tools:text="00:00:00" />
+
+    <TextView
+        android:id="@+id/chronometer_hours"
+        style="@style/body3Medium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        android:layout_marginBottom="@dimen/spacing_tiny"
+        android:text="Std"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/chronometer_minutes"
+        app:layout_constraintStart_toStartOf="@id/chronometer"
+        app:layout_constraintTop_toBottomOf="@id/chronometer" />
+
+    <TextView
+        android:id="@+id/chronometer_minutes"
+        style="@style/body3Medium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/spacing_tiny"
+        android:gravity="center"
+        android:text="Min"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@id/chronometer"
+        app:layout_constraintStart_toStartOf="@id/chronometer"
+        app:layout_constraintTop_toBottomOf="@id/chronometer" />
+
+    <TextView
+        android:id="@+id/chronometer_seconds"
+        style="@style/body3Medium"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginBottom="@dimen/spacing_tiny"
+        android:text="Sek"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="@id/chronometer"
+        app:layout_constraintStart_toEndOf="@id/chronometer_minutes"
+        app:layout_constraintTop_toBottomOf="@id/chronometer" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/Corona-Warn-App/src/main/res/layout/view_test_result_section.xml b/Corona-Warn-App/src/main/res/layout/view_test_result_section.xml
index b8662fe55f085e975550c95c2336ac8a8ea94e72..62fefc498eafb3ccab565a421fcbcd63a3d826f4 100644
--- a/Corona-Warn-App/src/main/res/layout/view_test_result_section.xml
+++ b/Corona-Warn-App/src/main/res/layout/view_test_result_section.xml
@@ -30,7 +30,6 @@
             android:layout_height="wrap_content"
             android:layout_marginEnd="@dimen/spacing_small"
             android:layout_marginBottom="@dimen/spacing_normal"
-            app:layout_constraintBottom_toTopOf="@id/test_result_section_registered_at_text"
             app:layout_constraintEnd_toStartOf="@id/test_result_section_status_icon"
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toBottomOf="@id/test_result_section_headline"
@@ -40,11 +39,11 @@
             android:id="@+id/test_result_section_status_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginEnd="24dp"
             android:importantForAccessibility="no"
             app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintTop_toTopOf="@id/test_result_section_content"
             tools:src="@drawable/ic_test_result_illustration_negative" />
-
         <TextView
             android:id="@+id/test_result_section_registered_at_text"
             style="@style/body2"
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 5ff1d99cff64e4f55a40b3bcde41772a920a595b..d4ff1223b1dbc4299c6d3a450a93c7913b1c61d4 100644
--- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml
@@ -64,6 +64,9 @@
         <action
             android:id="@+id/action_mainFragment_to_trace_location_organizer_nav_graph"
             app:destination="@id/trace_location_organizer_nav_graph" />
+        <action
+            android:id="@+id/action_mainFragment_to_submissionNegativeAntigenTestResultFragment"
+            app:destination="@id/submissionNegativeAntigenTestResultFragment" />
     </fragment>
 
     <fragment
@@ -278,6 +281,9 @@
             app:destination="@id/submissionResultReadyFragment"
             app:popUpTo="@id/mainFragment"
             app:popUpToInclusive="false" />
+        <argument
+            android:name="testType"
+            app:argType="de.rki.coronawarnapp.coronatest.type.CoronaTest$Type" />
         <action
             android:id="@+id/action_submissionResultPositiveOtherWarningNoConsentFragment_to_checkInsConsentFragment"
             app:destination="@id/checkInsConsentFragment"
@@ -311,7 +317,14 @@
             app:destination="@id/submissionTestResultAvailableFragment"
             app:popUpTo="@id/mainFragment"
             app:popUpToInclusive="false" />
-
+        <argument
+            android:name="testType"
+            app:argType="de.rki.coronawarnapp.coronatest.type.CoronaTest$Type" />
+        <action
+            android:id="@+id/action_submissionTestResultPendingFragment_to_submissionNegativeAntigenTestResultFragment"
+            app:destination="@id/submissionNegativeAntigenTestResultFragment"
+            app:popUpTo="@id/mainFragment"
+            app:popUpToInclusive="false" />
     </fragment>
 
     <fragment
@@ -348,7 +361,12 @@
     <fragment
         android:id="@+id/submissionQRCodeScanFragment"
         android:name="de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanFragment"
-        android:label="SubmissionQRCodeScanFragment">
+        android:label="SubmissionQRCodeScanFragment"
+        tools:layout="@layout/fragment_submission_qr_code_scan">
+        <argument
+            android:name="isConsentGiven"
+            android:defaultValue="false"
+            app:argType="boolean" />
         <action
             android:id="@+id/action_submissionQRCodeScanFragment_to_submissionDispatcherFragment"
             app:popUpTo="@id/submissionQRCodeScanFragment"
@@ -368,6 +386,9 @@
             app:destination="@id/submissionTestResultAvailableFragment"
             app:popUpTo="@id/mainFragment"
             app:popUpToInclusive="false" />
+        <action
+            android:id="@+id/action_submissionQRCodeScanFragment_to_submissionDeletionWarningFragment"
+            app:destination="@id/submissionDeletionWarningFragment" />
     </fragment>
     <fragment
         android:id="@+id/submissionResultReadyFragment"
@@ -503,7 +524,9 @@
             app:destination="@id/submissionTestResultNoConsentFragment"
             app:popUpTo="@id/mainFragment"
             app:popUpToInclusive="false" />
-
+        <argument
+            android:name="testType"
+            app:argType="de.rki.coronawarnapp.coronatest.type.CoronaTest$Type" />
     </fragment>
     <fragment
         android:id="@+id/submissionTestResultNegativeFragment"
@@ -513,7 +536,16 @@
         android:id="@+id/submissionTestResultInvalidFragment"
         android:name="de.rki.coronawarnapp.ui.submission.testresult.invalid.SubmissionTestResultInvalidFragment"
         android:label="SubmissionTestResultInvalidFragment"
-        tools:layout="@layout/fragment_submission_test_result_invalid" />
+        tools:layout="@layout/fragment_submission_test_result_invalid">
+        <argument
+            android:name="testType"
+            app:argType="de.rki.coronawarnapp.coronatest.type.CoronaTest$Type" />
+    </fragment>
+    <fragment
+        android:id="@+id/submissionNegativeAntigenTestResultFragment"
+        android:name="de.rki.coronawarnapp.submission.ui.testresults.negative.RATResultNegativeFragment"
+        android:label="SubmissionNegativeAntigenTestResultFragment"
+        tools:layout="@layout/fragment_submission_antigen_test_result_negative" />
     <fragment
         android:id="@+id/debuglogFragment"
         android:name="de.rki.coronawarnapp.bugreporting.debuglog.ui.DebugLogFragment"
@@ -619,6 +651,43 @@
         android:name="de.rki.coronawarnapp.bugreporting.debuglog.ui.legal.DebugLogLegalFragment"
         android:label="DebugLogLegalFragment"
         tools:layout="@layout/bugreporting_legal_fragment" />
+    <fragment
+        android:id="@+id/submissionDeletionWarningFragment"
+        android:name="de.rki.coronawarnapp.ui.submission.deletionwarning.SubmissionDeletionWarningFragment"
+        android:label="SubmissionDeletionWarningFragment"
+        tools:layout="@layout/fragment_submission_deletion_warning">
+        <argument
+            android:name="isConsentGiven"
+            android:defaultValue="false"
+            app:argType="boolean" />
+        <argument
+            android:name="coronaTestQrCode"
+            app:argType="de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode" />
+        <action
+            android:id="@+id/action_submissionDeletionWarningFragment_to_submissionDispatcherFragment"
+            app:popUpTo="@id/submissionQRCodeScanFragment"
+            app:popUpToInclusive="true" />
+        <action
+            android:id="@+id/action_submissionDeletionWarningFragment_to_submissionTestResultPendingFragment"
+            app:destination="@id/submissionTestResultPendingFragment"
+            app:popUpTo="@id/mainFragment"
+            app:popUpToInclusive="false">
+            <argument
+                android:name="skipInitialTestResultRefresh"
+                android:defaultValue="true"
+                app:argType="boolean" />
+        </action>
+        <action
+            android:id="@+id/action_submissionDeletionWarningFragment_to_submissionTestResultAvailableFragment"
+            app:destination="@id/submissionTestResultAvailableFragment"
+            app:popUpTo="@id/mainFragment"
+            app:popUpToInclusive="false" />
+        <action
+            android:id="@+id/action_submissionDeletionWarningFragment_to_submissionConsentFragment"
+            app:popUpTo="@id/mainFragment"
+            app:popUpToInclusive="false"
+            app:destination="@id/submissionConsentFragment" />
+    </fragment>
 
     <fragment
         android:id="@+id/checkInsConsentFragment"
diff --git a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml
index 3f69d7869cbb2f30d4ed86edf20e0273a6fe2135..a1aa228c7fe3ca55915981d14af0bf92cb8a5c76 100644
--- a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml
@@ -85,4 +85,56 @@
     <!-- XTXT: homescreen card: body - negative -->
     <string name="ag_homescreen_card_pcr_body_result_date">"Test registriert am %1$s"</string>
 
+    <!-- XBUT: submission deletion warning button continue -->
+    <string name="submission_deletion_warning_continue_button">"Weiter"</string>
+    <!-- XBUT: submission deletion warning button cancel -->
+    <string name="submission_deletion_warning_cancel_button">"Abbrechen"</string>
+    <!-- XHED: submission deletion warning title -->
+    <string name="submission_deletion_warning_title">"Hinweis"</string>
+    <!-- YTXT: Headline for rapid test submission deletion warning -->
+    <string name="submission_deletion_warning_headline_antigen_test">"Sie haben bereits einen Schnelltest registriert."</string>
+    <!-- YTXT: Body for rapid test submission deletion warning -->
+    <string name="submission_deletion_warning_body_antigen_test">"Sie haben bereits einen Schnelltest registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren Schnelltest registrieren, wird der erste Schnelltest aus der App gelöscht."</string>
+    <!-- YTXT: Headline for PCR test submission deletion warning -->
+    <string name="submission_deletion_warning_headline_pcr_test">"Sie haben bereits einen PCR-Test registriert."</string>
+    <!-- YTXT: Body for PCR test submission deletion warning -->
+    <string name="submission_deletion_warning_body_pcr_test">"Sie haben bereits einen PCR-Test registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren PCR-Test registrieren, wird der erste PCR-Test aus der App gelöscht."</string>
+
+    <!-- XHED: submission result antigen fragment toolbar text -->
+    <string name="submission_test_result_toolbar_text">"Ihr Testergebnis"</string>
+    <!-- XHED: submission result antigen fragment card title -->
+    <string name="submission_test_result_antigen_title">"Schnelltest"</string>
+    <!-- XTXT: submission result antigen fragment card "found" text -->
+    <string name="submission_result_subtitle">"Befund"</string>
+    <!-- XTXT: submission test result fragment negative diagnosis -->
+    <string name="submission_test_result_negative">"Negativ"</string>
+    <!-- XTXT: submission test result fragment positive diagnosis -->
+    <string name="submission_test_result_positive">"Positiv"</string>
+    <!-- XTXT: submission result antigen fragment card patient name placeholder -->
+    <string name="submission_test_result_antigen_patient_name_placeholder">"Max Mustermann,"</string>
+    <!-- XTXT: submission result antigen fragment card patient birth date placeholder -->
+    <string name="submission_test_result_antigen_patient_birth_date_placeholder">"geboren 14.03.1987"</string>
+    <!-- XTXT: coronatest negative antigen result time and date placeholder -->
+    <string name="coronatest_negative_antigen_result_time_date_placeholder">"Ausgestellt: 10.03.2021, 18:01 Uhr"</string>
+    <!-- XTXT: submission result antigen fragment card negative result message -->
+    <string name="submission_test_result_negative_message">"Das Virus SARS-CoV-2 wurde bei Ihnen nicht nachgewiesen."</string>
+    <!-- XHED: submission result antigen fragment negative result proof title -->
+    <string name="submission_test_result_antigen_negative_proof_title">"Nachweis-Funktion"</string>
+    <!-- XHED: submission result antigen fragment negative result proof body -->
+    <string name="submission_test_result_antigen_negative_proof_body">"Sie können den hier angezeigten Befund auch als Nachweis für das Vorliegen eines negativen Schnelltest-Ergebnisses verwenden. Informieren Sie sich hierzu bitte auch über die Kriterien für die Anerkennung von Test-Nachweisen in Ihrem Bundesland. Bitte beachten Sie, dass Sie grundsätzlich nicht zum Nachweis per App verpflichtet sind. Sie können Ihr Schnelltest-Ergebnis im Rahmen der rechtlichen Bestimmungen auf andere Weise nachweisen."</string>
+    <!-- XHED: submission result antigen negative result counter title -->
+    <string name="submission_test_result_antigen_negative_counter_title">"Ergebnis liegt vor seit"</string>
+    <!-- XHED: coronatest negative antigen result first info title -->
+    <string name="coronatest_negative_antigen_result_first_info_title">"Ihr Schnelltest wurde hinzugefügt."</string>
+    <!-- XTXT: coronatest negative antigen result first info body -->
+    <string name="coronatest_negative_antigen_result_first_info_body">"Das Test-Ergebnis wird 48 Stunden hier angezeigt. Zusätzlich legt die App einen Eintrag in Ihrem Kontakt-Tagebuch an."</string>
+    <!-- XHED: coronatest negative antigen result second info title -->
+    <string name="coronatest_negative_antigen_result_second_info_title">"Befund negativ"</string>
+    <!-- XTXT: coronatest negative antigen result second info body -->
+    <string name="coronatest_negative_antigen_result_second_info_body">"Der Schnelltest hat keinen Nachweis für das Coronavirus SARS-CoV-2 bei Ihnen ergeben."</string>
+    <!-- XTXT: coronatest negative antigen result third info title -->
+    <string name="coronatest_negative_antigen_result_third_info_title">"Test entfernen"</string>
+    <!-- XTXT: coronatest negative antigen result third info body -->
+    <string name="coronatest_negative_antigen_restul_third_info_body">"Bitte entfernen Sie den Test wieder aus der Corona-Warn-App, damit Sie bei Bedarf einen neuen Test hinterlegen können."</string>
+
 </resources>
diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml
index 6d3f3cf7cb1483798811bf17b2611f4a53d014fc..36def2bf1ce5745feab7d6a91394df5c088e22a8 100644
--- a/Corona-Warn-App/src/main/res/values-de/strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/strings.xml
@@ -1136,19 +1136,19 @@
     <!-- XHED: Page subheadline for dispatcher options asking if they have already been tested: QR and TAN  -->
     <string name="submission_dispatcher_needs_testing_subheadline">"Sie haben sich bereits testen lassen?"</string>
     <!-- XHED: Page subheadline for dispatcher options asking if they already have a positive test: tele-TAN  -->
-    <string name="submission_dispatcher_already_positive_subheadline">"Ihr Test ist positiv?"</string>
+    <string name="submission_dispatcher_already_positive_subheadline">"Ihr PCR-Test ist positiv?"</string>
     <!-- YTXT: Dispatcher text for QR code option -->
     <string name="submission_dispatcher_card_qr">"Test mit QR-Code"</string>
     <!-- YTXT: Body text for QR code dispatcher option -->
     <string name="submission_dispatcher_qr_card_text">"Registrieren Sie Ihren Test, indem Sie den QR-Code Ihres Testdokuments scannen."</string>
     <!-- YTXT: Dispatcher text for TAN code option -->
-    <string name="submission_dispatcher_card_tan_code">"TAN-Eingabe"</string>
+    <string name="submission_dispatcher_card_tan_code">"TAN für PCR-Test eingeben"</string>
     <!-- YTXT: Body text for TAN code dispatcher option -->
-    <string name="submission_dispatcher_tan_code_card_text">"Ihnen liegt eine TAN vor? Weiter zur TAN-Eingabe, um andere zu warnen. "</string>
+    <string name="submission_dispatcher_tan_code_card_text">"Ihnen liegt eine TAN für Ihren PCR-Test vor? Weiter zur TAN-Eingabe, um andere zu warnen. "</string>
     <!-- YTXT: Dispatcher text for TELE-TAN option -->
-    <string name="submission_dispatcher_card_tan_tele">"Noch keine TAN?"</string>
+    <string name="submission_dispatcher_card_tan_tele">"TAN für PCR-Test anfragen?"</string>
     <!-- YTXT: Body text for TELE_TAN dispatcher option -->
-    <string name="submission_dispatcher_tan_tele_card_text">"Rufen Sie uns an und erhalten Sie eine TAN."</string>
+    <string name="submission_dispatcher_tan_tele_card_text">"Rufen Sie uns an und erhalten Sie eine TAN für Ihren PCR-Test."</string>
     <!-- XACT: Dispatcher Tan page title -->
     <string name="submission_dispatcher_accessibility_title">"Welche Informationen liegen Ihnen vor?"</string>
 
@@ -1270,7 +1270,7 @@
 
     <!-- Submission Contact -->
     <!-- XHED: Page title for contact page in submission flow -->
-    <string name="submission_contact_title">"TAN anfragen"</string>
+    <string name="submission_contact_title">"TAN für PCR-Test anfragen"</string>
     <!-- XHED: Page headline for contact page in submission flow -->
     <string name="submission_contact_headline">"Info zum Ablauf:"</string>
     <!-- YTXT: Body text for contact page in submission flow-->
@@ -1286,7 +1286,7 @@
     <!-- XLNK: Technical number which is called when the user clicks on the display number -->
     <string name="submission_contact_number_dial">"0800 7540002"</string>
     <!-- YTXT: Body text for step 2 of contact page-->
-    <string name="submission_contact_step_2_body">"Geben Sie die TAN in der App ein, um Ihren Test zu registrieren."</string>
+    <string name="submission_contact_step_2_body">"Geben Sie die TAN in der App ein, um Ihren PCR-Test zu registrieren."</string>
     <!-- YTXT: Body text for operating hours in contact page-->
     <string name="submission_contact_operating_hours_body">"Unser Kundenservice ist in den folgenden Sprachen für Sie da: Deutsch, Englisch, Türkisch \n\nErreichbarkeit:\nMo-So: Täglich 24 Stunden"</string>
     <!-- YTXT: Body text for technical contact and hotline information page -->
@@ -1371,23 +1371,6 @@
     <!-- YTXT: text for share result card-->
     <string name="submission_status_card_positive_result_share">"Teilen Sie Ihre Zufalls-IDs, damit andere gewarnt werden können."</string>
 
-
-    <!-- XBUT: submission deletion warning button continue -->
-    <string name="submission_deletion_warning_continue_button">"Weiter"</string>
-    <!-- XBUT: submission deletion warning button cancel -->
-    <string name="submission_deletion_warning_cancel_button">"Abbrechen"</string>
-    <!-- XHED: submission deletion warning title -->
-    <string name="submission_deletion_warning_title">"Hinweis"</string>
-    <!-- YTXT: Headline for rapid test submission deletion warning -->
-    <string name="submission_deletion_warning_headline_antigen_test">"Sie haben bereits einen Schnelltest registriert."</string>
-    <!-- YTXT: Body for rapid test submission deletion warning -->
-    <string name="submission_deletion_warning_body_antigen_test">"Sie haben bereits einen Schnelltest registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren Schnelltest registrieren, wird der erste Schnelltest aus der App gelöscht."</string>
-    <!-- YTXT: Headline for PCR test submission deletion warning -->
-    <string name="submission_deletion_warning_headline_pcr_test">"Sie haben bereits einen PCR-Test registriert."</string>
-    <!-- YTXT: Body for PCR test submission deletion warning -->
-    <string name="submission_deletion_warning_body_pcr_test">"Sie haben bereits einen PCR-Test registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren PCR-Test registrieren, wird der erste PCR-Test aus der App gelöscht."</string>
-
-
     <!-- Reenable risk card -->
     <!-- XHED: Card title for re-enable risk card -->
     <string name="reenable_risk_card_title">"Risiko-Überprüfung beendet"</string>
@@ -1407,11 +1390,11 @@
     <string name="dialog_reactivate_risk_calculation_button_negative">"Abbrechen"</string>
 
     <!-- Test Result Card -->
-    <string name="test_result_card_headline">"Ihr Befund:"</string>
+    <string name="test_result_card_headline">"Ihr %s-Befund:"</string>
     <!-- YTXT: virus name text -->
     <string name="test_result_card_virus_name_text">"SARS-CoV-2"</string>
     <!-- YTXT: registered at text -->
-    <string name="test_result_card_registered_at_text">"Registriert am %s"</string>
+    <string name="test_result_card_registered_at_text">"Test hinzugefügt am %s"</string>
     <!-- YTXT: negative status text -->
     <string name="test_result_card_status_negative">"Negativ"</string>
     <!-- YTXT: positive status text -->
diff --git a/Corona-Warn-App/src/main/res/values/antigen_strings.xml b/Corona-Warn-App/src/main/res/values/antigen_strings.xml
index f31cf3f47e5e171b391924ccf15a33e4445f30c2..b4c738edb4fae45ccf819aea2225f0f9db0692e2 100644
--- a/Corona-Warn-App/src/main/res/values/antigen_strings.xml
+++ b/Corona-Warn-App/src/main/res/values/antigen_strings.xml
@@ -85,4 +85,56 @@
     <!-- XTXT: homescreen card: body - negative -->
     <string name="ag_homescreen_card_pcr_body_result_date">"Test registriert am %1$s"</string>
 
+    <!-- XBUT: submission deletion warning button continue -->
+    <string name="submission_deletion_warning_continue_button">"Weiter"</string>
+    <!-- XBUT: submission deletion warning button cancel -->
+    <string name="submission_deletion_warning_cancel_button">"Abbrechen"</string>
+    <!-- XHED: submission deletion warning title -->
+    <string name="submission_deletion_warning_title">"Hinweis"</string>
+    <!-- YTXT: Headline for rapid test submission deletion warning -->
+    <string name="submission_deletion_warning_headline_antigen_test">"Sie haben bereits einen Schnelltest registriert."</string>
+    <!-- YTXT: Body for rapid test submission deletion warning -->
+    <string name="submission_deletion_warning_body_antigen_test">"Sie haben bereits einen Schnelltest registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren Schnelltest registrieren, wird der erste Schnelltest aus der App gelöscht."</string>
+    <!-- YTXT: Headline for PCR test submission deletion warning -->
+    <string name="submission_deletion_warning_headline_pcr_test">"Sie haben bereits einen PCR-Test registriert."</string>
+    <!-- YTXT: Body for PCR test submission deletion warning -->
+    <string name="submission_deletion_warning_body_pcr_test">"Sie haben bereits einen PCR-Test registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren PCR-Test registrieren, wird der erste PCR-Test aus der App gelöscht."</string>
+
+    <!-- XHED: submission result antigen fragment toolbar text -->
+    <string name="submission_test_result_toolbar_text">"Ihr Testergebnis"</string>
+    <!-- XHED: submission result antigen fragment card title -->
+    <string name="submission_test_result_antigen_title">"Schnelltest"</string>
+    <!-- XTXT: submission result antigen fragment card "found" text -->
+    <string name="submission_result_subtitle">"Befund"</string>
+    <!-- XTXT: submission test result fragment negative diagnosis -->
+    <string name="submission_test_result_negative">"Negativ"</string>
+    <!-- XTXT: submission test result fragment positive diagnosis -->
+    <string name="submission_test_result_positive">"Positiv"</string>
+    <!-- XTXT: submission result antigen fragment card patient name placeholder -->
+    <string name="submission_test_result_antigen_patient_name_placeholder">"Max Mustermann,"</string>
+    <!-- XTXT: submission result antigen fragment card patient birth date placeholder -->
+    <string name="submission_test_result_antigen_patient_birth_date_placeholder">"geboren 14.03.1987"</string>
+    <!-- XTXT: coronatest negative antigen result time and date placeholder -->
+    <string name="coronatest_negative_antigen_result_time_date_placeholder">"Ausgestellt: 10.03.2021, 18:01 Uhr"</string>
+    <!-- XTXT: submission result antigen fragment card negative result message -->
+    <string name="submission_test_result_negative_message">"Das Virus SARS-CoV-2 wurde bei Ihnen nicht nachgewiesen."</string>
+    <!-- XHED: submission result antigen fragment negative result proof title -->
+    <string name="submission_test_result_antigen_negative_proof_title">"Nachweis-Funktion"</string>
+    <!-- XHED: submission result antigen fragment negative result proof body -->
+    <string name="submission_test_result_antigen_negative_proof_body">"Sie können den hier angezeigten Befund auch als Nachweis für das Vorliegen eines negativen Schnelltest-Ergebnisses verwenden. Informieren Sie sich hierzu bitte auch über die Kriterien für die Anerkennung von Test-Nachweisen in Ihrem Bundesland. Bitte beachten Sie, dass Sie grundsätzlich nicht zum Nachweis per App verpflichtet sind. Sie können Ihr Schnelltest-Ergebnis im Rahmen der rechtlichen Bestimmungen auf andere Weise nachweisen."</string>
+    <!-- XHED: submission result antigen negative result counter title -->
+    <string name="submission_test_result_antigen_negative_counter_title">"Ergebnis liegt vor seit"</string>
+    <!-- XHED: coronatest negative antigen result first info title -->
+    <string name="coronatest_negative_antigen_result_first_info_title">"Ihr Schnelltest wurde hinzugefügt."</string>
+    <!-- XTXT: coronatest negative antigen result first info body -->
+    <string name="coronatest_negative_antigen_result_first_info_body">"Das Test-Ergebnis wird 48 Stunden hier angezeigt. Zusätzlich legt die App einen Eintrag in Ihrem Kontakt-Tagebuch an."</string>
+    <!-- XHED: coronatest negative antigen result second info title -->
+    <string name="coronatest_negative_antigen_result_second_info_title">"Befund negativ"</string>
+    <!-- XTXT: coronatest negative antigen result second info body -->
+    <string name="coronatest_negative_antigen_result_second_info_body">"Der Schnelltest hat keinen Nachweis für das Coronavirus SARS-CoV-2 bei Ihnen ergeben."</string>
+    <!-- XTXT: coronatest negative antigen result third info title -->
+    <string name="coronatest_negative_antigen_result_third_info_title">"Test entfernen"</string>
+    <!-- XTXT: coronatest negative antigen result third info body -->
+    <string name="coronatest_negative_antigen_restul_third_info_body">"Bitte entfernen Sie den Test wieder aus der Corona-Warn-App, damit Sie bei Bedarf einen neuen Test hinterlegen können."</string>
+
 </resources>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/values/colors.xml b/Corona-Warn-App/src/main/res/values/colors.xml
index 19fa4b0abc97785476b9d4a4fbd9b0cc71c56e40..566b2ecaf1aa5b0f3135e86cf32a5f616039ed7b 100644
--- a/Corona-Warn-App/src/main/res/values/colors.xml
+++ b/Corona-Warn-App/src/main/res/values/colors.xml
@@ -24,6 +24,7 @@
     <color name="colorTextPrimary1Inverted">#FFFFFF</color>
     <color name="colorTextPrimary1Stable">#17191A</color>
     <color name="colorTextPrimary1InvertedStable">#FFFFFF</color>
+    <color name="colorTextPrimary1InvertedStableTransparent">#80FFFFFF</color>
     <color name="colorTextPrimary2">#9917191A</color>
     <color name="colorTextPrimary3">#4D17191A</color>
     <color name="colorTextEmphasizedButton">#FFFFFF</color>
diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml
index 25a9ac057c69fc7ac41a07be1f4ee2852c4b2e09..aba4d20383a17fd7024a8cdf7252b135eaf01745 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -1387,23 +1387,6 @@
     <!-- YTXT: text for share result card-->
     <string name="submission_status_card_positive_result_share">"Share your random IDs so that others can be warned."</string>
 
-
-    <!-- XBUT: submission deletion warning button continue -->
-    <string name="submission_deletion_warning_continue_button">"Weiter"</string>
-    <!-- XBUT: submission deletion warning button cancel -->
-    <string name="submission_deletion_warning_cancel_button">"Abbrechen"</string>
-    <!-- XHED: submission deletion warning title -->
-    <string name="submission_deletion_warning_title">"Hinweis"</string>
-    <!-- YTXT: Headline for rapid test submission deletion warning -->
-    <string name="submission_deletion_warning_headline_antigen_test">"Sie haben bereits einen Schnelltest registriert."</string>
-    <!-- YTXT: Body for rapid test submission deletion warning -->
-    <string name="submission_deletion_warning_body_antigen_test">"Sie haben bereits einen Schnelltest registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren Schnelltest registrieren, wird der erste Schnelltest aus der App gelöscht."</string>
-    <!-- YTXT: Headline for PCR test submission deletion warning -->
-    <string name="submission_deletion_warning_headline_pcr_test">"Sie haben bereits einen PCR-Test registriert."</string>
-    <!-- YTXT: Body for PCR test submission deletion warning -->
-    <string name="submission_deletion_warning_body_pcr_test">"Sie haben bereits einen PCR-Test registriert. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten. Wenn Sie einen weiteren PCR-Test registrieren, wird der erste PCR-Test aus der App gelöscht."</string>
-
-
     <!-- Reenable risk card -->
     <!-- XHED: Card title for re-enable risk card -->
     <string name="reenable_risk_card_title">"Exposure Check Ended"</string>
@@ -1427,7 +1410,7 @@
     <!-- YTXT: virus name text -->
     <string name="test_result_card_virus_name_text">"SARS-CoV-2"</string>
     <!-- YTXT: registered at text -->
-    <string name="test_result_card_registered_at_text">"Registered on %s"</string>
+    <string name="test_result_card_registered_at_text">"Test hinzugefügt am %s"</string>
     <!-- YTXT: negative status text -->
     <string name="test_result_card_status_negative">"Negative"</string>
     <!-- YTXT: positive status text -->
diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml
index 927a1ef0dba0d35470c54b1d435a62e8ae7f8b88..83fb7087b9c6515e8c37a04b8099c88c9ba08cc3 100644
--- a/Corona-Warn-App/src/main/res/values/styles.xml
+++ b/Corona-Warn-App/src/main/res/values/styles.xml
@@ -277,6 +277,9 @@
     <style name="headline4" parent="@style/TextAppearance.MaterialComponents.Headline4">
         <item name="android:textColor">@color/colorTextPrimary1</item>
     </style>
+    <style name="headline4Bold" parent="@style/headline4">
+        <item name="android:textStyle">bold</item>
+    </style>
 
     <style name="headline5" parent="@style/TextAppearance.MaterialComponents.Headline5">
         <item name="android:textColor">@color/colorTextPrimary1</item>
@@ -328,6 +331,11 @@
         <item name="android:textColor">@color/colorTextPrimary2</item>
     </style>
 
+    <style name="body3Medium" parent="@style/body2">
+        <item name="android:textSize">10sp</item>
+        <item name="android:textColor">@color/colorTextPrimary1InvertedStableTransparent</item>
+    </style>
+
     <style name="bodyButton" parent="@style/TextAppearance.MaterialComponents.Button">
         <item name="android:textColor">@color/colorTextPrimary1</item>
     </style>
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationServiceTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationServiceTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7e4f8ecd181e644e15a99cf419fbe757ae7711b0
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/notification/ShareTestResultNotificationServiceTest.kt
@@ -0,0 +1,126 @@
+package de.rki.coronawarnapp.coronatest.notification
+
+import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.type.CoronaTest
+import de.rki.coronawarnapp.main.CWASettings
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+import testhelpers.coroutines.runBlockingTest2
+
+class ShareTestResultNotificationServiceTest : BaseTest() {
+    @MockK lateinit var cwaSettings: CWASettings
+    @MockK lateinit var coronaTestRepository: CoronaTestRepository
+    @MockK lateinit var shareTestResultNotification: ShareTestResultNotification
+
+    private val coronaTestFlow = MutableStateFlow(
+        emptySet<CoronaTest>()
+    )
+    private var numberOfRemainingSharePositiveTestResultReminders: Int = Int.MIN_VALUE
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+        every { coronaTestRepository.coronaTests } returns coronaTestFlow
+        every { cwaSettings.numberOfRemainingSharePositiveTestResultReminders = any() } answers {
+            numberOfRemainingSharePositiveTestResultReminders = arg(0)
+        }
+        every { cwaSettings.numberOfRemainingSharePositiveTestResultReminders } answers {
+            numberOfRemainingSharePositiveTestResultReminders
+        }
+
+        every { shareTestResultNotification.showSharePositiveTestResultNotification(any()) } just Runs
+        every { shareTestResultNotification.cancelSharePositiveTestResultNotification() } just Runs
+        every { shareTestResultNotification.scheduleSharePositiveTestResultReminder() } just Runs
+    }
+
+    private fun createInstance(scope: CoroutineScope) = ShareTestResultNotificationService(
+        appScope = scope,
+        cwaSettings = cwaSettings,
+        coronaTestRepository = coronaTestRepository,
+        notification = shareTestResultNotification,
+    )
+
+    @Test
+    fun `any test which allows submission triggers scheduling`() = runBlockingTest2(ignoreActive = true) {
+        val instance = createInstance(this)
+
+        coronaTestFlow.value = setOf(
+            mockk<CoronaTest>().apply {
+                every { isSubmissionAllowed } returns true
+                every { isSubmitted } returns false
+            }
+        )
+
+        instance.setup()
+
+        verify { shareTestResultNotification.scheduleSharePositiveTestResultReminder() }
+        verify { cwaSettings.numberOfRemainingSharePositiveTestResultReminders = 2 }
+    }
+
+    @Test
+    fun `showing a notification consumes a token`() = runBlockingTest2(ignoreActive = true) {
+        val instance = createInstance(this)
+        numberOfRemainingSharePositiveTestResultReminders = 2
+
+        instance.maybeShowSharePositiveTestResultNotification(1)
+
+        numberOfRemainingSharePositiveTestResultReminders shouldBe 1
+
+        verify { shareTestResultNotification.showSharePositiveTestResultNotification(1) }
+    }
+
+    @Test
+    fun `if there are no tokens left to show a notification, cancel the current one`() =
+        runBlockingTest2(ignoreActive = true) {
+            val instance = createInstance(this)
+            numberOfRemainingSharePositiveTestResultReminders = 0
+
+            instance.maybeShowSharePositiveTestResultNotification(1)
+
+            numberOfRemainingSharePositiveTestResultReminders shouldBe 0
+
+            verify { shareTestResultNotification.cancelSharePositiveTestResultNotification() }
+        }
+
+    @Test
+    fun `any test which allowes submission triggers scheduling`() = runBlockingTest2(ignoreActive = true) {
+        val instance = createInstance(this)
+
+        coronaTestFlow.value = setOf(
+            mockk<CoronaTest>().apply {
+                every { isSubmissionAllowed } returns true
+                every { isSubmitted } returns false
+            }
+        )
+
+        instance.setup()
+
+        verify { shareTestResultNotification.scheduleSharePositiveTestResultReminder() }
+        verify { cwaSettings.numberOfRemainingSharePositiveTestResultReminders = 2 }
+    }
+
+    @Test
+    fun `if there are no tests, we reset scheduling`() = runBlockingTest2(ignoreActive = true) {
+        val instance = createInstance(this)
+
+        coronaTestFlow.value = emptySet()
+
+        instance.setup()
+
+        advanceUntilIdle()
+
+        verify { shareTestResultNotification.cancelSharePositiveTestResultNotification() }
+        verify { cwaSettings.numberOfRemainingSharePositiveTestResultReminders = Int.MIN_VALUE }
+    }
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
index a5a8653dd65751954d95be2c1057f26dd60f0a42..43dc0a8ba7f789d898f1d03bc73bfb4d0f632097 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt
@@ -3,12 +3,9 @@ package de.rki.coronawarnapp.main.home
 import android.content.Context
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
-import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
-import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest
 import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler
 import de.rki.coronawarnapp.environment.BuildConfigWrap
 import de.rki.coronawarnapp.main.CWASettings
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.statistics.source.StatisticsProvider
 import de.rki.coronawarnapp.storage.TracingRepository
 import de.rki.coronawarnapp.storage.TracingSettings
@@ -31,11 +28,9 @@ import io.mockk.every
 import io.mockk.impl.annotations.MockK
 import io.mockk.mockk
 import io.mockk.mockkObject
-import io.mockk.verify
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.runBlocking
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
@@ -56,7 +51,6 @@ class HomeFragmentViewModelTest : BaseTest() {
     @MockK lateinit var tracingStateProviderFactory: TracingStateProvider.Factory
     @MockK lateinit var coronaTestRepository: CoronaTestRepository
     @MockK lateinit var tracingRepository: TracingRepository
-    @MockK lateinit var shareTestResultNotificationService: ShareTestResultNotificationService
     @MockK lateinit var submissionRepository: SubmissionRepository
     @MockK lateinit var cwaSettings: CWASettings
     @MockK lateinit var appConfigProvider: AppConfigProvider
@@ -86,7 +80,6 @@ class HomeFragmentViewModelTest : BaseTest() {
         errorResetTool = errorResetTool,
         tracingStatus = generalTracingStatus,
         tracingRepository = tracingRepository,
-        shareTestResultNotificationService = shareTestResultNotificationService,
         submissionRepository = submissionRepository,
         coronaTestRepository = coronaTestRepository,
         tracingStateProviderFactory = tracingStateProviderFactory,
@@ -145,48 +138,6 @@ class HomeFragmentViewModelTest : BaseTest() {
         }
     }
 
-    @Test
-    fun `positive test result notification is triggered on positive QR code result`() {
-//        every { submissionRepository.deviceUIStateFlow } returns flowOf(
-//            NetworkRequestWrapper.RequestSuccessful(PAIRED_POSITIVE)
-//        )
-        every { submissionRepository.pcrTest } returns flowOf(
-            mockk<PCRCoronaTest>().apply {
-                every { testResult } returns CoronaTestResult.PCR_POSITIVE
-                every { lastError } returns null
-            }
-        )
-        every { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() } returns Unit
-
-        runBlocking {
-            createInstance().apply {
-                observeTestResultToSchedulePositiveTestResultReminder()
-                verify { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() }
-            }
-        }
-    }
-
-    @Test
-    fun `positive test result notification is triggered on positive TeleTan code result`() {
-//        every { submissionRepository.deviceUIStateFlow } returns flowOf(
-//            NetworkRequestWrapper.RequestSuccessful(PAIRED_POSITIVE_TELETAN)
-//        )
-        every { submissionRepository.pcrTest } returns flowOf(
-            mockk<PCRCoronaTest>().apply {
-                every { testResult } returns CoronaTestResult.PCR_POSITIVE
-                every { lastError } returns null
-            }
-        )
-        every { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() } returns Unit
-
-        runBlocking {
-            createInstance().apply {
-                observeTestResultToSchedulePositiveTestResultReminder()
-                verify { shareTestResultNotificationService.scheduleSharePositiveTestResultReminder() }
-            }
-        }
-    }
-
     @Test
     fun `test correct order of displaying delta onboarding, release notes and popups`() {
         every { cwaSettings.wasInteroperabilityShownAtLeastOnce } returns false andThen true
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationServiceTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationServiceTest.kt
index 96a785177817e7ca7dfc02ada3aec020d859fb44..e81162622143b53c91be6f9663985f1e6f020f13 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationServiceTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/notification/TestResultAvailableNotificationServiceTest.kt
@@ -9,7 +9,6 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.coronatest.server.CoronaTestResult
 import de.rki.coronawarnapp.main.CWASettings
 import de.rki.coronawarnapp.util.device.ForegroundState
-import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.Runs
 import io.mockk.coEvery
@@ -53,7 +52,7 @@ class TestResultAvailableNotificationServiceTest : BaseTest() {
         every { notificationHelper.newBaseBuilder() } returns mockk(relaxed = true)
     }
 
-    fun createInstance() = TestResultAvailableNotificationService(
+    fun createInstance() = PCRTestResultAvailableNotificationService(
         context = context,
         foregroundState = foregroundState,
         navDeepLinkBuilderProvider = navDeepLinkBuilderProvider,
@@ -61,18 +60,6 @@ class TestResultAvailableNotificationServiceTest : BaseTest() {
         cwaSettings = cwaSettings
     )
 
-    @Test
-    fun `check destination`() {
-        val negative = createInstance().getNotificationDestination(CoronaTestResult.PCR_NEGATIVE)
-        negative shouldBe (R.id.submissionTestResultPendingFragment)
-
-        val invalid = createInstance().getNotificationDestination(CoronaTestResult.PCR_INVALID)
-        invalid shouldBe (R.id.submissionTestResultPendingFragment)
-
-        val positive = createInstance().getNotificationDestination(CoronaTestResult.PCR_POSITIVE)
-        positive shouldBe (R.id.submissionTestResultPendingFragment)
-    }
-
     @Test
     fun `test notification in foreground`() = runBlockingTest {
         coEvery { foregroundState.isInForeground } returns flow { emit(true) }
@@ -98,7 +85,6 @@ class TestResultAvailableNotificationServiceTest : BaseTest() {
 
         verifyOrder {
             navDeepLinkBuilderProvider.get()
-            instance.getNotificationDestination(CoronaTestResult.PCR_POSITIVE)
             context.getString(R.string.notification_headline_test_result_ready)
             context.getString(R.string.notification_body_test_result_ready)
             notificationHelper.sendNotification(
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/task/SubmissionTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/task/SubmissionTaskTest.kt
index 9a10b45206fd8216eb1325d4da42275e83f3341f..db488d6baf44c6e360b26e399cced531bfa15238 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/task/SubmissionTaskTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/task/SubmissionTaskTest.kt
@@ -4,10 +4,10 @@ import com.google.android.gms.nearby.exposurenotification.TemporaryExposureKey
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.appconfig.ConfigData
 import de.rki.coronawarnapp.coronatest.CoronaTestRepository
+import de.rki.coronawarnapp.coronatest.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.playbook.Playbook
 import de.rki.coronawarnapp.presencetracing.checkins.CheckIn
 import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository
@@ -53,7 +53,7 @@ class SubmissionTaskTest : BaseTest() {
     @MockK lateinit var tekHistoryStorage: TEKHistoryStorage
     @MockK lateinit var submissionSettings: SubmissionSettings
     @MockK lateinit var shareTestResultNotificationService: ShareTestResultNotificationService
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
     @MockK lateinit var autoSubmission: AutoSubmission
 
     @MockK lateinit var tekBatch: TEKHistoryStorage.TEKBatch
@@ -145,7 +145,6 @@ class SubmissionTaskTest : BaseTest() {
         every { analyticsKeySubmissionCollector.reportSubmitted() } just Runs
         every { analyticsKeySubmissionCollector.reportSubmittedInBackground() } just Runs
 
-        every { shareTestResultNotificationService.cancelSharePositiveTestResultNotification() } just Runs
         every { testResultAvailableNotificationService.cancelTestResultAvailableNotification() } just Runs
 
         every { autoSubmission.updateMode(any()) } just Runs
@@ -236,7 +235,6 @@ class SubmissionTaskTest : BaseTest() {
             coronaTestRepository.markAsSubmitted(any())
             backgroundWorkScheduler.startWorkScheduler()
 
-            shareTestResultNotificationService.cancelSharePositiveTestResultNotification()
             testResultAvailableNotificationService.cancelTestResultAvailableNotification()
         }
 
@@ -293,7 +291,6 @@ class SubmissionTaskTest : BaseTest() {
             tekHistoryStorage.clear()
             checkInRepository.clear()
             settingSymptomsPreference.update(any())
-            shareTestResultNotificationService.cancelSharePositiveTestResultNotification()
             autoSubmission.updateMode(any())
         }
         submissionSettings.symptoms.value shouldBe userSymptoms
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt
index c8430ea2a07a0a8ee28b306d979c7157397ab775..4d4c07cbafcb1e682049f28e99e49d875d106588 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/submission/testresult/pending/SubmissionTestResultPendingViewModelTest.kt
@@ -2,7 +2,6 @@ package de.rki.coronawarnapp.submission.testresult.pending
 
 import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.exception.http.CwaWebException
-import de.rki.coronawarnapp.notification.ShareTestResultNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.ui.submission.testresult.pending.SubmissionTestResultPendingViewModel
 import io.kotest.matchers.shouldBe
@@ -25,8 +24,8 @@ import testhelpers.extensions.InstantExecutorExtension
 @ExtendWith(InstantExecutorExtension::class)
 class SubmissionTestResultPendingViewModelTest : BaseTest() {
 
-    @MockK lateinit var shareTestResultNotificationService: ShareTestResultNotificationService
     @MockK lateinit var submissionRepository: SubmissionRepository
+    @MockK lateinit var testType: CoronaTest.Type
 
     private val testFlow = MutableStateFlow<CoronaTest?>(null)
 
@@ -42,8 +41,8 @@ class SubmissionTestResultPendingViewModelTest : BaseTest() {
 
     fun createInstance(scope: CoroutineScope = TestCoroutineScope()) = SubmissionTestResultPendingViewModel(
         dispatcherProvider = scope.asDispatcherProvider(),
-        shareTestResultNotificationService = shareTestResultNotificationService,
-        submissionRepository = submissionRepository
+        submissionRepository = submissionRepository,
+        testType = testType
     )
 
     @Test
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModelTest.kt
index 2736f0d6a4fa231b0042554025af3b66bad965f6..4041652f0d9087a6b198b28fa8ce9d35b8f8021a 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/presencetracing/organizer/create/TraceLocationCreateViewModelTest.kt
@@ -12,9 +12,9 @@ import io.mockk.coEvery
 import io.mockk.impl.annotations.MockK
 import kotlinx.coroutines.test.runBlockingTest
 import okio.ByteString.Companion.encode
+import org.joda.time.DateTime
 import org.joda.time.Duration
 import org.joda.time.Instant
-import org.joda.time.LocalDateTime
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
@@ -60,8 +60,8 @@ internal class TraceLocationCreateViewModelTest : BaseTest() {
 
         viewModel.address = "Address"
         viewModel.description = "Description"
-        viewModel.begin = LocalDateTime()
-        viewModel.end = LocalDateTime().plusHours(1)
+        viewModel.begin = DateTime()
+        viewModel.end = DateTime().plusHours(1)
         viewModel.checkInLength = Duration.standardMinutes(1)
 
         viewModel.uiState.observeForTesting {
@@ -76,8 +76,8 @@ internal class TraceLocationCreateViewModelTest : BaseTest() {
 
         viewModel.address = "Address"
         viewModel.description = "A".repeat(101)
-        viewModel.begin = LocalDateTime()
-        viewModel.end = LocalDateTime().plusHours(1)
+        viewModel.begin = DateTime()
+        viewModel.end = DateTime().plusHours(1)
         viewModel.checkInLength = Duration.standardMinutes(1)
 
         viewModel.uiState.observeForTesting {
@@ -92,8 +92,8 @@ internal class TraceLocationCreateViewModelTest : BaseTest() {
 
         viewModel.address = "A".repeat(101)
         viewModel.description = "Description"
-        viewModel.begin = LocalDateTime()
-        viewModel.end = LocalDateTime().plusHours(1)
+        viewModel.begin = DateTime()
+        viewModel.end = DateTime().plusHours(1)
         viewModel.checkInLength = Duration.standardMinutes(1)
 
         viewModel.uiState.observeForTesting {
@@ -136,8 +136,8 @@ internal class TraceLocationCreateViewModelTest : BaseTest() {
 
         viewModel.address = "Address"
         viewModel.description = "Description"
-        viewModel.begin = LocalDateTime()
-        viewModel.end = LocalDateTime().plusHours(1)
+        viewModel.begin = DateTime()
+        viewModel.end = DateTime().plusHours(1)
         viewModel.checkInLength = Duration.ZERO
 
         viewModel.uiState.observeForTesting {
@@ -151,8 +151,8 @@ internal class TraceLocationCreateViewModelTest : BaseTest() {
 
         viewModel.address = "Address"
         viewModel.description = "Description"
-        viewModel.begin = LocalDateTime().plusHours(1)
-        viewModel.end = LocalDateTime()
+        viewModel.begin = DateTime().plusHours(1)
+        viewModel.end = DateTime()
         viewModel.checkInLength = Duration.ZERO
 
         viewModel.uiState.observeForTesting {
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt
index 8a3ef38c00f8fc402969356ee5b861921c3d73bc..73c696f80716aaa3db533ca76147bc8071b8bea8 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/consent/SubmissionConsentViewModelTest.kt
@@ -43,11 +43,9 @@ class SubmissionConsentViewModelTest {
         every { submissionRepository.giveConsentToSubmission(any()) } just Runs
         every { analyticsKeySubmissionCollector.reportAdvancedConsentGiven() } just Runs
         viewModel = SubmissionConsentViewModel(
-            submissionRepository,
             interoperabilityRepository,
             dispatcherProvider = TestDispatcherProvider(),
             tekHistoryProvider,
-            analyticsKeySubmissionCollector = analyticsKeySubmissionCollector
         )
     }
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt
index 02ed331db2b6e0bea748693db91616d7b058694b..78b2f8517bf46566e954672c23ff137bf253d5a0 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanViewModelTest.kt
@@ -15,11 +15,13 @@ import io.mockk.every
 import io.mockk.impl.annotations.MockK
 import io.mockk.mockk
 import io.mockk.verify
-import org.junit.Assert
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.runBlockingTest
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import testhelpers.BaseTest
+import testhelpers.TestDispatcherProvider
 import testhelpers.extensions.InstantExecutorExtension
 import testhelpers.preferences.mockFlowPreference
 
@@ -30,15 +32,23 @@ class SubmissionQRCodeScanViewModelTest : BaseTest() {
     @MockK lateinit var cameraSettings: CameraSettings
     @MockK lateinit var qrCodeValidator: CoronaTestQrCodeValidator
 
+    private val coronaTestFlow = MutableStateFlow<CoronaTest?>(
+        null
+    )
+
     @BeforeEach
     fun setUp() {
         MockKAnnotations.init(this)
+
+        every { submissionRepository.testForType(any()) } returns coronaTestFlow
     }
 
     private fun createViewModel() = SubmissionQRCodeScanViewModel(
+        TestDispatcherProvider(),
         submissionRepository,
         cameraSettings,
-        qrCodeValidator
+        isConsentGiven = true,
+        qrCodeValidator,
     )
 
     @Test
@@ -64,16 +74,17 @@ class SubmissionQRCodeScanViewModelTest : BaseTest() {
         QRCodeCensor.lastGUID = null
 
         viewModel.validateTestGUID(validQrCode)
-        viewModel.scanStatusValue.let { Assert.assertEquals(ScanStatus.SUCCESS, it.value) }
+        viewModel.scanStatusValue.observeForever {}
+        viewModel.scanStatusValue.value shouldBe ScanStatus.SUCCESS
         QRCodeCensor.lastGUID = guid
 
         // invalid guid
         viewModel.validateTestGUID(invalidQrCode)
-        viewModel.scanStatusValue.let { Assert.assertEquals(ScanStatus.INVALID, it.value) }
+        viewModel.scanStatusValue.value shouldBe ScanStatus.INVALID
     }
 
     @Test
-    fun `doDeviceRegistration calls TestResultDataCollector`() {
+    fun `doDeviceRegistration calls TestResultDataCollector`() = runBlockingTest {
         val viewModel = createViewModel()
         val mockResult = mockk<CoronaTestQRCode>().apply {
             every { registrationIdentifier } returns "guid"
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt
index 757497cea3cf68b9202ee6aa258789c4940c54ad..8d160fe6b9fe5ec7ffd334e7e9940ca2b03be061 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/testresult/SubmissionTestResultConsentGivenViewModelTest.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.ui.submission.testresult
 
 import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.submission.SubmissionRepository
 import de.rki.coronawarnapp.submission.auto.AutoSubmission
 import de.rki.coronawarnapp.ui.submission.testresult.positive.SubmissionTestResultConsentGivenViewModel
@@ -20,7 +20,7 @@ import testhelpers.extensions.InstantExecutorExtension
 class SubmissionTestResultConsentGivenViewModelTest : BaseTest() {
     @MockK lateinit var submissionRepository: SubmissionRepository
     @MockK lateinit var autoSubmission: AutoSubmission
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
     @MockK lateinit var analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector
     lateinit var viewModel: SubmissionTestResultConsentGivenViewModel
 
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 9b23f5eca4035d5da1266bdc020f05dc3bb131fe..9cfae529d79f8e5d2e56c1913b52789926d6335c 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
@@ -4,6 +4,7 @@ import de.rki.coronawarnapp.util.mutate
 import io.kotest.assertions.throwables.shouldThrow
 import io.kotest.matchers.shouldBe
 import io.kotest.matchers.types.instanceOf
+import io.kotest.matchers.types.shouldBeInstanceOf
 import io.mockk.coEvery
 import io.mockk.coVerify
 import io.mockk.mockk
@@ -26,8 +27,9 @@ import kotlin.concurrent.thread
 
 class HotDataFlowTest : BaseTest() {
 
+    // Without an init value, there isn't a way to keep using the flow
     @Test
-    fun `init happens on first collection and exception is forwarded`() {
+    fun `exceptions on initializen are rethrown`() {
         val testScope = TestCoroutineScope()
         val hotData = HotDataFlow<String>(
             loggingTag = "tag",
@@ -35,29 +37,6 @@ class HotDataFlowTest : BaseTest() {
             coroutineContext = Dispatchers.Unconfined,
             startValueProvider = { throw IOException() }
         )
-
-        runBlocking {
-            // This blocking scope get's the init exception as the first caller
-            shouldThrow<IOException> {
-                hotData.data.first()
-            }
-        }
-
-        testScope.advanceUntilIdle()
-
-        testScope.uncaughtExceptions.singleOrNull() shouldBe null
-    }
-
-    @Test
-    fun `exception is not forwarded if flag is set`() {
-        val testScope = TestCoroutineScope()
-        val hotData = HotDataFlow<String>(
-            loggingTag = "tag",
-            scope = testScope,
-            coroutineContext = Dispatchers.Unconfined,
-            forwardException = false,
-            startValueProvider = { throw IOException() }
-        )
         runBlocking {
             withTimeoutOrNull(500) {
                 // This blocking scope get's the init exception as the first caller
@@ -136,9 +115,10 @@ class HotDataFlowTest : BaseTest() {
             thread {
                 (1..200).forEach { _ ->
                     sleep(10)
-                    hotData.updateSafely {
-                        this + 1L
-                    }
+                    hotData.updateAsync(
+                        onUpdate = { this + 1L },
+                        onError = { throw it }
+                    )
                 }
             }
         }
@@ -174,7 +154,7 @@ class HotDataFlowTest : BaseTest() {
         (1..10).forEach { _ ->
             thread {
                 (1..400).forEach { _ ->
-                    hotData.updateSafely {
+                    hotData.updateAsync {
                         mutate {
                             this["data"] = getValue("data").copy(
                                 number = getValue("data").number + 1
@@ -207,10 +187,10 @@ class HotDataFlowTest : BaseTest() {
         val testCollector = hotData.data.test(startOnScope = testScope)
         testCollector.silent = true
 
-        hotData.updateSafely { "1" }
-        hotData.updateSafely { "2" }
-        hotData.updateSafely { "2" }
-        hotData.updateSafely { "1" }
+        hotData.updateAsync { "1" }
+        hotData.updateAsync { "2" }
+        hotData.updateAsync { "2" }
+        hotData.updateAsync { "1" }
 
         runBlocking {
             testCollector.await { list, l -> list.size == 3 }
@@ -236,9 +216,9 @@ class HotDataFlowTest : BaseTest() {
             val sub2 = hotData.data.test(tag = "sub2", startOnScope = this)
             val sub3 = hotData.data.test(tag = "sub3", startOnScope = this)
 
-            hotData.updateSafely { "A" }
-            hotData.updateSafely { "B" }
-            hotData.updateSafely { "C" }
+            hotData.updateAsync { "A" }
+            hotData.updateAsync { "B" }
+            hotData.updateAsync { "C" }
 
             listOf(sub1, sub2, sub3).forEach {
                 it.await { list, s -> list.size == 4 }
@@ -268,7 +248,7 @@ class HotDataFlowTest : BaseTest() {
         testCollector1.silent = false
 
         (1..10).forEach { _ ->
-            hotData.updateSafely {
+            hotData.updateAsync {
                 this + 1L
             }
         }
@@ -308,7 +288,7 @@ class HotDataFlowTest : BaseTest() {
             sharingBehavior = SharingStarted.Lazily
         )
 
-        hotData.updateSafely {
+        hotData.updateAsync {
             delay(2000)
             this + 1
         }
@@ -324,4 +304,86 @@ class HotDataFlowTest : BaseTest() {
 
         testCollector.cancel()
     }
+
+    @Test
+    fun `blocking update rethrows error`() = runBlocking {
+        val testScope = TestCoroutineScope()
+        val hotData = HotDataFlow(
+            loggingTag = "tag",
+            scope = testScope,
+            coroutineContext = testScope.coroutineContext,
+            startValueProvider = {
+                delay(2000)
+                2
+            },
+            sharingBehavior = SharingStarted.Lazily
+        )
+
+        val testCollector = hotData.data.test(startOnScope = testScope)
+
+        testScope.advanceUntilIdle()
+
+        shouldThrow<IOException> {
+            hotData.updateBlocking { throw IOException("Suprise") } shouldBe 0
+        }
+        hotData.data.first() shouldBe 2
+
+        hotData.updateBlocking { 3 } shouldBe 3
+        hotData.data.first() shouldBe 3
+
+        testScope.uncaughtExceptions.singleOrNull() shouldBe null
+
+        testCollector.cancel()
+    }
+
+    @Test
+    fun `async updates error handler`() {
+        val testScope = TestCoroutineScope()
+
+        val hotData = HotDataFlow(
+            loggingTag = "tag",
+            scope = testScope,
+            startValueProvider = { 1 },
+            sharingBehavior = SharingStarted.Lazily
+        )
+
+        val testCollector = hotData.data.test(startOnScope = testScope)
+        testScope.advanceUntilIdle()
+
+        hotData.updateAsync { throw IOException("Suprise") }
+
+        testScope.advanceUntilIdle()
+
+        testScope.uncaughtExceptions.single() shouldBe instanceOf(IOException::class)
+
+        testCollector.cancel()
+    }
+
+    @Test
+    fun `async updates rethrow errors on hotdata scope if no error handler is set`() = runBlocking {
+        val testScope = TestCoroutineScope()
+
+        val hotData = HotDataFlow(
+            loggingTag = "tag",
+            scope = testScope,
+            startValueProvider = { 1 },
+            sharingBehavior = SharingStarted.Lazily
+        )
+
+        val testCollector = hotData.data.test(startOnScope = testScope)
+        testScope.advanceUntilIdle()
+
+        var thrownError: Exception? = null
+
+        hotData.updateAsync(
+            onUpdate = { throw IOException("Suprise") },
+            onError = { thrownError = it }
+        )
+
+        testScope.advanceUntilIdle()
+        thrownError!!.shouldBeInstanceOf<IOException>()
+        testScope.uncaughtExceptions.singleOrNull() shouldBe null
+
+        testCollector.cancel()
+    }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt
index 54d8d584e583125d432175bc799c7845a1a6e17f..14bb4ab063279a7ec43ffa27d230088de0f65fab 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSubmissionHelperTest.kt
@@ -138,18 +138,10 @@ class FormatterSubmissionHelperTest : BaseTest() {
             oUiState = DeviceUIState.PAIRED_POSITIVE,
             iResult = context.getString(R.string.test_result_card_status_positive)
         )
-        formatTestResultStatusTextBase(
-            oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN,
-            iResult = context.getString(R.string.test_result_card_status_positive)
-        )
         formatTestResultStatusTextBase(
             oUiState = DeviceUIState.SUBMITTED_FINAL,
             iResult = context.getString(R.string.test_result_card_status_invalid)
         )
-        formatTestResultStatusTextBase(
-            oUiState = DeviceUIState.SUBMITTED_INITIAL,
-            iResult = context.getString(R.string.test_result_card_status_invalid)
-        )
         formatTestResultStatusTextBase(
             oUiState = DeviceUIState.UNPAIRED,
             iResult = context.getString(R.string.test_result_card_status_invalid)
@@ -174,18 +166,10 @@ class FormatterSubmissionHelperTest : BaseTest() {
             oUiState = DeviceUIState.PAIRED_POSITIVE,
             iResult = context.getColorCompat(R.color.colorTextSemanticRed)
         )
-        formatTestResultStatusColorBase(
-            oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN,
-            iResult = context.getColorCompat(R.color.colorTextSemanticRed)
-        )
         formatTestResultStatusColorBase(
             oUiState = DeviceUIState.SUBMITTED_FINAL,
             iResult = context.getColorCompat(R.color.colorTextSemanticRed)
         )
-        formatTestResultStatusColorBase(
-            oUiState = DeviceUIState.SUBMITTED_INITIAL,
-            iResult = context.getColorCompat(R.color.colorTextSemanticRed)
-        )
         formatTestResultStatusColorBase(
             oUiState = DeviceUIState.UNPAIRED,
             iResult = context.getColorCompat(R.color.colorTextSemanticRed)
@@ -198,9 +182,7 @@ class FormatterSubmissionHelperTest : BaseTest() {
         formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_ERROR)
         formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_NO_RESULT)
         formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE)
-        formatTestStatusIconBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN)
         formatTestStatusIconBase(oUiState = DeviceUIState.SUBMITTED_FINAL)
-        formatTestStatusIconBase(oUiState = DeviceUIState.SUBMITTED_INITIAL)
         formatTestStatusIconBase(oUiState = DeviceUIState.UNPAIRED)
     }
 
@@ -210,9 +192,7 @@ class FormatterSubmissionHelperTest : BaseTest() {
         formatTestResultBase(oUiState = DeviceUIState.PAIRED_ERROR)
         formatTestResultBase(oUiState = DeviceUIState.PAIRED_NO_RESULT)
         formatTestResultBase(oUiState = DeviceUIState.PAIRED_POSITIVE)
-        formatTestResultBase(oUiState = DeviceUIState.PAIRED_POSITIVE_TELETAN)
         formatTestResultBase(oUiState = DeviceUIState.SUBMITTED_FINAL)
-        formatTestResultBase(oUiState = DeviceUIState.SUBMITTED_INITIAL)
         formatTestResultBase(oUiState = DeviceUIState.UNPAIRED)
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt
index da4f8b70e31ad2b531ee73da732b52d8b1d83dc8..ab535e0277ead7364c92ef994970d204aea11ca8 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt
@@ -15,7 +15,7 @@ import de.rki.coronawarnapp.deadman.DeadmanNotificationSender
 import de.rki.coronawarnapp.deniability.NoiseScheduler
 import de.rki.coronawarnapp.nearby.ENFClient
 import de.rki.coronawarnapp.notification.GeneralNotifications
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.playbook.Playbook
 import de.rki.coronawarnapp.presencetracing.checkins.checkout.CheckOutNotification
 import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut
@@ -130,7 +130,7 @@ class MockProvider {
     fun exposureSummaryRepository(): RiskLevelStorage = mockk()
 
     @Provides
-    fun testResultAvailableNotification(): TestResultAvailableNotificationService = mockk()
+    fun testResultAvailableNotification(): PCRTestResultAvailableNotificationService = mockk()
 
     @Provides
     fun notificationHelper(): GeneralNotifications = mockk()
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt
index aa99fa078ddeaf064e3f154ba7e717bbdc8b2398..ee61927fa1c66cb697c65d3b0a95d0ef10ceb55d 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorkerTest.kt
@@ -11,7 +11,7 @@ import de.rki.coronawarnapp.coronatest.type.CoronaTest
 import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest
 import de.rki.coronawarnapp.notification.GeneralNotifications
 import de.rki.coronawarnapp.notification.NotificationConstants
-import de.rki.coronawarnapp.notification.TestResultAvailableNotificationService
+import de.rki.coronawarnapp.notification.PCRTestResultAvailableNotificationService
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.daysToMilliseconds
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.di.AppInjector
@@ -41,7 +41,7 @@ import testhelpers.BaseTest
 class DiagnosisTestResultRetrievalPeriodicWorkerTest : BaseTest() {
     @MockK lateinit var context: Context
     @MockK lateinit var request: WorkRequest
-    @MockK lateinit var testResultAvailableNotificationService: TestResultAvailableNotificationService
+    @MockK lateinit var testResultAvailableNotificationService: PCRTestResultAvailableNotificationService
     @MockK lateinit var notificationHelper: GeneralNotifications
     @MockK lateinit var appComponent: ApplicationComponent
     @MockK lateinit var encryptedPreferencesFactory: EncryptedPreferencesFactory