From 52b1fc2ef27d7077c6da8a10e6e3fdd55d02665e Mon Sep 17 00:00:00 2001
From: Lukas Lechner <lukas.lechner@sap.com>
Date: Wed, 19 May 2021 13:41:19 +0200
Subject: [PATCH] Deletion of vaccination certificates (EXPOSUREAPP-7111)
 (#3209)

* Add overflow menu icon to vaccination card

* Show overflow menu on click

* Delete vaccination certificate via overflow menu

* Navigate back from VaccinationListFragment when all certificates got deleted

* Lint

* Add swipe to delete

* Fine-tune layout

* Lint

* Sort vaccinations by vaccinatedAt property in vaccination list screen

* Address PR feedback

* Show error dialog when vaccination deletion fails

Co-authored-by: Mohamed Metwalli <mohamed.metwalli@sap.com>
Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com>
---
 .../ui/list/VaccinationListFragment.kt        | 46 +++++++++++++++++--
 .../ui/list/VaccinationListViewModel.kt       | 41 +++++++++++++++--
 ...nationListImmunityInformationCardItemVH.kt |  4 +-
 .../VaccinationListNameCardItemVH.kt          |  4 +-
 .../VaccinationListQrCodeCardItemVH.kt        |  4 +-
 .../VaccinationListVaccinationCardItemVH.kt   | 23 +++++++++-
 .../drawable-night/ic_arrow_right_grey.xml    | 10 ----
 .../main/res/drawable/ic_arrow_right_grey.xml | 11 -----
 .../vaccination_list_vaccination_card.xml     | 35 +++++++++-----
 .../main/res/menu/menu_vaccination_item.xml   |  5 ++
 .../res/values-de/vaccination_strings.xml     | 22 ++++-----
 .../src/main/res/values/styles.xml            |  4 ++
 .../main/res/values/vaccination_strings.xml   | 22 ++++-----
 13 files changed, 161 insertions(+), 70 deletions(-)
 delete mode 100644 Corona-Warn-App/src/main/res/drawable-night/ic_arrow_right_grey.xml
 delete mode 100644 Corona-Warn-App/src/main/res/drawable/ic_arrow_right_grey.xml
 create mode 100644 Corona-Warn-App/src/main/res/menu/menu_vaccination_item.xml

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt
index 58d6e8635..017f9a00c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt
@@ -10,12 +10,16 @@ import androidx.navigation.fragment.FragmentNavigatorExtras
 import androidx.navigation.fragment.findNavController
 import androidx.navigation.fragment.navArgs
 import com.google.android.material.appbar.AppBarLayout
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.imageview.ShapeableImageView
 import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder
 import de.rki.coronawarnapp.databinding.FragmentVaccinationListBinding
 import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs
 import de.rki.coronawarnapp.ui.view.onOffsetChange
 import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.list.isSwipeable
+import de.rki.coronawarnapp.util.list.onSwipeItem
 import de.rki.coronawarnapp.util.lists.diffutil.update
 import de.rki.coronawarnapp.util.ui.doNavigate
 import de.rki.coronawarnapp.util.ui.popBackStack
@@ -23,7 +27,10 @@ import de.rki.coronawarnapp.util.ui.viewBindingLazy
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
 import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted
 import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson
+import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListViewModel.Event.DeleteVaccinationEvent
+import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListViewModel.Event.NavigateBack
 import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationCertificateDetails
+import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListAdapter
 import javax.inject.Inject
 
@@ -43,7 +50,7 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
         }
     )
 
-    private val adapter = VaccinationListAdapter()
+    private val vaccinationListAdapter = VaccinationListAdapter()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
@@ -53,7 +60,15 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
                 popBackStack()
             }
 
