From 63f5a3a3d9625890e41aa2d2f239888a346d0e51 Mon Sep 17 00:00:00 2001
From: Juraj Kusnier <jurajkusnier@users.noreply.github.com>
Date: Wed, 26 May 2021 14:47:38 +0200
Subject: [PATCH]  Contact Journal Extension - Show RAT and PCR Tests results
 in Contact Journal (EXPOSUREAPP-6040) (#3258)

* prepare new TestResultRepository

* Code refactoring

* Update tests

* Remove old files

* Code refactoring

* Move TestJournal Table to ContactDiaryDatabase

* remove unused files

* revert DataReset.kt

* rename ContactDiaryTest > ContactDiaryCoronaTest

* rename ContactDiaryTestEntity > ContactDiaryCoronaTestEntity

* add some tests

* New UI row to contact journal day overview for corona tests

* Support multiple tests in the same day

* ListAdapter > BaseAdapter

* update tests

* update file formatting

* update ContactDiaryCoronaTestEntity

* add comment
---
 .../entity/ContactDiaryCoronaTestEntity.kt    |  2 +-
 .../overview/ContactDiaryOverviewViewModel.kt | 57 ++++++++++++++++---
 .../overview/adapter/day/DayOverviewItem.kt   |  2 +
 .../ui/overview/adapter/day/DayOverviewVH.kt  | 18 +++++-
 .../day/coronatest/CoronaTestAdapter.kt       | 39 +++++++++++++
 .../adapter/day/coronatest/CoronaTestItem.kt  | 13 +++++
 .../drawable/ic_corona_test_icon_green.xml    | 20 +++++++
 .../res/drawable/ic_corona_test_icon_red.xml  | 20 +++++++
 ...t_diary_overview_day_list_item_contact.xml |  5 +-
 ...ary_overview_day_list_item_test_result.xml | 49 ++++++++++++++++
 ...ry_overview_day_list_item_test_results.xml | 30 ++++++++++
 .../contact_diary_overview_list_item.xml      |  8 ++-
 .../res/values-de/contact_diary_strings.xml   |  9 +++
 .../main/res/values/contact_diary_strings.xml |  9 +++
 .../ContactDiaryOverviewViewModelTest.kt      |  1 +
 15 files changed, 269 insertions(+), 13 deletions(-)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestAdapter.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestItem.kt
 create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_green.xml
 create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_red.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_result.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_results.xml

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt
index 70f6e1104..d0c63e34e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt
@@ -38,7 +38,7 @@ data class ContactDiaryCoronaTestEntity(
 }
 
 fun CoronaTest.canBeAddedToJournal(): Boolean {
-    return isViewed && (isNegative || isPositive)
+    return isNegative || (isViewed && isPositive) // Negative RAT may not be mark as viewed
 }
 
 fun Map.Entry<CoronaTestGUID, CoronaTest>.asTestResultEntity(): ContactDiaryCoronaTestEntity {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
index a7170706b..ea36a41d7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
@@ -11,14 +11,20 @@ import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter.DurationClassification.LESS_THAN_15_MINUTES
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter.DurationClassification.MORE_THAN_15_MINUTES
 import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryCleanTask
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryCoronaTestEntity
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.contactdiary.ui.exporter.ContactDiaryExporter
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.DiaryOverviewItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.DayOverviewItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.contact.ContactItem
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest.CoronaTestItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskenf.RiskEnfItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskEventItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.subheader.OverviewSubHeaderItem
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryCoronaTestEntity.TestResult.NEGATIVE
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryCoronaTestEntity.TestResult.POSITIVE
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryCoronaTestEntity.TestType.ANTIGEN
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryCoronaTestEntity.TestType.PCR
 import de.rki.coronawarnapp.presencetracing.checkins.CheckIn
 import de.rki.coronawarnapp.presencetracing.checkins.CheckInRepository
 import de.rki.coronawarnapp.presencetracing.checkins.common.locationName
@@ -29,6 +35,7 @@ import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import de.rki.coronawarnapp.task.TaskController
 import de.rki.coronawarnapp.task.common.DefaultTaskRequest
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
@@ -43,6 +50,7 @@ import org.joda.time.LocalDate
 import timber.log.Timber
 import kotlin.concurrent.fixedRateTimer
 
+@Suppress("LongParameterList")
 class ContactDiaryOverviewViewModel @AssistedInject constructor(
     taskController: TaskController,
     dispatcherProvider: DispatcherProvider,
@@ -71,6 +79,7 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
 
     private val locationVisitsFlow = contactDiaryRepository.locationVisits
     private val personEncountersFlow = contactDiaryRepository.personEncounters
+    private val testResultsFlow = contactDiaryRepository.testResults
 
     private val riskLevelPerDateFlow = riskLevelStorage.ewDayRiskStates
     private val traceLocationCheckInRiskFlow = riskLevelStorage.traceLocationCheckInRiskStates
@@ -82,8 +91,15 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         personEncountersFlow,
         riskLevelPerDateFlow,
         traceLocationCheckInRiskFlow,
-        checkInsWithinRetentionFlow
-    ) { dateList, locationVisists, personEncounters, riskLevelPerDateList, traceLocationCheckInRiskList, checkInList ->
+        checkInsWithinRetentionFlow,
+        testResultsFlow
+    ) { dateList,
+        locationVisists,
+        personEncounters,
+        riskLevelPerDateList,
+        traceLocationCheckInRiskList,
+        checkInList,
+        testResults ->
         mutableListOf<DiaryOverviewItem>().apply {
             add(OverviewSubHeaderItem)
             addAll(
@@ -92,7 +108,8 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
                     personEncounters,
                     riskLevelPerDateList,
                     traceLocationCheckInRiskList,
-                    checkInList
+                    checkInList,
+                    testResults
                 )
             )
         }.toList()
@@ -112,7 +129,8 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         encounters: List<ContactDiaryPersonEncounter>,
         riskLevelPerDateList: List<ExposureWindowDayRisk>,
         traceLocationCheckInRiskList: List<TraceLocationCheckInRisk>,
-        checkInList: List<CheckIn>
+        checkInList: List<CheckIn>,
+        coronaTests: List<ContactDiaryCoronaTestEntity>
     ): List<DiaryOverviewItem> {
         Timber.v(
             "createListItemList(" +
@@ -121,19 +139,22 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
                 "encounters=%s, " +
                 "riskLevelPerDateList=%s, " +
                 "traceLocationCheckInRiskList=%s," +
-                "checkInList=%s",
+                "checkInList=%s" +
+                "coronaTests=%s",
             this,
             visits,
             encounters,
             riskLevelPerDateList,
             traceLocationCheckInRiskList,
-            checkInList
+            checkInList,
+            coronaTests
         )
         return map { date ->
 
             val visitsForDate = visits.filter { it.date == date }
             val encountersForDate = encounters.filter { it.date == date }
             val traceLocationCheckInRisksForDate = traceLocationCheckInRiskList.filter { it.localDateUtc == date }
+            val testResultForDate = coronaTests.filter { it.time.toLocalDateUtc() == date }
 
             val coreItemData =
                 encountersForDate.map { it.toContactItemData() } + visitsForDate.map { it.toContactItemData() }
@@ -152,11 +173,14 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
                     RiskEventDataHolder(it, checkIn)
                 }.toRiskEventItem()
 
+            val coronaTestItem = testResultForDate.toCoronaTestItem()
+
             DayOverviewItem(
                 date = date,
                 riskEnfItem = riskEnf,
                 riskEventItem = riskEventItem,
-                contactItem = contactItem
+                contactItem = contactItem,
+                coronaTestItem = coronaTestItem
             ) { onItemPress(it) }
         }
     }
