From 063442b64db92af21c273e6e51e670d8e0364d0d Mon Sep 17 00:00:00 2001
From: Chilja Gossow <49635654+chiljamgossow@users.noreply.github.com>
Date: Thu, 17 Dec 2020 17:25:15 +0100
Subject: [PATCH] Contact diary edit screens (EXPOSUREAPP-4161
 EXPOSUREAPP-4162) (#1908)

* Merge branch 'feature/4152-contact-diary' of https://github.com/corona-warn-app/cwa-app-android into feature/4152-contact-diary

# Conflicts:
#	Corona-Warn-App/schemas/de.rki.coronawarnapp.contactdiary.storage.ContactDiaryDatabase/1.json
#	Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt

* clean up DI

* add recycler view

* clean up

* new texts

* clean up

* clean up

* background, dialog

* klint

* klint

* klint

* review comments

* review comments

* review comments

* Merge branch 'feature/4152-contact-diary' into feature/4161-4162-edit-person-location

# Conflicts:
#	Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
---
 .../ui/ContactDiaryTestFragmentViewModel.kt   |   1 -
 Corona-Warn-App/src/main/AndroidManifest.xml  |   2 +-
 .../entity/ContactDiaryLocationEntity.kt      |   5 +-
 .../entity/ContactDiaryPersonEntity.kt        |   5 +-
 .../repo/DefaultContactDiaryRepository.kt     |   2 +-
 .../contactdiary/ui/ContactDiaryUIModule.kt   |   6 +
 ...tDiaryLocationBottomSheetDialogFragment.kt |  49 +++++++-
 ...DiaryLocationBottomSheetDialogViewModel.kt |  23 +++-
 ...actDiaryPersonBottomSheetDialogFragment.kt |  49 +++++++-
 ...ctDiaryPersonBottomSheetDialogViewModel.kt |  23 +++-
 .../edit/ContactDiaryEditLocationsFragment.kt | 113 ++++++++++++++++++
 .../ContactDiaryEditLocationsViewModel.kt     |  50 ++++++++
 .../ui/edit/ContactDiaryEditModule.kt         |  32 +++++
 .../edit/ContactDiaryEditPersonsFragment.kt   | 113 ++++++++++++++++++
 .../edit/ContactDiaryEditPersonsViewModel.kt  |  50 ++++++++
 .../ContactDiaryOnboardingFragment.kt         |   2 -
 .../ui/overview/ContactDiaryOverviewMenu.kt   |  30 ++++-
 .../ui/main/home/HomeFragment.kt              |   1 -
 .../main/res/drawable/ic_baseline_delete.xml  |   5 +
 .../main/res/drawable/ic_baseline_edit_24.xml |  10 ++
 .../layout/contact_diary_edit_list_item.xml   |  36 ++++++
 .../contact_diary_edit_locations_fragment.xml |  93 ++++++++++++++
 .../contact_diary_edit_persons_fragment.xml   |  93 ++++++++++++++
 .../contact_diary_homescreen_card_include.xml |   3 +-
 ...t_diary_location_bottom_sheet_fragment.xml |  28 ++++-
 .../contact_diary_location_list_fragment.xml  |   3 +-
 ...act_diary_person_bottom_sheet_fragment.xml |  17 ++-
 .../contact_diary_person_list_fragment.xml    |   3 +-
 .../include_contact_diary_privacy_card.xml    |   6 +-
 .../navigation/contact_diary_nav_graph.xml    |  48 +++++++-
 .../src/main/res/values/strings.xml           |   2 +-
 .../ContactDiaryEditLocationsViewModelTest.kt |  93 ++++++++++++++
 .../ContactDiaryEditPersonsViewModelTest.kt   |  94 +++++++++++++++
 33 files changed, 1046 insertions(+), 44 deletions(-)
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditModule.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt
 create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_baseline_delete.xml
 create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_baseline_edit_24.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/contact_diary_edit_list_item.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/contact_diary_edit_locations_fragment.xml
 create mode 100644 Corona-Warn-App/src/main/res/layout/contact_diary_edit_persons_fragment.xml
 create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt
 create mode 100644 Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt

diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt
index 9bf0a9e8d..44df7a141 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/contactdiary/ui/ContactDiaryTestFragmentViewModel.kt
@@ -14,7 +14,6 @@ import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import org.joda.time.LocalDate
-import java.lang.StringBuilder
 import kotlin.random.Random
 
 class ContactDiaryTestFragmentViewModel @AssistedInject constructor(
diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml
index e450de6d4..f4096417f 100644
--- a/Corona-Warn-App/src/main/AndroidManifest.xml
+++ b/Corona-Warn-App/src/main/AndroidManifest.xml
@@ -84,4 +84,4 @@
 
     </application>
 
-</manifest>
\ No newline at end of file
+</manifest>
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 c424bfdee..4eceee3de 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
@@ -1,15 +1,18 @@
 package de.rki.coronawarnapp.contactdiary.storage.entity
 
+import android.os.Parcelable
 import androidx.room.ColumnInfo
 import androidx.room.Entity
 import androidx.room.PrimaryKey
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
+import kotlinx.android.parcel.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
-) : ContactDiaryLocation {
+) : ContactDiaryLocation, Parcelable {
     override val stableId: Long
         get() = locationId
 }
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 0ea761f2c..62a8c194e 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
@@ -1,15 +1,18 @@
 package de.rki.coronawarnapp.contactdiary.storage.entity
 
+import android.os.Parcelable
 import androidx.room.ColumnInfo
 import androidx.room.Entity
 import androidx.room.PrimaryKey
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import kotlinx.android.parcel.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
-) : ContactDiaryPerson {
+) : ContactDiaryPerson, Parcelable {
     override val stableId: Long
         get() = personId
 }
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 5339351e8..8c63ce180 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
@@ -45,7 +45,7 @@ class DefaultContactDiaryRepository @Inject constructor(
         Timber.d("Updating location $contactDiaryLocation")
         val contactDiaryContactDiaryLocationEntity = contactDiaryLocation.toContactDiaryLocationEntity()
         executeWhenIdNotDefault(contactDiaryContactDiaryLocationEntity.locationId) {
-            contactDiaryLocationDao.insert(contactDiaryContactDiaryLocationEntity)
+            contactDiaryLocationDao.update(contactDiaryContactDiaryLocationEntity)
         }
     }
 
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 0ddeb1ca0..154d38473 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
@@ -12,12 +12,18 @@ import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.ContactDiaryLocati
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.location.ContactDiaryLocationListModule
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.ContactDiaryPersonListFragment
 import de.rki.coronawarnapp.contactdiary.ui.day.tabs.person.ContactDiaryPersonListModule
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditModule
 import de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFragment
 import de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFragmentModule
 import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragment
 import de.rki.coronawarnapp.contactdiary.ui.overview.ContactDiaryOverviewFragmentModule
 
 @Module