-            recyclerViewVaccinationList.adapter = adapter
+            recyclerViewVaccinationList.apply {
+                adapter = vaccinationListAdapter
+                onSwipeItem(requireContext()) { position, direction ->
+                    val vaccinationItem = vaccinationListAdapter.data[position]
+                    if (vaccinationItem.isSwipeable()) {
+                        vaccinationItem.onSwipe(position, direction)
+                    }
+                }
+            }
 
             viewModel.uiState.observe(viewLifecycleOwner) { uiState ->
                 bindViews(uiState)
@@ -65,7 +80,7 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
                         VaccinationListFragmentDirections
                             .actionVaccinationListFragmentToVaccinationDetailsFragment(event.vaccinationCertificateId)
                     )
-                    is VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen -> doNavigate(
+                    is NavigateToVaccinationQrCodeScanScreen -> doNavigate(
                         VaccinationListFragmentDirections.actionVaccinationListFragmentToVaccinationQrCodeScanFragment()
                     )
                     is VaccinationListViewModel.Event.NavigateToQrCodeFullScreen -> {
@@ -82,9 +97,16 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
                             navigatorExtras
                         )
                     }
+                    is DeleteVaccinationEvent ->
+                        showDeleteVaccinationDialog(event.vaccinationCertificateId, event.position)
+                    is NavigateBack -> popBackStack()
                 }
             }
 
+            viewModel.errors.observe(viewLifecycleOwner) { error ->
+                error.toErrorDialogBuilder(requireContext()).show()
+            }
+
             registerNewVaccinationButton.setOnClickListener {
                 viewModel.onRegisterNewVaccinationClick()
             }
@@ -97,7 +119,7 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
 
         setToolbarOverlay()
 
-        adapter.update(listItems)
+        vaccinationListAdapter.update(listItems)
 
         val background = if (hasImmunity) {
             R.drawable.vaccination_compelete_gradient
@@ -131,6 +153,22 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au
         behavior.overlayTop = (deviceWidth / divider) - 24
     }
 