@@ -266,6 +290,25 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         )
     }
 
+    fun List<ContactDiaryCoronaTestEntity>.toCoronaTestItem() = CoronaTestItem(
+        map {
+            CoronaTestItem.Data(
+                icon = when (it.result) {
+                    POSITIVE -> R.drawable.ic_corona_test_icon_red
+                    NEGATIVE -> R.drawable.ic_corona_test_icon_green
+                },
+                header = when (it.testType) {
+                    PCR -> R.string.contact_diary_corona_test_pcr_title
+                    ANTIGEN -> R.string.contact_diary_corona_test_rat_title
+                },
+                body = when (it.result) {
+                    POSITIVE -> R.string.contact_diary_corona_test_positive
+                    NEGATIVE -> R.string.contact_diary_corona_test_negative
+                }
+            )
+        }
+    )
+
     fun onBackButtonPress() {
         routeToScreen.postValue(ContactDiaryOverviewNavigationEvents.NavigateToMainActivity)
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewItem.kt
index 31aa016b9..8f41ba86d 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewItem.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewItem.kt
@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day
 
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.DiaryOverviewItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.contact.ContactItem
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest.CoronaTestItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskenf.RiskEnfItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskEventItem
 import org.joda.time.LocalDate
@@ -11,6 +12,7 @@ data class DayOverviewItem(
     val riskEnfItem: RiskEnfItem? = null,
     val riskEventItem: RiskEventItem? = null,
     val contactItem: ContactItem? = null,
+    val coronaTestItem: CoronaTestItem? = null,
     val onItemSelectionListener: (DayOverviewItem) -> Unit
 ) : DiaryOverviewItem {
     override val stableId: Long = date.hashCode().toLong()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewVH.kt
index 58671eaf6..8807a0a6b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/DayOverviewVH.kt
@@ -6,6 +6,8 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.DiaryOverviewAdapter
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.contact.ContactAdapter
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.contact.ContactItem
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest.CoronaTestAdapter
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest.CoronaTestItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskenf.RiskEnfItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskEventAdapter
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskEventItem
@@ -16,6 +18,7 @@ import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemContactBi
 import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemHeaderBinding
 import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemRiskEnfBinding
 import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemRiskEventBinding
+import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemTestResultsBinding
 import de.rki.coronawarnapp.databinding.ContactDiaryOverviewListItemBinding
 import org.joda.time.LocalDate
 
@@ -39,6 +42,7 @@ class DayOverviewVH(parent: ViewGroup) :
                 dayRiskEnf.apply(riskEnfItem = riskEnfItem)
                 dayRiskEvent.apply(riskEventItem = riskEventItem)
                 dayContact.apply(contactItem = contactItem)
+                dayTestResult.apply(coronaTestItem = coronaTestItem)
             }
 
             dayElementBody.apply {
@@ -68,8 +72,7 @@ class DayOverviewVH(parent: ViewGroup) :
         }
     }
 