+    (
+    includes = [
+        ContactDiaryEditModule::class
+    ]
+)
 abstract class ContactDiaryUIModule {
     @ContributesAndroidInjector(modules = [ContactDiaryDayModule::class])
     abstract fun contactDiaryDayFragment(): ContactDiaryDayFragment
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt
index f480e75dd..dcb237780 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogFragment.kt
@@ -4,9 +4,13 @@ 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.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
@@ -20,6 +24,8 @@ class ContactDiaryLocationBottomSheetDialogFragment : BottomSheetDialogFragment(
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
     private val viewModel: ContactDiaryLocationBottomSheetDialogViewModel by cwaViewModels { viewModelFactory }
 
+    private val navArgs: ContactDiaryLocationBottomSheetDialogFragmentArgs by navArgs()
+
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         _binding = ContactDiaryLocationBottomSheetFragmentBinding.inflate(inflater)
         return binding.root
@@ -28,18 +34,38 @@ class ContactDiaryLocationBottomSheetDialogFragment : BottomSheetDialogFragment(
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        binding.contactDiaryLocationBottomSheetCloseButton.buttonIcon.setOnClickListener {
-            viewModel.closePressed()
+        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.contactDiaryLocationBottomSheetSaveButton.setOnClickListener {
-            viewModel.saveLocation()
+        binding.contactDiaryLocationBottomSheetCloseButton.buttonIcon.setOnClickListener {
+            viewModel.closePressed()
         }
 
         binding.contactDiaryLocationBottomSheetTextInputEditText.doAfterTextChanged {
             viewModel.textChanged(it.toString())
         }
 
+        binding.contactDiaryLocationBottomSheetTextInputEditText.setOnEditorActionListener { v, actionId, event ->
+            return@setOnEditorActionListener when (actionId) {
+                EditorInfo.IME_ACTION_DONE -> false
+                else -> true
+            }
+        }
+
         viewModel.shouldClose.observe2(this) {
             dismiss()
         }
@@ -54,4 +80,19 @@ class ContactDiaryLocationBottomSheetDialogFragment : BottomSheetDialogFragment(
         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/day/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt
index 42f8dc3be..b81f23ab1 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/location/ContactDiaryLocationBottomSheetDialogViewModel.kt
@@ -3,12 +3,14 @@ package de.rki.coronawarnapp.contactdiary.ui.day.sheets.location
 import androidx.lifecycle.asLiveData
 import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
 
 class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor(
@@ -27,7 +29,7 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor
         text.value = locationName
     }
 
-    fun saveLocation() = launch {
+    fun addLocation() = launch {
         contactDiaryRepository.addLocation(
             DefaultContactDiaryLocation(
                 locationName = text.value.take(MAX_LOCATION_NAME_LENGTH)
@@ -36,6 +38,25 @@ class ContactDiaryLocationBottomSheetDialogViewModel @AssistedInject constructor
         shouldClose.postValue(null)
     }
 
+    fun updateLocation(location: ContactDiaryLocationEntity) = launch {
+        contactDiaryRepository.updateLocation(
+            DefaultContactDiaryLocation(
+                location.locationId,
+                locationName = text.value.take(MAX_LOCATION_NAME_LENGTH)
+            )
+        )
+        shouldClose.postValue(null)
+    }
+
+    fun deleteLocation(location: ContactDiaryLocationEntity) = launch {
+        contactDiaryRepository.locationVisits.firstOrNull()?.forEach {
+            if (it.contactDiaryLocation.locationId == location.locationId)
+                contactDiaryRepository.deleteLocationVisit(it)
+        }
+        contactDiaryRepository.deleteLocation(location)
+        shouldClose.postValue(null)
+    }
+
     fun closePressed() {
         shouldClose.postValue(null)
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt
index a96fdb12f..576668beb 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogFragment.kt
@@ -4,9 +4,13 @@ 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.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
@@ -20,6 +24,8 @@ class ContactDiaryPersonBottomSheetDialogFragment : BottomSheetDialogFragment(),
     @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
     private val viewModel: ContactDiaryPersonBottomSheetDialogViewModel by cwaViewModels { viewModelFactory }
 
+    private val navArgs: ContactDiaryPersonBottomSheetDialogFragmentArgs by navArgs()
+
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
         _binding = ContactDiaryPersonBottomSheetFragmentBinding.inflate(inflater)
         return binding.root
@@ -28,18 +34,38 @@ class ContactDiaryPersonBottomSheetDialogFragment : BottomSheetDialogFragment(),
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        binding.contactDiaryPersonBottomSheetCloseButton.buttonIcon.setOnClickListener {
-            viewModel.closePressed()
+        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.contactDiaryPersonBottomSheetSaveButton.setOnClickListener {
-            viewModel.savePerson()
+        binding.contactDiaryPersonBottomSheetCloseButton.buttonIcon.setOnClickListener {
+            viewModel.closePressed()
         }
 
         binding.contactDiaryPersonBottomSheetTextInputEditText.doAfterTextChanged {
             viewModel.textChanged(it.toString())
         }
 
+        binding.contactDiaryPersonBottomSheetTextInputEditText.setOnEditorActionListener { v, actionId, event ->
+            return@setOnEditorActionListener when (actionId) {
+                EditorInfo.IME_ACTION_DONE -> false
+                else -> true
+            }
+        }
+
         viewModel.shouldClose.observe2(this) {
             dismiss()
         }
@@ -54,4 +80,19 @@ class ContactDiaryPersonBottomSheetDialogFragment : BottomSheetDialogFragment(),
         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/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt
index 8ea2fe868..4cf4ec00a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/sheets/person/ContactDiaryPersonBottomSheetDialogViewModel.kt
@@ -3,12 +3,14 @@ package de.rki.coronawarnapp.contactdiary.ui.day.sheets.person
 import androidx.lifecycle.asLiveData
 import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.contactdiary.model.DefaultContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
 import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
 import de.rki.coronawarnapp.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
 
 class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
@@ -27,7 +29,7 @@ class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
         text.value = locationName
     }
 
-    fun savePerson() = launch {
+    fun addPerson() = launch {
         contactDiaryRepository.addPerson(
             DefaultContactDiaryPerson(
                 fullName = text.value.take(MAX_PERSON_NAME_LENGTH)
@@ -36,6 +38,25 @@ class ContactDiaryPersonBottomSheetDialogViewModel @AssistedInject constructor(
         shouldClose.postValue(null)
     }
 
+    fun updatePerson(person: ContactDiaryPersonEntity) = launch {
+        contactDiaryRepository.updatePerson(
+            DefaultContactDiaryPerson(
+                person.personId,
+                fullName = text.value.take(MAX_PERSON_NAME_LENGTH)
+            )
+        )
+        shouldClose.postValue(null)
+    }
+
+    fun deletePerson(person: ContactDiaryPersonEntity) = launch {
+        contactDiaryRepository.personEncounters.firstOrNull()?.forEach {
+            if (it.contactDiaryPerson.personId == person.personId)
+                contactDiaryRepository.deletePersonEncounter(it)
+        }
+        contactDiaryRepository.deletePerson(person)
+        shouldClose.postValue(null)
+    }
+
     fun closePressed() {
         shouldClose.postValue(null)
     }
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
new file mode 100644
index 000000000..0360880c1
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsFragment.kt
@@ -0,0 +1,113 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.RecyclerView
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailSheet
+import de.rki.coronawarnapp.databinding.ContactDiaryEditLocationsFragmentBinding
+import de.rki.coronawarnapp.util.DialogHelper
+import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.doNavigate
+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.cwaViewModels
+import javax.inject.Inject
+
+class ContactDiaryEditLocationsFragment : Fragment(R.layout.contact_diary_edit_locations_fragment), AutoInject {
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: ContactDiaryEditLocationsViewModel by cwaViewModels { viewModelFactory }
+    private val binding: ContactDiaryEditLocationsFragmentBinding by viewBindingLazy()
+
+    private val locationList: MutableList<ContactDiaryLocation> = mutableListOf()
+    private val listAdapter = ListAdapter()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        binding.viewModel = viewModel
+
+        setupRecyclerView()
+
+        binding.toolbar.setNavigationOnClickListener {
+            popBackStack()
+        }
+
+        viewModel.locationsLiveData.observe2(this) {
+            locationList.clear()
+            locationList.addAll(it)
+            listAdapter.notifyDataSetChanged()
+        }
+
+        viewModel.navigationEvent.observe2(this) {
+
+            when (it) {
+                ShowDeletionConfirmationDialog -> DialogHelper.showDialog(deleteAllLocationsConfirmationDialog)
+                is ShowLocationDetailSheet -> {
+                    doNavigate(
+                        ContactDiaryEditLocationsFragmentDirections
+                            .actionContactDiaryEditLocationsFragmentToContactDiaryLocationBottomSheetDialogFragment(
+                                it.location
+                            )
+                    )
+                }
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        binding.contentContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
+    }
+
+    private fun setupRecyclerView() {
+        binding.locationsRecyclerView.adapter = listAdapter
+    }
+
+    private val deleteAllLocationsConfirmationDialog by lazy {
+        DialogHelper.DialogInstance(
+            requireActivity(),
+            R.string.contact_diary_delete_locations_title,
+            R.string.contact_diary_delete_locations_message,
+            R.string.contact_diary_delete_button_positive,
+            R.string.contact_diary_delete_button_negative,
+            positiveButtonFunction = {
+                viewModel.onDeleteAllConfirmedClick()
+            }
+        )
+    }
+
+    inner class ListAdapter : RecyclerView.Adapter<ListAdapter.ViewHolder>() {
+
+        inner class ViewHolder(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
+            val nameTextView = itemView.findViewById<TextView>(R.id.name)
+            val itemContainerView = itemView.findViewById<View>(R.id.item_container)
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapter.ViewHolder {
+            val view = LayoutInflater.from(parent.context).inflate(R.layout.contact_diary_edit_list_item, parent, false)
+            return ViewHolder(view)
+        }
+
+        override fun onBindViewHolder(viewHolder: ListAdapter.ViewHolder, position: Int) {
+            val location = locationList[position]
+            viewHolder.nameTextView.text = location.locationName
+            viewHolder.itemContainerView.setOnClickListener {
+                viewModel.onEditLocationClick(location)
+            }
+        }
+
+        override fun getItemCount(): Int {
+            return locationList.size
+        }
+    }
+}
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
new file mode 100644
index 000000000..85b236d21
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt
@@ -0,0 +1,50 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import androidx.lifecycle.asLiveData
+import com.squareup.inject.assisted.AssistedInject
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity
+import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryLocationEntity
+import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
+import de.rki.coronawarnapp.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import kotlinx.coroutines.flow.map
+
+class ContactDiaryEditLocationsViewModel @AssistedInject constructor(
+    private val contactDiaryRepository: ContactDiaryRepository,
+    dispatcherProvider: DispatcherProvider
+) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
+
+    val locationsLiveData = contactDiaryRepository.locations.asLiveData()
+
+    val navigationEvent = SingleLiveEvent<NavigationEvent>()
+
+    val isButtonEnabled = contactDiaryRepository.locations.map { !it.isNullOrEmpty() }.asLiveData()
+
+    val isListVisible = contactDiaryRepository.locations.map { !it.isNullOrEmpty() }.asLiveData()
+
+    fun onDeleteAllLocationsClick() {
+        navigationEvent.postValue(NavigationEvent.ShowDeletionConfirmationDialog)
+    }
+
+    fun onDeleteAllConfirmedClick() {
+        launch {
+            contactDiaryRepository.deleteAllLocationVisits()
+            contactDiaryRepository.deleteAllLocations()
+        }
+    }
+
+    fun onEditLocationClick(location: ContactDiaryLocation) {
+        navigationEvent.postValue(NavigationEvent.ShowLocationDetailSheet(location.toContactDiaryLocationEntity()))
+    }
+
+    @AssistedInject.Factory
+    interface Factory : SimpleCWAViewModelFactory<ContactDiaryEditLocationsViewModel>
+
+    sealed class NavigationEvent {
+        object ShowDeletionConfirmationDialog : NavigationEvent()
+        data class ShowLocationDetailSheet(val location: ContactDiaryLocationEntity) : NavigationEvent()
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditModule.kt
new file mode 100644
index 000000000..ce0499513
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditModule.kt
@@ -0,0 +1,32 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import dagger.Binds
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import dagger.multibindings.IntoMap
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
+
+@Module
+abstract class ContactDiaryEditModule {
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(ContactDiaryEditLocationsViewModel::class)
+    abstract fun contactDiaryEditLocationsFragment(
+        factory: ContactDiaryEditLocationsViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+
+    @ContributesAndroidInjector
+    abstract fun contactDiaryEditLocationsFragment(): ContactDiaryEditLocationsFragment
+
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(ContactDiaryEditPersonsViewModel::class)
+    abstract fun contactDiaryEditPersonsFragment(
+        factory: ContactDiaryEditPersonsViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+
+    @ContributesAndroidInjector
+    abstract fun contactDiaryEditPersonsFragment(): ContactDiaryEditPersonsFragment
+}
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
new file mode 100644
index 000000000..46d2d3b24
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsFragment.kt
@@ -0,0 +1,113 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.recyclerview.widget.RecyclerView
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
+import de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailSheet
+import de.rki.coronawarnapp.databinding.ContactDiaryEditPersonsFragmentBinding
+import de.rki.coronawarnapp.util.DialogHelper
+import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.doNavigate
+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.cwaViewModels
+import javax.inject.Inject
+
+class ContactDiaryEditPersonsFragment : Fragment(R.layout.contact_diary_edit_persons_fragment), AutoInject {
+
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val viewModel: ContactDiaryEditPersonsViewModel by cwaViewModels { viewModelFactory }
+    private val binding: ContactDiaryEditPersonsFragmentBinding by viewBindingLazy()
+
+    private val personList: MutableList<ContactDiaryPerson> = mutableListOf()
+    private val listAdapter = ListAdapter()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        binding.viewModel = viewModel
+
+        setupRecyclerView()
+
+        binding.toolbar.setNavigationOnClickListener {
+            popBackStack()
+        }
+
+        viewModel.personsLiveData.observe2(this) {
+            personList.clear()
+            personList.addAll(it)
+            listAdapter.notifyDataSetChanged()
+        }
+
+        viewModel.navigationEvent.observe2(this) {
+
+            when (it) {
+                ShowDeletionConfirmationDialog -> DialogHelper.showDialog(deleteAllPersonsConfirmationDialog)
+                is ShowPersonDetailSheet -> {
+                    doNavigate(
+                        ContactDiaryEditPersonsFragmentDirections
+                            .actionContactDiaryEditPersonsFragmentToContactDiaryPersonBottomSheetDialogFragment(
+                                it.person
+                            )
+                    )
+                }
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        binding.contentContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
+    }
+
+    private fun setupRecyclerView() {
+        binding.personsRecyclerView.adapter = listAdapter
+    }
+
+    private val deleteAllPersonsConfirmationDialog by lazy {
+        DialogHelper.DialogInstance(
+            requireActivity(),
+            R.string.contact_diary_delete_persons_title,
+            R.string.contact_diary_delete_persons_message,
+            R.string.contact_diary_delete_button_positive,
+            R.string.contact_diary_delete_button_negative,
+            positiveButtonFunction = {
+                viewModel.onDeleteAllConfirmedClick()
+            }
+        )
+    }
+
+    inner class ListAdapter : RecyclerView.Adapter<ListAdapter.ViewHolder>() {
+
+        inner class ViewHolder(listItemView: View) : RecyclerView.ViewHolder(listItemView) {
+            val nameTextView = itemView.findViewById<TextView>(R.id.name)
+            val itemContainerView = itemView.findViewById<View>(R.id.item_container)
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListAdapter.ViewHolder {
+            val view = LayoutInflater.from(parent.context).inflate(R.layout.contact_diary_edit_list_item, parent, false)
+            return ViewHolder(view)
+        }
+
+        override fun onBindViewHolder(viewHolder: ListAdapter.ViewHolder, position: Int) {
+            val person = personList[position]
+            viewHolder.nameTextView.text = person.fullName
+            viewHolder.itemContainerView.setOnClickListener {
+                viewModel.onEditPersonClick(person)
+            }
+        }
+
+        override fun getItemCount(): Int {
+            return personList.size
+        }
+    }
+}
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
new file mode 100644
index 000000000..582879125
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt
@@ -0,0 +1,50 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import androidx.lifecycle.asLiveData
+import com.squareup.inject.assisted.AssistedInject
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity
+import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryPersonEntity
+import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
+import de.rki.coronawarnapp.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import kotlinx.coroutines.flow.map
+
+class ContactDiaryEditPersonsViewModel @AssistedInject constructor(
+    private val contactDiaryRepository: ContactDiaryRepository,
+    dispatcherProvider: DispatcherProvider
+) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
+
+    val personsLiveData = contactDiaryRepository.people.asLiveData()
+
+    val navigationEvent = SingleLiveEvent<NavigationEvent>()
+
+    val isButtonEnabled = contactDiaryRepository.people.map { !it.isNullOrEmpty() }.asLiveData()
+
+    val isListVisible = contactDiaryRepository.people.map { !it.isNullOrEmpty() }.asLiveData()
+
+    fun onDeleteAllPersonsClick() {
+        navigationEvent.postValue(NavigationEvent.ShowDeletionConfirmationDialog)
+    }
+
+    fun onDeleteAllConfirmedClick() {
+        launch {
+            contactDiaryRepository.deleteAllPersonEncounters()
+            contactDiaryRepository.deleteAllPeople()
+        }
+    }
+
+    fun onEditPersonClick(person: ContactDiaryPerson) {
+        navigationEvent.postValue(NavigationEvent.ShowPersonDetailSheet(person.toContactDiaryPersonEntity()))
+    }
+
+    @AssistedInject.Factory
+    interface Factory : SimpleCWAViewModelFactory<ContactDiaryEditPersonsViewModel>
+
+    sealed class NavigationEvent {
+        object ShowDeletionConfirmationDialog : NavigationEvent()
+        data class ShowPersonDetailSheet(val person: ContactDiaryPersonEntity) : NavigationEvent()
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
index a144e4f56..1dd44a3ee 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/onboarding/ContactDiaryOnboardingFragment.kt
@@ -56,9 +56,7 @@ class ContactDiaryOnboardingFragment : Fragment(R.layout.contact_diary_onboardin
                 }
 
                 ContactDiaryOnboardingNavigationEvents.NavigateToOverviewFragment -> {
-
                     onboardingComplete()
-
                     doNavigate(
                         ContactDiaryOnboardingFragmentDirections
                             .actionContactDiaryOnboardingFragmentToContactDiaryOverviewFragment()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt
index 07b6fe6ae..e2f3d0b2b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewMenu.kt
@@ -3,7 +3,10 @@ package de.rki.coronawarnapp.contactdiary.ui.overview
 import android.content.Context
 import android.view.View
 import android.widget.PopupMenu
+import androidx.navigation.NavController
+import androidx.navigation.fragment.findNavController
 import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.ui.doNavigate
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
 import javax.inject.Inject
 
@@ -12,21 +15,36 @@ class ContactDiaryOverviewMenu @Inject constructor(
     private val contactDiaryOverviewFragment: ContactDiaryOverviewFragment
 ) {
     private val context: Context = contactDiaryOverviewFragment.requireContext()
+    private val navController: NavController
+        get() = contactDiaryOverviewFragment.findNavController()
     private val vm: ContactDiaryOverviewViewModel by contactDiaryOverviewFragment.cwaViewModels {
         contactDiaryOverviewFragment.viewModelFactory }
-
     // TODO(Move this to ContactDiaryOverviewFragment)
     fun showMenuFor(view: View) = PopupMenu(context, view).apply {
         inflate(R.menu.menu_contact_diary_overview)
         setOnMenuItemClickListener {
             when (it.itemId) {
-                R.id.menu_contact_diary_information -> { true }
-                R.id.menu_contact_diary_export_entries -> {
-                    vm.onExportPress(context)
+                R.id.menu_contact_diary_information -> {
+                    navController.doNavigate(
+                        ContactDiaryOverviewFragmentDirections
+                            .actionContactDiaryOverviewFragmentToContactDiaryOnboardingFragment()
+                    )
+                    true
+                }
+                R.id.menu_contact_diary_export_entries -> { vm.onExportPress(context)
+                    true }
+                R.id.menu_contact_diary_edit_persons -> {
+                    navController.doNavigate(
+                        ContactDiaryOverviewFragmentDirections
+                            .actionContactDiaryOverviewFragmentToContactDiaryEditPersonsFragment())
+                    true
+                }
+                R.id.menu_contact_diary_edit_locations -> {
+                    navController.doNavigate(
+                        ContactDiaryOverviewFragmentDirections
+                            .actionContactDiaryOverviewFragmentToContactDiaryEditLocationsFragment())
                     true
                 }
-                R.id.menu_contact_diary_edit_persons -> { true }
-                R.id.menu_contact_diary_edit_locations -> { true }
                 else -> contactDiaryOverviewFragment.onOptionsItemSelected(it)
             }
         }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
index 952f98013..731d5515c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt
@@ -19,7 +19,6 @@ import de.rki.coronawarnapp.util.ui.observe2
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
-import kotlinx.android.synthetic.main.include_submission_status_card_ready.*
 import javax.inject.Inject
 
 /**
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_baseline_delete.xml b/Corona-Warn-App/src/main/res/drawable/ic_baseline_delete.xml
new file mode 100644
index 000000000..6df11c698
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_baseline_delete.xml
@@ -0,0 +1,5 @@
+<vector android:height="18dp" android:tint="?attr/colorControlNormal"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_baseline_edit_24.xml b/Corona-Warn-App/src/main/res/drawable/ic_baseline_edit_24.xml
new file mode 100644
index 000000000..2844bafeb
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/drawable/ic_baseline_edit_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_edit_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_list_item.xml
new file mode 100644
index 000000000..73cebe4ab
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_list_item.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/item_container"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_margin="4dp"
+    style="@style/cardGrey"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <TextView
+        style="@style/subtitle"
+        android:id="@+id/name"
+        android:layout_width="@dimen/match_constraint"
+        android:layout_height="wrap_content"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:padding="@dimen/spacing_small"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/edit_icon"
+        tools:text="Julia Maria"
+        />
+
+    <ImageView
+        android:id="@+id/edit_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/ic_baseline_edit_24"
+        android:padding="@dimen/spacing_small"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        android:contentDescription="@string/contact_diary_edit_icon_content_description"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_edit_locations_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_locations_fragment.xml
new file mode 100644
index 000000000..745b56b84
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_locations_fragment.xml
@@ -0,0 +1,93 @@
+<?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">
+
+    <data>
+
+        <variable
+            name="viewModel"
+            type="de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsViewModel" />
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/content_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="@dimen/match_constraint"
+            android:layout_height="wrap_content"
+            app:title="@string/contact_diary_edit_locations_title"
+            app:navigationIcon="@drawable/ic_close"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            />
+
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/contact_diary_location_list_no_items_group"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:gone="@{viewModel.isListVisible()}"
+            app:constraint_referenced_ids="contact_diary_location_list_no_items_image,contact_diary_location_list_no_items_title" />
+
+        <ImageView
+            android:id="@+id/contact_diary_location_list_no_items_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintBottom_toTopOf="@id/contact_diary_location_list_no_items_title"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:srcCompat="@drawable/ic_illustration_no_locations"
+            android:contentDescription="@string/contact_diary_edit_locations_image_content_description"/>
+
+        <TextView
+            android:id="@+id/contact_diary_location_list_no_items_title"
+            style="@style/subtitleMedium"
+            android:layout_width="@dimen/match_constraint"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_huge"
+            android:layout_marginTop="@dimen/spacing_normal"
+            android:text="@string/contact_diary_location_list_no_items_title"
+            android:textAlignment="center"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_location_list_no_items_image"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/locations_recycler_view"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:layout_marginEnd="@dimen/spacing_small"
+            android:layout_marginTop="@dimen/spacing_normal"
+            android:layout_marginBottom="@dimen/spacing_normal"
+            android:importantForAccessibility="no"
+            android:scrollbars="vertical"
+            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/toolbar"
+            app:layout_constraintBottom_toTopOf="@id/delete_button"/>
+
+        <Button
+            android:id="@+id/delete_button"
+            style="@style/buttonReset"
+            android:layout_width="@dimen/match_constraint"
+            android:layout_height="wrap_content"
+            android:text="@string/contact_diary_remove_all_button"
+            android:textAllCaps="true"
+            android:onClick="@{ () -> viewModel.onDeleteAllLocationsClick()}"
+            android:enabled="@{viewModel.isButtonEnabled()}"
+            android:layout_margin="@dimen/spacing_normal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_edit_persons_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_persons_fragment.xml
new file mode 100644
index 000000000..3b284bff7
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_edit_persons_fragment.xml
@@ -0,0 +1,93 @@
+<?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">
+
+    <data>
+
+        <variable
+            name="viewModel"
+            type="de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsViewModel" />
+
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/content_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            app:title="@string/contact_diary_edit_persons_title"
+            app:navigationIcon="@drawable/ic_close"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            />
+
+        <androidx.constraintlayout.widget.Group
+            android:id="@+id/contact_diary_person_list_no_items_group"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:gone="@{viewModel.isListVisible()}"
+            app:constraint_referenced_ids="contact_diary_person_list_no_items_image,contact_diary_person_list_no_items_title" />
+
+        <ImageView
+            android:id="@+id/contact_diary_person_list_no_items_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:layout_constraintBottom_toTopOf="@id/contact_diary_person_list_no_items_title"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_chainStyle="packed"
+            app:srcCompat="@drawable/ic_illustration_no_people"
+            android:contentDescription="@string/contact_diary_edit_persons_image_content_description"/>
+
+        <TextView
+            android:id="@+id/contact_diary_person_list_no_items_title"
+            style="@style/subtitleMedium"
+            android:layout_width="@dimen/match_constraint"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="@dimen/spacing_huge"
+            android:layout_marginTop="@dimen/spacing_normal"
+            android:text="@string/contact_diary_person_list_no_items_title"
+            android:textAlignment="center"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/contact_diary_person_list_no_items_image" />
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/persons_recycler_view"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginStart="@dimen/spacing_small"
+            android:layout_marginEnd="@dimen/spacing_small"
+            android:layout_marginTop="@dimen/spacing_normal"
+            android:layout_marginBottom="@dimen/spacing_normal"
+            android:importantForAccessibility="no"
+            android:scrollbars="vertical"
+            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/toolbar"
+            app:layout_constraintBottom_toTopOf="@id/delete_button"/>
+
+        <Button
+            android:id="@+id/delete_button"
+            style="@style/buttonReset"
+            android:layout_width="@dimen/match_constraint"
+            android:layout_height="wrap_content"
+            android:text="@string/contact_diary_remove_all_button"
+            android:textAllCaps="true"
+            android:onClick="@{ () -> viewModel.onDeleteAllPersonsClick()}"
+            android:enabled="@{viewModel.isButtonEnabled()}"
+            android:layout_margin="@dimen/spacing_normal"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
+</layout>
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml
index 858c66976..87bd64046 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_homescreen_card_include.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:tools="http://schemas.android.com/tools"
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <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
index 8a2c25860..9b05d3a6b 100644
--- 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
@@ -20,13 +20,27 @@
         <TextView
             android:id="@+id/contact_diary_location_bottom_sheet_title"
             style="@style/headline6"
-            android:layout_width="wrap_content"
+            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_constraintStart_toEndOf="@+id/contact_diary_location_bottom_sheet_close_button"
-            app:layout_constraintTop_toTopOf="@+id/contact_diary_location_bottom_sheet_close_button" />
+            app:layout_constraintBottom_toBottomOf="@id/contact_diary_location_bottom_sheet_close_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"
+            app:layout_constraintEnd_toStartOf="@id/contact_diary_location_bottom_sheet_delete_button"/>
+
+        <ImageView
+            android:id="@+id/contact_diary_location_bottom_sheet_delete_button"
+            android:layout_width="@dimen/circle_icon_big"
+            android:layout_height="@dimen/circle_icon_big"
+            android:layout_marginEnd="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:padding="@dimen/circle_icon_big_padding"
+            android:src="@drawable/ic_baseline_delete"
+            android:contentDescription="@string/contact_diary_delete_icon_content_description"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            style="@style/buttonIcon" />
 
         <com.google.android.material.textfield.TextInputLayout
             android:id="@+id/contact_diary_location_bottom_sheet_text_input_layout"
@@ -39,12 +53,14 @@
             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">
+            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:layout_height="wrap_content"
+                android:inputType="text"
+                android:imeOptions="actionDone"/>
 
         </com.google.android.material.textfield.TextInputLayout>
 
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_fragment.xml
index 9d491caba..402383d8c 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_fragment.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_location_list_fragment.xml
@@ -32,7 +32,8 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintVertical_chainStyle="packed"
-            app:srcCompat="@drawable/ic_illustration_no_locations" />
+            app:srcCompat="@drawable/ic_illustration_no_locations"
+            android:contentDescription="@string/contact_diary_edit_locations_image_content_description"/>
 
         <TextView
             android:id="@+id/contact_diary_location_list_no_items_title"
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
index 8cfcbf0ff..146896254 100644
--- 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
@@ -28,6 +28,19 @@
             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"
+            android:layout_width="@dimen/circle_icon_big"
+            android:layout_height="@dimen/circle_icon_big"
+            android:layout_marginEnd="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_small"
+            android:padding="@dimen/circle_icon_big_padding"
+            android:src="@drawable/ic_baseline_delete"
+            android:contentDescription="@string/contact_diary_delete_icon_content_description"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            style="@style/buttonIcon" />
+
         <com.google.android.material.textfield.TextInputLayout
             android:id="@+id/contact_diary_person_bottom_sheet_text_input_layout"
             android:layout_width="@dimen/match_constraint"
@@ -44,7 +57,9 @@
             <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:layout_height="wrap_content"
+                android:inputType="text"
+                android:imeOptions="actionDone"/>
 
         </com.google.android.material.textfield.TextInputLayout>
 
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_fragment.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_fragment.xml
index 47f1a6197..a5a7ce3bd 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_fragment.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_person_list_fragment.xml
@@ -32,7 +32,8 @@
             app:layout_constraintStart_toStartOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintVertical_chainStyle="packed"
-            app:srcCompat="@drawable/ic_illustration_no_people" />
+            app:srcCompat="@drawable/ic_illustration_no_people"
+            android:contentDescription="@string/contact_diary_edit_persons_image_content_description"/>
 
         <TextView
             android:id="@+id/contact_diary_person_list_no_items_title"
diff --git a/Corona-Warn-App/src/main/res/layout/include_contact_diary_privacy_card.xml b/Corona-Warn-App/src/main/res/layout/include_contact_diary_privacy_card.xml
index be909a096..fa066d4d3 100644
--- a/Corona-Warn-App/src/main/res/layout/include_contact_diary_privacy_card.xml
+++ b/Corona-Warn-App/src/main/res/layout/include_contact_diary_privacy_card.xml
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layout xmlns:tools2="http://schemas.android.com/tools"
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/apk/res-auto">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/contact_diary_privacy_card"
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 c5ceb7671..cbea34864 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
@@ -3,7 +3,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/contact_diary_nav_graph"
-    app:startDestination="@id/contactDiaryOnboardingFragment">
+    app:startDestination="@id/contactDiaryOverviewFragment">
     <fragment
         android:id="@+id/contactDiaryDayFragment"
         android:name="de.rki.coronawarnapp.contactdiary.ui.day.ContactDiaryDayFragment"
@@ -38,11 +38,23 @@
     <dialog
         android:id="@+id/contactDiaryPersonBottomSheetDialogFragment"
         android:name="de.rki.coronawarnapp.contactdiary.ui.day.sheets.person.ContactDiaryPersonBottomSheetDialogFragment"
-        android:label="ContactDiaryPersonBottomSheetDialogFragment" />
+        android:label="ContactDiaryPersonBottomSheetDialogFragment" >
+        <argument
+            android:name="selectedPerson"
+            app:nullable="true"
+            android:defaultValue="@null"
+            app:argType="de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity" />
+    </dialog>
     <dialog
         android:id="@+id/contactDiaryLocationBottomSheetDialogFragment"
         android:name="de.rki.coronawarnapp.contactdiary.ui.day.sheets.location.ContactDiaryLocationBottomSheetDialogFragment"
-        android:label="ContactDiaryLocationBottomSheetDialogFragment" />
+        android:label="ContactDiaryLocationBottomSheetDialogFragment" >
+        <argument
+            android:name="selectedLocation"
+            app:nullable="true"
+            android:defaultValue="@null"
+            app:argType="de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryLocationEntity" />
+    </dialog>
     <fragment
         android:id="@+id/contactDiaryOnboardingFragment"
         android:name="de.rki.coronawarnapp.contactdiary.ui.onboarding.ContactDiaryOnboardingFragment"
@@ -52,7 +64,9 @@
             app:destination="@id/contactDiaryInformationPrivacyFragment" />
         <action
             android:id="@+id/action_contactDiaryOnboardingFragment_to_contactDiaryOverviewFragment"
-            app:destination="@id/contactDiaryOverviewFragment" />
+            app:destination="@id/contactDiaryOverviewFragment"
+            app:popUpTo="@id/contact_diary_nav_graph"
+            app:popUpToInclusive="true"/>
     </fragment>
     <fragment
         android:id="@+id/contactDiaryInformationPrivacyFragment"
@@ -66,5 +80,31 @@
         <action
             android:id="@+id/action_contactDiaryOverviewFragment_to_contactDiaryDayFragment"
             app:destination="@id/contactDiaryDayFragment" />
+        <action
+            android:id="@+id/action_contactDiaryOverviewFragment_to_contactDiaryEditLocationsFragment"
+            app:destination="@id/contactDiaryEditLocationsFragment" />
+        <action
+            android:id="@+id/action_contactDiaryOverviewFragment_to_contactDiaryEditPersonsFragment"
+            app:destination="@id/contactDiaryEditPersonsFragment" />
+        <action
+            android:id="@+id/action_contactDiaryOverviewFragment_to_contactDiaryOnboardingFragment"
+            app:destination="@id/contactDiaryOnboardingFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/contactDiaryEditLocationsFragment"
+        android:name="de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditLocationsFragment"
+        android:label="ContactDiaryEditLocationsFragment">
+        <action
+            android:id="@+id/action_contactDiaryEditLocationsFragment_to_contactDiaryLocationBottomSheetDialogFragment"
+            app:destination="@id/contactDiaryLocationBottomSheetDialogFragment" />
+    </fragment>
+    <fragment
+        android:id="@+id/contactDiaryEditPersonsFragment"
+        android:name="de.rki.coronawarnapp.contactdiary.ui.edit.ContactDiaryEditPersonsFragment"
+        android:label="ContactDiaryEditPersonsFragment" >
+        <action
+            android:id="@+id/action_contactDiaryEditPersonsFragment_to_contactDiaryPersonBottomSheetDialogFragment"
+            app:destination="@id/contactDiaryPersonBottomSheetDialogFragment" />
     </fragment>
+
 </navigation>
diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml
index 3f28d165d..0e80c1c78 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -1473,4 +1473,4 @@
     <!-- XBUT: Abort button for test result positive no consent screen -->
     <string name="submission_test_result_positive_no_consent_button_abort">"Cancel"</string>
 
-</resources>
\ No newline at end of file
+</resources>
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
new file mode 100644
index 000000000..ea6e55c46
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt
@@ -0,0 +1,93 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocation
+import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryLocationEntity
+import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import testhelpers.TestDispatcherProvider
+import testhelpers.extensions.InstantExecutorExtension
+
+@ExtendWith(InstantExecutorExtension::class)
+class ContactDiaryEditLocationsViewModelTest {
+
+    lateinit var viewModel: ContactDiaryEditLocationsViewModel
+    @MockK lateinit var contactDiaryRepository: ContactDiaryRepository
+    private val location = object : ContactDiaryLocation {
+        override val locationId = 1L
+        override var locationName = "Supermarket"
+        override val stableId = 1L
+    }
+    private val locationList = listOf(location)
+
+    @BeforeEach
+    fun setUp() {
+        MockKAnnotations.init(this)
+    }
+
+    @Test
+    fun testOnDeleteAllLocationsClick() {
+        every { contactDiaryRepository.locations } returns MutableStateFlow(locationList)
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.navigationEvent.observeForever { }
+        viewModel.onDeleteAllLocationsClick()
+        viewModel.navigationEvent.value shouldBe ContactDiaryEditLocationsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
+    }
+
+    @Test
+    fun testOnDeleteAllConfirmedClick() {
+        coEvery { contactDiaryRepository.deleteAllLocationVisits() } just Runs
+        coEvery { contactDiaryRepository.deleteAllLocations() } just Runs
+        every { contactDiaryRepository.locations } returns MutableStateFlow(locationList)
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.onDeleteAllConfirmedClick()
+        coVerify(exactly = 1) {
+            contactDiaryRepository.deleteAllLocationVisits()
+            contactDiaryRepository.deleteAllLocations()
+        }
+    }
+
+    @Test
+    fun testOnEditLocationClick() {
+        every { contactDiaryRepository.locations } returns MutableStateFlow(locationList)
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.navigationEvent.observeForever { }
+        viewModel.onEditLocationClick(location)
+        viewModel.navigationEvent.value shouldBe
+            ContactDiaryEditLocationsViewModel.NavigationEvent.ShowLocationDetailSheet(location.toContactDiaryLocationEntity())
+    }
+
+    @Test
+    fun testIsButtonEnabled() {
+        every { contactDiaryRepository.locations } returns MutableStateFlow(locationList)
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.isButtonEnabled.observeForever { }
+        viewModel.isButtonEnabled.value shouldBe true
+    }
+
+    @Test
+    fun testIsButtonNotEnabledWhenListIsEmpty() {
+        every { contactDiaryRepository.locations } returns MutableStateFlow(emptyList())
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.isButtonEnabled.observeForever { }
+        viewModel.isButtonEnabled.value shouldBe false
+    }
+
+    @Test
+    fun testLocations() {
+        every { contactDiaryRepository.locations } returns MutableStateFlow(locationList)
+        viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.locationsLiveData.observeForever { }
+        viewModel.locationsLiveData.value shouldBe locationList
+    }
+}
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
new file mode 100644
index 000000000..183722bb6
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt
@@ -0,0 +1,94 @@
+package de.rki.coronawarnapp.contactdiary.ui.edit
+
+import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPerson
+import de.rki.coronawarnapp.contactdiary.storage.entity.toContactDiaryPersonEntity
+import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.Runs
+import io.mockk.coEvery
+import io.mockk.coVerify
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import io.mockk.just
+import kotlinx.coroutines.flow.MutableStateFlow
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import testhelpers.TestDispatcherProvider
+import testhelpers.extensions.InstantExecutorExtension
+
+@ExtendWith(InstantExecutorExtension::class)
+class ContactDiaryEditPersonsViewModelTest {
+
+    lateinit var viewModel: ContactDiaryEditPersonsViewModel
+    @MockK lateinit var contactDiaryRepository: ContactDiaryRepository
+    private val person = object : ContactDiaryPerson {
+        override val personId = 1L
+        override var fullName = "Julia"
+        override val stableId = 1L
+    }
+
+    private val personList = listOf(person)
+
+    @BeforeEach
+    fun setUp() {
+        MockKAnnotations.init(this)
+    }
+
+    @Test
+    fun testOnDeleteAllLocationsClick() {
+        every { contactDiaryRepository.people } returns MutableStateFlow(personList)
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.navigationEvent.observeForever { }
+        viewModel.onDeleteAllPersonsClick()
+        viewModel.navigationEvent.value shouldBe ContactDiaryEditPersonsViewModel.NavigationEvent.ShowDeletionConfirmationDialog
+    }
+
+    @Test
+    fun testOnDeleteAllConfirmedClick() {
+        coEvery { contactDiaryRepository.deleteAllPeople() } just Runs
+        coEvery { contactDiaryRepository.deleteAllPersonEncounters() } just Runs
+        every { contactDiaryRepository.people } returns MutableStateFlow(personList)
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.onDeleteAllConfirmedClick()
+        coVerify(exactly = 1) {
+            contactDiaryRepository.deleteAllPeople()
+            contactDiaryRepository.deleteAllPersonEncounters()
+        }
+    }
+
+    @Test
+    fun testOnEditLocationClick() {
+        every { contactDiaryRepository.people } returns MutableStateFlow(personList)
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.navigationEvent.observeForever { }
+        viewModel.onEditPersonClick(person)
+        viewModel.navigationEvent.value shouldBe
+            ContactDiaryEditPersonsViewModel.NavigationEvent.ShowPersonDetailSheet(person.toContactDiaryPersonEntity())
+    }
+
+    @Test
+    fun testIsButtonEnabled() {
+        every { contactDiaryRepository.people } returns MutableStateFlow(personList)
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.isButtonEnabled.observeForever { }
+        viewModel.isButtonEnabled.value shouldBe true
+    }
+
+    @Test
+    fun testIsButtonNotEnabledWhenListIsEmpty() {
+        every { contactDiaryRepository.people } returns MutableStateFlow(emptyList())
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.isButtonEnabled.observeForever { }
+        viewModel.isButtonEnabled.value shouldBe false
+    }
+
+    @Test
+    fun testLocations() {
+        every { contactDiaryRepository.people } returns MutableStateFlow(personList)
+        viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider)
+        viewModel.personsLiveData.observeForever { }
+        viewModel.personsLiveData.value shouldBe personList
+    }
+}
-- 
GitLab