+    private fun showDeleteVaccinationDialog(vaccinationCertificateId: String, position: Int?) {
+        MaterialAlertDialogBuilder(requireContext()).apply {
+            setTitle(R.string.vaccination_list_deletion_dialog_title)
+            setMessage(R.string.vaccination_list_deletion_dialog_message)
+            setPositiveButton(R.string.vaccination_list_deletion_dialog_positive_button) { _, _ ->
+                viewModel.deleteVaccination(vaccinationCertificateId)
+            }
+            setNegativeButton(R.string.vaccination_list_deletion_dialog_negative_button) { _, _ ->
+                position?.let { vaccinationListAdapter.notifyItemChanged(it) }
+            }
+            setOnCancelListener {
+                position?.let { vaccinationListAdapter.notifyItemChanged(it) }
+            }
+        }.show()
+    }
+
     companion object {
         fun navigationUri(personIdentifierCodeSha256: String) =
             "coronawarnapp://vaccination-list/$personIdentifierCodeSha256".toUri()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt
index f1bbfe430..1a9e0d6ee 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt
@@ -10,6 +10,7 @@ import dagger.assisted.AssistedInject
 import de.rki.coronawarnapp.contactdiary.util.getLocale
 import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat
+import de.rki.coronawarnapp.util.coroutine.AppScope
 import de.rki.coronawarnapp.util.di.AppContext
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
@@ -22,16 +23,19 @@ import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationLi
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem
+import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.transform
+import timber.log.Timber
 
 class VaccinationListViewModel @AssistedInject constructor(
-    vaccinationRepository: VaccinationRepository,
+    private val vaccinationRepository: VaccinationRepository,
     valueSetsRepository: ValueSetsRepository,
     @AppContext context: Context,
+    @AppScope private val appScope: CoroutineScope,
     private val qrCodeGenerator: QrCodeGenerator,
     @Assisted private val personIdentifierCodeSha256: String
 ) : CWAViewModel() {
@@ -41,6 +45,7 @@ class VaccinationListViewModel @AssistedInject constructor(
     }
 
     val events = SingleLiveEvent<Event>()
+    val errors = SingleLiveEvent<Throwable>()
 
     private val vaccinatedPersonFlow = vaccinationRepository.vaccinationInfos.map { vaccinatedPersonSet ->
         vaccinatedPersonSet.single { it.identifier.codeSHA256 == personIdentifierCodeSha256 }
@@ -59,8 +64,17 @@ class VaccinationListViewModel @AssistedInject constructor(
             listItems = assembleItemList(vaccinatedPerson = vaccinatedPerson, qrCode),
             vaccinationStatus = vaccinatedPerson.getVaccinationStatus()
         )
-    }.catch {
-        // TODO Error Handling in an upcoming subtask
+    }.catch { exception ->
+        when (exception) {
+            is NoSuchElementException -> {
+                Timber.d(exception, "Seems like all vaccination certificates got deleted. Navigate back ...")
+                events.postValue(Event.NavigateBack)
+            }
+            else -> {
+                Timber.e(exception, "Something unexpected went wrong... Let's navigate back...")
+                events.postValue(Event.NavigateBack)
+            }
+        }
     }.asLiveData()
 
     private fun assembleItemList(vaccinatedPerson: VaccinatedPerson, qrCode: Bitmap?) =
@@ -102,7 +116,7 @@ class VaccinationListViewModel @AssistedInject constructor(
                 }
             }
 
-            vaccinatedPerson.vaccinationCertificates.forEach { vaccinationCertificate ->
+            vaccinatedPerson.vaccinationCertificates.sortedBy { it.vaccinatedAt }.forEach { vaccinationCertificate ->
                 with(vaccinationCertificate) {
                     add(
                         VaccinationListVaccinationCardItem(
@@ -114,6 +128,12 @@ class VaccinationListViewModel @AssistedInject constructor(
                             isFinalVaccination = doseNumber == totalSeriesOfDoses,
                             onCardClick = { certificateId ->
                                 events.postValue(Event.NavigateToVaccinationCertificateDetails(certificateId))
+                            },
+                            onDeleteClick = { certificateId ->
+                                events.postValue(Event.DeleteVaccinationEvent(certificateId))
+                            },
+                            onSwipeToDelete = { certificateId, position ->
+                                events.postValue(Event.DeleteVaccinationEvent(certificateId, position))
                             }
                         )
                     )
@@ -125,6 +145,17 @@ class VaccinationListViewModel @AssistedInject constructor(
         events.postValue(Event.NavigateToVaccinationQrCodeScanScreen)
     }
 
+    fun deleteVaccination(vaccinationCertificateId: String) {
+        launch(scope = appScope) {
+            try {
+                vaccinationRepository.deleteVaccinationCertificate(vaccinationCertificateId)
+            } catch (exception: Exception) {
+                errors.postValue(exception)
+                Timber.e(exception, "Something went wrong when trying to delete a vaccination certificate.")
+            }
+        }
+    }
+
     data class UiState(
         val listItems: List<VaccinationListItem>,
         val vaccinationStatus: VaccinatedPerson.Status
@@ -134,6 +165,8 @@ class VaccinationListViewModel @AssistedInject constructor(
         data class NavigateToVaccinationCertificateDetails(val vaccinationCertificateId: String) : Event()
         object NavigateToVaccinationQrCodeScanScreen : Event()
         data class NavigateToQrCodeFullScreen(val qrCode: String, val positionInList: Int) : Event()
+        data class DeleteVaccinationEvent(val vaccinationCertificateId: String, val position: Int? = null) : Event()
+        object NavigateBack : Event()
     }
 
     @AssistedFactory
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt
index f6824b79b..d347b79da 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder
 import android.view.ViewGroup
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.VaccinationListImmunityCardBinding
+import de.rki.coronawarnapp.util.list.Movable
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListAdapter
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem
@@ -12,7 +13,8 @@ class VaccinationListImmunityInformationCardItemVH(parent: ViewGroup) :
     VaccinationListAdapter.ItemVH<VaccinationListImmunityInformationCardItem, VaccinationListImmunityCardBinding>(
         layoutRes = R.layout.vaccination_list_immunity_card,
         parent = parent
-    ) {
+    ),
+    Movable {
 
     override val viewBinding: Lazy<VaccinationListImmunityCardBinding> = lazy {
         VaccinationListImmunityCardBinding.bind(itemView)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt
index 5cd1501ca..392007562 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder
 import android.view.ViewGroup
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.VaccinationListNameCardBinding
+import de.rki.coronawarnapp.util.list.Movable
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListAdapter
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem
@@ -11,7 +12,8 @@ class VaccinationListNameCardItemVH(parent: ViewGroup) :
     VaccinationListAdapter.ItemVH<VaccinationListNameCardItem, VaccinationListNameCardBinding>(
         layoutRes = R.layout.vaccination_list_name_card,
         parent = parent
-    ) {
+    ),
+    Movable {
 
     override val viewBinding: Lazy<VaccinationListNameCardBinding> = lazy {
         VaccinationListNameCardBinding.bind(itemView)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt
index 9510850ad..cf5b3b331 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt
@@ -6,6 +6,7 @@ import androidx.core.view.isVisible
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.VaccinationListQrcodeCardBinding
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortDayFormat
+import de.rki.coronawarnapp.util.list.Movable
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListAdapter
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem
 import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem
@@ -16,7 +17,8 @@ class VaccinationListQrCodeCardItemVH(parent: ViewGroup) :
     VaccinationListAdapter.ItemVH<VaccinationListQrCodeCardItem, VaccinationListQrcodeCardBinding>(
         layoutRes = R.layout.vaccination_list_qrcode_card,
         parent = parent
-    ) {
+    ),
+    Movable {
 
     override val viewBinding: Lazy<VaccinationListQrcodeCardBinding> = lazy {
         VaccinationListQrcodeCardBinding.bind(itemView)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt
index 80b9dc19f..f38f54c83 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt
@@ -1,8 +1,11 @@
 package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder
 
+import android.view.Gravity
 import android.view.ViewGroup
+import androidx.appcompat.widget.PopupMenu
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.VaccinationListVaccinationCardBinding
+import de.rki.coronawarnapp.util.list.SwipeConsumer
 import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson
 import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.COMPLETE
 import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.IMMUNITY
@@ -54,6 +57,18 @@ class VaccinationListVaccinationCardItemVH(
                 }
             }
             vaccinationIcon.setImageResource(iconRes)
+
+            val menu = PopupMenu(context, overflowMenu, Gravity.TOP or Gravity.END).apply {
+                inflate(R.menu.menu_vaccination_item)
+                setOnMenuItemClickListener {
+                    when (it.itemId) {
+                        R.id.menu_delete -> item.onDeleteClick(item.vaccinationCertificateId).let { true }
+                        else -> false
+                    }
+                }
+            }
+
+            overflowMenu.setOnClickListener { menu.show() }
         }
     }
 
@@ -64,8 +79,10 @@ class VaccinationListVaccinationCardItemVH(
         val vaccinatedAt: String,
         val vaccinationStatus: VaccinatedPerson.Status,
         val isFinalVaccination: Boolean,
-        val onCardClick: (String) -> Unit
-    ) : VaccinationListItem {
+        val onCardClick: (String) -> Unit,
+        val onDeleteClick: (String) -> Unit,
+        val onSwipeToDelete: (String, Int) -> Unit
+    ) : VaccinationListItem, SwipeConsumer {
 
         override val stableId: Long = Objects.hash(
             vaccinationCertificateId,
@@ -76,6 +93,8 @@ class VaccinationListVaccinationCardItemVH(
             isFinalVaccination
         ).toLong()
 
+        override fun onSwipe(position: Int, direction: Int) = onSwipeToDelete(vaccinationCertificateId, position)
+
         // Ignore onCardClick Listener in equals() to avoid re-drawing when only the click listener is updated
         override fun equals(other: Any?): Boolean {
             if (this === other) return true
diff --git a/Corona-Warn-App/src/main/res/drawable-night/ic_arrow_right_grey.xml b/Corona-Warn-App/src/main/res/drawable-night/ic_arrow_right_grey.xml
deleted file mode 100644
index 40001f61b..000000000
--- a/Corona-Warn-App/src/main/res/drawable-night/ic_arrow_right_grey.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="19dp"
-    android:height="16dp"
-    android:viewportWidth="19"
-    android:viewportHeight="16">
-  <path
-      android:pathData="M11.9186,0.7474L10.4286,2.3179L14.9685,7.103H0.9918V9.197H14.9685L10.4286,13.9821L11.9186,15.5525L18.9419,8.15L11.9186,0.7474Z"
-      android:fillColor="#A7A7A7"
-      android:fillType="evenOdd"/>
-</vector>
diff --git a/Corona-Warn-App/src/main/res/drawable/ic_arrow_right_grey.xml b/Corona-Warn-App/src/main/res/drawable/ic_arrow_right_grey.xml
deleted file mode 100644
index fea245299..000000000
--- a/Corona-Warn-App/src/main/res/drawable/ic_arrow_right_grey.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="19dp"
-    android:height="16dp"
-    android:viewportWidth="19"
-    android:viewportHeight="16">
-  <path
-      android:pathData="M11.9186,0.7475L10.4286,2.318L14.9685,7.1031H0.9918V9.197H14.9685L10.4286,13.9821L11.9186,15.5526L18.9419,8.15L11.9186,0.7475Z"
-      android:fillColor="#17191A"
-      android:fillAlpha="0.6"
-      android:fillType="evenOdd"/>
-</vector>
diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_list_vaccination_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_list_vaccination_card.xml
index 2078b26bd..20d3f5f2d 100644
--- a/Corona-Warn-App/src/main/res/layout/vaccination_list_vaccination_card.xml
+++ b/Corona-Warn-App/src/main/res/layout/vaccination_list_vaccination_card.xml
@@ -2,17 +2,18 @@
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/vaccination_card"
-    style="@style/Card.Vaccination.Ripple"
+    style="@style/Card.Vaccination.Ripple.NoPadding"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginHorizontal="24dp"
-    android:layout_marginTop="8dp"
-    android:padding="16dp">
+    android:layout_marginTop="8dp">
 
     <ImageView
         android:id="@+id/vaccination_icon"
         android:layout_width="88dp"
         android:layout_height="95dp"
+        android:layout_marginStart="16dp"
+        android:layout_marginTop="16dp"
         android:importantForAccessibility="no"
         app:srcCompat="@drawable/ic_vaccination_incomplete"
         app:layout_constraintStart_toStartOf="parent"
@@ -27,9 +28,9 @@
         android:text="@string/vaccination_list_vaccination_card_title"
         android:textSize="18sp"
         android:textStyle="bold"
-        app:layout_constraintEnd_toStartOf="@id/arrow_right"
+        app:layout_constraintEnd_toStartOf="@id/overflow_menu"
         app:layout_constraintStart_toEndOf="@id/vaccination_icon"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintTop_toTopOf="@id/vaccination_icon" />
 
     <TextView
         android:id="@+id/vaccination_card_subtitle"
@@ -39,17 +40,27 @@
         android:layout_marginStart="16dp"
         android:layout_marginTop="4dp"
         android:text="@string/vaccination_list_vaccination_card_subtitle"
-        app:layout_constraintEnd_toStartOf="@id/arrow_right"
+        app:layout_constraintEnd_toStartOf="@id/overflow_menu"
         app:layout_constraintStart_toEndOf="@id/vaccination_icon"
         app:layout_constraintTop_toBottomOf="@id/vaccination_card_title" />
 
-    <ImageView
-        android:id="@+id/arrow_right"
+    <ImageButton
+        android:id="@+id/overflow_menu"
+        style="@style/CardOverFlowButton"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/bottom_barrier"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:importantForAccessibility="no"
-        app:srcCompat="@drawable/ic_arrow_right_grey"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:barrierDirection="bottom"
+        app:constraint_referenced_ids="vaccination_icon,vaccination_card_subtitle" />
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="16dp"
+        app:layout_constraintTop_toBottomOf="@id/bottom_barrier"
+        app:layout_constraintStart_toStartOf="parent"/>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/menu/menu_vaccination_item.xml b/Corona-Warn-App/src/main/res/menu/menu_vaccination_item.xml
new file mode 100644
index 000000000..7e750fd6e
--- /dev/null
+++ b/Corona-Warn-App/src/main/res/menu/menu_vaccination_item.xml
@@ -0,0 +1,5 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/menu_delete"
+        android:title="@string/vaccination_list_delete_button" />
+</menu>
\ No newline at end of file
diff --git a/Corona-Warn-App/src/main/res/values-de/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-de/vaccination_strings.xml
index 9529309ba..31cbf05d3 100644
--- a/Corona-Warn-App/src/main/res/values-de/vaccination_strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/vaccination_strings.xml
@@ -16,8 +16,6 @@
     <string name="vaccination_details_certificate_country">Land</string>
     <!-- XTXT: Vaccination Details certificate id -->
     <string name="vaccination_details_certificate_id">Zertifikationsnummer</string>
-    <!-- XBUT: Vaccination Details delete button -->
-    <string name="vaccination_details_delete_button">Impfzertifikat entfernen</string>
     <!-- XTXT: Vaccination Details subtitle-->
     <string name="vaccination_details_subtitle">Impfzertifikat</string>
     <!-- XTXT: Vaccination Details title-->
@@ -26,14 +24,6 @@
     <string name="vaccination_qrcode_card_title">Impfzertifikat %1$d von %2$d</string>
     <!-- XTXT: Vaccination Qr Code card subtitle-->
     <string name="vaccination_qrcode_card_subtitle">Geimpft %1$s - gültig bis %2$s</string>
-    <!-- XTXT: Vaccination Details deletion dialog title-->
-    <string name="vaccination_details_deletion_dialog_title">Wollen Sie das Impfzertifikat wirklich entfernen?</string>
-    <!-- XTXT: Vaccination Details deletion dialog message-->
-    <string name="vaccination_details_deletion_dialog_message">Wenn Sie das Impfzertifikat entfernen, kann die App die Impfung nicht mehr für die Prüfung Ihres Impfstatus berücksichtigen.</string>
-    <!-- XBUT: Vaccination Details deletion dialog positive button-->
-    <string name="vaccination_details_deletion_dialog_positive_button">Entfernen</string>
-    <!-- XBUT: Vaccination Details deletion dialog negative button-->
-    <string name="vaccination_details_deletion_dialog_negative_button">Abbrechen</string>
 
     <!-- XTXT: Vaccination List title-->
     <string name="vaccination_list_title">Digitaler Impfnachweis</string>
@@ -54,8 +44,16 @@
     </plurals>
     <!-- XBUT: Vaccination List register additional vaccination button -->
     <string name="vaccination_list_register_new_vaccination_button">Weitere Impfung registrieren</string>
-    <!-- XBUT: Vaccination List refresh button -->
-    <string name="vaccination_list_refresh_button">Aktualisieren</string>
+    <!-- XBUT: Vaccination List delete button -->
+    <string name="vaccination_list_delete_button">Entfernen</string>
+    <!-- XTXT: Vaccination List deletion dialog title-->
+    <string name="vaccination_list_deletion_dialog_title">Wollen Sie das Impfzertifikat wirklich entfernen?</string>
+    <!-- XTXT: Vaccination List deletion dialog message-->
+    <string name="vaccination_list_deletion_dialog_message">Wenn Sie das Impfzertifikat entfernen, kann die App die Impfung nicht mehr für die Prüfung Ihres Impfstatus berücksichtigen.</string>
+    <!-- XBUT: Vaccination List deletion dialog positive button-->
+    <string name="vaccination_list_deletion_dialog_positive_button">Entfernen</string>
+    <!-- XBUT: Vaccination List deletion dialog negative button-->
+    <string name="vaccination_list_deletion_dialog_negative_button">Abbrechen</string>
     <!-- XACT: Vaccination List Qr-Code for screen readers -->
     <string name="vaccination_list_qr_code_accessibility">Qr-Code</string>
 
diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml
index e24c21b57..9057e165e 100644
--- a/Corona-Warn-App/src/main/res/values/styles.xml
+++ b/Corona-Warn-App/src/main/res/values/styles.xml
@@ -243,6 +243,10 @@
         <item name="android:background">@drawable/grey_card_ripple</item>
     </style>
 
+    <style name="Card.Vaccination.Ripple.NoPadding">
+        <item name="android:padding">@dimen/no_padding</item>
+    </style>
+
     <style name="Card.NoPadding">
         <item name="android:padding">@dimen/no_padding</item>
     </style>
diff --git a/Corona-Warn-App/src/main/res/values/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values/vaccination_strings.xml
index 62730ced6..80ce5d67e 100644
--- a/Corona-Warn-App/src/main/res/values/vaccination_strings.xml
+++ b/Corona-Warn-App/src/main/res/values/vaccination_strings.xml
@@ -16,8 +16,6 @@
     <string name="vaccination_details_certificate_country">Land</string>
     <!-- XTXT: Vaccination Details certificate id -->
     <string name="vaccination_details_certificate_id">Zertifikationsnummer</string>
-    <!-- XBUT: Vaccination Details delete button -->
-    <string name="vaccination_details_delete_button">Impfzertifikat entfernen</string>
     <!-- XTXT: Vaccination Details subtitle-->
     <string name="vaccination_details_subtitle">Impfzertifikat</string>
     <!-- XTXT: Vaccination Details title-->
@@ -26,14 +24,6 @@
     <string name="vaccination_qrcode_card_title">Impfzertifikat %1$d von %2$d</string>
     <!-- XTXT: Vaccination Qr Code card subtitle-->
     <string name="vaccination_qrcode_card_subtitle">Geimpft %1$s - gültig bis %2$s</string>
-    <!-- XTXT: Vaccination Details deletion dialog title-->
-    <string name="vaccination_details_deletion_dialog_title">Wollen Sie das Impfzertifikat wirklich entfernen?</string>
-    <!-- XTXT: Vaccination Details deletion dialog message-->
-    <string name="vaccination_details_deletion_dialog_message">Wenn Sie das Impfzertifikat entfernen, kann die App die Impfung nicht mehr für die Prüfung Ihres Impfstatus berücksichtigen.</string>
-    <!-- XBUT: Vaccination Details deletion dialog positive button-->
-    <string name="vaccination_details_deletion_dialog_positive_button">Entfernen</string>
-    <!-- XBUT: Vaccination Details deletion dialog negative button-->
-    <string name="vaccination_details_deletion_dialog_negative_button">Abbrechen</string>
 
     <!-- XTXT: Vaccination List title-->
     <string name="vaccination_list_title">Digitaler Impfnachweis</string>
@@ -60,8 +50,16 @@
     </plurals>
     <!-- XBUT: Vaccination List register additional vaccination button -->
     <string name="vaccination_list_register_new_vaccination_button">Weitere Impfung registrieren</string>
-    <!-- XBUT: Vaccination List refresh button -->
-    <string name="vaccination_list_refresh_button">Aktualisieren</string>
+    <!-- XBUT: Vaccination List delete button -->
+    <string name="vaccination_list_delete_button">Entfernen</string>
+    <!-- XTXT: Vaccination List deletion dialog title-->
+    <string name="vaccination_list_deletion_dialog_title">Wollen Sie das Impfzertifikat wirklich entfernen?</string>
+    <!-- XTXT: Vaccination List deletion dialog message-->
+    <string name="vaccination_list_deletion_dialog_message">Wenn Sie das Impfzertifikat entfernen, kann die App die Impfung nicht mehr für die Prüfung Ihres Impfstatus berücksichtigen.</string>
+    <!-- XBUT: Vaccination List deletion dialog positive button-->
+    <string name="vaccination_list_deletion_dialog_positive_button">Entfernen</string>
+    <!-- XBUT: Vaccination List deletion dialog negative button-->
+    <string name="vaccination_list_deletion_dialog_negative_button">Abbrechen</string>
     <!-- XACT: Vaccination List Qr-Code for screen readers -->
     <string name="vaccination_list_qr_code_accessibility">Qr-Code</string>
 
-- 
GitLab