-    private fun ContactDiaryOverviewDayListItemRiskEventBinding
-    .apply(riskEventItem: RiskEventItem?) {
+    private fun ContactDiaryOverviewDayListItemRiskEventBinding.apply(riskEventItem: RiskEventItem?) {
         root.isGone = riskEventItem == null
 
         riskEventItem?.let {
@@ -99,4 +102,15 @@ class DayOverviewVH(parent: ViewGroup) :
             }
         }
     }
+
+    private fun ContactDiaryOverviewDayListItemTestResultsBinding.apply(coronaTestItem: CoronaTestItem?) {
+        root.isGone = coronaTestItem == null || coronaTestItem.data.isEmpty()
+
+        coronaTestItem?.let {
+            recyclerView.apply {
+                adapter = CoronaTestAdapter(it.data)
+                suppressLayout(true)
+            }
+        }
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestAdapter.kt
new file mode 100644
index 000000000..26fe7568a
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestAdapter.kt
@@ -0,0 +1,39 @@
+package de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest
+
+import android.view.ViewGroup
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest.CoronaTestAdapter.ViewHolder
+import de.rki.coronawarnapp.databinding.ContactDiaryOverviewDayListItemTestResultBinding
+import de.rki.coronawarnapp.ui.lists.BaseAdapter
+import de.rki.coronawarnapp.util.lists.BindableVH
+
+class CoronaTestAdapter(val items: List<CoronaTestItem.Data>) : BaseAdapter<ViewHolder>() {
+
+    override fun onCreateBaseVH(parent: ViewGroup, viewType: Int) = ViewHolder(parent)
+
+    override fun onBindBaseVH(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
+        holder.bind(items[position], payloads)
+    }
+
+    override fun getItemCount(): Int = items.size
+
+    class ViewHolder(parent: ViewGroup) :
+        BaseAdapter.VH(R.layout.contact_diary_overview_day_list_item_test_result, parent),
+        BindableVH<CoronaTestItem.Data, ContactDiaryOverviewDayListItemTestResultBinding> {
+
+        override val viewBinding: Lazy<ContactDiaryOverviewDayListItemTestResultBinding> =
+            lazy { ContactDiaryOverviewDayListItemTestResultBinding.bind(itemView) }
+
+        override val onBindData: ContactDiaryOverviewDayListItemTestResultBinding.(
+            item: CoronaTestItem.Data,
+            payloads: List<Any>
+        ) -> Unit
+            get() = { coronaTestItem, _ ->
+                with(root.context) {
+                    contactDiaryCoronaTestTitle.text = getString(coronaTestItem.header)
+                    contactDiaryCoronaTestImage.setImageResource(coronaTestItem.icon)
+                    contactDiaryCoronaTestBody.text = getString(coronaTestItem.body)
+                }
+            }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestItem.kt
new file mode 100644
index 000000000..a8b730d41
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/day/coronatest/CoronaTestItem.kt
@@ -0,0 +1,13 @@
+package de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.coronatest
+
+import androidx.annotation.DrawableRes
+import androidx.annotation.StringRes
+import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.DayDataItem
+
+data class CoronaTestItem(val data: List<Data>) :
+    DayDataItem {
+
+    override val stableId: Long = data.hashCode().toLong()
+
+    data class Data(@DrawableRes val icon: Int, @StringRes val header: Int, @StringRes val body: Int)
+}
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_green.xml b/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_green.xml
new file mode 100644
index 000000000..f2e69e3a8
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_green.xml
@@ -0,0 +1,20 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="32dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+  <path
+      android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
+      android:fillColor="#2E854B"/>
+  <path
+      android:pathData="M12,5L20,5A1,1 0,0 1,21 6L21,6A1,1 0,0 1,20 7L12,7A1,1 0,0 1,11 6L11,6A1,1 0,0 1,12 5z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M13.2449,11.1667V8.1579H18.7551V11.1667V11.3816L18.8323,11.5822L21.8949,19.5486C23.0611,22.5821 20.8218,25.8421 17.5717,25.8421H14.4283C11.1782,25.8421 8.9389,22.5821 10.1051,19.5486L13.1677,11.5822L13.2449,11.3816V11.1667Z"
+      android:strokeWidth="2.31579"
+      android:fillColor="#00000000"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:pathData="M9.5,21.5295L10.5,24.5295L11.5,25.5304L16,26.0304L21.5,25.0304L22.5,21.5295C22.8333,20.5295 22.4,16.4011 20,18.0011C17.6,19.6011 14,22.9875 10,21L9.5,21.5295Z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_red.xml b/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_red.xml
new file mode 100644
index 000000000..735934f39
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_corona_test_icon_red.xml
@@ -0,0 +1,20 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="32dp"
+    android:height="32dp"
+    android:viewportWidth="32"
+    android:viewportHeight="32">
+  <path
+      android:pathData="M16,16m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
+      android:fillColor="#BF0F2D"/>
+  <path
+      android:pathData="M12,5L20,5A1,1 0,0 1,21 6L21,6A1,1 0,0 1,20 7L12,7A1,1 0,0 1,11 6L11,6A1,1 0,0 1,12 5z"
+      android:fillColor="#ffffff"/>
+  <path
+      android:pathData="M13.2449,11.1667V8.1579H18.7551V11.1667V11.3816L18.8323,11.5822L21.8949,19.5486C23.0611,22.5821 20.8218,25.8421 17.5717,25.8421H14.4283C11.1782,25.8421 8.9389,22.5821 10.1051,19.5486L13.1677,11.5822L13.2449,11.3816V11.1667Z"
+      android:strokeWidth="2.31579"
+      android:fillColor="#00000000"
+      android:strokeColor="#ffffff"/>
+  <path
+      android:pathData="M9.5,21.5295L10.5,24.5295L11.5,25.5304L16,26.0304L21.5,25.0304L22.5,21.5295C22.8333,20.5295 22.4,16.4011 20,18.0011C17.6,19.6011 14,22.9875 10,21L9.5,21.5295Z"
+      android:fillColor="#ffffff"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_contact.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_contact.xml
index 8333ce839..8af58bb97 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_contact.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_contact.xml
@@ -2,7 +2,8 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    xmlns:tools="http://schemas.android.com/tools">
 
     <View
         android:layout_width="match_parent"
@@ -18,6 +19,8 @@
         android:layout_height="wrap_content"
         android:nestedScrollingEnabled="false"
         android:paddingVertical="@dimen/spacing_tiny"
+        tools:itemCount="2"
+        tools:listitem="@layout/contact_diary_overview_nested_list_item"
         app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_result.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_result.xml
new file mode 100644
index 000000000..3c988fe86
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_result.xml
@@ -0,0 +1,49 @@
+<?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/contact_diary_overview_risk_item"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    android:paddingBottom="@dimen/spacing_small">
+
+    <ImageView
+        android:id="@+id/contact_diary_corona_test_image"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginStart="@dimen/spacing_small"
+        android:layout_marginTop="@dimen/spacing_mega_tiny"
+        android:importantForAccessibility="no"
+        android:scaleType="centerInside"
+        app:layout_constraintEnd_toStartOf="@id/contact_diary_corona_test_title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/contact_diary_corona_test_title"
+        tools:src="@drawable/ic_corona_test_icon_red" />
+
+    <TextView
+        android:id="@+id/contact_diary_corona_test_title"
+        style="@style/subtitle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_small"
+        android:layout_marginTop="@dimen/spacing_small"
+        android:layout_marginEnd="@dimen/spacing_small"
+        android:focusable="true"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/contact_diary_corona_test_image"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="PCR-Test registriert" />
+
+    <TextView
+        android:id="@+id/contact_diary_corona_test_body"
+        style="@style/subtitleMedium"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_mega_tiny"
+        android:focusable="true"
+        app:layout_constraintEnd_toEndOf="@id/contact_diary_corona_test_title"
+        app:layout_constraintStart_toStartOf="@+id/contact_diary_corona_test_title"
+        app:layout_constraintTop_toBottomOf="@id/contact_diary_corona_test_title"
+        tools:text="Befund positiv" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_results.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_results.xml
new file mode 100644
index 000000000..7e24305d3
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_day_list_item_test_results.xml
@@ -0,0 +1,30 @@
+<?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="wrap_content">
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/card_divider"
+        android:background="?android:attr/listDivider"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/recycler_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:nestedScrollingEnabled="false"
+        android:paddingVertical="@dimen/spacing_tiny"
+        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:itemCount="1"
+        tools:listitem="@layout/contact_diary_overview_day_list_item_test_result" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
index e3e49b04b..a87d1f99f 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
@@ -9,7 +9,7 @@
 
     <include
         android:id="@+id/day_header"
-        layout="@layout/contact_diary_overview_day_list_item_header"/>
+        layout="@layout/contact_diary_overview_day_list_item_header" />
 
     <include
         android:id="@+id/day_risk_enf"
@@ -19,6 +19,10 @@
         android:id="@+id/day_risk_event"
         layout="@layout/contact_diary_overview_day_list_item_risk_event" />
 
+    <include
+        android:id="@+id/day_test_result"
+        layout="@layout/contact_diary_overview_day_list_item_test_results" />
+
     <include
         android:id="@+id/day_contact"
         layout="@layout/contact_diary_overview_day_list_item_contact" />
@@ -26,7 +30,7 @@
     <androidx.constraintlayout.helper.widget.Flow
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        app:constraint_referenced_ids="day_header,day_risk_enf,day_risk_event,day_contact"
+        app:constraint_referenced_ids="day_header,day_risk_enf,day_risk_event,day_test_result,day_contact"
         app:flow_maxElementsWrap="1"
         app:flow_wrapMode="chain"
         app:layout_constraintBottom_toBottomOf="parent"
diff --git a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml
index 61235ef3c..db2d3f748 100644
--- a/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/contact_diary_strings.xml
@@ -183,4 +183,13 @@
     <string name="contact_diary_location_visit_duration_hour">"Std."</string>
     <!-- XTXT: Option - person encounter - circumstances hint-->
     <string name="contact_diary_location_visit_circumstances_hint">Notiz (z.B. sehr voll)</string>
+
+    <!-- XTXT: PCR test title in the day overview -->
+    <string name="contact_diary_corona_test_pcr_title">PCR-Test registriert</string>
+    <!-- XTXT: RAT test title in the day overview -->
+    <string name="contact_diary_corona_test_rat_title">Schnelltest durchgeführt</string>
+    <!-- XTXT: positive test result in the day overview -->
+    <string name="contact_diary_corona_test_positive">Befund positiv</string>
+    <!-- XTXT: negative test result in the day overview -->
+    <string name="contact_diary_corona_test_negative">Befund negativ</string>
 </resources>
diff --git a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml
index 2a15f03cf..a9e1cc9ce 100644
--- a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml
@@ -210,4 +210,13 @@
     <string name="contact_diary_location_visit_duration_hour">"hrs"</string>
     <!-- XTXT: Option - person encounter - circumstances hint-->
     <string name="contact_diary_location_visit_circumstances_hint">"Note (e.g. very full)"</string>
+
+    <!-- XTXT: PCR test title in the day overview -->
+    <string name="contact_diary_corona_test_pcr_title">PCR-Test registriert</string>
+    <!-- XTXT: RAT test title in the day overview -->
+    <string name="contact_diary_corona_test_rat_title">Schnelltest durchgeführt</string>
+    <!-- XTXT: positive test result in the day overview -->
+    <string name="contact_diary_corona_test_positive">Befund positiv</string>
+    <!-- XTXT: negative test result in the day overview -->
+    <string name="contact_diary_corona_test_negative">Befund negativ</string>
 </resources>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
index 86f776afc..1644a3c19 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
@@ -69,6 +69,7 @@ open class ContactDiaryOverviewViewModelTest {
         every { taskController.submit(any()) } just runs
         every { contactDiaryRepository.locationVisits } returns flowOf(emptyList())
         every { contactDiaryRepository.personEncounters } returns flowOf(emptyList())
+        every { contactDiaryRepository.testResults } returns flowOf(emptyList())
         every { riskLevelStorage.ewDayRiskStates } returns flowOf(emptyList())
         every { riskLevelStorage.traceLocationCheckInRiskStates } returns flowOf(emptyList())
         every { checkInRepository.checkInsWithinRetention } returns flowOf(emptyList())
-- 
GitLab