diff --git a/Corona-Warn-App/schemas/de.rki.coronawarnapp.contactdiary.storage.ContactDiaryDatabase/2.json b/Corona-Warn-App/schemas/de.rki.coronawarnapp.contactdiary.storage.ContactDiaryDatabase/2.json
new file mode 100644
index 0000000000000000000000000000000000000000..7fac999cccd2e6a75fb830f388529d813b2b4de6
--- /dev/null
+++ b/Corona-Warn-App/schemas/de.rki.coronawarnapp.contactdiary.storage.ContactDiaryDatabase/2.json
@@ -0,0 +1,232 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 2,
+    "identityHash": "d702472d6dd506b73ff6a7b340686c9a",
+    "entities": [
+      {
+        "tableName": "locations",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`locationId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationName` TEXT NOT NULL, `phoneNumber` TEXT, `emailAddress` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "locationId",
+            "columnName": "locationId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "locationName",
+            "columnName": "locationName",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "phoneNumber",
+            "columnName": "phoneNumber",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "emailAddress",
+            "columnName": "emailAddress",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "locationId"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "locationvisits",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkLocationId` INTEGER NOT NULL, `duration` INTEGER, `circumstances` TEXT, FOREIGN KEY(`fkLocationId`) REFERENCES `locations`(`locationId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "fkLocationId",
+            "columnName": "fkLocationId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "duration",
+            "columnName": "duration",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "circumstances",
+            "columnName": "circumstances",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_locationvisits_fkLocationId",
+            "unique": false,
+            "columnNames": [
+              "fkLocationId"
+            ],
+            "createSql": "CREATE INDEX IF NOT EXISTS `index_locationvisits_fkLocationId` ON `${TABLE_NAME}` (`fkLocationId`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "locations",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "fkLocationId"
+            ],
+            "referencedColumns": [
+              "locationId"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "persons",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`personId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fullName` TEXT NOT NULL, `phoneNumber` TEXT, `emailAddress` TEXT)",
+        "fields": [
+          {
+            "fieldPath": "personId",
+            "columnName": "personId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "fullName",
+            "columnName": "fullName",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "phoneNumber",
+            "columnName": "phoneNumber",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "emailAddress",
+            "columnName": "emailAddress",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "personId"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "personencounters",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkPersonId` INTEGER NOT NULL, `durationClassification` TEXT, `withMask` INTEGER, `wasOutside` INTEGER, `circumstances` TEXT, FOREIGN KEY(`fkPersonId`) REFERENCES `persons`(`personId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "date",
+            "columnName": "date",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "fkPersonId",
+            "columnName": "fkPersonId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "durationClassification",
+            "columnName": "durationClassification",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "withMask",
+            "columnName": "withMask",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "wasOutside",
+            "columnName": "wasOutside",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "circumstances",
+            "columnName": "circumstances",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [
+          {
+            "name": "index_personencounters_fkPersonId",
+            "unique": false,
+            "columnNames": [
+              "fkPersonId"
+            ],
+            "createSql": "CREATE INDEX IF NOT EXISTS `index_personencounters_fkPersonId` ON `${TABLE_NAME}` (`fkPersonId`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "persons",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "fkPersonId"
+            ],
+            "referencedColumns": [
+              "personId"
+            ]
+          }
+        ]
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd702472d6dd506b73ff6a7b340686c9a')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseMigrationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseMigrationTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bd23cf8461818998bacbad0daf0f931762ab63a4
--- /dev/null
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseMigrationTest.kt
@@ -0,0 +1,196 @@
+package de.rki.coronawarnapp.contactdiary.storage
+
+import android.database.sqlite.SQLiteException
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationVisitEntity
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationVisitWrapper
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEncounterEntity
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEncounterWrapper
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
+import de.rki.coronawarnapp.contactdiary.storage.internal.migrations.ContactDiaryDatabaseMigration1To2
+import io.kotest.assertions.throwables.shouldThrow
+import io.kotest.matchers.shouldBe
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.runBlocking
+import org.joda.time.LocalDate
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import testhelpers.BaseTest
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class ContactDiaryDatabaseMigrationTest : BaseTest() {
+    private val DB_NAME = "contactdiary_migration_test.db"
+
+    @get:Rule
+    val helper: MigrationTestHelper = MigrationTestHelper(
+        InstrumentationRegistry.getInstrumentation(),
+        ContactDiaryDatabase::class.java.canonicalName,
+        FrameworkSQLiteOpenHelperFactory()
+    )
+
+    /**
+     * Test migration to add new optional attributes
+     */
+    @Test
+    fun migrate1To2() {
+        helper.createDatabase(DB_NAME, 1).apply {
+            execSQL(
+                """
+                    INSERT INTO "locations" (
+                        "locationId",
+                        "locationName"
+                    ) VALUES (
+                        '1',
+                        'Location1'
+                    );
+                """.trimIndent()
+            )
+            execSQL(
+                """
+                    INSERT INTO "persons" (
+                        "personId",
+                        "fullName"
+                    ) VALUES (
+                        '100',
+                        'Person100'
+                    );
+                """.trimIndent()
+            )
+            execSQL(
+                """
+                    INSERT INTO "locationvisits" (
+                        "id",
+                        "date",
+                        "fkLocationId"
+                    ) VALUES (
+                        '2',
+                        '2020-04-20',
+                        '1'
+                    );
+                """.trimIndent()
+            )
+
+            execSQL(
+                """
+                    INSERT INTO "personencounters" (
+                        "id",
+                        "date",
+                        "fkPersonId"
+                    ) VALUES (
+                        '3',
+                        '2020-12-31',
+                        '100'
+                    );
+                """.trimIndent()
+            )
+
+            close()
+        }
+
+        // Run migration
+        helper.runMigrationsAndValidate(
+            DB_NAME,
+            2,
+            true,
+            ContactDiaryDatabaseMigration1To2
+        )
+
+        val daoDb = ContactDiaryDatabase.Factory(
+            ctx = ApplicationProvider.getApplicationContext()
+        ).create(databaseName = DB_NAME)
+
+        val location = ContactDiaryLocationEntity(
+            locationId = 1,
+            locationName = "Location1",
+            phoneNumber = null,
+            emailAddress = null
+        )
+        runBlocking { daoDb.locationDao().allEntries().first() }.single() shouldBe location
+
+        val person = ContactDiaryPersonEntity(
+            personId = 100,
+            fullName = "Person100",
+            phoneNumber = null,
+            emailAddress = null
+        )
+        runBlocking { daoDb.personDao().allEntries().first() }.single() shouldBe person
+
+        runBlocking {
+            daoDb.locationVisitDao().allEntries().first()
+        }.single() shouldBe ContactDiaryLocationVisitWrapper(
+            contactDiaryLocationEntity = location,
+            contactDiaryLocationVisitEntity = ContactDiaryLocationVisitEntity(
+                id = 2,
+                date = LocalDate.parse("2020-04-20"),
+                fkLocationId = 1,
+                duration = null,
+                circumstances = null
+            )
+        )
+
+        runBlocking {
+            daoDb.personEncounterDao().allEntries().first()
+        }.single() shouldBe ContactDiaryPersonEncounterWrapper(
+            contactDiaryPersonEntity = person,
+            contactDiaryPersonEncounterEntity = ContactDiaryPersonEncounterEntity(
+                id = 3,
+                date = LocalDate.parse("2020-12-31"),
+                fkPersonId = 100,
+                withMask = null,
+                wasOutside = null,
+                durationClassification = null,
+                circumstances = null
+            )
+        )
+    }
+
+    @Test
+    fun migrate1To2_failure_throws() {
+        helper.createDatabase(DB_NAME, 1).apply {
+            execSQL("DROP TABLE IF EXISTS locations")
+            execSQL("DROP TABLE IF EXISTS locationvisits")
+            execSQL("DROP TABLE IF EXISTS persons")
+            execSQL("DROP TABLE IF EXISTS personencounters")
+            // Has incompatible existing column phoneNumber of wrong type
+            execSQL("CREATE TABLE IF NOT EXISTS `locations` (`locationId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `locationName` TEXT NOT NULL, `phoneNumber` INTEGER )")
+            execSQL("CREATE TABLE IF NOT EXISTS `persons` (`personId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fullName` TEXT NOT NULL)")
+            execSQL("CREATE TABLE IF NOT EXISTS `locationvisits` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkLocationId` INTEGER NOT NULL, FOREIGN KEY(`fkLocationId`) REFERENCES `locations`(`locationId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)")
+            execSQL("CREATE TABLE IF NOT EXISTS `personencounters` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `date` TEXT NOT NULL, `fkPersonId` INTEGER NOT NULL, FOREIGN KEY(`fkPersonId`) REFERENCES `persons`(`personId`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)")
+            close()
+        }
+
+        shouldThrow<SQLiteException> {
+            // Run migration
+            helper.runMigrationsAndValidate(
+                DB_NAME,
+                2,
+                true,
+                ContactDiaryDatabaseMigration1To2
+            )
+        }
+    }
+
+    @Test
+    @Throws(IOException::class)
+    fun migrateAll() {
+        helper.createDatabase(DB_NAME, 1).apply {
+            close()
+        }
+
+        // Open latest version of the database. Room will validate the schema
+        // once all migrations execute.
+        ContactDiaryDatabase.Factory(
+            ctx = ApplicationProvider.getApplicationContext()
+        ).create(databaseName = DB_NAME).apply {
+            openHelper.writableDatabase
+            close()
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseTest.kt
index 11fb352a5b7f982f910ade41c89c384dd121783c..6853d1150cb63ba33f903ccf2f61a0cc1a580c98 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabaseTest.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.contactdiary.storage
 import androidx.room.Room
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationVisitEntity
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationVisitWrapper
@@ -13,6 +14,7 @@ import io.kotest.matchers.shouldBe
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.runBlocking
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 import org.junit.After
 import org.junit.Test
@@ -24,18 +26,40 @@ class ContactDiaryDatabaseTest : BaseTest() {
 
     // TestData
     private val date = LocalDate.now()
-    private val person = ContactDiaryPersonEntity(personId = 1, fullName = "Peter")
-    private val location = ContactDiaryLocationEntity(locationId = 2, locationName = "Rewe Wiesloch")
-    private val personEncounter = ContactDiaryPersonEncounterEntity(id = 3, date = date, fkPersonId = person.personId)
-    private val locationVisit = ContactDiaryLocationVisitEntity(id = 4, date = date, fkLocationId = location.locationId)
+    private val person = ContactDiaryPersonEntity(
+        personId = 1,
+        fullName = "Peter",
+        emailAddress = "person-emailAddress",
+        phoneNumber = "person-phoneNumber"
+    )
+    private val location = ContactDiaryLocationEntity(
+        locationId = 2,
+        locationName = "Rewe Wiesloch",
+        emailAddress = "location-emailAddress",
+        phoneNumber = "location-phoneNumber"
+    )
+    private val personEncounter = ContactDiaryPersonEncounterEntity(
+        id = 3,
+        date = date,
+        fkPersonId = person.personId,
+        durationClassification = ContactDiaryPersonEncounter.DurationClassification.MORE_THAN_15_MINUTES,
+        withMask = true,
+        wasOutside = false,
+        circumstances = "You could see the smile under his mask."
+    )
+    private val locationVisit = ContactDiaryLocationVisitEntity(
+        id = 4,
+        date = date,
+        fkLocationId = location.locationId,
+        duration = Duration.standardMinutes(99),
+        circumstances = "I had to buy snacks."
+    )
 
     // DB
-    private val contactDiaryDatabase: ContactDiaryDatabase =
-        Room.inMemoryDatabaseBuilder(
-            ApplicationProvider.getApplicationContext(),
-            ContactDiaryDatabase::class.java
-        )
-            .build()
+    private val contactDiaryDatabase: ContactDiaryDatabase = Room.inMemoryDatabaseBuilder(
+        ApplicationProvider.getApplicationContext(),
+        ContactDiaryDatabase::class.java
+    ).build()
 
     private val personDao = contactDiaryDatabase.personDao()
     private val locationDao = contactDiaryDatabase.locationDao()
@@ -97,14 +121,38 @@ class ContactDiaryDatabaseTest : BaseTest() {
     fun getCorrectEntityForDate() = runBlocking {
         val yesterday = LocalDate.now().minusDays(1)
         val tomorrow = LocalDate.now().plusDays(1)
-        val personEncounterYesterday =
-            ContactDiaryPersonEncounterEntity(id = 5, date = yesterday, fkPersonId = person.personId)
-        val personEncounterTomorrow =
-            ContactDiaryPersonEncounterEntity(id = 6, date = tomorrow, fkPersonId = person.personId)
-        val locationVisitYesterday =
-            ContactDiaryLocationVisitEntity(id = 7, date = yesterday, fkLocationId = location.locationId)
-        val locationVisitTomorrow =
-            ContactDiaryLocationVisitEntity(id = 8, date = tomorrow, fkLocationId = location.locationId)
+        val personEncounterYesterday = ContactDiaryPersonEncounterEntity(
+            id = 5,
+            date = yesterday,
+            fkPersonId = person.personId,
+            durationClassification = ContactDiaryPersonEncounter.DurationClassification.LESS_THAN_15_MINUTES,
+            withMask = false,
+            wasOutside = false,
+            circumstances = "encounter-yesterday"
+        )
+        val personEncounterTomorrow = ContactDiaryPersonEncounterEntity(
+            id = 6,
+            date = tomorrow,
+            fkPersonId = person.personId,
+            durationClassification = ContactDiaryPersonEncounter.DurationClassification.MORE_THAN_15_MINUTES,
+            withMask = true,
+            wasOutside = true,
+            circumstances = "encounter-today"
+        )
+        val locationVisitYesterday = ContactDiaryLocationVisitEntity(
+            id = 7,
+            date = yesterday,
+            fkLocationId = location.locationId,
+            duration = Duration.standardMinutes(42),
+            circumstances = "visit-yesterday"
+        )
+        val locationVisitTomorrow = ContactDiaryLocationVisitEntity(
+            id = 8,
+            date = tomorrow,
+            fkLocationId = location.locationId,
+            duration = Duration.standardMinutes(1),
+            circumstances = "visit-today"
+        )
         val encounterList = listOf(personEncounter, personEncounterYesterday, personEncounterTomorrow)
         val visitList = listOf(locationVisit, locationVisitYesterday, locationVisitTomorrow)
         val personEncounterFlow = personEncounterDao.allEntries().map { it.toContactDiaryPersonEncounterEntityList() }
@@ -119,12 +167,61 @@ class ContactDiaryDatabaseTest : BaseTest() {
         personEncounterFlow.first() shouldBe encounterList
         locationVisitFlow.first() shouldBe visitList
 
-        personEncounterDao.entitiesForDate(yesterday).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(personEncounterYesterday)
-        personEncounterDao.entitiesForDate(date).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(personEncounter)
-        personEncounterDao.entitiesForDate(tomorrow).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(personEncounterTomorrow)
+        personEncounterDao.entitiesForDate(yesterday).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(
+            personEncounterYesterday
+        )
+        personEncounterDao.entitiesForDate(date).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(
+            personEncounter
+        )
+        personEncounterDao.entitiesForDate(tomorrow).first().toContactDiaryPersonEncounterEntityList() shouldBe listOf(
+            personEncounterTomorrow
+        )
+
+        locationVisitDao.entitiesForDate(yesterday).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(
+            locationVisitYesterday
+        )
+        locationVisitDao.entitiesForDate(date).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(
+            locationVisit
+        )
+        locationVisitDao.entitiesForDate(tomorrow).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(
+            locationVisitTomorrow
+        )
+    }
+
+    @Test
+    fun updatingLocationVisits(): Unit = runBlocking {
+        val locationVisitFlow = locationVisitDao.allEntries().map { it.toContactDiaryLocationVisitEntityList() }
+
+        locationDao.insert(location)
+        locationVisitDao.insert(listOf(locationVisit))
+
+        locationVisitFlow.first().single() shouldBe locationVisit
+
+        val updatedLocation = locationVisit.copy(
+            duration = Duration.millis(123L),
+            circumstances = "Suspicious"
+        )
+        locationVisitDao.update(updatedLocation)
+
+        locationVisitFlow.first().single() shouldBe updatedLocation
+    }
 
-        locationVisitDao.entitiesForDate(yesterday).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(locationVisitYesterday)
-        locationVisitDao.entitiesForDate(date).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(locationVisit)
-        locationVisitDao.entitiesForDate(tomorrow).first().toContactDiaryLocationVisitEntityList() shouldBe listOf(locationVisitTomorrow)
+    @Test
+    fun updatingPersonEncounters(): Unit = runBlocking {
+        val personEncounterFlow = personEncounterDao.allEntries().map { it.toContactDiaryPersonEncounterEntityList() }
+
+        personDao.insert(person)
+        personEncounterDao.insert(personEncounter)
+
+        personEncounterFlow.first().single() shouldBe personEncounter
+
+        val updatedEncounter = personEncounter.copy(
+            withMask = true,
+            wasOutside = false,
+            durationClassification = ContactDiaryPersonEncounter.DurationClassification.MORE_THAN_15_MINUTES,
+            circumstances = "He lend me a coffee cup but the handle broke and it dropped onto my laptop."
+        )
+        personEncounterDao.update(updatedEncounter)
+        personEncounterFlow.first().single() shouldBe updatedEncounter
     }
 }
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt
index 2202440649b5b02ff4377e738bb7e04853500402..578c99ae6d7b47aa319708cc0b2f5b4c0a59a74c 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryDayFragmentTest.kt
@@ -7,17 +7,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
 import dagger.Module
 import dagger.android.ContributesAndroidInjector
 import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.contactdiary.ui.day.ContactDiaryDayFragment
 import de.rki.coronawarnapp.contactdiary.ui.day.ContactDiaryDayFragmentArgs
 import de.rki.coronawarnapp.contactdiary.ui.day.ContactDiaryDayViewModel
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.ContactDiaryLocationListFragment
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.ContactDiaryLocationListViewModel
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.DiaryLocationListItem
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.ContactDiaryPersonListFragment
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.ContactDiaryPersonListViewModel
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.DiaryPersonListItem
 import de.rki.coronawarnapp.contactdiary.util.toFormattedDay
 import de.rki.coronawarnapp.ui.contactdiary.DiaryData.LOCATIONS
 import de.rki.coronawarnapp.ui.contactdiary.DiaryData.PERSONS
@@ -33,13 +32,13 @@ import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import testhelpers.BaseUITest
-import testhelpers.takeScreenshot
 import testhelpers.Screenshot
 import testhelpers.SystemUIDemoModeRule
 import testhelpers.TestDispatcherProvider
 import testhelpers.launchFragment2
 import testhelpers.launchFragmentInContainer2
 import testhelpers.selectTabAtPosition
+import testhelpers.takeScreenshot
 import tools.fastlane.screengrab.locale.LocaleTestRule
 import tools.fastlane.screengrab.locale.LocaleUtil
 import java.util.Locale
@@ -103,8 +102,8 @@ class ContactDiaryDayFragmentTest : BaseUITest() {
     }
 
     private fun captureScreen(
-        persons: List<SelectableItem<ContactDiaryPerson>>,
-        locations: List<SelectableItem<ContactDiaryLocation>>,
+        persons: List<DiaryPersonListItem>,
+        locations: List<DiaryLocationListItem>,
         suffix: String
     ) {
         every { personListViewModel.uiList } returns MutableLiveData(persons)
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt
index c6b8e97ffd73055f414903dfca305524848b04c4..55b95459ee73e9658f413de65d6ebbf0c1b5f1bd 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/DiaryData.kt
@@ -4,10 +4,14 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPersonEncounter
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.DiaryLocationListItem
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.DiaryPersonListItem
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ListItem
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
-import de.rki.coronawarnapp.util.ui.toLazyString
+import org.joda.time.Duration
+import org.joda.time.LocalDate
 
 object DiaryData {
 
@@ -15,24 +19,42 @@ object DiaryData {
         ListItem.Data(
             R.drawable.ic_contact_diary_person_item,
             "Max Mustermann",
+            null,
+            listOf(
+                R.string.contact_diary_person_encounter_duration_below_15_min,
+                R.string.contact_diary_person_encounter_mask_with,
+                R.string.contact_diary_person_encounter_environment_inside
+            ),
+            "Notizen notizen",
             ListItem.Type.PERSON
         ),
 
         ListItem.Data(
             R.drawable.ic_contact_diary_person_item,
             "Erika Mustermann",
+            null,
+            listOf(
+                R.string.contact_diary_person_encounter_environment_inside
+            ),
+            "Notizen notizen",
             ListItem.Type.PERSON
         ),
 
         ListItem.Data(
             R.drawable.ic_contact_diary_location,
             "Fitnessstudio",
+            Duration.millis(1800000),
+            null,
+            "Notizen notizen",
             ListItem.Type.LOCATION
         ),
 
         ListItem.Data(
             R.drawable.ic_contact_diary_location,
             "Supermarket",
+            null,
+            null,
+            null,
             ListItem.Type.LOCATION
         )
     )
@@ -49,69 +71,95 @@ object DiaryData {
         R.drawable.ic_low_risk_alert
     )
 
-    val LOCATIONS: List<SelectableItem<ContactDiaryLocation>> = listOf(
-        SelectableItem(
-            selected = true,
+    val LOCATIONS: List<DiaryLocationListItem> = listOf(
+        DiaryLocationListItem(
             item = DefaultContactDiaryLocation(locationName = "Sport"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_location,
-            onClickLabel = R.string.accessibility_location
+            visit = DefaultContactDiaryLocationVisit(
+                contactDiaryLocation = DefaultContactDiaryLocation(locationName = ""),
+                date = LocalDate.now()
+            ),
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onCircumStanceInfoClicked = {}
         ),
-        SelectableItem(
-            selected = true,
+        DiaryLocationListItem(
             item = DefaultContactDiaryLocation(locationName = "Büro"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_location,
-            onClickLabel = R.string.accessibility_location
+            visit = DefaultContactDiaryLocationVisit(
+                contactDiaryLocation = DefaultContactDiaryLocation(locationName = ""),
+                date = LocalDate.now()
+            ),
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onCircumStanceInfoClicked = {}
         ),
-        SelectableItem(
-            selected = false,
+        DiaryLocationListItem(
             item = DefaultContactDiaryLocation(locationName = "Supermarkt"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_location,
-            onClickLabel = R.string.accessibility_location
+            visit = null,
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onCircumStanceInfoClicked = {}
         )
     )
 
-    val PERSONS: List<SelectableItem<ContactDiaryPerson>> = listOf(
-        SelectableItem(
-            selected = true,
+    val PERSONS: List<DiaryPersonListItem> = listOf(
+        DiaryPersonListItem(
             item = DefaultContactDiaryPerson(fullName = "Erika Mustermann"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_person,
-            onClickLabel = R.string.accessibility_person
+            personEncounter = DefaultContactDiaryPersonEncounter(
+                contactDiaryPerson = DefaultContactDiaryPerson(fullName = ""),
+                date = LocalDate.now()
+            ),
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onWithMaskChanged = { _, _ -> },
+            onWasOutsideChanged = { _, _ -> },
+            onCircumstanceInfoClicked = {}
         ),
-        SelectableItem(
-            selected = true,
+        DiaryPersonListItem(
             item = DefaultContactDiaryPerson(fullName = "Max Mustermann"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_person,
-            onClickLabel = R.string.accessibility_person
+            personEncounter = DefaultContactDiaryPersonEncounter(
+                contactDiaryPerson = DefaultContactDiaryPerson(fullName = ""),
+                date = LocalDate.now()
+            ),
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onWithMaskChanged = { _, _ -> },
+            onWasOutsideChanged = { _, _ -> },
+            onCircumstanceInfoClicked = {}
         ),
-        SelectableItem(
-            selected = false,
+        DiaryPersonListItem(
             item = DefaultContactDiaryPerson(fullName = "John Doe"),
-            contentDescription = "".toLazyString(),
-            onClickDescription = "".toLazyString(),
-            clickLabel = R.string.accessibility_person,
-            onClickLabel = R.string.accessibility_person
+            personEncounter = null,
+            onItemClick = {},
+            onDurationChanged = { _, _ -> },
+            onCircumstancesChanged = { _, _ -> },
+            onWithMaskChanged = { _, _ -> },
+            onWasOutsideChanged = { _, _ -> },
+            onCircumstanceInfoClicked = {}
         )
     )
 
     val LOCATIONS_EDIT_LIST: List<ContactDiaryLocation> = listOf(
         DefaultContactDiaryLocation(locationName = "Sport"),
-        DefaultContactDiaryLocation(locationName = "Büro"),
+        DefaultContactDiaryLocation(
+            locationName = "Büro",
+            phoneNumber = "+49153397029",
+            emailAddress = "office@work.com"
+        ),
         DefaultContactDiaryLocation(locationName = "Supermarkt")
     )
 
     val PERSONS_EDIT_LIST: List<ContactDiaryPerson> = listOf(
-        DefaultContactDiaryPerson(fullName = "Max Mustermann"),
-        DefaultContactDiaryPerson(fullName = "Erika Mustermann"),
+        DefaultContactDiaryPerson(
+            fullName = "Max Mustermann",
+            phoneNumber = "+49151237865",
+            emailAddress = "max.musterman@me.com"
+        ),
+        DefaultContactDiaryPerson(fullName = "Erika Mustermann", emailAddress = "erika.mustermann@me.com"),
         DefaultContactDiaryPerson(fullName = "John Doe")
     )
 }
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryCommentInfoTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryCommentInfoTestFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..39a057c7646d9d23ee459f866004d5bb91a517f6
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryCommentInfoTestFragment.kt
@@ -0,0 +1,31 @@
+package de.rki.coronawarnapp.test.contactdiary.ui
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.databinding.ContactDiaryCommentInfoFragmentBinding
+import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
+import de.rki.coronawarnapp.util.ui.popBackStack
+import de.rki.coronawarnapp.util.ui.viewBindingLazy
+
+class ContactDiaryCommentInfoTestFragment : Fragment(R.layout.contact_diary_comment_info_fragment) {
+
+    private val binding: ContactDiaryCommentInfoFragmentBinding by viewBindingLazy()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        binding.toolbar.setNavigationOnClickListener {
+            popBackStack()
+        }
+    }
+
+    companion object {
+        val MENU_ITEM = TestMenuItem(
+            title = "Contact Diary Comment Info",
+            description = "Contact diary comment info screen",
+            targetId = R.id.test_contact_diary_comment_fragment
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt
index 5d067c9d65353d79255bb7171e7ba427332443de..7ff3c8f2039548359ae0d95a8412e986c6c077f9 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragment.kt
@@ -5,6 +5,8 @@ import android.os.Bundle
 import android.view.View
 import androidx.fragment.app.Fragment
 import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.ui.durationpicker.ContactDiaryDurationPickerFragment
+import de.rki.coronawarnapp.contactdiary.ui.durationpicker.toContactDiaryFormat
 import de.rki.coronawarnapp.databinding.FragmentTestContactDiaryBinding
 import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
 import de.rki.coronawarnapp.util.di.AutoInject
@@ -12,10 +14,14 @@ 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 org.joda.time.Duration
 import javax.inject.Inject
 
 @SuppressLint("SetTextI18n")
-class ContactDiaryTestFragment : Fragment(R.layout.fragment_test_contact_diary), AutoInject {
+class ContactDiaryTestFragment :
+    Fragment(R.layout.fragment_test_contact_diary),
+    AutoInject,
+    ContactDiaryDurationPickerFragment.OnChangeListener {
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
     private val vm: ContactDiaryTestFragmentViewModel by cwaViewModels { viewModelFactory }
 
@@ -42,6 +48,31 @@ class ContactDiaryTestFragment : Fragment(R.layout.fragment_test_contact_diary),
             normalPersonEncountersButton.setOnClickListener { vm.createPersonEncounters(false) }
             locationVisitsCleanButton.setOnClickListener { vm.clearLocationVisits() }
             personEncountersCleanButton.setOnClickListener { vm.clearPersonEncounters() }
+            durationValue.setOnClickListener {
+                val args = Bundle()
+                args.putString(
+                    ContactDiaryDurationPickerFragment.DURATION_ARGUMENT_KEY,
+                    binding.durationValue.text.toString()
+                )
+
+                val durationPicker = ContactDiaryDurationPickerFragment()
+                durationPicker.arguments = args
+                durationPicker.setTargetFragment(this@ContactDiaryTestFragment, 0)
+                durationPicker.show(parentFragmentManager, "ContactDiaryDurationPickerFragment")
+            }
+        }
+    }
+
+    override fun onChange(duration: Duration) {
+        with(binding.durationValue) {
+            text = duration.toContactDiaryFormat()
+            if (duration.millis == 0L) {
+                setBackgroundResource(R.drawable.contact_diary_duration_background_default)
+                setTextAppearance(R.style.bodyNeutral)
+            } else {
+                setBackgroundResource(R.drawable.contact_diary_duration_background_selected)
+                setTextAppearance(R.style.body1)
+            }
         }
     }
 
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
index 6d09d4e6bc5d38de5e6556b177aa116a3a37b415..7fdfb6b6f7bf074f727bd22a550a72c5b12adb80 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt
@@ -5,6 +5,7 @@ import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.miscinfo.MiscInfoFragment
 import de.rki.coronawarnapp.test.appconfig.ui.AppConfigTestFragment
+import de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryCommentInfoTestFragment
 import de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryTestFragment
 import de.rki.coronawarnapp.test.crash.ui.SettingsCrashReportFragment
 import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment
@@ -34,7 +35,8 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() {
             ContactDiaryTestFragment.MENU_ITEM,
             PlaygroundFragment.MENU_ITEM,
             DataDonationTestFragment.MENU_ITEM,
-            DeltaonboardingFragment.MENU_ITEM
+            DeltaonboardingFragment.MENU_ITEM,
+            ContactDiaryCommentInfoTestFragment.MENU_ITEM
         ).let { MutableLiveData(it) }
     }
     val showTestScreenEvent = SingleLiveEvent<TestMenuItem>()
diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_contact_diary.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_contact_diary.xml
index 248f44fa2b398f89f1a4a9b01e3de69116927664..8069c616d6f6db8c8de3515ac1167695900cbbbd 100644
--- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_contact_diary.xml
+++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_contact_diary.xml
@@ -14,6 +14,39 @@
         android:orientation="vertical"
         android:paddingBottom="32dp">
 
+        <androidx.constraintlayout.widget.ConstraintLayout
+            style="@style/Card"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:backgroundTint="@color/colorContactDiaryListItem"
+            android:layout_margin="@dimen/spacing_tiny">
+
+            <TextView
+                android:id="@+id/duration_picker"
+                style="@style/body1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="Open duration picker dialog"
+                app:layout_constraintTop_toTopOf="parent" />
+
+            <TextView
+                android:id="@+id/duration_value"
+                style="@style/bodyNeutral"
+                android:layout_width="70dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:paddingTop="11dp"
+                android:paddingBottom="11dp"
+                android:paddingLeft="13dp"
+                android:paddingRight="13dp"
+                android:text="@string/duration_dialog_default_value"
+                android:background="@drawable/contact_diary_duration_background_default"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@+id/duration_picker"/>
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
         <androidx.constraintlayout.widget.ConstraintLayout
             style="@style/Card"
             android:layout_width="match_parent"
diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml
index 3fd5c94a377c5b2af530a939ceecc65a54017cc8..5a10dea8abfb6f61fb5adcdded0d4b41c082e6b3 100644
--- a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml
+++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml
@@ -43,6 +43,9 @@
         <action
             android:id="@+id/action_test_menu_fragment_to_dataDonationFragment"
             app:destination="@id/test_datadonation_fragment" />
+        <action
+            android:id="@+id/action_test_menu_fragment_to_test_contact_diary_person_comment_fragment"
+            app:destination="@id/test_contact_diary_comment_fragment" />
         <action
             android:id="@+id/action_test_menu_fragment_to_deltaonboardingFragment"
             app:destination="@id/test_deltaonboarding_fragment" />
@@ -112,17 +115,23 @@
     <fragment
         android:id="@+id/playgroundFragment"
         android:name="de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment"
-        tools:layout="@layout/fragment_test_playground"
-        android:label="PlaygroundFragment" />
+        android:label="PlaygroundFragment"
+        tools:layout="@layout/fragment_test_playground" />
     <fragment
         android:id="@+id/test_datadonation_fragment"
-        tools:layout="@layout/fragment_test_datadonation"
         android:name="de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment"
-        android:label="DataDonationFragment" />
+        android:label="DataDonationFragment"
+        tools:layout="@layout/fragment_test_datadonation" />
     <fragment
         android:id="@+id/test_deltaonboarding_fragment"
         android:name="de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment"
         android:label="DeltaonboardingFragment"
         tools:layout="@layout/fragment_test_deltaonboarding" />
+    <fragment
+        android:id="@+id/test_contact_diary_comment_fragment"
+        android:name="de.rki.coronawarnapp.test.contactdiary.ui.ContactDiaryCommentInfoTestFragment"
+        android:label="CommentInfoTestFragment"
+        tools:layout="@layout/contact_diary_comment_info_fragment" />
+
 
 </navigation>
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocation.kt
index d4192af041ce8789f72bb20a020d7e0d645126e0..1221b89bb7badc8431fbc5b73e148145799686f2 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocation.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocation.kt
@@ -6,6 +6,8 @@ import java.util.Locale
 interface ContactDiaryLocation : HasStableId {
     val locationId: Long
     var locationName: String
+    val phoneNumber: String?
+    val emailAddress: String?
 }
 
 fun List<ContactDiaryLocation>.sortByNameAndIdASC(): List<ContactDiaryLocation> =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocationVisit.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocationVisit.kt
index 2d477bcee18248a08aa8ecdfd9d4937f7e635ed2..d5eb4b2852a4ef1a1a26d59d765e762303f8e58b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocationVisit.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryLocationVisit.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.contactdiary.model
 
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 import java.util.Locale
 
@@ -7,6 +8,12 @@ interface ContactDiaryLocationVisit {
     val id: Long
     val date: LocalDate
     val contactDiaryLocation: ContactDiaryLocation
+
+    /*
+        Value in miliseconds
+     */
+    val duration: Duration?
+    val circumstances: String?
 }
 
 fun List<ContactDiaryLocationVisit>.sortByNameAndIdASC(): List<ContactDiaryLocationVisit> =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPerson.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPerson.kt
index de391152bcfba7267c0d095124025137b2fc3214..0340fa66e10e5d3f7565b5ad4c69ada52a749039 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPerson.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPerson.kt
@@ -6,6 +6,8 @@ import java.util.Locale
 interface ContactDiaryPerson : HasStableId {
     val personId: Long
     var fullName: String
+    val phoneNumber: String?
+    val emailAddress: String?
 }
 
 fun List<ContactDiaryPerson>.sortByNameAndIdASC(): List<ContactDiaryPerson> =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPersonEncounter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPersonEncounter.kt
index 47975a267757d5a18018d5a21c46d088bf32da3f..be1c173f981f2730cf80736a11a8749c569f25e7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPersonEncounter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/ContactDiaryPersonEncounter.kt
@@ -7,6 +7,17 @@ interface ContactDiaryPersonEncounter {
     val id: Long
     val date: LocalDate
     val contactDiaryPerson: ContactDiaryPerson
+    val durationClassification: DurationClassification?
+    val withMask: Boolean?
+    val wasOutside: Boolean?
+    val circumstances: String?
+
+    enum class DurationClassification(
+        val key: String
+    ) {
+        LESS_THAN_15_MINUTES("LessThan15Minutes"),
+        MORE_THAN_15_MINUTES("MoreThan15Minutes")
+    }
 }
 
 fun List<ContactDiaryPersonEncounter>.sortByNameAndIdASC(): List<ContactDiaryPersonEncounter> =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocation.kt
index 78ff94aa01a3af64f4036d54b4e8daeb14cc1321..3c20aa3cbdcd9dd56371687bacc227c80a8d7c4b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocation.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocation.kt
@@ -2,7 +2,9 @@ package de.rki.coronawarnapp.contactdiary.model
 
 data class DefaultContactDiaryLocation(
     override val locationId: Long = 0L,
-    override var locationName: String
+    override var locationName: String,
+    override val phoneNumber: String? = null,
+    override val emailAddress: String? = null
 ) : ContactDiaryLocation {
     override val stableId: Long
         get() = locationId
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocationVisit.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocationVisit.kt
index 0c2124a3c625123c3bac258c68db6e82e92e6702..e9c16848e2c7a7ffdf8a82d54f901e3779477e45 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocationVisit.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryLocationVisit.kt
@@ -1,9 +1,23 @@
 package de.rki.coronawarnapp.contactdiary.model
 
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 
 data class DefaultContactDiaryLocationVisit(
     override val id: Long = 0L,
     override val date: LocalDate,
-    override val contactDiaryLocation: ContactDiaryLocation
+    override val contactDiaryLocation: ContactDiaryLocation,
+    override val duration: Duration? = null,
+    override val circumstances: String? = null
 ) : ContactDiaryLocationVisit
+
+fun ContactDiaryLocationVisit.toEditableVariant(): DefaultContactDiaryLocationVisit {
+    if (this is DefaultContactDiaryLocationVisit) return this
+    return DefaultContactDiaryLocationVisit(
+        id = id,
+        date = date,
+        contactDiaryLocation = contactDiaryLocation,
+        duration = duration,
+        circumstances = circumstances
+    )
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPerson.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPerson.kt
index 911bd939bfc14b4fbdebc634d6ccdaf11bf68d7e..8827126fc9d1850d950573ffe1c4d274c38eee48 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPerson.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPerson.kt
@@ -2,7 +2,9 @@ package de.rki.coronawarnapp.contactdiary.model
 
 data class DefaultContactDiaryPerson(
     override val personId: Long = 0L,
-    override var fullName: String
+    override var fullName: String,
+    override val phoneNumber: String? = null,
+    override val emailAddress: String? = null
 ) : ContactDiaryPerson {
     override val stableId: Long
         get() = personId
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPersonEncounter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPersonEncounter.kt
index bdd303ef066b478fc64b0063e8800bab7a098285..409c766f41af39e645090e8182f5585a8aa22d6c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPersonEncounter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/model/DefaultContactDiaryPersonEncounter.kt
@@ -5,5 +5,23 @@ import org.joda.time.LocalDate
 data class DefaultContactDiaryPersonEncounter(
     override val id: Long = 0L,
     override val date: LocalDate,
-    override val contactDiaryPerson: ContactDiaryPerson
+    override val contactDiaryPerson: ContactDiaryPerson,
+    override val durationClassification: ContactDiaryPersonEncounter.DurationClassification? = null,
+    override val withMask: Boolean? = null,
+    override val wasOutside: Boolean? = null,
+    override val circumstances: String? = null
 ) : ContactDiaryPersonEncounter
+
+fun ContactDiaryPersonEncounter.toEditableVariant(): DefaultContactDiaryPersonEncounter {
+    if (this is DefaultContactDiaryPersonEncounter) return this
+
+    return DefaultContactDiaryPersonEncounter(
+        id = id,
+        date = date,
+        contactDiaryPerson = contactDiaryPerson,
+        durationClassification = durationClassification,
+        withMask = withMask,
+        wasOutside = wasOutside,
+        circumstances = circumstances
+    )
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabase.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabase.kt
index b80474dca2a053ec526af698686352a6904cb838..bb7eb9c19a64048d56d2f992a978bdb80011065a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabase.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/ContactDiaryDatabase.kt
@@ -13,6 +13,8 @@ import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEnti
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationVisitEntity
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEncounterEntity
 import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
+import de.rki.coronawarnapp.contactdiary.storage.internal.converters.ContactDiaryRoomConverters
+import de.rki.coronawarnapp.contactdiary.storage.internal.migrations.ContactDiaryDatabaseMigration1To2
 import de.rki.coronawarnapp.util.database.CommonConverters
 import de.rki.coronawarnapp.util.di.AppContext
 import javax.inject.Inject
@@ -24,10 +26,10 @@ import javax.inject.Inject
         ContactDiaryPersonEntity::class,
         ContactDiaryPersonEncounterEntity::class
     ],
-    version = 1,
+    version = 2,
     exportSchema = true
 )
-@TypeConverters(CommonConverters::class)
+@TypeConverters(CommonConverters::class, ContactDiaryRoomConverters::class)
 abstract class ContactDiaryDatabase : RoomDatabase() {
 
     abstract fun locationDao(): ContactDiaryLocationDao
@@ -36,9 +38,9 @@ abstract class ContactDiaryDatabase : RoomDatabase() {
     abstract fun personEncounterDao(): ContactDiaryPersonEncounterDao
 
     class Factory @Inject constructor(@AppContext private val ctx: Context) {
-        fun create(): ContactDiaryDatabase = Room
-            .databaseBuilder(ctx, ContactDiaryDatabase::class.java, CONTACT_DIARY_DATABASE_NAME)
-            .fallbackToDestructiveMigration()
+        fun create(databaseName: String = CONTACT_DIARY_DATABASE_NAME): ContactDiaryDatabase = Room
+            .databaseBuilder(ctx, ContactDiaryDatabase::class.java, databaseName)
+            .addMigrations(ContactDiaryDatabaseMigration1To2)
             .build()
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationEntity.kt
index ed36e6276d4349e459baa7180fe60f778c7fd069..c7e21989fd781d408136627899c6b122adea96bc 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationEntity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationEntity.kt
@@ -11,11 +11,18 @@ import kotlinx.parcelize.Parcelize
 @Entity(tableName = "locations")
 data class ContactDiaryLocationEntity(
     @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "locationId") override val locationId: Long = 0L,
-    @ColumnInfo(name = "locationName") override var locationName: String
+    @ColumnInfo(name = "locationName") override var locationName: String,
+    override val phoneNumber: String?,
+    override val emailAddress: String?
 ) : ContactDiaryLocation, Parcelable {
     override val stableId: Long
         get() = locationId
 }
 
 fun ContactDiaryLocation.toContactDiaryLocationEntity(): ContactDiaryLocationEntity =
-    ContactDiaryLocationEntity(this.locationId, this.locationName)
+    ContactDiaryLocationEntity(
+        locationId = this.locationId,
+        locationName = this.locationName,
+        phoneNumber = this.phoneNumber,
+        emailAddress = this.emailAddress
+    )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitEntity.kt
index 3bfdd7541c082e0559f490ae39a3fda5bef1ff6b..4dfe111b24f432c8555818aeab22edd926bba60b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitEntity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitEntity.kt
@@ -6,6 +6,8 @@ import androidx.room.ForeignKey
 import androidx.room.Index
 import androidx.room.PrimaryKey
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
+import de.rki.coronawarnapp.util.trimToLength
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 
 @Entity(
@@ -25,8 +27,16 @@ import org.joda.time.LocalDate
 data class ContactDiaryLocationVisitEntity(
     @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long = 0L,
     @ColumnInfo(name = "date") val date: LocalDate,
-    @ColumnInfo(name = "fkLocationId") val fkLocationId: Long
+    @ColumnInfo(name = "fkLocationId") val fkLocationId: Long,
+    @ColumnInfo(name = "duration") val duration: Duration?,
+    @ColumnInfo(name = "circumstances") val circumstances: String?
 )
 
 fun ContactDiaryLocationVisit.toContactDiaryLocationVisitEntity(): ContactDiaryLocationVisitEntity =
-    ContactDiaryLocationVisitEntity(id = this.id, date = this.date, fkLocationId = this.contactDiaryLocation.locationId)
+    ContactDiaryLocationVisitEntity(
+        id = this.id,
+        date = this.date,
+        fkLocationId = this.contactDiaryLocation.locationId,
+        duration = this.duration,
+        circumstances = this.circumstances?.trimToLength(250)
+    )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitWrapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitWrapper.kt
index 333659e2cdb0f3e8a116457c1bea579581906dcc..040de1b5c260a36759ae000e8196454d4e49b09e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitWrapper.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryLocationVisitWrapper.kt
@@ -6,7 +6,7 @@ import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit
 import de.rki.coronawarnapp.contactdiary.model.sortByNameAndIdASC
 
-class ContactDiaryLocationVisitWrapper(
+data class ContactDiaryLocationVisitWrapper(
     @Embedded val contactDiaryLocationVisitEntity: ContactDiaryLocationVisitEntity,
     @Relation(parentColumn = "fkLocationId", entityColumn = "locationId")
     val contactDiaryLocationEntity: ContactDiaryLocationEntity
@@ -16,7 +16,9 @@ fun ContactDiaryLocationVisitWrapper.toContactDiaryLocationVisit(): ContactDiary
     DefaultContactDiaryLocationVisit(
         id = this.contactDiaryLocationVisitEntity.id,
         date = this.contactDiaryLocationVisitEntity.date,
-        contactDiaryLocation = this.contactDiaryLocationEntity
+        contactDiaryLocation = this.contactDiaryLocationEntity,
+        duration = contactDiaryLocationVisitEntity.duration,
+        circumstances = contactDiaryLocationVisitEntity.circumstances
     )
 
 fun List<ContactDiaryLocationVisitWrapper>.toContactDiaryLocationVisitSortedList(): List<ContactDiaryLocationVisit> =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterEntity.kt
index a98ba8661d9f28757e2adee4ce7cdd28f6655e89..a8df756f9fd1911c740bf273d0de5ed40c67afcb 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterEntity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterEntity.kt
@@ -6,6 +6,8 @@ import androidx.room.ForeignKey
 import androidx.room.Index
 import androidx.room.PrimaryKey
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter.DurationClassification
+import de.rki.coronawarnapp.util.trimToLength
 import org.joda.time.LocalDate
 
 @Entity(
@@ -25,8 +27,20 @@ import org.joda.time.LocalDate
 data class ContactDiaryPersonEncounterEntity(
     @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") val id: Long = 0L,
     @ColumnInfo(name = "date") val date: LocalDate,
-    @ColumnInfo(name = "fkPersonId") val fkPersonId: Long
+    @ColumnInfo(name = "fkPersonId") val fkPersonId: Long,
+    @ColumnInfo(name = "durationClassification") val durationClassification: DurationClassification?,
+    @ColumnInfo(name = "withMask") val withMask: Boolean?,
+    @ColumnInfo(name = "wasOutside") val wasOutside: Boolean?,
+    @ColumnInfo(name = "circumstances") val circumstances: String?
 )
 
 fun ContactDiaryPersonEncounter.toContactDiaryPersonEncounterEntity(): ContactDiaryPersonEncounterEntity =
-    ContactDiaryPersonEncounterEntity(id = this.id, date = this.date, fkPersonId = this.contactDiaryPerson.personId)
+    ContactDiaryPersonEncounterEntity(
+        id = this.id,
+        date = this.date,
+        fkPersonId = this.contactDiaryPerson.personId,
+        durationClassification = this.durationClassification,
+        withMask = this.withMask,
+        wasOutside = this.wasOutside,
+        circumstances = this.circumstances?.trimToLength(250)
+    )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterWrapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterWrapper.kt
index 4371d4e3aeda27e4e557112f7a36b7e0fc736f7a..5d34506b2b1f552bcc1778ac742ff68c2a5f1cc3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterWrapper.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEncounterWrapper.kt
@@ -16,7 +16,11 @@ fun ContactDiaryPersonEncounterWrapper.toContactDiaryPersonEncounter(): ContactD
     DefaultContactDiaryPersonEncounter(
         id = this.contactDiaryPersonEncounterEntity.id,
         date = this.contactDiaryPersonEncounterEntity.date,
-        contactDiaryPerson = contactDiaryPersonEntity
+        contactDiaryPerson = this.contactDiaryPersonEntity,
+        durationClassification = this.contactDiaryPersonEncounterEntity.durationClassification,
+        withMask = this.contactDiaryPersonEncounterEntity.withMask,
+        wasOutside = this.contactDiaryPersonEncounterEntity.wasOutside,
+        circumstances = this.contactDiaryPersonEncounterEntity.circumstances
     )
 
 fun List<ContactDiaryPersonEncounterWrapper>.toContactDiaryPersonEncounterSortedList():
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEntity.kt
index 0d1d1738d28c0b648327e6a18e09212eb752de06..f9a10824c76675487d7466cfb996a0a3131b517b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEntity.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryPersonEntity.kt
@@ -11,11 +11,18 @@ import kotlinx.parcelize.Parcelize
 @Entity(tableName = "persons")
 data class ContactDiaryPersonEntity(
     @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "personId") override val personId: Long = 0L,
-    @ColumnInfo(name = "fullName") override var fullName: String
+    @ColumnInfo(name = "fullName") override var fullName: String,
+    @ColumnInfo(name = "phoneNumber") override val phoneNumber: String?,
+    @ColumnInfo(name = "emailAddress") override val emailAddress: String?
 ) : ContactDiaryPerson, Parcelable {
     override val stableId: Long
         get() = personId
 }
 
 fun ContactDiaryPerson.toContactDiaryPersonEntity(): ContactDiaryPersonEntity =
-    ContactDiaryPersonEntity(this.personId, this.fullName)
+    ContactDiaryPersonEntity(
+        personId = this.personId,
+        fullName = this.fullName,
+        phoneNumber = this.phoneNumber,
+        emailAddress = this.emailAddress
+    )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConverters.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConverters.kt
new file mode 100644
index 0000000000000000000000000000000000000000..95821990d0dd7fd91dcccb6da51a8012793083d1
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConverters.kt
@@ -0,0 +1,28 @@
+package de.rki.coronawarnapp.contactdiary.storage.internal.converters
+
+import androidx.room.TypeConverter
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
+import org.joda.time.Duration
+
+class ContactDiaryRoomConverters {
+    @TypeConverter
+    fun toContactDurationClassification(value: String?): ContactDiaryPersonEncounter.DurationClassification? {
+        if (value == null) return null
+        return ContactDiaryPersonEncounter.DurationClassification.values().singleOrNull { it.key == value }
+    }
+
+    @TypeConverter
+    fun fromContactDurationClassification(value: ContactDiaryPersonEncounter.DurationClassification?): String? {
+        return value?.key
+    }
+
+    @TypeConverter
+    fun toJodaDuration(millis: Long?): Duration? {
+        return millis?.let { Duration.millis(it) }
+    }
+
+    @TypeConverter
+    fun fromJodaDuration(duration: Duration?): Long? {
+        return duration?.millis
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/migrations/ContactDiaryDatabaseMigration1To2.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/migrations/ContactDiaryDatabaseMigration1To2.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ca5ad82ca3e31d1e445a847a528826e3feefc448
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/internal/migrations/ContactDiaryDatabaseMigration1To2.kt
@@ -0,0 +1,77 @@
+package de.rki.coronawarnapp.contactdiary.storage.internal.migrations
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import de.rki.coronawarnapp.exception.ExceptionCategory
+import de.rki.coronawarnapp.exception.reporting.report
+import timber.log.Timber
+
+/**
+ * Migrates the contact diary database from schema version 1 to schema version 2.
+ * We are adding additional columns for new optional attributes
+ * Person: PhoneNumber, Email
+ * Location: PhoneNumber, Email
+ * PersonEncounter: DurationType, Mask?, Outside?, Comment
+ * LocationVisit: Duration, Comment
+ */
+object ContactDiaryDatabaseMigration1To2 : Migration(1, 2) {
+
+    override fun migrate(database: SupportSQLiteDatabase) {
+        try {
+            Timber.i("Attempting migration 1->2...")
+            performMigration(database)
+            Timber.i("Migration 1->2 successful.")
+        } catch (e: Exception) {
+            Timber.e(e, "Migration 1->2 failed")
+            e.report(ExceptionCategory.INTERNAL, "ContactDiary database migration failed.")
+            throw e
+        }
+    }
+
+    private fun performMigration(database: SupportSQLiteDatabase) = with(database) {
+        Timber.d("Running MIGRATION_1_2")
+
+        migratePersonsTable()
+        migrateLocationsTable()
+        migratePersonEncounterTable()
+        migrateLocationVisitTable()
+    }
+
+    private val migratePersonsTable: SupportSQLiteDatabase.() -> Unit = {
+        Timber.d("Table 'persons': Add column 'phoneNumber'")
+        execSQL("ALTER TABLE `persons` ADD COLUMN `phoneNumber` TEXT")
+
+        Timber.d("Table 'emailAddress': Add column 'phoneNumber'")
+        execSQL("ALTER TABLE `persons` ADD COLUMN `emailAddress` TEXT")
+    }
+
+    private val migrateLocationsTable: SupportSQLiteDatabase.() -> Unit = {
+        Timber.d("Table 'locations': Add column 'phoneNumber'")
+        execSQL("ALTER TABLE `locations` ADD COLUMN `phoneNumber` TEXT")
+
+        Timber.d("Table 'locations': Add column 'emailAddress'")
+        execSQL("ALTER TABLE `locations` ADD COLUMN `emailAddress` TEXT")
+    }
+
+    private val migratePersonEncounterTable: SupportSQLiteDatabase.() -> Unit = {
+        Timber.d("Table 'personencounters': Add column 'durationClassification'")
+        execSQL("ALTER TABLE `personencounters` ADD COLUMN `durationClassification` TEXT")
+
+        Timber.d("Table 'personencounters': Add column 'circumstances'")
+        execSQL("ALTER TABLE `personencounters` ADD COLUMN `circumstances` TEXT")
+
+        Timber.d("Table 'personencounters': Add column 'withMask'")
+        execSQL("ALTER TABLE `personencounters` ADD COLUMN `withMask` INTEGER")
+
+        Timber.d("Table 'personencounters': Add column 'wasOutside'")
+        execSQL("ALTER TABLE `personencounters` ADD COLUMN `wasOutside` INTEGER")
+    }
+
+    private val migrateLocationVisitTable: SupportSQLiteDatabase.() -> Unit = {
+        Timber.d("Table 'locationvisits': Add column 'duration'")
+        execSQL("ALTER TABLE `locationvisits` ADD COLUMN `duration` INTEGER")
+
+        Timber.d("Table 'locationvisits': Add column 'circumstances'")
+        execSQL("ALTER TABLE `locationvisits` ADD COLUMN `circumstances` TEXT")
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/ContactDiaryRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/ContactDiaryRepository.kt
index 286e5d9ed7842d6dfcfd6f01962d328ba10d9224..d2685422c75f1161e171c2fc4a5316c7ffd85542 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/ContactDiaryRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/ContactDiaryRepository.kt
@@ -22,6 +22,7 @@ interface ContactDiaryRepository {
     val locationVisits: Flow<List<ContactDiaryLocationVisit>>
     fun locationVisitsForDate(date: LocalDate): Flow<List<ContactDiaryLocationVisit>>
     suspend fun addLocationVisit(contactDiaryLocationVisit: ContactDiaryLocationVisit)
+    suspend fun updateLocationVisit(contactDiaryLocationVisit: ContactDiaryLocationVisit)
     suspend fun deleteLocationVisit(contactDiaryLocationVisit: ContactDiaryLocationVisit)
     suspend fun deleteLocationVisits(contactDiaryLocationVisits: List<ContactDiaryLocationVisit>)
     suspend fun deleteAllLocationVisits()
@@ -38,6 +39,7 @@ interface ContactDiaryRepository {
     val personEncounters: Flow<List<ContactDiaryPersonEncounter>>
     fun personEncountersForDate(date: LocalDate): Flow<List<ContactDiaryPersonEncounter>>
     suspend fun addPersonEncounter(contactDiaryPersonEncounter: ContactDiaryPersonEncounter)
+    suspend fun updatePersonEncounter(contactDiaryPersonEncounter: ContactDiaryPersonEncounter)
     suspend fun deletePersonEncounter(contactDiaryPersonEncounter: ContactDiaryPersonEncounter)
     suspend fun deletePersonEncounters(contactDiaryPersonEncounters: List<ContactDiaryPersonEncounter>)
     suspend fun deleteAllPersonEncounters()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/DefaultContactDiaryRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/DefaultContactDiaryRepository.kt
index 6cdec5daf2882792cdb9449f6bc5d250d99c0634..b4a1e1efa058075f393ef3fd6c18b11110ea7bbf 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/DefaultContactDiaryRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/repo/DefaultContactDiaryRepository.kt
@@ -108,6 +108,13 @@ class DefaultContactDiaryRepository @Inject constructor(
         contactDiaryLocationVisitDao.insert(contactDiaryLocationVisitEntity)
     }
 
+    override suspend fun updateLocationVisit(contactDiaryLocationVisit: ContactDiaryLocationVisit) {
+        executeWhenIdNotDefault(contactDiaryLocationVisit.id) {
+            val contactDiaryLocationVisitEntity = contactDiaryLocationVisit.toContactDiaryLocationVisitEntity()
+            contactDiaryLocationVisitDao.update(contactDiaryLocationVisitEntity)
+        }
+    }
+
     override suspend fun deleteLocationVisit(contactDiaryLocationVisit: ContactDiaryLocationVisit) {
         Timber.d("Deleting location visit $contactDiaryLocationVisit")
         executeWhenIdNotDefault(contactDiaryLocationVisit.id) {
@@ -195,6 +202,13 @@ class DefaultContactDiaryRepository @Inject constructor(
         contactDiaryPersonEncounterDao.insert(contactDiaryPersonEncounterEntity)
     }
 
+    override suspend fun updatePersonEncounter(contactDiaryPersonEncounter: ContactDiaryPersonEncounter) {
+        executeWhenIdNotDefault(contactDiaryPersonEncounter.id) {
+            val contactDiaryPersonEncounterEntity = contactDiaryPersonEncounter.toContactDiaryPersonEncounterEntity()
+            contactDiaryPersonEncounterDao.update(contactDiaryPersonEncounterEntity)
+        }
+    }
+
     override suspend fun deletePersonEncounter(contactDiaryPersonEncounter: ContactDiaryPersonEncounter) {
         Timber.d("Deleting person encounter $contactDiaryPersonEncounter")
         executeWhenIdNotDefault(contactDiaryPersonEncounter.id) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryUIModule.kt
index 0947e119ce9ba4f567f88b54f69493a846dd466e..69968ec42f1554ea3a008148f4041fbae07663a7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryUIModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/ContactDiaryUIModule.kt
@@ -13,10 +13,10 @@ import de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFra
 import de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFragmentModule
 import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragment
 import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragmentModule
-import de.rki.coronawarnapp.contactdiary.ui.sheets.location.ContactDiaryLocationBottomSheetDialogFragment
-import de.rki.coronawarnapp.contactdiary.ui.sheets.location.ContactDiaryLocationBottomSheetDialogModule
-import de.rki.coronawarnapp.contactdiary.ui.sheets.person.ContactDiaryPersonBottomSheetDialogFragment
-import de.rki.coronawarnapp.contactdiary.ui.sheets.person.ContactDiaryPersonBottomSheetDialogModule
+import de.rki.coronawarnapp.contactdiary.ui.location.ContactDiaryAddLocationFragment
+import de.rki.coronawarnapp.contactdiary.ui.location.ContactDiaryAddLocationFragmentModule
+import de.rki.coronawarnapp.contactdiary.ui.person.ContactDiaryAddPersonFragment
+import de.rki.coronawarnapp.contactdiary.ui.person.ContactDiaryAddPersonModule
 
 @Module(includes = [ContactDiaryEditModule::class])
 abstract class ContactDiaryUIModule {
@@ -29,11 +29,11 @@ abstract class ContactDiaryUIModule {
     @ContributesAndroidInjector(modules = [ContactDiaryLocationListModule::class])
     abstract fun contactDiaryLocationListFragment(): ContactDiaryLocationListFragment
 
-    @ContributesAndroidInjector(modules = [ContactDiaryPersonBottomSheetDialogModule::class])
-    abstract fun contactDiaryPersonBottomSheetDialogFragment(): ContactDiaryPersonBottomSheetDialogFragment
+    @ContributesAndroidInjector(modules = [ContactDiaryAddPersonModule::class])
+    abstract fun contactDiaryAddPersonFragment(): ContactDiaryAddPersonFragment
 
-    @ContributesAndroidInjector(modules = [ContactDiaryLocationBottomSheetDialogModule::class])
-    abstract fun contactDiaryLocationBottomSheetDialogFragment(): ContactDiaryLocationBottomSheetDialogFragment
+    @ContributesAndroidInjector(modules = [ContactDiaryAddLocationFragmentModule::class])
+    abstract fun contactDiaryAddLocationFragment(): ContactDiaryAddLocationFragment
 
     @ContributesAndroidInjector(modules = [ContactDiaryOnboardingFragmentModule::class])
     abstract fun contactDiaryOnboardingFragment(): ContactDiaryOnboardingFragment
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayFragment.kt
index 61fb40e02c09387e53d8d431055de6a16d30f223..56bc855e2d76bb55ae29113cd674629a019fcfa6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayFragment.kt
@@ -73,15 +73,15 @@ class ContactDiaryDayFragment : Fragment(R.layout.contact_diary_day_fragment), A
         viewModel.routeToScreen.observe2(this) {
             when (it) {
                 ContactDiaryDayNavigationEvents.NavigateToOverviewFragment -> popBackStack()
-                ContactDiaryDayNavigationEvents.NavigateToAddPersonBottomSheet -> doNavigate(
+                ContactDiaryDayNavigationEvents.NavigateToAddPersonFragment -> doNavigate(
                     ContactDiaryDayFragmentDirections
-                        .actionContactDiaryDayFragmentToContactDiaryPersonBottomSheetDialogFragment(
+                        .actionContactDiaryDayFragmentToContactDiaryAddPersonFragment(
                             addedAt = navArgs.selectedDay
                         )
                 )
-                ContactDiaryDayNavigationEvents.NavigateToAddLocationBottomSheet -> doNavigate(
+                ContactDiaryDayNavigationEvents.NavigateToAddLocationFragment -> doNavigate(
                     ContactDiaryDayFragmentDirections
-                        .actionContactDiaryDayFragmentToContactDiaryLocationBottomSheetDialogFragment(
+                        .actionContactDiaryDayFragmentToContactDiaryAddLocationFragment(
                             addedAt = navArgs.selectedDay
                         )
                 )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayNavigationEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayNavigationEvents.kt
index 8524c31442a6e990256712d41f3fb09e764f69ce..5dae4103991127874f4711be26e3a3ab84e641c3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayNavigationEvents.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayNavigationEvents.kt
@@ -2,6 +2,6 @@ package de.rki.coronawarnapp.contactdiary.ui.day
 
 sealed class ContactDiaryDayNavigationEvents {
     object NavigateToOverviewFragment : ContactDiaryDayNavigationEvents()
-    object NavigateToAddPersonBottomSheet : ContactDiaryDayNavigationEvents()
-    object NavigateToAddLocationBottomSheet : ContactDiaryDayNavigationEvents()
+    object NavigateToAddPersonFragment : ContactDiaryDayNavigationEvents()
+    object NavigateToAddLocationFragment : ContactDiaryDayNavigationEvents()
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt
index 6e6a3244d38da0fabf5bf6bba29afd1bef89acb4..530d026fd3ba426a917600d2b8688e7b82001a33 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/ContactDiaryDayViewModel.kt
@@ -39,11 +39,9 @@ class ContactDiaryDayViewModel @AssistedInject constructor(
     fun onCreateButtonClicked(activeTab: ContactDiaryDayTab) {
         when (activeTab) {
             ContactDiaryDayTab.LocationTab ->
-                routeToScreen
-                    .postValue(ContactDiaryDayNavigationEvents.NavigateToAddLocationBottomSheet)
+                routeToScreen.postValue(ContactDiaryDayNavigationEvents.NavigateToAddLocationFragment)
             ContactDiaryDayTab.PersonTab ->
-                routeToScreen
-                    .postValue(ContactDiaryDayNavigationEvents.NavigateToAddPersonBottomSheet)
+                routeToScreen.postValue(ContactDiaryDayNavigationEvents.NavigateToAddPersonFragment)
         }
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/ContactDiaryCommentInfoFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/ContactDiaryCommentInfoFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..06b77c17c8ed3726de6e0b502949e004679e0d3f
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/ContactDiaryCommentInfoFragment.kt
@@ -0,0 +1,22 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.Fragment
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.databinding.ContactDiaryCommentInfoFragmentBinding
+import de.rki.coronawarnapp.util.ui.popBackStack
+import de.rki.coronawarnapp.util.ui.viewBindingLazy
+
+class ContactDiaryCommentInfoFragment : Fragment(R.layout.contact_diary_comment_info_fragment) {
+
+    private val binding: ContactDiaryCommentInfoFragmentBinding by viewBindingLazy()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        binding.toolbar.setNavigationOnClickListener {
+            popBackStack()
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..11db3cab18d1285d54c2c1d70cd85381ec0f67e5
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryCircumstancesTextView.kt
@@ -0,0 +1,67 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.common
+
+import android.content.Context
+import android.text.InputType
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.inputmethod.EditorInfo
+import android.widget.EditText
+import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import de.rki.coronawarnapp.R
+import timber.log.Timber
+
+class DiaryCircumstancesTextView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val input: EditText
+    private val infoButton: ImageView
+
+    private var afterTextChangedListener: ((String) -> Unit)? = null
+
+    init {
+        LayoutInflater.from(context).inflate(R.layout.view_diary_circumstances_textview, this, true)
+
+        input = findViewById<EditText>(R.id.input).apply {
+            setOnFocusChangeListener { v, hasFocus ->
+                if (hasFocus) {
+                    Timber.v("Focused on %s", v)
+                } else {
+                    Timber.v("Lost focus on %s", v)
+                    notifyTextChanged(text.toString())
+                }
+            }
+            imeOptions = EditorInfo.IME_ACTION_DONE
+            setRawInputType(InputType.TYPE_CLASS_TEXT)
+        }
+        infoButton = findViewById(R.id.info_button)
+    }
+
+    override fun onFinishInflate() {
+        input.clearFocus()
+        super.onFinishInflate()
+    }
+
+    private fun notifyTextChanged(text: String) {
+        // Prevent Copy&Paste inserting new lines.
+        afterTextChangedListener?.invoke(text.trim().replace("\n", ""))
+    }
+
+    fun setInfoButtonClickListener(listener: () -> Unit) {
+        infoButton.setOnClickListener { listener() }
+    }
+
+    fun setInputTextChangedListener(listener: ((String) -> Unit)?) {
+        afterTextChangedListener = listener
+    }
+
+    fun setInputText(text: String) {
+        val temp = afterTextChangedListener
+        afterTextChangedListener = null
+        input.setText(text)
+        afterTextChangedListener = temp
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryTabViewHolderExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryTabViewHolderExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9df0a38f4b04dbf7276fef3cad44447ac6819edb
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/DiaryTabViewHolderExtensions.kt
@@ -0,0 +1,14 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.common
+
+import android.view.View
+import com.google.android.material.button.MaterialButtonToggleGroup
+
+fun MaterialButtonToggleGroup.setOnCheckedChangeListener(listener: (checkedId: Int?) -> Unit) {
+    clearOnButtonCheckedListeners()
+    addOnButtonCheckedListener { group, checkedId, isChecked ->
+        when {
+            isChecked -> listener.invoke(checkedId)
+            group.checkedButtonId == View.NO_ID -> listener.invoke(null)
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/ExpandingDiaryListItemView.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/ExpandingDiaryListItemView.kt
new file mode 100644
index 0000000000000000000000000000000000000000..83a6be14d2c5b6fe0f3d9f170cf2ac10f62ffc0c
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/ExpandingDiaryListItemView.kt
@@ -0,0 +1,66 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.common
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.util.ui.setGone
+
+class ExpandingDiaryListItemView @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private val titleView: TextView
+    private val checkboxView: ImageView
+    private val container: ViewGroup
+    private val divider: View
+    val header: ViewGroup
+
+    init {
+        LayoutInflater.from(context).inflate(R.layout.view_expanding_diary_listitem, this, true)
+
+        titleView = findViewById(R.id.header_title)
+        checkboxView = findViewById(R.id.header_checkbox)
+        header = findViewById(R.id.header)
+        container = findViewById(R.id.container)
+        divider = findViewById(R.id.divider)
+    }
+
+    override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams?) {
+        if (TOPLEVEL_IDS.contains(child.id)) {
+            super.addView(child, index, params)
+        } else {
+            container.addView(child, index, params)
+        }
+    }
+
+    var title: String
+        get() = titleView.text.toString()
+        set(value) {
+            titleView.text = value
+        }
+
+    var isExpanded: Boolean
+        get() = isSelected
+        set(expanded) {
+            isSelected = expanded
+            checkboxView.setImageResource(if (expanded) R.drawable.ic_selected else R.drawable.ic_unselected)
+            container.setGone(!expanded)
+            divider.setGone(!expanded)
+        }
+
+    companion object {
+        private val TOPLEVEL_IDS = listOf(
+            R.id.header,
+            R.id.divider,
+            R.id.container
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/SelectableDiaryItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/SelectableDiaryItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..57445c07acbbbaa24b4ec64c4d7127e38f47d70a
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/common/SelectableDiaryItem.kt
@@ -0,0 +1,17 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.common
+
+import androidx.annotation.StringRes
+import de.rki.coronawarnapp.util.lists.HasStableId
+import de.rki.coronawarnapp.util.ui.LazyString
+
+abstract class SelectableDiaryItem<T : HasStableId> : HasStableId {
+    abstract val onItemClick: (SelectableDiaryItem<T>) -> Unit
+    abstract val selected: Boolean
+    abstract val item: T
+    abstract val contentDescription: LazyString
+    abstract val onClickDescription: LazyString
+    @get:StringRes abstract val clickLabel: Int
+    @get:StringRes abstract val onClickLabel: Int
+    override val stableId: Long
+        get() = item.stableId
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListAdapter.kt
index 050dcc52be0bb9b00fd006db11f403e96bb4ce18..c92dce45ff8934c4288cad6e307a092ffea92598 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListAdapter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListAdapter.kt
@@ -1,53 +1,16 @@
 package de.rki.coronawarnapp.contactdiary.ui.day.tabs.location
 
 import android.view.ViewGroup
-import android.view.accessibility.AccessibilityEvent
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
 import de.rki.coronawarnapp.contactdiary.util.AbstractAdapter
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
-import de.rki.coronawarnapp.contactdiary.util.setClickLabel
-import de.rki.coronawarnapp.databinding.ContactDiaryLocationListItemBinding
-import de.rki.coronawarnapp.ui.lists.BaseAdapter
-import de.rki.coronawarnapp.util.lists.BindableVH
 import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter
-import de.rki.coronawarnapp.util.ui.setOnClickListenerThrottled
 
-internal class ContactDiaryLocationListAdapter(
-    private val onTappedCallback: (item: SelectableItem<ContactDiaryLocation>) -> Unit
-) : AbstractAdapter<SelectableItem<ContactDiaryLocation>, ContactDiaryLocationListAdapter.CachedLocationViewHolder>(),
-    AsyncDiffUtilAdapter<SelectableItem<ContactDiaryLocation>> {
+internal class ContactDiaryLocationListAdapter :
+    AbstractAdapter<DiaryLocationListItem, DiaryLocationViewHolder>(),
+    AsyncDiffUtilAdapter<DiaryLocationListItem> {
 
-    override fun onCreateBaseVH(parent: ViewGroup, viewType: Int): CachedLocationViewHolder =
-        CachedLocationViewHolder(parent)
+    override fun onCreateBaseVH(parent: ViewGroup, viewType: Int) = DiaryLocationViewHolder(parent)
 
-    override fun onBindBaseVH(holder: CachedLocationViewHolder, position: Int, payloads: MutableList<Any>) {
-        val item = data[position]
-        holder.itemView.setOnClickListenerThrottled {
-            it.contentDescription = item.onClickDescription.get(holder.context)
-            it.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
-            onTappedCallback(item)
-        }
-        holder.bind(item)
-    }
-
-    class CachedLocationViewHolder(
-        parent: ViewGroup
-    ) : BaseAdapter.VH(R.layout.contact_diary_location_list_item, parent),
-        BindableVH<SelectableItem<ContactDiaryLocation>, ContactDiaryLocationListItemBinding> {
-        override val viewBinding = lazy { ContactDiaryLocationListItemBinding.bind(itemView) }
-
-        override val onBindData: ContactDiaryLocationListItemBinding.(
-            key: SelectableItem<ContactDiaryLocation>,
-            payloads: List<Any>
-        ) -> Unit = { key, _ ->
-            contactDiaryLocationListItemName.text = key.item.locationName
-            contactDiaryLocationListItem.contentDescription = key.contentDescription.get(context)
-            contactDiaryLocationListItem.setClickLabel(context.getString(key.clickLabel))
-            when (key.selected) {
-                true -> contactDiaryLocationListItemIcon.setImageResource(R.drawable.ic_selected)
-                false -> contactDiaryLocationListItemIcon.setImageResource(R.drawable.ic_unselected)
-            }
-        }
+    override fun onBindBaseVH(holder: DiaryLocationViewHolder, position: Int, payloads: MutableList<Any>) {
+        holder.bind(data[position])
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListFragment.kt
index ee5cedaaad5e8115464aa21674b2a20c850d34da..3321505cd75328547b00da24991918abc0977ee7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListFragment.kt
@@ -33,9 +33,7 @@ class ContactDiaryLocationListFragment : Fragment(R.layout.contact_diary_locatio
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        val locationListAdapter = ContactDiaryLocationListAdapter {
-            viewModel.onLocationSelectionChanged(it)
-        }
+        val locationListAdapter = ContactDiaryLocationListAdapter()
 
         binding.contactDiaryLocationListRecyclerView.apply {
             adapter = locationListAdapter
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt
index f232f9f94455927fc274822a9e9711c084e7c091..d9155c01dbe5bc9d3c8f597dc5fb101017dc2a41 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/ContactDiaryLocationListViewModel.kt
@@ -4,20 +4,19 @@ import androidx.lifecycle.asLiveData
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocationVisit
+import de.rki.coronawarnapp.contactdiary.model.toEditableVariant
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
-import de.rki.coronawarnapp.util.ui.toResolvingString
+import de.rki.coronawarnapp.util.trimToLength
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import kotlinx.coroutines.CoroutineExceptionHandler
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.first
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 
 class ContactDiaryLocationListViewModel @AssistedInject constructor(
@@ -34,31 +33,29 @@ class ContactDiaryLocationListViewModel @AssistedInject constructor(
     private val dayElement = contactDiaryRepository.locationVisitsForDate(localDate)
     private val selectableLocations = contactDiaryRepository.locations
 
-    val uiList = selectableLocations.combine(dayElement) { locations, dayElement ->
-        locations.map { contactDiaryLocation ->
-            if (dayElement.any { it.contactDiaryLocation.locationId == contactDiaryLocation.locationId }) {
-                SelectableItem(
-                    true,
-                    contactDiaryLocation,
-                    SELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryLocation.locationName),
-                    UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryLocation.locationName),
-                    DESELECT_ACTION_DESCRIPTION,
-                    SELECT_ACTION_DESCRIPTION
-                )
-            } else {
-                SelectableItem(
-                    false,
-                    contactDiaryLocation,
-                    UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryLocation.locationName),
-                    SELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryLocation.locationName),
-                    SELECT_ACTION_DESCRIPTION,
-                    DESELECT_ACTION_DESCRIPTION
-                )
+    val uiList = selectableLocations.combine(dayElement) { locations, encounters ->
+        locations.map { location ->
+            val visit = encounters.singleOrNull {
+                it.contactDiaryLocation.locationId == location.locationId
             }
+            DiaryLocationListItem(
+                item = location,
+                visit = visit,
+                onItemClick = { onLocationSelectionChanged(it as DiaryLocationListItem) },
+                onDurationChanged = { item, duration ->
+                    onDurationChanged(item, duration)
+                },
+                onCircumstancesChanged = { item, circumstances ->
+                    onCircumstancesChanged(item, circumstances)
+                },
+                onCircumStanceInfoClicked = {
+                    // TODO
+                }
+            )
         }
     }.asLiveData()
 
-    fun onLocationSelectionChanged(item: SelectableItem<ContactDiaryLocation>) = launch(coroutineExceptionHandler) {
+    private fun onLocationSelectionChanged(item: DiaryLocationListItem) = launch(coroutineExceptionHandler) {
         if (!item.selected) {
             contactDiaryRepository.addLocationVisit(
                 DefaultContactDiaryLocationVisit(
@@ -74,6 +71,27 @@ class ContactDiaryLocationListViewModel @AssistedInject constructor(
         }
     }
 
+    private fun onDurationChanged(
+        item: DiaryLocationListItem,
+        duration: Duration?
+    ) {
+        val visit = item.visit?.toEditableVariant() ?: return
+        launch {
+            contactDiaryRepository.updateLocationVisit(visit.copy(duration = duration))
+        }
+    }
+
+    private fun onCircumstancesChanged(
+        item: DiaryLocationListItem,
+        circumstances: String
+    ) {
+        val visit = item.visit?.toEditableVariant() ?: return
+        val sanitized = circumstances.trim().trimToLength(250)
+        launch {
+            contactDiaryRepository.updateLocationVisit(visit.copy(circumstances = sanitized))
+        }
+    }
+
     @AssistedFactory
     interface Factory : CWAViewModelFactory<ContactDiaryLocationListViewModel> {
         fun create(selectedDay: String): ContactDiaryLocationListViewModel
@@ -81,7 +99,3 @@ class ContactDiaryLocationListViewModel @AssistedInject constructor(
 }
 
 private val TAG = ContactDiaryLocationListViewModel::class.java.simpleName
-private const val SELECTED_CONTENT_DESCRIPTION = R.string.accessibility_location_selected
-private const val UNSELECTED_CONTENT_DESCRIPTION = R.string.accessibility_location_unselected
-private const val SELECT_ACTION_DESCRIPTION = R.string.accessibility_action_select
-private const val DESELECT_ACTION_DESCRIPTION = R.string.accessibility_action_deselect
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationListItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationListItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..df57fbbe68b4263ecdb1a7079c0f634d452dc277
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationListItem.kt
@@ -0,0 +1,82 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.location
+
+import androidx.annotation.StringRes
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.SelectableDiaryItem
+import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer
+import de.rki.coronawarnapp.util.ui.LazyString
+import de.rki.coronawarnapp.util.ui.toResolvingString
+import org.joda.time.Duration
+
+data class DiaryLocationListItem(
+    override val item: ContactDiaryLocation,
+    val visit: ContactDiaryLocationVisit?,
+    override val onItemClick: (SelectableDiaryItem<ContactDiaryLocation>) -> Unit,
+    val onDurationChanged: (DiaryLocationListItem, Duration?) -> Unit,
+    val onCircumstancesChanged: (DiaryLocationListItem, String) -> Unit,
+    val onCircumStanceInfoClicked: () -> Unit
+) : SelectableDiaryItem<ContactDiaryLocation>(), HasPayloadDiffer {
+    override val selected: Boolean
+        get() = visit != null
+
+    override val contentDescription: LazyString
+        get() = if (selected) {
+            SELECTED_CONTENT_DESCRIPTION.toResolvingString(item.locationName)
+        } else {
+            UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(item.locationName)
+        }
+    override val onClickDescription: LazyString
+        get() = if (selected) {
+            UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(item.locationName)
+        } else {
+            SELECTED_CONTENT_DESCRIPTION.toResolvingString(item.locationName)
+        }
+    override val clickLabel: Int
+        @StringRes get() = if (selected) {
+            DESELECT_ACTION_DESCRIPTION
+        } else {
+            SELECT_ACTION_DESCRIPTION
+        }
+    override val onClickLabel: Int
+        @StringRes get() = if (selected) {
+            SELECT_ACTION_DESCRIPTION
+        } else {
+            DESELECT_ACTION_DESCRIPTION
+        }
+
+    override fun diffPayload(old: Any, new: Any): Any? {
+        old as DiaryLocationListItem
+        new as DiaryLocationListItem
+        // null causes a full re-layout to be executed
+        return when {
+            old.item != new.item -> null // Major change
+            old.visit == null && new.visit != null -> null // Container needs to grow
+            else -> new
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as DiaryLocationListItem
+
+        if (item != other.item) return false
+        if (visit != other.visit) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = item.hashCode()
+        result = 31 * result + (visit?.hashCode() ?: 0)
+        return result
+    }
+}
+
+private const val SELECTED_CONTENT_DESCRIPTION = R.string.accessibility_location_selected
+private const val UNSELECTED_CONTENT_DESCRIPTION = R.string.accessibility_location_unselected
+private const val SELECT_ACTION_DESCRIPTION = R.string.accessibility_action_select
+private const val DESELECT_ACTION_DESCRIPTION = R.string.accessibility_action_deselect
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt
new file mode 100644
index 0000000000000000000000000000000000000000..720d330f2e5255fb4cda6f29b48eda9f2e00bb99
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt
@@ -0,0 +1,44 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.location
+
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.util.setClickLabel
+import de.rki.coronawarnapp.databinding.ContactDiaryLocationListItemBinding
+import de.rki.coronawarnapp.ui.lists.BaseAdapter
+import de.rki.coronawarnapp.util.lists.BindableVH
+import de.rki.coronawarnapp.util.ui.setOnClickListenerThrottled
+
+class DiaryLocationViewHolder(
+    parent: ViewGroup
+) : BaseAdapter.VH(R.layout.contact_diary_location_list_item, parent),
+    BindableVH<DiaryLocationListItem, ContactDiaryLocationListItemBinding> {
+
+    override val viewBinding = lazy { ContactDiaryLocationListItemBinding.bind(itemView) }
+
+    override val onBindData: ContactDiaryLocationListItemBinding.(
+        item: DiaryLocationListItem,
+        changes: List<Any>
+    ) -> Unit = { initial, changes ->
+        val item = changes.firstOrNull() as? DiaryLocationListItem ?: initial
+
+        mainBox.apply {
+            header.setOnClickListenerThrottled {
+                it.contentDescription = item.onClickDescription.get(context)
+                it.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
+                item.onItemClick(item)
+            }
+
+            title = item.item.locationName
+            isExpanded = item.selected
+            contentDescription = item.contentDescription.get(context)
+            setClickLabel(context.getString(item.clickLabel))
+        }
+
+        circumstances.apply {
+            if (changes.isEmpty()) setInputText(item.visit?.circumstances ?: "")
+            circumstances.setInputTextChangedListener { item.onCircumstancesChanged(item, it) }
+            setInfoButtonClickListener { item.onCircumStanceInfoClicked() }
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListAdapter.kt
index c5787ef0561d03c23ec19f3bf9053dedbf53c966..af8b594fef960a20a074062b1f6b5d5c7ca6b7df 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListAdapter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListAdapter.kt
@@ -1,53 +1,16 @@
 package de.rki.coronawarnapp.contactdiary.ui.day.tabs.person
 
 import android.view.ViewGroup
-import android.view.accessibility.AccessibilityEvent
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
 import de.rki.coronawarnapp.contactdiary.util.AbstractAdapter
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
-import de.rki.coronawarnapp.contactdiary.util.setClickLabel
-import de.rki.coronawarnapp.databinding.ContactDiaryPersonListItemBinding
-import de.rki.coronawarnapp.ui.lists.BaseAdapter
-import de.rki.coronawarnapp.util.lists.BindableVH
 import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter
-import de.rki.coronawarnapp.util.ui.setOnClickListenerThrottled
 
-internal class ContactDiaryPersonListAdapter(
-    private val onTappedCallback: (item: SelectableItem<ContactDiaryPerson>) -> Unit
-) : AbstractAdapter<SelectableItem<ContactDiaryPerson>, ContactDiaryPersonListAdapter.CachedPersonViewHolder>(),
-    AsyncDiffUtilAdapter<SelectableItem<ContactDiaryPerson>> {
+internal class ContactDiaryPersonListAdapter :
+    AbstractAdapter<DiaryPersonListItem, DiaryPersonViewHolder>(),
+    AsyncDiffUtilAdapter<DiaryPersonListItem> {
 
-    override fun onCreateBaseVH(parent: ViewGroup, viewType: Int): CachedPersonViewHolder =
-        CachedPersonViewHolder(parent)
+    override fun onCreateBaseVH(parent: ViewGroup, viewType: Int) = DiaryPersonViewHolder(parent)
 
-    override fun onBindBaseVH(holder: CachedPersonViewHolder, position: Int, payloads: MutableList<Any>) {
-        val item = data[position]
-        holder.itemView.setOnClickListenerThrottled {
-            it.contentDescription = item.onClickDescription.get(holder.context)
-            it.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
-            onTappedCallback(item)
-        }
-        holder.bind(item, payloads)
-    }
-
-    class CachedPersonViewHolder(
-        parent: ViewGroup
-    ) : BaseAdapter.VH(R.layout.contact_diary_person_list_item, parent),
-        BindableVH<SelectableItem<ContactDiaryPerson>, ContactDiaryPersonListItemBinding> {
-        override val viewBinding = lazy { ContactDiaryPersonListItemBinding.bind(itemView) }
-
-        override val onBindData: ContactDiaryPersonListItemBinding.(
-            key: SelectableItem<ContactDiaryPerson>,
-            payloads: List<Any>
-        ) -> Unit = { key, _ ->
-            contactDiaryPersonListItemName.text = key.item.fullName
-            contactDiaryPersonListItem.contentDescription = key.contentDescription.get(context)
-            contactDiaryPersonListItem.setClickLabel(context.getString(key.clickLabel))
-            when (key.selected) {
-                true -> contactDiaryPersonListItemIcon.setImageResource(R.drawable.ic_selected)
-                false -> contactDiaryPersonListItemIcon.setImageResource(R.drawable.ic_unselected)
-            }
-        }
+    override fun onBindBaseVH(holder: DiaryPersonViewHolder, position: Int, payloads: MutableList<Any>) {
+        holder.bind(data[position], payloads)
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListFragment.kt
index bab1d0324e3b531dde01514248a80889307bdd84..0d6e9c0525bdb01e4e1908adc223936ceeec79ad 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListFragment.kt
@@ -33,17 +33,11 @@ class ContactDiaryPersonListFragment : Fragment(R.layout.contact_diary_person_li
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        val personListAdapter = ContactDiaryPersonListAdapter() {
-            viewModel.onPersonSelectionChanged(it)
-        }
+        val personListAdapter = ContactDiaryPersonListAdapter()
 
         binding.contactDiaryPersonListRecyclerView.apply {
             adapter = personListAdapter
-            addItemDecoration(
-                MarginRecyclerViewDecoration(
-                    resources.getDimensionPixelSize(R.dimen.spacing_tiny)
-                )
-            )
+            addItemDecoration(MarginRecyclerViewDecoration(resources.getDimensionPixelSize(R.dimen.spacing_tiny)))
         }
 
         viewModel.uiList.observe2(this) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt
index 3051b597a387d14d5b2391e2a4ba307f14c05ff9..5df0f9adddb61faa799f9d07a0d7081a962e3f43 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/ContactDiaryPersonListViewModel.kt
@@ -1,24 +1,25 @@
 package de.rki.coronawarnapp.contactdiary.ui.day.tabs.person
 
+import androidx.lifecycle.LiveData
 import androidx.lifecycle.asLiveData
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPersonEncounter
+import de.rki.coronawarnapp.contactdiary.model.toEditableVariant
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
-import de.rki.coronawarnapp.contactdiary.util.SelectableItem
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
-import de.rki.coronawarnapp.util.ui.toResolvingString
+import de.rki.coronawarnapp.util.flow.combine
+import de.rki.coronawarnapp.util.trimToLength
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import kotlinx.coroutines.CoroutineExceptionHandler
-import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.first
 import org.joda.time.LocalDate
+import timber.log.Timber
 
 class ContactDiaryPersonListViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
@@ -31,34 +32,43 @@ class ContactDiaryPersonListViewModel @AssistedInject constructor(
 
     private val localDate = LocalDate.parse(selectedDay)
 
-    private val dayElement = contactDiaryRepository.personEncountersForDate(localDate)
+    private val dayEncounters = contactDiaryRepository.personEncountersForDate(localDate)
     private val selectablePersons = contactDiaryRepository.people
 
-    val uiList = selectablePersons.combine(dayElement) { persons, dayElement ->
-        persons.map { contactDiaryPerson ->
-            if (dayElement.any { it.contactDiaryPerson.personId == contactDiaryPerson.personId }) {
-                SelectableItem(
-                    true,
-                    contactDiaryPerson,
-                    SELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryPerson.fullName),
-                    UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryPerson.fullName),
-                    DESELECT_ACTION_DESCRIPTION,
-                    SELECT_ACTION_DESCRIPTION
-                )
-            } else {
-                SelectableItem(
-                    false,
-                    contactDiaryPerson,
-                    UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryPerson.fullName),
-                    SELECTED_CONTENT_DESCRIPTION.toResolvingString(contactDiaryPerson.fullName),
-                    SELECT_ACTION_DESCRIPTION,
-                    DESELECT_ACTION_DESCRIPTION
-                )
+    val uiList: LiveData<List<DiaryPersonListItem>> = combine(
+        selectablePersons,
+        dayEncounters
+    ) { persons, encounters ->
+        persons.map { person ->
+            val encounter = encounters.singleOrNull {
+                it.contactDiaryPerson.personId == person.personId
             }
+            DiaryPersonListItem(
+                item = person,
+                personEncounter = encounter,
+                onItemClick = { onPersonSelectionChanged(it as DiaryPersonListItem) },
+                onDurationChanged = { item, duration ->
+                    onDurationChanged(item, duration)
+                },
+                onWasOutsideChanged = { item, wasOutside ->
+                    onWasOutsideChanged(item, wasOutside)
+                },
+                onWithMaskChanged = { item, withMask ->
+                    onWithmaskChanged(item, withMask)
+                },
+                onCircumstancesChanged = { item, circumstances ->
+                    onCircumstancesChanged(item, circumstances)
+                },
+                onCircumstanceInfoClicked = {
+                    // TODO
+                }
+            )
         }
-    }.asLiveData()
+    }.asLiveData(context = dispatcherProvider.Default)
 
-    fun onPersonSelectionChanged(item: SelectableItem<ContactDiaryPerson>) = launch(coroutineExceptionHandler) {
+    private fun onPersonSelectionChanged(
+        item: DiaryPersonListItem
+    ) = launch(coroutineExceptionHandler) {
         if (!item.selected) {
             contactDiaryRepository.addPersonEncounter(
                 DefaultContactDiaryPersonEncounter(
@@ -67,12 +77,57 @@ class ContactDiaryPersonListViewModel @AssistedInject constructor(
                 )
             )
         } else {
-            val visit = dayElement.first()
+            val visit = dayEncounters.first()
                 .find { it.contactDiaryPerson.personId == item.item.personId }
             visit?.let { contactDiaryRepository.deletePersonEncounter(it) }
         }
     }
 
+    private fun onDurationChanged(
+        item: DiaryPersonListItem,
+        duration: ContactDiaryPersonEncounter.DurationClassification?
+    ) {
+        Timber.d("onDurationChanged(item=%s, duration=%s)", item, duration)
+        val encounter = item.personEncounter?.toEditableVariant() ?: return
+        launch {
+            contactDiaryRepository.updatePersonEncounter(encounter.copy(durationClassification = duration))
+        }
+    }
+
+    private fun onWithmaskChanged(
+        item: DiaryPersonListItem,
+        withMask: Boolean?
+    ) {
+        Timber.d("onWithmaskChanged(item=%s, withMask=%s)", item, withMask)
+        val encounter = item.personEncounter?.toEditableVariant() ?: return
+        launch {
+            contactDiaryRepository.updatePersonEncounter(encounter.copy(withMask = withMask))
+        }
+    }
+
+    private fun onWasOutsideChanged(
+        item: DiaryPersonListItem,
+        wasOutside: Boolean?
+    ) {
+        Timber.d("onWasOutsideChanged(item=%s, onWasOutside=%s)", item, wasOutside)
+        val encounter = item.personEncounter?.toEditableVariant() ?: return
+        launch {
+            contactDiaryRepository.updatePersonEncounter(encounter.copy(wasOutside = wasOutside))
+        }
+    }
+
+    private fun onCircumstancesChanged(
+        item: DiaryPersonListItem,
+        circumstances: String
+    ) {
+        Timber.d("onCircumstancesChanged(item=%s, circumstances=%s)", item, circumstances)
+        val encounter = item.personEncounter?.toEditableVariant() ?: return
+        launch {
+            val sanitized = circumstances.trim().trimToLength(250)
+            contactDiaryRepository.updatePersonEncounter(encounter.copy(circumstances = sanitized))
+        }
+    }
+
     @AssistedFactory
     interface Factory : CWAViewModelFactory<ContactDiaryPersonListViewModel> {
         fun create(selectedDay: String): ContactDiaryPersonListViewModel
@@ -80,7 +135,3 @@ class ContactDiaryPersonListViewModel @AssistedInject constructor(
 }
 
 private val TAG = ContactDiaryPersonListViewModel::class.java.simpleName
-private const val SELECTED_CONTENT_DESCRIPTION = R.string.accessibility_person_selected
-private const val UNSELECTED_CONTENT_DESCRIPTION = R.string.accessibility_person_unselected
-private const val SELECT_ACTION_DESCRIPTION = R.string.accessibility_action_select
-private const val DESELECT_ACTION_DESCRIPTION = R.string.accessibility_action_deselect
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonListItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonListItem.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bcd78c442864838c69f7207f7b4c503626be279f
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonListItem.kt
@@ -0,0 +1,85 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.person
+
+import androidx.annotation.StringRes
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.SelectableDiaryItem
+import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer
+import de.rki.coronawarnapp.util.ui.LazyString
+import de.rki.coronawarnapp.util.ui.toResolvingString
+
+data class DiaryPersonListItem(
+    override val item: ContactDiaryPerson,
+    val personEncounter: ContactDiaryPersonEncounter?,
+    override val onItemClick: (SelectableDiaryItem<ContactDiaryPerson>) -> Unit,
+    val onDurationChanged: (DiaryPersonListItem, ContactDiaryPersonEncounter.DurationClassification?) -> Unit,
+    val onWithMaskChanged: (DiaryPersonListItem, Boolean?) -> Unit,
+    val onWasOutsideChanged: (DiaryPersonListItem, Boolean?) -> Unit,
+    val onCircumstancesChanged: (DiaryPersonListItem, String) -> Unit,
+    val onCircumstanceInfoClicked: () -> Unit
+) : SelectableDiaryItem<ContactDiaryPerson>(), HasPayloadDiffer {
+
+    override val selected: Boolean
+        get() = personEncounter != null
+
+    override val contentDescription: LazyString
+        get() = if (selected) {
+            SELECTED_CONTENT_DESCRIPTION.toResolvingString(item.fullName)
+        } else {
+            UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(item.fullName)
+        }
+
+    override val onClickDescription: LazyString
+        get() = if (selected) {
+            UNSELECTED_CONTENT_DESCRIPTION.toResolvingString(item.fullName)
+        } else {
+            SELECTED_CONTENT_DESCRIPTION.toResolvingString(item.fullName)
+        }
+    override val clickLabel: Int
+        @StringRes get() = if (selected) {
+            DESELECT_ACTION_DESCRIPTION
+        } else {
+            SELECT_ACTION_DESCRIPTION
+        }
+    override val onClickLabel: Int
+        @StringRes get() = if (selected) {
+            SELECT_ACTION_DESCRIPTION
+        } else {
+            DESELECT_ACTION_DESCRIPTION
+        }
+
+    override fun diffPayload(old: Any, new: Any): Any? {
+        old as DiaryPersonListItem
+        new as DiaryPersonListItem
+        // null causes a full re-layout to be executed
+        return when {
+            old.item != new.item -> null // Major change
+            old.personEncounter == null && new.personEncounter != null -> null // Container needs to grow
+            else -> new
+        }
+    }
+
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+
+        other as DiaryPersonListItem
+
+        if (item != other.item) return false
+        if (personEncounter != other.personEncounter) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        var result = item.hashCode()
+        result = 31 * result + (personEncounter?.hashCode() ?: 0)
+        return result
+    }
+}
+
+private const val SELECTED_CONTENT_DESCRIPTION = R.string.accessibility_person_selected
+private const val UNSELECTED_CONTENT_DESCRIPTION = R.string.accessibility_person_unselected
+private const val SELECT_ACTION_DESCRIPTION = R.string.accessibility_action_select
+private const val DESELECT_ACTION_DESCRIPTION = R.string.accessibility_action_deselect
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonViewHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonViewHolder.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bae3f5575dfa1c90a1e6ea5afae426efd88012d2
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/person/DiaryPersonViewHolder.kt
@@ -0,0 +1,96 @@
+package de.rki.coronawarnapp.contactdiary.ui.day.tabs.person
+
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter.DurationClassification
+import de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.setOnCheckedChangeListener
+import de.rki.coronawarnapp.contactdiary.util.setClickLabel
+import de.rki.coronawarnapp.databinding.ContactDiaryPersonListItemBinding
+import de.rki.coronawarnapp.ui.lists.BaseAdapter
+import de.rki.coronawarnapp.util.lists.BindableVH
+import de.rki.coronawarnapp.util.ui.setOnClickListenerThrottled
+
+class DiaryPersonViewHolder(
+    parent: ViewGroup
+) : BaseAdapter.VH(R.layout.contact_diary_person_list_item, parent),
+    BindableVH<DiaryPersonListItem, ContactDiaryPersonListItemBinding> {
+
+    override val viewBinding = lazy { ContactDiaryPersonListItemBinding.bind(itemView) }
+
+    override val onBindData: ContactDiaryPersonListItemBinding.(
+        item: DiaryPersonListItem,
+        changes: List<Any>
+    ) -> Unit = { initial, changes ->
+        val item = changes.firstOrNull() as? DiaryPersonListItem ?: initial
+
+        mainBox.apply {
+            header.setOnClickListenerThrottled {
+                it.contentDescription = item.onClickDescription.get(context)
+                it.sendAccessibilityEvent(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
+                item.onItemClick(item)
+            }
+
+            title = item.item.fullName
+            isExpanded = item.selected
+            contentDescription = item.contentDescription.get(context)
+            setClickLabel(context.getString(item.clickLabel))
+        }
+
+        durationGroup.apply {
+            clearOnButtonCheckedListeners()
+            when (item.personEncounter?.durationClassification) {
+                DurationClassification.MORE_THAN_15_MINUTES -> check(R.id.duration_above_15)
+                DurationClassification.LESS_THAN_15_MINUTES -> check(R.id.duration_below_15)
+                null -> clearChecked()
+            }
+            setOnCheckedChangeListener { checkedId ->
+                when (checkedId) {
+                    R.id.duration_above_15 -> DurationClassification.MORE_THAN_15_MINUTES
+                    R.id.duration_below_15 -> DurationClassification.LESS_THAN_15_MINUTES
+                    else -> null
+                }.let { item.onDurationChanged(item, it) }
+            }
+        }
+
+        maskGroup.apply {
+            clearOnButtonCheckedListeners()
+            when (item.personEncounter?.withMask) {
+                true -> check(R.id.mask_with)
+                false -> check(R.id.mask_without)
+                null -> clearChecked()
+            }
+            setOnCheckedChangeListener { checkedId ->
+                when (checkedId) {
+                    R.id.mask_with -> true
+                    R.id.mask_without -> false
+                    else -> null
+                }.let { item.onWithMaskChanged(item, it) }
+            }
+        }
+
+        environmentGroup.apply {
+            clearOnButtonCheckedListeners()
+            when (item.personEncounter?.wasOutside) {
+                true -> check(R.id.environment_outside)
+                false -> check(R.id.environment_inside)
+                null -> clearChecked()
+            }
+            setOnCheckedChangeListener { checkedId ->
+                when (checkedId) {
+                    R.id.environment_outside -> true
+                    R.id.environment_inside -> false
+                    else -> null
+                }.let { item.onWasOutsideChanged(item, it) }
+            }
+        }
+
+        circumstances.apply {
+            // When data changes, we get that via payload
+            // To not update the edittext while typing, only the the text input on the first "bind"
+            if (changes.isEmpty()) setInputText(item.personEncounter?.circumstances ?: "")
+            circumstances.setInputTextChangedListener { item.onCircumstancesChanged(item, it) }
+            setInfoButtonClickListener { item.onCircumstanceInfoClicked() }
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6a39ab7edd0af915f3786eea3a32c7630941fc0c
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragment.kt
@@ -0,0 +1,73 @@
+package de.rki.coronawarnapp.contactdiary.ui.durationpicker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.DialogFragment
+import de.rki.coronawarnapp.databinding.ContactDiaryDurationPickerDialogFragmentBinding
+import org.joda.time.Duration
+import org.joda.time.format.PeriodFormatter
+import org.joda.time.format.PeriodFormatterBuilder
+
+class ContactDiaryDurationPickerFragment : DialogFragment() {
+
+    interface OnChangeListener {
+        fun onChange(duration: Duration)
+    }
+
+    val binding: Lazy<ContactDiaryDurationPickerDialogFragmentBinding> = lazy {
+        ContactDiaryDurationPickerDialogFragmentBinding.inflate(
+            layoutInflater
+        )
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+        return binding.value.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        with(binding.value.hours) {
+            minValue = 0
+            maxValue = hoursArray.size - 1
+            displayedValues = hoursArray
+        }
+
+        with(binding.value.minutes) {
+            minValue = 0
+            maxValue = minutesArray.size - 1
+            displayedValues = minutesArray
+        }
+
+        with(binding.value) {
+            val duration = requireArguments().getString(DURATION_ARGUMENT_KEY)!!.split(":").toTypedArray()
+            hours.value = hoursArray.indexOf(duration[0])
+            minutes.value = minutesArray.indexOf(duration[1])
+
+            cancelButton.setOnClickListener { dismiss() }
+            okButton.setOnClickListener {
+                (targetFragment as? OnChangeListener)?.onChange(getDuration(hours.value, minutes.value))
+                dismiss()
+            }
+        }
+    }
+
+    companion object {
+        const val DURATION_ARGUMENT_KEY = "duration"
+
+        val minutesArray = arrayOf("00", "15", "30", "45")
+        val hoursArray = Array(24) { "%02d".format(it) }
+
+        fun getDuration(hours: Int, minutes: Int): Duration {
+            val durationString = hoursArray[hours] + ":" + minutesArray[minutes]
+            val formatter: PeriodFormatter = PeriodFormatterBuilder()
+                .appendHours()
+                .appendLiteral(":")
+                .appendMinutes()
+                .toFormatter()
+            return formatter.parsePeriod(durationString).toStandardDuration()
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/DurationExtension.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/DurationExtension.kt
new file mode 100644
index 0000000000000000000000000000000000000000..270c455e0cda681f10ebcb4e2d0736acac986481
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/DurationExtension.kt
@@ -0,0 +1,18 @@
+package de.rki.coronawarnapp.contactdiary.ui.durationpicker
+
+import org.joda.time.Duration
+
+fun Duration.toContactDiaryFormat(): String {
+    val hours = if (standardHours < 10) {
+        "0$standardHours"
+    } else {
+        standardHours.toString()
+    }
+    val minutesCleaned = standardMinutes - standardHours * 60
+    val minutes = if (minutesCleaned < 10) {
+        "0$minutesCleaned"
+    } else {
+        minutesCleaned.toString()
+    }
+    return "$hours:$minutes"
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt
index 5a226c1139680e183acad1ee8307828b1a8fc347..d5a83e00c287beb09f8e0bda7f47b37c309854af 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt
@@ -7,7 +7,7 @@ import androidx.core.view.isGone
 import androidx.fragment.app.Fragment
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
-import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailSheet
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailFragment
 import de.rki.coronawarnapp.contactdiary.ui.edit.adapter.LocationEditAdapter
 import de.rki.coronawarnapp.databinding.ContactDiaryEditLocationsFragmentBinding
 import de.rki.coronawarnapp.util.DialogHelper
@@ -56,10 +56,10 @@ class ContactDiaryEditLocationsFragment : Fragment(R.layout.contact_diary_edit_l
 
             when (it) {
                 ShowDeletionConfirmationDialog -> DialogHelper.showDialog(deleteAllLocationsConfirmationDialog)
-                is ShowLocationDetailSheet -> {
+                is ShowLocationDetailFragment -> {
                     doNavigate(
                         ContactDiaryEditLocationsFragmentDirections
-                            .actionContactDiaryEditLocationsFragmentToContactDiaryLocationBottomSheetDialogFragment(
+                            .actionContactDiaryEditLocationsFragmentToContactDiaryAddLocationFragment(
                                 it.location
                             )
                     )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt
index 76f288d4efe549ed745434ba65f962250eb5ccba..d18e809397cc4ea72290cd873c3c9f7700501e27 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt
@@ -47,7 +47,7 @@ class ContactDiaryEditLocationsViewModel @AssistedInject constructor(
     }
 
     fun onEditLocationClick(location: ContactDiaryLocation) {
-        navigationEvent.postValue(NavigationEvent.ShowLocationDetailSheet(location.toContactDiaryLocationEntity()))
+        navigationEvent.postValue(NavigationEvent.ShowLocationDetailFragment(location.toContactDiaryLocationEntity()))
     }
 
     @AssistedFactory
@@ -55,7 +55,7 @@ class ContactDiaryEditLocationsViewModel @AssistedInject constructor(
 
     sealed class NavigationEvent {
         object ShowDeletionConfirmationDialog : NavigationEvent()
-        data class ShowLocationDetailSheet(val location: ContactDiaryLocationEntity) : NavigationEvent()
+        data class ShowLocationDetailFragment(val location: ContactDiaryLocationEntity) : NavigationEvent()
     }
 }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt
index bd46ac7977fcb952ecb6a172ed22f49935327ff5..cc8a3c514ef9df8de6b549da05c9e00ab398a309 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt
@@ -7,7 +7,7 @@ import androidx.core.view.isGone
 import androidx.fragment.app.Fragment
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
-import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailSheet
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailFragment
 import de.rki.coronawarnapp.contactdiary.ui.edit.adapter.PersonEditAdapter
 import de.rki.coronawarnapp.databinding.ContactDiaryEditPersonsFragmentBinding
 import de.rki.coronawarnapp.util.DialogHelper
@@ -54,10 +54,10 @@ class ContactDiaryEditPersonsFragment : Fragment(R.layout.contact_diary_edit_per
 
             when (it) {
                 ShowDeletionConfirmationDialog -> DialogHelper.showDialog(deleteAllPersonsConfirmationDialog)
-                is ShowPersonDetailSheet -> {
+                is ShowPersonDetailFragment -> {
                     doNavigate(
                         ContactDiaryEditPersonsFragmentDirections
-                            .actionContactDiaryEditPersonsFragmentToContactDiaryPersonBottomSheetDialogFragment(
+                            .actionContactDiaryEditPersonsFragmentToContactDiaryAddPersonFragment(
                                 it.person
                             )
                     )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt
index 052ec29c6a45352a75b540a203b2175f0a942d34..d648222c2cc2c0ad8196031e4295f02499c4ce28 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt
@@ -48,7 +48,7 @@ class ContactDiaryEditPersonsViewModel @AssistedInject constructor(
     }
 
     fun onEditPersonClick(person: ContactDiaryPerson) {
-        navigationEvent.postValue(NavigationEvent.ShowPersonDetailSheet(person.toContactDiaryPersonEntity()))
+        navigationEvent.postValue(NavigationEvent.ShowPersonDetailFragment(person.toContactDiaryPersonEntity()))
     }
 
     @AssistedFactory
@@ -56,7 +56,7 @@ class ContactDiaryEditPersonsViewModel @AssistedInject constructor(
 
     sealed class NavigationEvent {
         object ShowDeletionConfirmationDialog : NavigationEvent()
-        data class ShowPersonDetailSheet(val person: ContactDiaryPersonEntity) : NavigationEvent()
+        data class ShowPersonDetailFragment(val person: ContactDiaryPersonEntity) : NavigationEvent()
     }
 }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..078300ce3c65481721f754a75d063ac56cddaa27
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragment.kt
@@ -0,0 +1,120 @@
+package de.rki.coronawarnapp.contactdiary.ui.location
+
+import android.os.Bundle
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import androidx.core.widget.doAfterTextChanged
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.util.focusAndShowKeyboard
+import de.rki.coronawarnapp.contactdiary.util.hideKeyboard
+import de.rki.coronawarnapp.databinding.ContactDiaryAddLocationFragmentBinding
+import de.rki.coronawarnapp.util.DialogHelper
+import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.observe2
+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 javax.inject.Inject
+
+class ContactDiaryAddLocationFragment : Fragment(R.layout.contact_diary_add_location_fragment), AutoInject {
+
+    private val binding: ContactDiaryAddLocationFragmentBinding by viewBindingLazy()
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: ContactDiaryAddLocationViewModel by cwaViewModelsAssisted(
+        factoryProducer = { viewModelFactory },
+        constructorCall = { factory, _ ->
+            factory as ContactDiaryAddLocationViewModel.Factory
+            factory.create(navArgs.addedAt)
+        }
+    )
+
+    private val navArgs: ContactDiaryAddLocationFragmentArgs by navArgs()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val location = navArgs.selectedLocation
+        if (location != null) {
+            binding.apply {
+                contactDiaryAddLocationNameInputEditText.setText(location.locationName)
+                contactDiaryAddLocationPhoneInputEditText.setText(location.phoneNumber)
+                contactDiaryAddLocationEmailInputEditText.setText(location.emailAddress)
+                contactDiaryAddLocationDeleteButton.visibility = View.VISIBLE
+                contactDiaryAddLocationDeleteButton.setOnClickListener {
+                    DialogHelper.showDialog(deleteLocationConfirmationDialog)
+                }
+                contactDiaryAddLocationSaveButton.setOnClickListener {
+                    it.hideKeyboard()
+                    viewModel.updateLocation(
+                        location,
+                        phoneNumber = binding.contactDiaryAddLocationPhoneInputEditText.text.toString().trim(),
+                        emailAddress = binding.contactDiaryAddLocationEmailInputEditText.text.toString().trim()
+                    )
+                }
+            }
+            viewModel.locationChanged(location.locationName)
+        } else {
+            binding.apply {
+                contactDiaryAddLocationDeleteButton.visibility = View.GONE
+                contactDiaryAddLocationSaveButton.setOnClickListener {
+                    it.hideKeyboard()
+                    viewModel.addLocation(
+                        phoneNumber = binding.contactDiaryAddLocationPhoneInputEditText.text.toString().trim(),
+                        emailAddress = binding.contactDiaryAddLocationEmailInputEditText.text.toString().trim()
+                    )
+                }
+            }
+        }
+
+        binding.apply {
+            contactDiaryAddLocationNameInputEditText.focusAndShowKeyboard()
+
+            contactDiaryAddLocationCloseButton.setOnClickListener {
+                it.hideKeyboard()
+                viewModel.closePressed()
+            }
+            contactDiaryAddLocationNameInputEditText.doAfterTextChanged {
+                viewModel.locationChanged(it.toString())
+            }
+
+            contactDiaryAddLocationEmailInputEditText.setOnEditorActionListener { _, actionId, _ ->
+                return@setOnEditorActionListener when (actionId) {
+                    EditorInfo.IME_ACTION_DONE -> {
+                        if (viewModel.isValid.value == true) {
+                            binding.contactDiaryAddLocationSaveButton.performClick()
+                        }
+                        false
+                    }
+                    else -> true
+                }
+            }
+        }
+
+        viewModel.shouldClose.observe2(this) {
+            popBackStack()
+        }
+
+        viewModel.isValid.observe2(this) {
+            binding.contactDiaryAddLocationSaveButton.isEnabled = it
+        }
+    }
+
+    private val deleteLocationConfirmationDialog by lazy {
+        DialogHelper.DialogInstance(
+            requireActivity(),
+            R.string.contact_diary_delete_location_title,
+            R.string.contact_diary_delete_locations_message,
+            R.string.contact_diary_delete_button_positive,
+            R.string.contact_diary_delete_button_negative,
+            positiveButtonFunction = {
+                navArgs.selectedLocation?.let {
+                    viewModel.deleteLocation(it)
+                }
+            }
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragmentModule.kt
similarity index 50%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogModule.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragmentModule.kt
index f1a84587d419662e38ede9d162cd8290c4589057..e73bc057f18868ae3b69361c333ddc3145b55e2b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationFragmentModule.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.person
+package de.rki.coronawarnapp.contactdiary.ui.location
 
 import dagger.Binds
 import dagger.Module
@@ -8,11 +8,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
 
 @Module
-abstract class ContactDiaryPersonBottomSheetDialogModule {
+abstract class ContactDiaryAddLocationFragmentModule {
     @Binds
     @IntoMap
-    @CWAViewModelKey(ContactDiaryPersonBottomSheetDialogViewModel::class)
-    abstract fun contactDiaryPersonBottomSheetDialogFragment(
-        factory: ContactDiaryPersonBottomSheetDialogViewModel.Factory
+    @CWAViewModelKey(ContactDiaryAddLocationViewModel::class)
+    abstract fun contactDiaryAddLocationFragment(
+        factory: ContactDiaryAddLocationViewModel.Factory
     ): CWAViewModelFactory<out CWAViewModel>
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt
similarity index 64%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt
index 14263b7dc44788935daf863f5432db8251f22fef..3f0b7c7ab96dfecadf57e861e7a475b3ad9f09d9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.location
+package de.rki.coronawarnapp.contactdiary.ui.location
 
 import androidx.lifecycle.asLiveData
 import dagger.assisted.Assisted
@@ -10,7 +10,6 @@ import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEnti
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
-import de.rki.coronawarnapp.contactdiary.util.formatContactDiaryNameField
 import de.rki.coronawarnapp.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
@@ -21,7 +20,7 @@ import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
 import org.joda.time.LocalDate
 
-class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor(
+class ContactDiaryAddLocationViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
     @Assisted private val addedAt: String?,
     private val contactDiaryRepository: ContactDiaryRepository
@@ -31,25 +30,24 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor
         ex.report(ExceptionCategory.INTERNAL, TAG)
     }
 
-    private val text = MutableStateFlow("")
-
-    val isValid = text.map {
-        it.isNotEmpty() && it.length <= MAX_LOCATION_NAME_LENGTH
-    }.asLiveData()
-
     val shouldClose = SingleLiveEvent<Unit>()
 
-    private val formattedName: String
-        get() = text.value.formatContactDiaryNameField(MAX_LOCATION_NAME_LENGTH)
+    private val locationName = MutableStateFlow("")
+
+    val isValid = locationName
+        .map { it.isNotEmpty() }
+        .asLiveData()
 
-    fun textChanged(locationName: String) {
-        text.value = locationName
+    fun locationChanged(value: String) {
+        locationName.value = value.trim()
     }
 
-    fun addLocation() = launch(coroutineExceptionHandler) {
+    fun addLocation(phoneNumber: String, emailAddress: String) = launch(coroutineExceptionHandler) {
         val location = contactDiaryRepository.addLocation(
             DefaultContactDiaryLocation(
-                locationName = formattedName
+                locationName = locationName.value,
+                phoneNumber = phoneNumber,
+                emailAddress = emailAddress
             )
         )
 
@@ -61,19 +59,21 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor
                 )
             )
         }
-
         shouldClose.postValue(null)
     }
 
-    fun updateLocation(location: ContactDiaryLocationEntity) = launch(coroutineExceptionHandler) {
-        contactDiaryRepository.updateLocation(
-            DefaultContactDiaryLocation(
-                location.locationId,
-                locationName = formattedName
+    fun updateLocation(location: ContactDiaryLocationEntity, phoneNumber: String, emailAddress: String) =
+        launch(coroutineExceptionHandler) {
+            contactDiaryRepository.updateLocation(
+                DefaultContactDiaryLocation(
+                    location.locationId,
+                    locationName = locationName.value,
+                    phoneNumber = phoneNumber,
+                    emailAddress = emailAddress
+                )
             )
-        )
-        shouldClose.postValue(null)
-    }
+            shouldClose.postValue(null)
+        }
 
     fun deleteLocation(location: ContactDiaryLocationEntity) = launch(coroutineExceptionHandler) {
         contactDiaryRepository.locationVisits.firstOrNull()?.forEach {
@@ -89,12 +89,11 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor
     }
 
     companion object {
-        private const val MAX_LOCATION_NAME_LENGTH = 250
-        private val TAG = ContactDiaryLocationBottomSheetDialogViewModel::class.java.simpleName
+        private val TAG = ContactDiaryAddLocationViewModel::class.java.simpleName
     }
 
     @AssistedFactory
-    interface Factory : CWAViewModelFactory<ContactDiaryLocationBottomSheetDialogViewModel> {
-        fun create(addedAt: String?): ContactDiaryLocationBottomSheetDialogViewModel
+    interface Factory : CWAViewModelFactory<ContactDiaryAddLocationViewModel> {
+        fun create(addedAt: String?): ContactDiaryAddLocationViewModel
     }
 }
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 f295f4cd5b72cefd2c6eacaa46349984291424cb..f2c304ca1f3daa15c2c6b0c0bf004a4fa9677fc3 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
@@ -9,6 +9,8 @@ import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
 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.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.ListItem
@@ -117,7 +119,10 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
             .map { personEncounter ->
                 ListItem.Data(
                     R.drawable.ic_contact_diary_person_item,
-                    personEncounter.contactDiaryPerson.fullName,
+                    name = personEncounter.contactDiaryPerson.fullName,
+                    duration = null,
+                    attributes = getPersonAttributes(personEncounter),
+                    circumstances = personEncounter.circumstances,
                     ListItem.Type.PERSON
                 )
             }
@@ -133,6 +138,9 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
                 ListItem.Data(
                     R.drawable.ic_contact_diary_location_item,
                     locationVisit.contactDiaryLocation.locationName,
+                    duration = locationVisit.duration,
+                    attributes = null,
+                    circumstances = locationVisit.circumstances,
                     ListItem.Type.LOCATION
                 )
             }
@@ -146,6 +154,24 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         routeToScreen.postValue(ContactDiaryOverviewNavigationEvents.NavigateToContactDiaryDayFragment(listItem.date))
     }
 
+    private fun getPersonAttributes(personEncounter: ContactDiaryPersonEncounter): List<Int> =
+        mutableListOf<Int>().apply {
+            when (personEncounter.durationClassification) {
+                LESS_THAN_15_MINUTES -> add(R.string.contact_diary_person_encounter_duration_below_15_min)
+                MORE_THAN_15_MINUTES -> add(R.string.contact_diary_person_encounter_duration_above_15_min)
+            }
+
+            when (personEncounter.withMask) {
+                true -> add(R.string.contact_diary_person_encounter_mask_with)
+                false -> add(R.string.contact_diary_person_encounter_mask_without)
+            }
+
+            when (personEncounter.wasOutside) {
+                true -> add(R.string.contact_diary_person_encounter_environment_outside)
+                false -> add(R.string.contact_diary_person_encounter_environment_inside)
+            }
+        }
+
     fun onExportPress(ctx: Context) {
         Timber.d("Exporting person and location entries")
         launch {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt
index e9047030c4fae8d17c1bda662c86adfe6271e080..1846b08686dc16a092bc0aa886930f7213631f14 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ContactDiaryOverviewNestedAdapter.kt
@@ -6,6 +6,7 @@ import de.rki.coronawarnapp.contactdiary.util.clearAndAddAll
 import de.rki.coronawarnapp.databinding.ContactDiaryOverviewNestedListItemBinding
 import de.rki.coronawarnapp.ui.lists.BaseAdapter
 import de.rki.coronawarnapp.util.lists.BindableVH
+import org.joda.time.Duration
 
 class ContactDiaryOverviewNestedAdapter : BaseAdapter<ContactDiaryOverviewNestedAdapter.NestedItemViewHolder>() {
 
@@ -27,19 +28,28 @@ class ContactDiaryOverviewNestedAdapter : BaseAdapter<ContactDiaryOverviewNested
     inner class NestedItemViewHolder(parent: ViewGroup) :
         BaseAdapter.VH(R.layout.contact_diary_overview_nested_list_item, parent),
         BindableVH<ListItem.Data, ContactDiaryOverviewNestedListItemBinding> {
-        override val viewBinding:
-            Lazy<ContactDiaryOverviewNestedListItemBinding> =
-                lazy { ContactDiaryOverviewNestedListItemBinding.bind(itemView) }
-
-        override val onBindData:
-            ContactDiaryOverviewNestedListItemBinding.(item: ListItem.Data, payloads: List<Any>) -> Unit =
-                { key, _ ->
-                    contactDiaryOverviewElementImage.setImageResource(key.drawableId)
-                    contactDiaryOverviewElementName.text = key.text
-                    contactDiaryOverviewElementName.contentDescription = when (key.type) {
-                        ListItem.Type.LOCATION -> context.getString(R.string.accessibility_location, key.text)
-                        ListItem.Type.PERSON -> context.getString(R.string.accessibility_person, key.text)
-                    }
-                }
+        override val viewBinding: Lazy<ContactDiaryOverviewNestedListItemBinding> =
+            lazy { ContactDiaryOverviewNestedListItemBinding.bind(itemView) }
+
+        override val onBindData: ContactDiaryOverviewNestedListItemBinding.(
+            item: ListItem.Data,
+            payloads: List<Any>
+        ) -> Unit = { key, _ ->
+            contactDiaryOverviewElementImage.setImageResource(key.drawableId)
+            contactDiaryOverviewElementName.text = key.name
+            contactDiaryOverviewElementName.contentDescription = when (key.type) {
+                ListItem.Type.LOCATION -> context.getString(R.string.accessibility_location, key.name)
+                ListItem.Type.PERSON -> context.getString(R.string.accessibility_person, key.name)
+            }
+            contactDiaryOverviewElementAttributes.text =
+                getAttributes(key.duration, key.attributes, key.circumstances)
+        }
+
+        private fun getAttributes(duration: Duration?, resources: List<Int>?, circumstances: String?): String =
+            mutableListOf<String>().apply {
+                duration?.run { add(toStandardHours().toString()) }
+                resources?.run { forEach { add(context.getString(it)) } }
+                circumstances?.run { add(this) }
+            }.joinToString()
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt
index f45c8704107408158b164ae2f94edb6ba52f9176..525d988f570055861e91369b66af1d1258063993 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/adapter/ListItem.kt
@@ -2,6 +2,7 @@ package de.rki.coronawarnapp.contactdiary.ui.overview.adapter
 
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
+import org.joda.time.Duration
 import org.joda.time.LocalDate
 
 data class ListItem(
@@ -12,7 +13,10 @@ data class ListItem(
 
     data class Data(
         @DrawableRes val drawableId: Int,
-        val text: String,
+        val name: String,
+        val duration: Duration?,
+        val attributes: List<Int>?,
+        val circumstances: String?,
         val type: Type
     )
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonFragment.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0a897ddac8ad4a258e06da3eaa3bedae8d93a840
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonFragment.kt
@@ -0,0 +1,120 @@
+package de.rki.coronawarnapp.contactdiary.ui.person
+
+import android.os.Bundle
+import android.view.View
+import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
+import androidx.core.widget.doAfterTextChanged
+import androidx.fragment.app.Fragment
+import androidx.navigation.fragment.navArgs
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.util.focusAndShowKeyboard
+import de.rki.coronawarnapp.contactdiary.util.hideKeyboard
+import de.rki.coronawarnapp.databinding.ContactDiaryAddPersonFragmentBinding
+import de.rki.coronawarnapp.util.DialogHelper
+import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.observe2
+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 javax.inject.Inject
+
+class ContactDiaryAddPersonFragment :
+    Fragment(R.layout.contact_diary_add_person_fragment),
+    AutoInject {
+
+    private val binding: ContactDiaryAddPersonFragmentBinding by viewBindingLazy()
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: ContactDiaryAddPersonViewModel by cwaViewModelsAssisted(
+        factoryProducer = { viewModelFactory },
+        constructorCall = { factory, _ ->
+            factory as ContactDiaryAddPersonViewModel.Factory
+            factory.create(navArgs.addedAt)
+        }
+    )
+
+    private val navArgs: ContactDiaryAddPersonFragmentArgs by navArgs()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        val person = navArgs.selectedPerson
+        if (person != null) {
+            binding.apply {
+                contactDiaryPersonNameEditText.setText(person.fullName)
+                contactDiaryPersonPhoneNumberEditText.setText(person.phoneNumber)
+                contactDiaryPersonEmailEditText.setText(person.emailAddress)
+                contactDiaryPersonDeleteButton.visibility = View.VISIBLE
+                contactDiaryPersonDeleteButton.setOnClickListener {
+                    DialogHelper.showDialog(deletePersonConfirmationDialog)
+                }
+                contactDiaryPersonSaveButton.setOnClickListener {
+                    it.hideKeyboard()
+                    viewModel.updatePerson(
+                        person,
+                        phoneNumber = binding.contactDiaryPersonPhoneNumberEditText.text.toString().trim(),
+                        emailAddress = binding.contactDiaryPersonEmailEditText.text.toString().trim()
+                    )
+                }
+            }
+            viewModel.nameChanged(person.fullName)
+        } else {
+            binding.contactDiaryPersonDeleteButton.visibility = View.GONE
+            binding.contactDiaryPersonSaveButton.setOnClickListener {
+                it.hideKeyboard()
+                viewModel.addPerson(
+                    phoneNumber = binding.contactDiaryPersonPhoneNumberEditText.text.toString().trim(),
+                    emailAddress = binding.contactDiaryPersonEmailEditText.text.toString().trim()
+                )
+            }
+        }
+
+        binding.apply {
+            contactDiaryPersonNameEditText.focusAndShowKeyboard()
+
+            contactDiaryPersonCloseButton.setOnClickListener {
+                it.hideKeyboard()
+                viewModel.closePressed()
+            }
+            contactDiaryPersonNameEditText.doAfterTextChanged {
+                viewModel.nameChanged(it.toString())
+            }
+
+            contactDiaryPersonEmailEditText.setOnEditorActionListener { _, actionId, _ ->
+                return@setOnEditorActionListener when (actionId) {
+                    IME_ACTION_DONE -> {
+                        if (viewModel.isNameValid.value == true) {
+                            binding.contactDiaryPersonSaveButton.performClick()
+                        }
+                        false
+                    }
+                    else -> true
+                }
+            }
+        }
+
+        viewModel.shouldClose.observe2(this) {
+            popBackStack()
+        }
+
+        viewModel.isNameValid.observe2(this) { isValid ->
+            binding.contactDiaryPersonSaveButton.isEnabled = isValid
+        }
+    }
+
+    private val deletePersonConfirmationDialog by lazy {
+        DialogHelper.DialogInstance(
+            requireActivity(),
+            R.string.contact_diary_delete_person_title,
+            R.string.contact_diary_delete_persons_message,
+            R.string.contact_diary_delete_button_positive,
+            R.string.contact_diary_delete_button_negative,
+            positiveButtonFunction = {
+                navArgs.selectedPerson?.let {
+                    viewModel.deletePerson(it)
+                }
+            }
+        )
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonModule.kt
similarity index 50%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogModule.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonModule.kt
index 5a3123d10b890a0d4fbede8091a4295847d3ce46..9693086615ccf75f9410d51478eb2b0cb08f8359 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonModule.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.location
+package de.rki.coronawarnapp.contactdiary.ui.person
 
 import dagger.Binds
 import dagger.Module
@@ -8,11 +8,11 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
 
 @Module
-abstract class ContactDiaryLocationBottomSheetDialogModule {
+abstract class ContactDiaryAddPersonModule {
     @Binds
     @IntoMap
-    @CWAViewModelKey(ContactDiaryLocationBottomSheetDialogViewModel::class)
-    abstract fun contactDiaryLocationBottomSheetDialogFragment(
-        factory: ContactDiaryLocationBottomSheetDialogViewModel.Factory
+    @CWAViewModelKey(ContactDiaryAddPersonViewModel::class)
+    abstract fun contactDiaryAddPersonFragment(
+        factory: ContactDiaryAddPersonViewModel.Factory
     ): CWAViewModelFactory<out CWAViewModel>
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt
similarity index 63%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt
index 6f96510f35871ea2425d763025523792d04e9087..f00889cfb4be8cc61242db89f5a8bc90f1fb271a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt
@@ -1,4 +1,4 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.person
+package de.rki.coronawarnapp.contactdiary.ui.person
 
 import androidx.lifecycle.asLiveData
 import dagger.assisted.Assisted
@@ -10,9 +10,8 @@ import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.exception.ExceptionCategory
 import de.rki.coronawarnapp.exception.reporting.report
-import de.rki.coronawarnapp.contactdiary.util.formatContactDiaryNameField
-import de.rki.coronawarnapp.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
 import kotlinx.coroutines.CoroutineExceptionHandler
@@ -21,35 +20,33 @@ import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
 import org.joda.time.LocalDate
 
-class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
+class ContactDiaryAddPersonViewModel @AssistedInject constructor(
     dispatcherProvider: DispatcherProvider,
     @Assisted private val addedAt: String?,
     private val contactDiaryRepository: ContactDiaryRepository
 ) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
     private val coroutineExceptionHandler = CoroutineExceptionHandler { _, ex ->
-        shouldClose.postValue(null)
         ex.report(ExceptionCategory.INTERNAL, TAG)
     }
 
-    private val text = MutableStateFlow("")
-
-    val isValid = text.map {
-        it.isNotEmpty() && it.length <= MAX_PERSON_NAME_LENGTH
-    }.asLiveData()
-
     val shouldClose = SingleLiveEvent<Unit>()
 
-    private val formattedName: String
-        get() = text.value.formatContactDiaryNameField(MAX_PERSON_NAME_LENGTH)
+    private val name = MutableStateFlow("")
 
-    fun textChanged(locationName: String) {
-        text.value = locationName
+    val isNameValid = name
+        .map { it.isNotEmpty() }
+        .asLiveData()
+
+    fun nameChanged(value: String) {
+        name.value = value.trim()
     }
 
-    fun addPerson() = launch(coroutineExceptionHandler) {
+    fun addPerson(phoneNumber: String, emailAddress: String) = launch(coroutineExceptionHandler) {
         val person = contactDiaryRepository.addPerson(
             DefaultContactDiaryPerson(
-                fullName = formattedName
+                fullName = name.value,
+                phoneNumber = phoneNumber,
+                emailAddress = emailAddress
             )
         )
 
@@ -61,19 +58,22 @@ class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
                 )
             )
         }
-
         shouldClose.postValue(null)
     }
 
-    fun updatePerson(person: ContactDiaryPersonEntity) = launch(coroutineExceptionHandler) {
-        contactDiaryRepository.updatePerson(
-            DefaultContactDiaryPerson(
-                person.personId,
-                fullName = formattedName
+    fun updatePerson(person: ContactDiaryPersonEntity, phoneNumber: String, emailAddress: String) =
+        launch(coroutineExceptionHandler) {
+            contactDiaryRepository.updatePerson(
+                DefaultContactDiaryPerson(
+                    person.personId,
+                    fullName = name.value,
+                    phoneNumber = phoneNumber,
+                    emailAddress = emailAddress
+
+                )
             )
-        )
-        shouldClose.postValue(null)
-    }
+            shouldClose.postValue(null)
+        }
 
     fun deletePerson(person: ContactDiaryPersonEntity) = launch(coroutineExceptionHandler) {
         contactDiaryRepository.personEncounters.firstOrNull()?.forEach {
@@ -89,12 +89,11 @@ class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
     }
 
     companion object {
-        private const val MAX_PERSON_NAME_LENGTH = 250
-        private val TAG = ContactDiaryPersonBottomSheetDialogViewModel::class.java.simpleName
+        private val TAG = ContactDiaryAddPersonViewModel::class.java.simpleName
     }
 
     @AssistedFactory
-    interface Factory : CWAViewModelFactory<ContactDiaryPersonBottomSheetDialogViewModel> {
-        fun create(addedAt: String?): ContactDiaryPersonBottomSheetDialogViewModel
+    interface Factory : CWAViewModelFactory<ContactDiaryAddPersonViewModel> {
+        fun create(addedAt: String?): ContactDiaryAddPersonViewModel
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt
deleted file mode 100644
index af7f6db44b2bfd9cf382e2e0c4ad8eb93563a37b..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.location
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
-import androidx.core.widget.doAfterTextChanged
-import androidx.navigation.fragment.navArgs
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.util.focusAndShowKeyboard
-import de.rki.coronawarnapp.databinding.ContactDiaryLocationBottomSheetFragmentBinding
-import de.rki.coronawarnapp.util.DialogHelper
-import de.rki.coronawarnapp.util.di.AutoInject
-import de.rki.coronawarnapp.util.ui.observe2
-import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
-import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
-import javax.inject.Inject
-
-class ContactDiaryLocationBottomSheetDialogFragment : BottomSheetDialogFragment(), AutoInject {
-    private var _binding: ContactDiaryLocationBottomSheetFragmentBinding? = null
-    private val binding get() = _binding!!
-
-    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val viewModel: ContactDiaryLocationBottomSheetDialogViewModel by cwaViewModelsAssisted(
-        factoryProducer = { viewModelFactory },
-        constructorCall = { factory, _ ->
-            factory as ContactDiaryLocationBottomSheetDialogViewModel.Factory
-            factory.create(navArgs.addedAt)
-        }
-    )
-
-    private val navArgs: ContactDiaryLocationBottomSheetDialogFragmentArgs by navArgs()
-
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        _binding = ContactDiaryLocationBottomSheetFragmentBinding.inflate(inflater)
-        return binding.root
-    }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-
-        val location = navArgs.selectedLocation
-        if (location != null) {
-            binding.contactDiaryLocationBottomSheetTextInputEditText.setText(location.locationName)
-            binding.contactDiaryLocationBottomSheetDeleteButton.visibility = View.VISIBLE
-            binding.contactDiaryLocationBottomSheetDeleteButton.setOnClickListener {
-                DialogHelper.showDialog(deleteLocationConfirmationDialog)
-            }
-            binding.contactDiaryLocationBottomSheetSaveButton.setOnClickListener {
-                viewModel.updateLocation(location)
-            }
-        } else {
-            binding.contactDiaryLocationBottomSheetDeleteButton.visibility = View.GONE
-            binding.contactDiaryLocationBottomSheetSaveButton.setOnClickListener {
-                viewModel.addLocation()
-            }
-        }
-
-        binding.contactDiaryLocationBottomSheetCloseButton.setOnClickListener {
-            viewModel.closePressed()
-        }
-
-        binding.contactDiaryLocationBottomSheetTextInputEditText.doAfterTextChanged {
-            viewModel.textChanged(it.toString())
-        }
-
-        binding.contactDiaryLocationBottomSheetTextInputEditText.setOnEditorActionListener { _, actionId, _ ->
-            return@setOnEditorActionListener when (actionId) {
-                EditorInfo.IME_ACTION_DONE -> {
-                    if (viewModel.isValid.value == true) {
-                        binding.contactDiaryLocationBottomSheetSaveButton.performClick()
-                    }
-                    false
-                }
-                else -> true
-            }
-        }
-
-        binding.contactDiaryLocationBottomSheetTextInputEditText.focusAndShowKeyboard()
-
-        viewModel.shouldClose.observe2(this) {
-            dismiss()
-        }
-
-        viewModel.isValid.observe2(this) {
-            binding.contactDiaryLocationBottomSheetTextInputLayout.isErrorEnabled = it
-            binding.contactDiaryLocationBottomSheetSaveButton.isEnabled = it
-        }
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-
-    private val deleteLocationConfirmationDialog by lazy {
-        DialogHelper.DialogInstance(
-            requireActivity(),
-            R.string.contact_diary_delete_location_title,
-            R.string.contact_diary_delete_locations_message,
-            R.string.contact_diary_delete_button_positive,
-            R.string.contact_diary_delete_button_negative,
-            positiveButtonFunction = {
-                navArgs.selectedLocation?.let {
-                    viewModel.deleteLocation(it)
-                }
-            }
-        )
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt
deleted file mode 100644
index e0e470712e92be4f54763c1d9e553e3c36beced3..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package de.rki.coronawarnapp.contactdiary.ui.sheets.person
-
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
-import androidx.core.widget.doAfterTextChanged
-import androidx.navigation.fragment.navArgs
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.contactdiary.util.focusAndShowKeyboard
-import de.rki.coronawarnapp.databinding.ContactDiaryPersonBottomSheetFragmentBinding
-import de.rki.coronawarnapp.util.DialogHelper
-import de.rki.coronawarnapp.util.di.AutoInject
-import de.rki.coronawarnapp.util.ui.observe2
-import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
-import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
-import javax.inject.Inject
-
-class ContactDiaryPersonBottomSheetDialogFragment : BottomSheetDialogFragment(), AutoInject {
-    private var _binding: ContactDiaryPersonBottomSheetFragmentBinding? = null
-    private val binding get() = _binding!!
-
-    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
-    private val viewModel: ContactDiaryPersonBottomSheetDialogViewModel by cwaViewModelsAssisted(
-        factoryProducer = { viewModelFactory },
-        constructorCall = { factory, _ ->
-            factory as ContactDiaryPersonBottomSheetDialogViewModel.Factory
-            factory.create(navArgs.addedAt)
-        }
-    )
-
-    private val navArgs: ContactDiaryPersonBottomSheetDialogFragmentArgs by navArgs()
-
-    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
-        _binding = ContactDiaryPersonBottomSheetFragmentBinding.inflate(inflater)
-        return binding.root
-    }
-
-    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        super.onViewCreated(view, savedInstanceState)
-
-        val person = navArgs.selectedPerson
-        if (person != null) {
-            binding.contactDiaryPersonBottomSheetTextInputEditText.setText(person.fullName)
-            binding.contactDiaryPersonBottomSheetDeleteButton.visibility = View.VISIBLE
-            binding.contactDiaryPersonBottomSheetDeleteButton.setOnClickListener {
-                DialogHelper.showDialog(deletePersonConfirmationDialog)
-            }
-            binding.contactDiaryPersonBottomSheetSaveButton.setOnClickListener {
-                viewModel.updatePerson(person)
-            }
-        } else {
-            binding.contactDiaryPersonBottomSheetDeleteButton.visibility = View.GONE
-            binding.contactDiaryPersonBottomSheetSaveButton.setOnClickListener {
-                viewModel.addPerson()
-            }
-        }
-
-        binding.contactDiaryPersonBottomSheetCloseButton.setOnClickListener {
-            viewModel.closePressed()
-        }
-
-        binding.contactDiaryPersonBottomSheetTextInputEditText.doAfterTextChanged {
-            viewModel.textChanged(it.toString())
-        }
-
-        binding.contactDiaryPersonBottomSheetTextInputEditText.setOnEditorActionListener { _, actionId, _ ->
-            return@setOnEditorActionListener when (actionId) {
-                IME_ACTION_DONE -> {
-                    if (viewModel.isValid.value == true) {
-                        binding.contactDiaryPersonBottomSheetSaveButton.performClick()
-                    }
-                    false
-                }
-                else -> true
-            }
-        }
-
-        binding.contactDiaryPersonBottomSheetTextInputEditText.focusAndShowKeyboard()
-
-        viewModel.shouldClose.observe2(this) {
-            dismiss()
-        }
-
-        viewModel.isValid.observe2(this) { isValid ->
-            binding.contactDiaryPersonBottomSheetTextInputLayout.isErrorEnabled = isValid
-            binding.contactDiaryPersonBottomSheetSaveButton.isEnabled = isValid
-        }
-    }
-
-    override fun onDestroyView() {
-        super.onDestroyView()
-        _binding = null
-    }
-
-    private val deletePersonConfirmationDialog by lazy {
-        DialogHelper.DialogInstance(
-            requireActivity(),
-            R.string.contact_diary_delete_person_title,
-            R.string.contact_diary_delete_persons_message,
-            R.string.contact_diary_delete_button_positive,
-            R.string.contact_diary_delete_button_negative,
-            positiveButtonFunction = {
-                navArgs.selectedPerson?.let {
-                    viewModel.deletePerson(it)
-                }
-            }
-        )
-    }
-}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/AbstractAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/AbstractAdapter.kt
index bb33057d5a087dcf09310442a0049505d0a3a705..9fd0d0a9f6e032e5680babda46a20763570640ea 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/AbstractAdapter.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/AbstractAdapter.kt
@@ -8,6 +8,11 @@ import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffer
 internal abstract class AbstractAdapter<T : HasStableId, U : BaseAdapter.VH> :
     BaseAdapter<U>(),
     AsyncDiffUtilAdapter<T> {
+
+    init {
+        setHasStableIds(true)
+    }
+
     override val asyncDiffer: AsyncDiffer<T> = AsyncDiffer(this)
 
     override fun getItemCount(): Int = data.size
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensions.kt
index 0d2a4be3c8201527fb525074141fed432f338f44..f09c9babde3f90577844a98b469c265eb9cd340b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensions.kt
@@ -3,7 +3,6 @@ package de.rki.coronawarnapp.contactdiary.util
 import android.content.Context
 import android.os.Build
 import android.view.View
-import android.view.ViewTreeObserver
 import android.view.inputmethod.InputMethodManager
 import androidx.core.view.AccessibilityDelegateCompat
 import androidx.core.view.ViewCompat
@@ -44,16 +43,6 @@ fun LocalDate.toFormattedDayForAccessibility(locale: Locale): String {
         DateTimeFormat.longDate().withLocale(locale).print(this)
 }
 
-fun String.formatContactDiaryNameField(maxLength: Int): String {
-    val newName = if (isNotBlank()) {
-        trim()
-    } else {
-        // allow only spaces as a name
-        this
-    }
-    return newName.take(maxLength)
-}
-
 fun View.focusAndShowKeyboard() {
     /**
      * This is to be called when the window already has focus.
@@ -70,17 +59,13 @@ fun View.focusAndShowKeyboard() {
     requestFocus()
     if (hasWindowFocus()) {
         showTheKeyboardNow()
-    } else {
-        viewTreeObserver.addOnWindowFocusChangeListener(
-            object : ViewTreeObserver.OnWindowFocusChangeListener {
-                override fun onWindowFocusChanged(hasFocus: Boolean) {
-                    if (hasFocus) {
-                        this@focusAndShowKeyboard.showTheKeyboardNow()
-                        viewTreeObserver.removeOnWindowFocusChangeListener(this)
-                    }
-                }
-            }
-        )
+    }
+}
+
+fun View.hideKeyboard() {
+    post {
+        val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+        imm.hideSoftInputFromWindow(this.windowToken, 0)
     }
 }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/SelectableItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/SelectableItem.kt
deleted file mode 100644
index dcf17cd5fb31dbc2712f4fcbf04d828fc4fef7ea..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/util/SelectableItem.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.rki.coronawarnapp.contactdiary.util
-
-import androidx.annotation.StringRes
-import de.rki.coronawarnapp.util.lists.HasStableId
-import de.rki.coronawarnapp.util.ui.LazyString
-
-data class SelectableItem<T : HasStableId>(
-    val selected: Boolean,
-    val item: T,
-    val contentDescription: LazyString,
-    val onClickDescription: LazyString,
-    @StringRes val clickLabel: Int,
-    @StringRes val onClickLabel: Int,
-    override val stableId: Long = item.stableId
-) : HasStableId
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/StringExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/StringExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d598bf4300425054865c97e6129ab2946f70aa92
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/StringExtensions.kt
@@ -0,0 +1,5 @@
+package de.rki.coronawarnapp.util
+
+import kotlin.math.min
+
+fun String.trimToLength(maxLength: Int) = this.substring(0, min(length, maxLength))
diff --git a/Corona-Warn-App/src/main/res/color/button_contact_diary_person.xml b/Corona-Warn-App/src/main/res/color/button_contact_diary_person.xml
new file mode 100644
index 0000000000000000000000000000000000000000..271ca0d8ea170d06cba1ecdc1814778960527bcb
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/color/button_contact_diary_person.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/colorBackground" android:state_checked="false" />
+    <item android:color="@color/colorAccentTintButtonPressed" android:state_checked="true" />
+</selector>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/color/button_text_color_emphasized.xml b/Corona-Warn-App/src/main/res/color/button_text_color_emphasized.xml
index b50e718df93fef8b535cb1c85212cc1c07b7957e..f3502eefc53ea2e4584d34ef3c5060cf6909b219 100644
--- a/Corona-Warn-App/src/main/res/color/button_text_color_emphasized.xml
+++ b/Corona-Warn-App/src/main/res/color/button_text_color_emphasized.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="@color/colorTextPrimary2" android:state_enabled="false" /> <!-- disabled -->
+    <item android:color="@color/colorBackground" android:state_enabled="false" /> <!-- disabled -->
     <item android:color="@color/colorTextEmphasizedButton" /> <!-- default -->
 </selector>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_default.xml b/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_default.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1da6f7a32549b63954dbbf71075912fe0ec5c639
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_default.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/radius_card" />
+    <solid android:color="@color/colorSurface1" />
+</shape>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_selected.xml b/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_selected.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b230c96594d3925ca38a19c3802cfaeb1fd3ebc9
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/contact_diary_duration_background_selected.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/radius_card" />
+    <solid android:color="@color/colorSurface1" />
+    <stroke android:width="2dip" android:color="@color/colorAccent" />
+</shape>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_add_location_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_add_location_fragment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6b605a2815d151fa890c93c30566a5d92a2633e9
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_add_location_fragment.xml
@@ -0,0 +1,129 @@
+<ScrollView 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="match_parent"
+    android:fillViewport="true">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:id="@+id/contact_diary_add_location_close_button"
+            style="@style/buttonIcon"
+            android:layout_width="@dimen/button_icon"
+            android:layout_height="@dimen/button_icon"
+            android:layout_marginStart="@dimen/spacing_tiny"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:contentDescription="@string/accessibility_close"
+            android:padding="@dimen/spacing_mega_tiny"
+            android:src="@drawable/ic_close"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/contact_diary_add_location_title"
+            style="@style/headline6"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/spacing_normal"
+            android:text="@string/contact_diary_add_location_title"
+            app:layout_constraintBottom_toBottomOf="@id/contact_diary_add_location_close_button"
+            app:layout_constraintEnd_toStartOf="@id/contact_diary_add_location_delete_button"
+            app:layout_constraintStart_toEndOf="@id/contact_diary_add_location_close_button"
+            app:layout_constraintTop_toTopOf="@id/contact_diary_add_location_close_button" />
+
+        <ImageView
+            android:id="@+id/contact_diary_add_location_delete_button"
+            style="@style/buttonIcon"
+            android:layout_width="@dimen/button_icon"
+            android:layout_height="@dimen/button_icon"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:layout_marginEnd="@dimen/spacing_tiny"
+            android:contentDescription="@string/contact_diary_delete_icon_content_description"
+            android:padding="@dimen/button_icon_padding"
+            android:src="@drawable/ic_baseline_delete"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_add_location_name_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:hint="@string/contact_diary_add_location_text_input_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/contact_diary_add_location_close_button">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_add_location_name_input_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionNext"
+                android:inputType="textCapWords" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_add_location_phone_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:hint="@string/contact_diary_add_text_input_phone_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/contact_diary_add_location_name_input_layout">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_add_location_phone_input_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionNext"
+                android:inputType="phone" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_add_location_email_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:hint="@string/contact_diary_add_text_input_email_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/contact_diary_add_location_phone_input_layout">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_add_location_email_input_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionDone"
+                android:inputType="textEmailAddress" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <Button
+            android:id="@+id/contact_diary_add_location_save_button"
+            style="@style/buttonPrimary"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:text="@string/contact_diary_add_location_save_button"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.166"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_add_location_email_input_layout"
+            app:layout_constraintVertical_bias="1.0" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
+
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_add_person_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_add_person_fragment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..90420e38aacae58b2a691c99c11305ba18e98e40
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_add_person_fragment.xml
@@ -0,0 +1,129 @@
+<ScrollView 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="match_parent"
+    android:fillViewport="true">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:id="@+id/contact_diary_person_close_button"
+            style="@style/buttonIcon"
+            android:layout_width="@dimen/button_icon"
+            android:layout_height="@dimen/button_icon"
+            android:layout_marginStart="@dimen/spacing_tiny"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:contentDescription="@string/accessibility_close"
+            android:padding="@dimen/spacing_mega_tiny"
+            android:src="@drawable/ic_close"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/contact_diary_person_title"
+            style="@style/headline6"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/spacing_normal"
+            android:text="@string/contact_diary_add_person_title"
+            app:layout_constraintBottom_toBottomOf="@+id/contact_diary_person_close_button"
+            app:layout_constraintStart_toEndOf="@+id/contact_diary_person_close_button"
+            app:layout_constraintTop_toTopOf="@+id/contact_diary_person_close_button" />
+
+        <ImageView
+            android:id="@+id/contact_diary_person_delete_button"
+            style="@style/buttonIcon"
+            android:layout_width="@dimen/button_icon"
+            android:layout_height="@dimen/button_icon"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:layout_marginEnd="@dimen/spacing_tiny"
+            android:contentDescription="@string/contact_diary_delete_icon_content_description"
+            android:padding="@dimen/button_icon_padding"
+            android:src="@drawable/ic_baseline_delete"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_person_name_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:hint="@string/contact_diary_add_person_text_input_name_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_close_button">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_person_name_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionNext"
+                android:inputType="textCapWords"
+                app:backgroundTint="@color/colorContactDiaryListItem" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_person_phone_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:hint="@string/contact_diary_add_text_input_phone_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_name_input_layout">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_person_phone_number_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionNext"
+                android:inputType="phone" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <com.google.android.material.textfield.TextInputLayout
+            android:id="@+id/contact_diary_person_email_input_layout"
+            style="@style/TextInputLayoutTheme"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_tiny"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:hint="@string/contact_diary_add_text_input_email_hint"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_phone_input_layout">
+
+            <com.google.android.material.textfield.TextInputEditText
+                android:id="@+id/contact_diary_person_email_edit_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:imeOptions="actionDone"
+                android:inputType="textEmailAddress" />
+
+        </com.google.android.material.textfield.TextInputLayout>
+
+        <Button
+            android:id="@+id/contact_diary_person_save_button"
+            style="@style/buttonPrimary"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:layout_marginBottom="@dimen/spacing_small"
+            android:text="@string/contact_diary_add_person_save_button"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.333"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/contact_diary_person_email_input_layout"
+            app:layout_constraintVertical_bias="1.0" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</ScrollView>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_comment_info_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_comment_info_fragment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b2d7b3114fcaeacdf31acd056688e643154c1a15
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_comment_info_fragment.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <Toolbar
+        android:id="@+id/toolbar"
+        style="@style/CWAToolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:navigationIcon="@drawable/ic_close" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <TextView
+                style="@style/headline4"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="24dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginEnd="24dp"
+                android:text="@string/contact_diary_comment_info_screen_title" />
+
+            <TextView
+                style="@style/body1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="24dp"
+                android:layout_marginTop="28dp"
+                android:layout_marginEnd="24dp"
+                android:letterSpacing="0.015"
+                android:text="@string/contact_diary_comment_info_screen_description"
+                android:textStyle="bold" />
+
+            <TextView
+                style="@style/body1"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="24dp"
+                android:layout_marginTop="14dp"
+                android:layout_marginEnd="24dp"
+                android:text="@string/contact_diary_comment_info_screen_body" />
+
+        </LinearLayout>
+
+    </ScrollView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_duration_picker_dialog_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_duration_picker_dialog_fragment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4a5b66cef9fd8e37cb7d063f7782ff6e8a6ae9d8
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_duration_picker_dialog_fragment.xml
@@ -0,0 +1,89 @@
+<?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/duration_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/title"
+        style="@style/headline6"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="24dp"
+        android:layout_marginTop="@dimen/spacing_small"
+        android:padding="@dimen/spacing_mega_tiny"
+        android:text="@string/duration_dialog_title"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Dauer" />
+
+    <NumberPicker
+        android:id="@+id/hours"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="67dp"
+        android:theme="@style/DefaultNumberPickerTheme"
+        app:layout_constraintBottom_toBottomOf="@+id/divider"
+        app:layout_constraintEnd_toStartOf="@+id/divider"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/divider" />
+
+    <TextView
+        android:id="@+id/divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginStart="6dp"
+        android:layout_marginEnd="6dp"
+        android:text=":"
+        app:layout_constraintBottom_toBottomOf="@+id/minutes"
+        app:layout_constraintEnd_toStartOf="@+id/minutes"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/hours"
+        app:layout_constraintTop_toTopOf="@+id/minutes"
+        tools:ignore="HardcodedText"
+        tools:text=":" />
+
+    <NumberPicker
+        android:id="@+id/minutes"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="18dp"
+        android:layout_marginEnd="67dp"
+        android:theme="@style/DefaultNumberPickerTheme"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/divider"
+        app:layout_constraintTop_toBottomOf="@+id/title" />
+
+    <Button
+        android:id="@+id/cancel_button"
+        style="@style/buttonLight"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="29dp"
+        android:layout_marginBottom="@dimen/spacing_small"
+        android:text="@string/duration_dialog_cancel_button"
+        android:textColor="@color/colorTextTint"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/ok_button"
+        app:layout_constraintTop_toBottomOf="@+id/minutes" />
+
+    <Button
+        android:id="@+id/ok_button"
+        style="@style/buttonLight"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="29dp"
+        android:layout_marginEnd="14dp"
+        android:layout_marginBottom="@dimen/spacing_small"
+        android:text="@string/duration_dialog_ok_button"
+        android:textColor="@color/colorTextTint"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/minutes" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml
deleted file mode 100644
index 8fb6fceb7729465237cdc28fce7a9effabc43eae..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_location_bottom_sheet_fragment.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            android:id="@+id/contact_diary_location_bottom_sheet_close_button"
-            style="@style/buttonIcon"
-            android:layout_width="@dimen/button_icon"
-            android:layout_height="@dimen/button_icon"
-            android:layout_marginStart="@dimen/spacing_tiny"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:padding="@dimen/spacing_mega_tiny"
-            android:contentDescription="@string/accessibility_close"
-            android:src="@drawable/ic_close"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <TextView
-            android:id="@+id/contact_diary_location_bottom_sheet_title"
-            style="@style/headline6"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/spacing_normal"
-            android:text="@string/contact_diary_location_bottom_sheet_title"
-            app:layout_constraintBottom_toBottomOf="@id/contact_diary_location_bottom_sheet_close_button"
-            app:layout_constraintEnd_toStartOf="@id/contact_diary_location_bottom_sheet_delete_button"
-            app:layout_constraintStart_toEndOf="@id/contact_diary_location_bottom_sheet_close_button"
-            app:layout_constraintTop_toTopOf="@id/contact_diary_location_bottom_sheet_close_button" />
-
-        <ImageView
-            android:id="@+id/contact_diary_location_bottom_sheet_delete_button"
-            style="@style/buttonIcon"
-            android:layout_width="@dimen/button_icon"
-            android:layout_height="@dimen/button_icon"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:layout_marginEnd="@dimen/spacing_tiny"
-            android:contentDescription="@string/contact_diary_delete_icon_content_description"
-            android:padding="@dimen/button_icon_padding"
-            android:src="@drawable/ic_baseline_delete"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <com.google.android.material.textfield.TextInputLayout
-            android:id="@+id/contact_diary_location_bottom_sheet_text_input_layout"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_normal"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:hint="@string/contact_diary_location_bottom_sheet_text_input_hint"
-            app:counterEnabled="true"
-            app:counterMaxLength="250"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@id/contact_diary_location_bottom_sheet_close_button">
-
-            <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/contact_diary_location_bottom_sheet_text_input_edit_text"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:imeOptions="actionDone"
-                android:inputType="textCapWords" />
-
-        </com.google.android.material.textfield.TextInputLayout>
-
-        <android.widget.Button
-            android:id="@+id/contact_diary_location_bottom_sheet_save_button"
-            style="@style/buttonPrimary"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_normal"
-            android:layout_marginTop="@dimen/spacing_normal"
-            android:layout_marginBottom="@dimen/spacing_small"
-            android:text="@string/contact_diary_location_bottom_sheet_save_button"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/contact_diary_location_bottom_sheet_text_input_layout" />
-    </androidx.constraintlayout.widget.ConstraintLayout>
-</layout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_item.xml
index 4a07d8eb7bfea1df946364dab5ace744ed508b88..26c08436cf8452e4495b48674ee599bddecbd333 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_item.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_item.xml
@@ -1,37 +1,46 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
+<de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.ExpandingDiaryListItemView 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/main_box"
+    style="@style/ContactDiaryExpandableListItem"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:id="@+id/contact_diary_location_list_item"
-        style="@style/contactDiaryCardRipple"
-        android:layout_width="match_parent"
+    <FrameLayout
+        android:id="@+id/duration_container"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/spacing_huge">
-
-        <ImageView
-            android:id="@+id/contact_diary_location_list_item_icon"
-            android:layout_width="@dimen/spacing_medium"
-            android:layout_height="@dimen/spacing_medium"
-            android:layout_marginStart="@dimen/spacing_small"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            tools:srcCompat="@drawable/ic_selected" />
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+        <TextView
+            style="@style/subtitleBoldSixteen"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|start"
+            android:text="@string/contact_diary_location_visit_duration_label" />
 
         <TextView
-            android:id="@+id/contact_diary_location_list_item_name"
-            style="@style/subtitle"
-            android:layout_width="0dp"
+            android:id="@+id/duration_input"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_small"
-            android:layout_marginVertical="@dimen/spacing_small"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@+id/contact_diary_location_list_item_icon"
-            app:layout_constraintTop_toTopOf="parent"
-            tools:text="@tools:sample/cities" />
-    </androidx.constraintlayout.widget.ConstraintLayout>
+            android:layout_gravity="center_vertical|end"
+            android:padding="8dp"
+            android:text="00:00" />
+    </FrameLayout>
+
+    <de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.DiaryCircumstancesTextView
+        android:id="@+id/circumstances"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="16dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/duration_container" />
 
-</layout>
+</de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.ExpandingDiaryListItemView>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item.xml
index e05f8779d5a8df94f0d1c4ff8dd521df19083fe7..cce9651223d844aed1d06834edd727bd6deb04bb 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_nested_list_item.xml
@@ -2,54 +2,54 @@
 <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_element_nested_container"
+    android:id="@+id/contact_diary_overview_element_nested_body"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_marginVertical="@dimen/spacing_tiny"
     android:focusable="true"
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintEnd_toEndOf="parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:id="@+id/contact_diary_overview_element_nested_body"
-        android:layout_width="match_parent"
+    <ImageView
+        android:id="@+id/contact_diary_overview_element_image"
+        android:layout_width="wrap_content"
+        android:layout_height="0dp"
+        android:layout_marginStart="@dimen/spacing_small"
+        android:importantForAccessibility="no"
+        android:scaleType="centerInside"
+        android:src="@drawable/ic_contact_diary_person_item"
+        app:layout_constraintBottom_toBottomOf="@+id/contact_diary_overview_element_name"
+        app:layout_constraintEnd_toStartOf="@id/contact_diary_overview_element_name"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/contact_diary_overview_element_name" />
+
+    <TextView
+        android:id="@+id/contact_diary_overview_element_name"
+        style="@style/subtitle"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginVertical="@dimen/spacing_tiny"
+        android:layout_marginStart="@dimen/spacing_small"
+        android:layout_marginEnd="@dimen/spacing_small"
+        android:ellipsize="end"
         android:focusable="true"
-        app:layout_constraintBottom_toBottomOf="parent"
+        android:maxLines="3"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent">
+        app:layout_constraintStart_toEndOf="@+id/contact_diary_overview_element_image"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Andrea Steinhauer" />
 
-        <ImageView
-            android:id="@+id/contact_diary_overview_element_image"
-            android:layout_width="wrap_content"
-            android:layout_height="0dp"
-            android:layout_marginStart="@dimen/spacing_small"
-            android:importantForAccessibility="no"
-            android:scaleType="centerInside"
-            android:src="@drawable/ic_contact_diary_person_item"
-            app:layout_constraintBaseline_toBaselineOf="@id/contact_diary_overview_element_name"
-            app:layout_constraintEnd_toStartOf="@id/contact_diary_overview_element_name"
-            app:layout_constraintStart_toStartOf="parent" />
+    <TextView
+        android:id="@+id/contact_diary_overview_element_attributes"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:maxLines="3"
+        android:text="unter 15 Min, im Freien"
+        app:layout_constraintEnd_toEndOf="@+id/contact_diary_overview_element_name"
+        app:layout_constraintStart_toStartOf="@+id/contact_diary_overview_element_name"
+        app:layout_constraintTop_toBottomOf="@+id/contact_diary_overview_element_name" />
 
-        <TextView
-            android:id="@+id/contact_diary_overview_element_name"
-            style="@style/subtitle"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/spacing_small"
-            android:layout_marginEnd="@dimen/spacing_small"
-            android:ellipsize="end"
-            android:focusable="true"
-            android:maxLines="3"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@+id/contact_diary_overview_element_image"
-            app:layout_constraintTop_toTopOf="parent"
-            tools:text="Andrea Steinhauer" />
+</androidx.constraintlayout.widget.ConstraintLayout>
 
-    </androidx.constraintlayout.widget.ConstraintLayout>
 
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml
deleted file mode 100644
index 202230fd6d7fbc51ea072e18361baa9ece99ebdf..0000000000000000000000000000000000000000
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_person_bottom_sheet_fragment.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto">
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <ImageView
-            android:id="@+id/contact_diary_person_bottom_sheet_close_button"
-            style="@style/buttonIcon"
-            android:layout_width="@dimen/button_icon"
-            android:layout_height="@dimen/button_icon"
-            android:layout_marginStart="@dimen/spacing_tiny"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:contentDescription="@string/accessibility_close"
-            android:src="@drawable/ic_close"
-            android:padding="@dimen/spacing_mega_tiny"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <TextView
-            android:id="@+id/contact_diary_person_bottom_sheet_title"
-            style="@style/headline6"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/spacing_normal"
-            android:text="@string/contact_diary_person_bottom_sheet_title"
-            app:layout_constraintBottom_toBottomOf="@+id/contact_diary_person_bottom_sheet_close_button"
-            app:layout_constraintStart_toEndOf="@+id/contact_diary_person_bottom_sheet_close_button"
-            app:layout_constraintTop_toTopOf="@+id/contact_diary_person_bottom_sheet_close_button" />
-
-        <ImageView
-            android:id="@+id/contact_diary_person_bottom_sheet_delete_button"
-            style="@style/buttonIcon"
-            android:layout_width="@dimen/button_icon"
-            android:layout_height="@dimen/button_icon"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:layout_marginEnd="@dimen/spacing_tiny"
-            android:contentDescription="@string/contact_diary_delete_icon_content_description"
-            android:padding="@dimen/button_icon_padding"
-            android:src="@drawable/ic_baseline_delete"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintTop_toTopOf="parent" />
-
-        <com.google.android.material.textfield.TextInputLayout
-            android:id="@+id/contact_diary_person_bottom_sheet_text_input_layout"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_normal"
-            android:layout_marginTop="@dimen/spacing_small"
-            android:hint="@string/contact_diary_person_bottom_sheet_text_input_hint"
-            app:counterEnabled="true"
-            app:counterMaxLength="250"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_bottom_sheet_close_button">
-
-            <com.google.android.material.textfield.TextInputEditText
-                android:id="@+id/contact_diary_person_bottom_sheet_text_input_edit_text"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:imeOptions="actionDone"
-                android:inputType="textCapWords" />
-
-        </com.google.android.material.textfield.TextInputLayout>
-
-        <android.widget.Button
-            android:id="@+id/contact_diary_person_bottom_sheet_save_button"
-            style="@style/buttonPrimary"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_normal"
-            android:layout_marginTop="@dimen/spacing_normal"
-            android:layout_marginBottom="@dimen/spacing_small"
-            android:text="@string/contact_diary_person_bottom_sheet_save_button"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_bottom_sheet_text_input_layout" />
-    </androidx.constraintlayout.widget.ConstraintLayout>
-</layout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_item.xml
index 1b4da486f70f01340c52b402921f1ec591b890ff..df108259f04fafd619afddac188c2238fd161e07 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_item.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_item.xml
@@ -1,37 +1,123 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:android="http://schemas.android.com/apk/res/android"
+<de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.ExpandingDiaryListItemView 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/main_box"
+    style="@style/ContactDiaryExpandableListItem"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
 
-    <androidx.constraintlayout.widget.ConstraintLayout
-        android:id="@+id/contact_diary_person_list_item"
-        style="@style/contactDiaryCardRipple"
+    <com.google.android.material.button.MaterialButtonToggleGroup
+        android:id="@+id/duration_group"
+        style="?attr/materialButtonToggleGroupStyle"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="16dp"
+        android:orientation="horizontal"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:singleSelection="true">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/duration_below_15"
+            android:layout_width="match_parent"
+            app:backgroundTint="@color/colorBackground"
+            style="@style/contactDiaryPersonButton"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:paddingHorizontal="5dp"
+            android:text="@string/contact_diary_person_encounter_duration_below_15_min" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="@color/cwaGrayHighlight" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/duration_above_15"
+            style="@style/contactDiaryPersonButton"
+            app:backgroundTint="@color/colorBackground"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:text="@string/contact_diary_person_encounter_duration_above_15_min" />
+
+    </com.google.android.material.button.MaterialButtonToggleGroup>
+
+    <com.google.android.material.button.MaterialButtonToggleGroup
+        android:id="@+id/mask_group"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:orientation="horizontal"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/duration_group"
+        app:singleSelection="true">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/mask_with"
+            style="@style/contactDiaryPersonButton"
+            android:layout_width="match_parent"
+            app:backgroundTint="@color/colorBackground"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:text="@string/contact_diary_person_encounter_mask_with" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/mask_without"
+            style="@style/contactDiaryPersonButton"
+            app:backgroundTint="@color/colorBackground"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:text="@string/contact_diary_person_encounter_mask_without" />
+
+    </com.google.android.material.button.MaterialButtonToggleGroup>
+
+    <com.google.android.material.button.MaterialButtonToggleGroup
+        android:id="@+id/environment_group"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp"
+        android:orientation="horizontal"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/mask_group"
+        app:singleSelection="true">
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/environment_outside"
+            style="@style/contactDiaryPersonButton"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:backgroundTint="@color/colorBackground"
+            android:layout_weight="1"
+            android:text="@string/contact_diary_person_encounter_environment_outside" />
+
+        <com.google.android.material.button.MaterialButton
+            android:id="@+id/environment_inside"
+            style="@style/contactDiaryPersonButton"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:backgroundTint="@color/colorBackground"
+            android:layout_weight="1"
+            android:text="@string/contact_diary_person_encounter_environment_inside" />
+
+    </com.google.android.material.button.MaterialButtonToggleGroup>
+
+    <de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.DiaryCircumstancesTextView
+        android:id="@+id/circumstances"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/spacing_huge">
-
-        <ImageView
-            android:id="@+id/contact_diary_person_list_item_icon"
-            android:layout_width="@dimen/spacing_medium"
-            android:layout_height="@dimen/spacing_medium"
-            android:layout_marginStart="@dimen/spacing_small"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintStart_toStartOf="parent"
-            app:layout_constraintTop_toTopOf="parent"
-            tools:srcCompat="@drawable/ic_selected" />
-
-        <TextView
-            android:id="@+id/contact_diary_person_list_item_name"
-            style="@style/subtitle"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_marginHorizontal="@dimen/spacing_small"
-            android:layout_marginVertical="@dimen/spacing_small"
-            app:layout_constraintBottom_toBottomOf="parent"
-            app:layout_constraintEnd_toEndOf="parent"
-            app:layout_constraintStart_toEndOf="@+id/contact_diary_person_list_item_icon"
-            app:layout_constraintTop_toTopOf="parent"
-            tools:text="@tools:sample/full_names" />
-    </androidx.constraintlayout.widget.ConstraintLayout>
-
-</layout>
+        android:layout_margin="16dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/environment_group" />
+
+</de.rki.coronawarnapp.contactdiary.ui.day.tabs.common.ExpandingDiaryListItemView>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/view_diary_circumstances_textview.xml b/Corona-Warn-App/src/main/res/layout/view_diary_circumstances_textview.xml
new file mode 100644
index 0000000000000000000000000000000000000000..17a22866f6708fd479dc41c16d4257a5908205ce
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/view_diary_circumstances_textview.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge 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"
+    style="@style/contactDiaryCardRipple"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+
+    <com.google.android.material.textfield.TextInputLayout
+        android:id="@+id/input_layout"
+        style="@style/ContactDiaryCircumstancesTextInputLayout"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        app:hintEnabled="false"
+        app:layout_constraintEnd_toStartOf="@id/info_button"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/environment_group">
+
+        <com.google.android.material.textfield.TextInputEditText
+            android:id="@+id/input"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:hint="@string/contact_diary_person_encounter_circumstances_hint"
+            android:inputType="textMultiLine"
+            android:maxLength="250"
+            android:padding="@dimen/spacing_tiny"
+            tools:text="This was a triumph. I'm making a note here; 'Huge success'. It's hard to overstate my satisfaction. We do what we must, because we can." />
+
+    </com.google.android.material.textfield.TextInputLayout>
+
+    <ImageButton
+        android:id="@+id/info_button"
+        style="@style/ContactDiaryInfoButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/environment_group" />
+
+</merge>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/layout/view_expanding_diary_listitem.xml b/Corona-Warn-App/src/main/res/layout/view_expanding_diary_listitem.xml
new file mode 100644
index 0000000000000000000000000000000000000000..612acf6411c1ea4ea37aee00bbeb35895078d954
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/view_expanding_diary_listitem.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge 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"
+    style="@style/contactDiaryCard"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="?selectableItemBackground"
+        android:clickable="true"
+        android:focusable="true"
+        android:minHeight="@dimen/spacing_huge">
+
+        <ImageView
+            android:id="@+id/header_checkbox"
+            android:layout_width="@dimen/spacing_medium"
+            android:layout_height="@dimen/spacing_medium"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:clickable="false"
+            android:focusable="false"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:srcCompat="@drawable/ic_unselected"
+            tools:srcCompat="@drawable/ic_selected" />
+
+        <TextView
+            android:id="@+id/header_title"
+            style="@style/subtitle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_small"
+            android:layout_marginVertical="@dimen/spacing_small"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/header_checkbox"
+            app:layout_constraintTop_toTopOf="parent"
+            tools:text="Primary list item title" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?android:attr/listDivider"
+        android:visibility="gone"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/header"
+        tools:visibility="visible" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/container"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:minHeight="@dimen/spacing_huge"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/divider"
+        tools:visibility="visible" />
+
+</merge>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
index 97572c5b032ecd86fc0757b48b245b2fd8103007..c51fa6233504c24597917c39694102d63bf3b5c6 100644
--- a/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
+++ b/Corona-Warn-App/src/main/res/navigation/contact_diary_nav_graph.xml
@@ -14,11 +14,11 @@
             android:defaultValue="2020-03-25"
             app:argType="string" />
         <action
-            android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryPersonBottomSheetDialogFragment"
-            app:destination="@id/contactDiaryPersonBottomSheetDialogFragment" />
+            android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryAddPersonFragment"
+            app:destination="@id/contactDiaryAddPersonFragment" />
         <action
-            android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryLocationBottomSheetDialogFragment"
-            app:destination="@id/contactDiaryLocationBottomSheetDialogFragment" />
+            android:id="@+id/action_contactDiaryDayFragment_to_contactDiaryAddLocationFragment"
+            app:destination="@id/contactDiaryAddLocationFragment" />
         <deepLink app:uri="coronawarnapp://contact-journal/day/{selectedDay}" />
     </fragment>
     <fragment
@@ -29,6 +29,9 @@
         <argument
             android:name="selectedDay"
             app:argType="string" />
+        <action
+            android:id="@+id/action_contactDiaryPersonListFragment_to_contactDiaryPersonCommentInfoFragment"
+            app:destination="@id/contactDiaryCommentInfoFragment" />
     </fragment>
     <fragment
         android:id="@+id/contactDiaryPlaceListFragment"
@@ -39,11 +42,11 @@
             android:name="selectedDay"
             app:argType="string" />
     </fragment>
-    <dialog
-        android:id="@+id/contactDiaryPersonBottomSheetDialogFragment"
-        android:name="de.rki.coronawarnapp.contactdiary.ui.sheets.person.ContactDiaryPersonBottomSheetDialogFragment"
-        android:label="ContactDiaryPersonBottomSheetDialogFragment"
-        tools:layout="@layout/contact_diary_person_bottom_sheet_fragment">
+    <fragment
+        android:id="@+id/contactDiaryAddPersonFragment"
+        android:name="de.rki.coronawarnapp.contactdiary.ui.person.ContactDiaryAddPersonFragment"
+        android:label="ContactDiaryAddPersonFragment"
+        tools:layout="@layout/contact_diary_add_person_fragment">
         <argument
             android:name="selectedPerson"
             android:defaultValue="@null"
@@ -54,12 +57,12 @@
             android:defaultValue="@null"
             app:argType="string"
             app:nullable="true" />
-    </dialog>
-    <dialog
-        android:id="@+id/contactDiaryLocationBottomSheetDialogFragment"
-        android:name="de.rki.coronawarnapp.contactdiary.ui.sheets.location.ContactDiaryLocationBottomSheetDialogFragment"
-        android:label="ContactDiaryLocationBottomSheetDialogFragment"
-        tools:layout="@layout/contact_diary_location_bottom_sheet_fragment">
+    </fragment>
+    <fragment
+        android:id="@+id/contactDiaryAddLocationFragment"
+        android:name="de.rki.coronawarnapp.contactdiary.ui.location.ContactDiaryAddLocationFragment"
+        android:label="ContactDiaryAddLocationFragment"
+        tools:layout="@layout/contact_diary_add_location_fragment">
         <argument
             android:name="selectedLocation"
             android:defaultValue="@null"
@@ -70,7 +73,7 @@
             android:defaultValue="@null"
             app:argType="string"
             app:nullable="true" />
-    </dialog>
+    </fragment>
     <fragment
         android:id="@+id/contactDiaryOnboardingFragment"
         android:name="de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFragment"
@@ -88,9 +91,10 @@
             android:name="showBottomNav"
             android:defaultValue="true"
             app:argType="boolean" />
-        <deepLink app:uri="coronawarnapp://contact-journal/oboarding/?goToDay={goToDay}"
+        <deepLink
             app:popUpTo="@id/contact_diary_nav_graph"
-            app:popUpToInclusive="true" />
+            app:popUpToInclusive="true"
+            app:uri="coronawarnapp://contact-journal/oboarding/?goToDay={goToDay}" />
     </fragment>
     <fragment
         android:id="@+id/contactDiaryInformationPrivacyFragment"
@@ -121,8 +125,8 @@
         android:label="ContactDiaryEditLocationsFragment"
         tools:layout="@layout/contact_diary_edit_locations_fragment">
         <action
-            android:id="@+id/action_contactDiaryEditLocationsFragment_to_contactDiaryLocationBottomSheetDialogFragment"
-            app:destination="@id/contactDiaryLocationBottomSheetDialogFragment" />
+            android:id="@+id/action_contactDiaryEditLocationsFragment_to_contactDiaryAddLocationFragment"
+            app:destination="@id/contactDiaryAddLocationFragment" />
     </fragment>
     <fragment
         android:id="@+id/contactDiaryEditPersonsFragment"
@@ -130,8 +134,13 @@
         android:label="ContactDiaryEditPersonsFragment"
         tools:layout="@layout/contact_diary_edit_persons_fragment">
         <action
-            android:id="@+id/action_contactDiaryEditPersonsFragment_to_contactDiaryPersonBottomSheetDialogFragment"
-            app:destination="@id/contactDiaryPersonBottomSheetDialogFragment" />
+            android:id="@+id/action_contactDiaryEditPersonsFragment_to_contactDiaryAddPersonFragment"
+            app:destination="@id/contactDiaryAddPersonFragment" />
     </fragment>
+    <fragment
+        android:id="@+id/contactDiaryCommentInfoFragment"
+        android:name="de.rki.coronawarnapp.contactdiary.ui.day.tabs.ContactDiaryCommentInfoFragment"
+        android:label="CommentInfoFragment"
+        tools:layout="@layout/contact_diary_comment_info_fragment" />
 
 </navigation>
diff --git a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml
index f86871acd91c094facd263febcf699f21ab59e07..7b9a7397daa1aebab0d499105fe5c82e02457ee4 100644
--- a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml
@@ -10,12 +10,12 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Създайте място и го добавете към Вашия дневник на контактите."</string>
     <string name="contact_diary_person_list_no_items_title">"Все още няма въведени лица"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Създайте лице и го добавете към Вашия дневник на контактите."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Лице"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Име"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Запазване"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Място"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Описание"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Запазване"</string>
+    <string name="contact_diary_add_person_title">"Лице"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Име"</string>
+    <string name="contact_diary_add_person_save_button">"Запазване"</string>
+    <string name="contact_diary_add_location_title">"Място"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Описание"</string>
+    <string name="contact_diary_add_location_save_button">"Запазване"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Дневник на контактите"</string>
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 9e707f3c8350e7bbfaf5cee0213fa486f818a9ea..2bf855a09b9124fc7a354ccbe4f0528bca6edf11 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
@@ -11,12 +11,14 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Legen Sie einen Ort an und fügen Sie ihn in Ihrem Kontakt-Tagebuch hinzu."</string>
     <string name="contact_diary_person_list_no_items_title">"Noch keine Personen vorhanden"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Legen Sie eine Person an und fügen Sie sie in Ihrem Kontakt-Tagebuch hinzu."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Person"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Name"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Speichern"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Ort"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Bezeichnung"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Speichern"</string>
+    <string name="contact_diary_add_person_title">"Person"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Vorname, Nachname"</string>
+    <string name="contact_diary_add_text_input_phone_hint">"Telefon"</string>
+    <string name="contact_diary_add_text_input_email_hint">"E-Mail"</string>
+    <string name="contact_diary_add_person_save_button">"Speichern"</string>
+    <string name="contact_diary_add_location_title">"Ort"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Bezeichnung"</string>
+    <string name="contact_diary_add_location_save_button">"Speichern"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Kontakt-Tagebuch"</string>
@@ -112,6 +114,13 @@
     <!-- XHED: Title for the contact diary dialog to delete delete a single person -->
     <string name="contact_diary_delete_person_title">"Wollen Sie wirklich diese Person entfernen?"</string>
 
+    <!-- XHED: Title for the contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_title">"Notiz"</string>
+    <!-- XTXT: Description for contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_description">"Hier können Sie Notizen machen, die Ihnen helfen, das Risiko einer Infektion besser einzuschätzen."</string>
+    <!-- XTXT: Body for contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_body">"Notieren Sie Umstände, die zu einem erhöhten Risiko führen könnten, z.B. räumliche Nähe zu anderen Personen oder die Ausübung einer Tätigkeit, die sich auf die Luftqualität auswirkt (Beispiele: „saßen nah beieinander“, „es wurde Sport getrieben“, „wir haben gesungen“).\n\nSollten Sie sich tatsächlich anstecken, können diese Notizen auch dem Gesundheitsamt bei der Nachverfolgung möglicher Infektionsketten helfen."</string>
+
     <!-- XTXT: location (description for screen readers) -->
     <string name="accessibility_location">"Ort %s"</string>
     <!-- XTXT: person (description for screen readers) -->
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 21ad6bdfe6638df5d6ae9b85f666dea8c8513f84..8d848ddb9485be9b63290b219540ca5ba3fb5679 100644
--- a/Corona-Warn-App/src/main/res/values-de/strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/strings.xml
@@ -1882,4 +1882,16 @@
     <!-- XTXT: Analytics voluntary user input, district: UNSPECIFIED -->
     <string name="analytics_userinput_district_unspecified">keine Angabe</string>
 
+    <!-- ####################################
+       Duration dialog
+   ###################################### -->
+
+    <!-- XHED: Duration dialog title -->
+    <string name="duration_dialog_title">Dauer</string>
+    <!-- XBUT: Duration dialog cancel button -->
+    <string name="duration_dialog_cancel_button">Cancel</string>
+    <!-- XBUT: Duration dialog ok button -->
+    <string name="duration_dialog_ok_button">OK</string>
+    <!-- NOTR -->
+    <string name="duration_dialog_default_value">00:00</string>
 </resources>
diff --git a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml
index 747776becd9b4a779b9510a16968b289c25d8536..4590f73acfddedd5dcddbf5dbe74a2b8e330aa8f 100644
--- a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml
@@ -10,12 +10,12 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Create a place and add it to your contact journal."</string>
     <string name="contact_diary_person_list_no_items_title">"No people defined yet"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Create a person and add them to your contact journal."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Person"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Name"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Save"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Place"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Description"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Save"</string>
+    <string name="contact_diary_add_person_title">"Person"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Name"</string>
+    <string name="contact_diary_add_person_save_button">"Save"</string>
+    <string name="contact_diary_add_location_title">"Place"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Description"</string>
+    <string name="contact_diary_add_location_save_button">"Save"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Contact Journal"</string>
diff --git a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml
index 0f24b87a458c8948a1a47a330d631f630ef5c603..38df500cf588e0f9c76fc2d46671d4d9ac3affb0 100644
--- a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml
@@ -10,12 +10,12 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Utwórz miejsce i dodaj je do dziennika kontaktów."</string>
     <string name="contact_diary_person_list_no_items_title">"Nie zdefiniowano jeszcze żadnych osób"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Utwórz osobę i dodaj ją do dziennika kontaktów."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Osoba"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"ImiÄ™ i nazwisko"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Zapisz"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Miejsce"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Opis"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Zapisz"</string>
+    <string name="contact_diary_add_person_title">"Osoba"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"ImiÄ™ i nazwisko"</string>
+    <string name="contact_diary_add_person_save_button">"Zapisz"</string>
+    <string name="contact_diary_add_location_title">"Miejsce"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Opis"</string>
+    <string name="contact_diary_add_location_save_button">"Zapisz"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Dziennik kontaktów"</string>
diff --git a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml
index c2c2a61be302e0fe1c1b6a793a59993996803360..90539612e53b315e2e8674688e51d65ae34521da 100644
--- a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml
@@ -10,12 +10,12 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Creați un loc și adăugați-l la jurnalul dvs. de contacte."</string>
     <string name="contact_diary_person_list_no_items_title">"Nicio persoană definită deocamdată"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Creați o persoană și adăugați-o la jurnalul dvs. de contacte."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Persoană"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Nume"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Salvare"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Loc"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Descriere"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Salvare"</string>
+    <string name="contact_diary_add_person_title">"Persoană"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Nume"</string>
+    <string name="contact_diary_add_person_save_button">"Salvare"</string>
+    <string name="contact_diary_add_location_title">"Loc"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Descriere"</string>
+    <string name="contact_diary_add_location_save_button">"Salvare"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Jurnal de contacte"</string>
diff --git a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml
index 15528c32aeadeb66ebca8f4789036e4292b23234..c8b833111fc2d158b4b355975f9dde90202d6968 100644
--- a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml
@@ -10,12 +10,12 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Bir yer oluşturun ve temas güncenize ekleyin."</string>
     <string name="contact_diary_person_list_no_items_title">"Henüz hiçbir kişi tanımlanmadı"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Bir kişi oluşturun ve temas güncenize ekleyin."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"KiÅŸi"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Ad"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Kaydet"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Yer"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Tanım"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Kaydet"</string>
+    <string name="contact_diary_add_person_title">"KiÅŸi"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Ad"</string>
+    <string name="contact_diary_add_person_save_button">"Kaydet"</string>
+    <string name="contact_diary_add_location_title">"Yer"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Tanım"</string>
+    <string name="contact_diary_add_location_save_button">"Kaydet"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Temas Güncesi"</string>
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 88e5af74820651c19482cf9617dcd98fe99d7cee..b9c30d5c6f88ffd376e71d628ed4a36df1405e99 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
@@ -11,12 +11,14 @@
     <string name="contact_diary_location_list_no_items_subtitle">"Create a place and add it to your contact journal."</string>
     <string name="contact_diary_person_list_no_items_title">"No people defined yet"</string>
     <string name="contact_diary_person_list_no_items_subtitle">"Create a person and add them to your contact journal."</string>
-    <string name="contact_diary_person_bottom_sheet_title">"Person"</string>
-    <string name="contact_diary_person_bottom_sheet_text_input_hint">"Name"</string>
-    <string name="contact_diary_person_bottom_sheet_save_button">"Save"</string>
-    <string name="contact_diary_location_bottom_sheet_title">"Place"</string>
-    <string name="contact_diary_location_bottom_sheet_text_input_hint">"Description"</string>
-    <string name="contact_diary_location_bottom_sheet_save_button">"Save"</string>
+    <string name="contact_diary_add_person_title">"Person"</string>
+    <string name="contact_diary_add_person_text_input_name_hint">"Name"</string>
+    <string name="contact_diary_add_text_input_phone_hint" />
+    <string name="contact_diary_add_text_input_email_hint"/>
+    <string name="contact_diary_add_person_save_button">"Save"</string>
+    <string name="contact_diary_add_location_title">"Place"</string>
+    <string name="contact_diary_add_location_text_input_hint">"Description"</string>
+    <string name="contact_diary_add_location_save_button">"Save"</string>
 
     <!-- XHED: Title for the contact diary card displayed in the homescreen -->
     <string name="contact_diary_homescreen_card_header">"Contact Journal"</string>
@@ -119,6 +121,13 @@
     <!-- XTXT: Intro text  for the contact journal email export part two-->
     <string name="contact_diary_export_intro_two" translatable="false">"Die nachfolgende Liste dient dem zuständigen Gesundheitsamt zur Kontaktnachverfolgung gem. § 25 IfSG."</string>
 
+    <!-- XHED: Title for the contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_title">"Notiz"</string>
+    <!-- XTXT: Description for contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_description">"Hier können Sie Notizen machen, die Ihnen helfen, das Risiko einer Infektion besser einzuschätzen."</string>
+    <!-- XTXT: Body for contact diary comment info screen -->
+    <string name="contact_diary_comment_info_screen_body">"Notieren Sie Umstände, die zu einem erhöhten Risiko führen könnten, z.B. räumliche Nähe zu anderen Personen oder die Ausübung einer Tätigkeit, die sich auf die Luftqualität auswirkt (Beispiele: „saßen nah beieinander“, „es wurde Sport getrieben“, „wir haben gesungen“).\n\nSollten Sie sich tatsächlich anstecken, können diese Notizen auch dem Gesundheitsamt bei der Nachverfolgung möglicher Infektionsketten helfen."</string>
+
     <!-- XTXT: location (description for screen readers) -->
     <string name="accessibility_location">"Place %s"</string>
     <!-- XTXT: person (description for screen readers) -->
diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml
index 1971d65abbab3b0d66f688e25ac1e108cb42febe..de77c976edb37749ce346df5532e89ab9d307def 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -1896,4 +1896,17 @@
     <string name="analytics_userinput_district_title">"Your District (County)"</string>
     <!-- XTXT: Analytics voluntary user input, district: UNSPECIFIED -->
     <string name="analytics_userinput_district_unspecified">"No answer"</string>
+
+    <!-- ####################################
+       Duration dialog
+   ###################################### -->
+
+    <!-- XHED: Duration dialog title -->
+    <string name="duration_dialog_title">Dauer</string>
+    <!-- XBUT: Duration dialog cancel button -->
+    <string name="duration_dialog_cancel_button">Cancel</string>
+    <!-- XBUT: Duration dialog ok button -->
+    <string name="duration_dialog_ok_button">OK</string>
+    <!-- NOTR -->
+    <string name="duration_dialog_default_value">00:00</string>
 </resources>
diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml
index e2ad52d5cd4a351f797ca5b9a8ade871a1d5fbf8..03958da9288e050a5cd1727203a4c2683433120a 100644
--- a/Corona-Warn-App/src/main/res/values/styles.xml
+++ b/Corona-Warn-App/src/main/res/values/styles.xml
@@ -12,7 +12,6 @@
         <item name="windowActionBar">false</item>
         <item name="windowNoTitle">true</item>
         <item name="dialogTheme">@style/DialogTheme</item>
-        <item name="bottomSheetDialogTheme">@style/BottomSheetDialogTheme</item>
     </style>
 
     <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
@@ -69,11 +68,6 @@
         <item name="buttonBarNegativeButtonStyle">@style/DialogButtonTheme</item>
     </style>
 
-    <style name="BottomSheetDialogTheme" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
-        <item name="colorPrimary">@color/colorBrandSecondary</item>
-        <item name="colorPrimaryDark">@color/colorStableDark</item>
-    </style>
-
     <style name="DialogButtonTheme" parent="Widget.MaterialComponents.Button.TextButton">
         <item name="android:textColor">@color/colorAccentTintButton</item>
     </style>
@@ -110,6 +104,17 @@
         <item name="android:textColor">@color/button_text_color_emphasized</item>
     </style>
 
+    <style name="contactDiaryPersonButton" parent="Widget.MaterialComponents.Button.OutlinedButton">
+        <item name="strokeColor">@color/button_contact_diary_person</item>
+        <item name="strokeWidth">2dp</item>
+        <item name="android:letterSpacing">0</item>
+        <item name="textAllCaps">false</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#000000</item>
+
+    </style>
+
     <style name="buttonReset" parent="button">
         <item name="android:backgroundTint">@color/button_red</item>
         <item name="android:textColor">@color/button_text_color_emphasized</item>
@@ -287,6 +292,10 @@
         <item name="android:textColor">@color/colorTextSixteen</item>
     </style>
 
+    <style name="bodyNeutral" parent="@style/TextAppearance.MaterialComponents.Body1">
+        <item name="android:textColor">@color/colorTextSemanticNeutral</item>
+    </style>
+
     <!-- ####################################
               Icons
     ###################################### -->
@@ -400,6 +409,35 @@
         <item name="android:background">@drawable/contact_diary_card_ripple</item>
     </style>
 
+    <style name="ContactDiaryExpandableListItem">
+        <item name="android:background">@drawable/contact_diary_card_ripple</item>
+    </style>
+
+    <style name="ContactDiaryCircumstancesTextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense">
+        <item name="boxBackgroundColor">@color/colorBackground</item>
+        <item name="android:textColorHint">@color/colorTextPrimary2</item>
+        <item name="android:textSize">@dimen/font_small</item>
+        <item name="hintTextColor">@color/colorTextPrimary2</item>
+        <item name="boxCornerRadiusBottomEnd">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusBottomStart">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusTopEnd">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusTopStart">@dimen/spacing_mega_tiny</item>
+        <item name="boxStrokeWidth">0dp</item>
+        <item name="boxStrokeWidthFocused">0dp</item>
+    </style>
+
+    <style name="ContactDiaryInfoButton" parent="buttonIcon">
+        <item name="android:height">@dimen/button_icon</item>
+        <item name="android:width">@dimen/button_icon</item>
+        <item name="android:src">@drawable/ic_info</item>
+        <item name="android:paddingStart">12dp</item>
+        <item name="android:paddingEnd">12dp</item>
+        <item name="android:paddingTop">7dp</item>
+        <item name="android:paddingBottom">7dp</item>
+        <item name="android:contentDescription">@string/statistics_info_button</item>
+        <item name="android:tint">@color/colorAccent</item>
+    </style>
+
     <style name="StatisticsCardInfoButton" parent="buttonIcon">
         <item name="android:height">@dimen/button_icon</item>
         <item name="android:width">@dimen/button_icon</item>
@@ -429,4 +467,22 @@
         <item name="android:textColor">@color/colorTextPrimary1</item>
     </style>
 
+    <style name="TextInputLayoutTheme" parent="Widget.MaterialComponents.TextInputLayout.FilledBox">
+        <item name="boxBackgroundColor">@color/colorContactDiaryListItem</item>
+        <item name="android:textColorHint">@color/colorTextPrimary2</item>
+        <item name="hintTextColor">@color/colorTextPrimary2</item>
+        <item name="boxCornerRadiusBottomEnd">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusBottomStart">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusTopEnd">@dimen/spacing_mega_tiny</item>
+        <item name="boxCornerRadiusTopStart">@dimen/spacing_mega_tiny</item>
+        <item name="boxStrokeWidth">0dp</item>
+        <item name="boxStrokeWidthFocused">0dp</item>
+        <item name="counterTextAppearance">@color/colorTransparent</item>
+        <item name="android:maxLength">5</item>
+    </style>
+
+    <style name="DefaultNumberPickerTheme" parent="AppTheme">
+        <item name="colorControlNormal">@color/colorAccent</item>
+    </style>
+
 </resources>
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConvertersTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConvertersTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..18b89cc2e5267c46da57f423372b620876fc89ee
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/storage/internal/converters/ContactDiaryRoomConvertersTest.kt
@@ -0,0 +1,24 @@
+package de.rki.coronawarnapp.contactdiary.storage.internal.converters
+
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter.DurationClassification
+import io.kotest.matchers.shouldBe
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+
+class ContactDiaryRoomConvertersTest : BaseTest() {
+
+    @Test
+    fun `test person encounter duration classification`() {
+        ContactDiaryRoomConverters().apply {
+            toContactDurationClassification(null) shouldBe null
+            toContactDurationClassification(
+                DurationClassification.MORE_THAN_15_MINUTES.key
+            ) shouldBe DurationClassification.MORE_THAN_15_MINUTES
+
+            fromContactDurationClassification(null) shouldBe null
+            fromContactDurationClassification(
+                DurationClassification.LESS_THAN_15_MINUTES
+            ) shouldBe DurationClassification.LESS_THAN_15_MINUTES.key
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragmentTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragmentTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..3506e3065244d200abf8c8d573f9b267fdc83a38
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/durationpicker/ContactDiaryDurationPickerFragmentTest.kt
@@ -0,0 +1,43 @@
+package de.rki.coronawarnapp.contactdiary.ui.durationpicker
+
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+class ContactDiaryDurationPickerFragmentTest {
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+    }
+
+    @Test
+    fun `check hours array`() {
+        ContactDiaryDurationPickerFragment.hoursArray.count() shouldBe 24
+        for (i in 0..9) {
+            ContactDiaryDurationPickerFragment.hoursArray[i] shouldBe "0$i"
+        }
+        for (i in 10..23) {
+            ContactDiaryDurationPickerFragment.hoursArray[i] shouldBe "$i"
+        }
+    }
+
+    @Test
+    fun `check minutes array`() {
+        ContactDiaryDurationPickerFragment.minutesArray.count() shouldBe 4
+        ContactDiaryDurationPickerFragment.minutesArray[0] shouldBe "00"
+        for (i in 1..3) {
+            ContactDiaryDurationPickerFragment.minutesArray[i] shouldBe "${i * 15}"
+        }
+    }
+
+    @Test
+    fun `check duration`() {
+        ContactDiaryDurationPickerFragment.getDuration(0, 0).toContactDiaryFormat() shouldBe "00:00"
+        ContactDiaryDurationPickerFragment.getDuration(1, 0).toContactDiaryFormat() shouldBe "01:00"
+        ContactDiaryDurationPickerFragment.getDuration(23, 3).toContactDiaryFormat() shouldBe "23:45"
+        ContactDiaryDurationPickerFragment.getDuration(9, 2).toContactDiaryFormat() shouldBe "09:30"
+        ContactDiaryDurationPickerFragment.getDuration(10, 1).toContactDiaryFormat() shouldBe "10:15"
+    }
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt
index a9022f4fe16940addcb635c13383dec6c14083e1..6dd141a3353bcc2b643258d518b28e8eb5afcecc 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt
@@ -26,6 +26,8 @@ class ContactDiaryEditLocationsViewModelTest {
     private val location = object : ContactDiaryLocation {
         override val locationId = 1L
         override var locationName = "Supermarket"
+        override val phoneNumber: String? = null
+        override val emailAddress: String? = null
         override val stableId = 1L
     }
     private val locationList = listOf(location)
@@ -64,7 +66,7 @@ class ContactDiaryEditLocationsViewModelTest {
         viewModel.navigationEvent.observeForever { }
         viewModel.onEditLocationClick(location)
         viewModel.navigationEvent.value shouldBe
-            ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailSheet(location.toContactDiaryLocationEntity())
+            ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailFragment(location.toContactDiaryLocationEntity())
     }
 
     @Test
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt
index f1a8aa1e22d2f65e329e9dbfade18366738ab302..892d74d8be0c919fcea222d3f520c2a54e1904d0 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt
@@ -26,6 +26,8 @@ class ContactDiaryEditPersonsViewModelTest {
     private val person = object : ContactDiaryPerson {
         override val personId = 1L
         override var fullName = "Julia"
+        override val phoneNumber: String? = null
+        override val emailAddress: String? = null
         override val stableId = 1L
     }
 
@@ -65,7 +67,7 @@ class ContactDiaryEditPersonsViewModelTest {
         viewModel.navigationEvent.observeForever { }
         viewModel.onEditPersonClick(person)
         viewModel.navigationEvent.value shouldBe
-            ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailSheet(person.toContactDiaryPersonEntity())
+            ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailFragment(person.toContactDiaryPersonEntity())
     }
 
     @Test
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensionsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensionsTest.kt
index c380d34f94e962168f623b5baab946790b7aab78..3723e0471f0809ac63495b7d877ab5c2048b5363 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensionsTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/util/ContactDiaryExtensionsTest.kt
@@ -8,14 +8,6 @@ import org.junit.jupiter.api.Test
 
 class ContactDiaryExtensionsTest {
 
-    @Test
-    fun testFormatContactDiaryNameField() {
-        Assert.assertEquals("   Granny   ".formatContactDiaryNameField(5), "Grann")
-        Assert.assertEquals("   Grann y".formatContactDiaryNameField(5), "Grann")
-        Assert.assertEquals("Granny   ".formatContactDiaryNameField(5), "Grann")
-        Assert.assertEquals("    ".formatContactDiaryNameField(2), "  ")
-    }
-
     @Test
     fun `upper and lowercase mix sorting for names`() {
         val testList = listOf(
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/clientmetadata/ClientMetadataDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/clientmetadata/ClientMetadataDonorTest.kt
index b3b262d2539f79fb470db185316c559f8c44c34a..7ab9baac100d97da81b068a3cd71d9d625e2f50e 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/clientmetadata/ClientMetadataDonorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/clientmetadata/ClientMetadataDonorTest.kt
@@ -76,9 +76,11 @@ class ClientMetadataDonorTest : BaseTest() {
         val parentBuilder = PpaData.PPADataAndroid.newBuilder()
 
         runBlockingTest2 {
-            val contribution = createInstance().beginDonation(object : DonorModule.Request {
-                override val currentConfig: ConfigData = mockk()
-            })
+            val contribution = createInstance().beginDonation(
+                object : DonorModule.Request {
+                    override val currentConfig: ConfigData = mockk()
+                }
+            )
             contribution.injectData(parentBuilder)
             contribution.finishDonation(true)
         }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoFragmentTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoFragmentTest.kt
index 03ad1e074b0739a12faf6f28f5e6657fe59efed4..4e8a58260c50bdb7856b437d526c9aa1feefbaf0 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoFragmentTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoFragmentTest.kt
@@ -57,7 +57,6 @@ class NewReleaseInfoFragmentTest : BaseTest() {
     @Test
     fun `ensure DEFAULT aka FRENCH new release info arrays are of equal length`() = loadAndCompareStringArrayResources()
 
-
     private fun loadAndCompareStringArrayResources() {
         val titles = context.resources.getStringArray(R.array.new_release_title)
         val bodies = context.resources.getStringArray(R.array.new_release_body)
@@ -68,5 +67,4 @@ class NewReleaseInfoFragmentTest : BaseTest() {
         bodies.size shouldBe labels.size
         labels.size shouldBe urls.size
     }
-
-}
\ No newline at end of file
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModelTest.kt
index 094ae35516f1f4e74ca1d9f2d63c3e54e7369fa3..f9ae8c5c3879e6d6417e0ee3c51ecd8fa41c555f 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModelTest.kt
@@ -41,7 +41,7 @@ class NewReleaseInfoViewModelTest {
         val item2 = NewReleaseInfoItemText("title2", "body2")
         val titles = arrayOf(item1.title, item2.title)
         val bodies = arrayOf(item1.body, item2.body)
-        viewModel.getItems(titles, bodies, arrayOf("",""),  arrayOf("","")) shouldBe listOf(item1, item2)
+        viewModel.getItems(titles, bodies, arrayOf("", ""), arrayOf("", "")) shouldBe listOf(item1, item2)
     }
 
     @Test
@@ -50,7 +50,7 @@ class NewReleaseInfoViewModelTest {
         val item2 = NewReleaseInfoItemText("title2", "body2")
         val titles = arrayOf(item1.title)
         val bodies = arrayOf(item1.body, item2.body)
-        viewModel.getItems(titles, bodies,  arrayOf("",""),  arrayOf("","")) shouldBe emptyList()
+        viewModel.getItems(titles, bodies, arrayOf("", ""), arrayOf("", "")) shouldBe emptyList()
     }
 
     @Test
@@ -59,7 +59,7 @@ class NewReleaseInfoViewModelTest {
         val item2 = NewReleaseInfoItemText("title2", "body2")
         val titles = arrayOf(item1.title, item2.title)
         val bodies = arrayOf(item1.body)
-        viewModel.getItems(titles, bodies,  arrayOf("",""),  arrayOf("","")) shouldBe emptyList()
+        viewModel.getItems(titles, bodies, arrayOf("", ""), arrayOf("", "")) shouldBe emptyList()
     }
 
     @Test
@@ -68,7 +68,7 @@ class NewReleaseInfoViewModelTest {
         val item2 = NewReleaseInfoItemText("title2", "body2")
         val titles = arrayOf(item1.title, item2.title)
         val bodies = arrayOf(item1.body, item2.body)
-        viewModel.getItems(titles, bodies,  arrayOf(""),  arrayOf("","")) shouldBe emptyList()
+        viewModel.getItems(titles, bodies, arrayOf(""), arrayOf("", "")) shouldBe emptyList()
     }
 
     @Test
@@ -77,7 +77,7 @@ class NewReleaseInfoViewModelTest {
         val item2 = NewReleaseInfoItemText("title2", "body2")
         val titles = arrayOf(item1.title, item2.title)
         val bodies = arrayOf(item1.body, item2.body)
-        viewModel.getItems(titles, bodies,  arrayOf("",""),  arrayOf("")) shouldBe emptyList()
+        viewModel.getItems(titles, bodies, arrayOf("", ""), arrayOf("")) shouldBe emptyList()
     }
 
     @Test