From a2bf066cef62e45e5f36ee2697a16d0ebb97cfd0 Mon Sep 17 00:00:00 2001 From: Lukas Lechner <lukas.lechner@sap.com> Date: Tue, 18 May 2021 15:00:08 +0200 Subject: [PATCH] Adjust vaccination list screen (EXPOSUREAPP-7238) (#3185) * Adjust QrCode Card * Adjust incomplete state in vaccination list screen * Add ImmunityInformation card for state COMPLETE * Fix detekt issue * Lint around * Use actual qr code string to generate the QR code * Fix merge conflicts * Update top spacing * Rename function * Refactor property types of VaccinationListVaccinationCardItem * Fix Lint issue * Open QR-Code in Full Screen * Use hashed identifier to open VaccinationList from the HomeScreen Co-authored-by: Mohamed Metwalli <mohamed.metwalli@sap.com> Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> --- .../util/TimeAndDateExtensions.kt | 11 ++ .../vaccination/core/VaccinatedPerson.kt | 5 + .../ui/details/VaccinationDetailsFragment.kt | 4 +- .../ui/list/VaccinationListFragment.kt | 44 +++++--- .../ui/list/VaccinationListViewModel.kt | 104 ++++++++++++------ .../ui/list/adapter/VaccinationListAdapter.kt | 18 +-- .../VaccinationListCertificateCardItemVH.kt | 46 -------- ...nationListImmunityInformationCardItemVH.kt | 35 ++++++ .../VaccinationListIncompleteTopCardItemVH.kt | 27 ----- .../VaccinationListQrCodeCardItemVH.kt | 62 +++++++++++ .../VaccinationListVaccinationCardItemVH.kt | 18 +-- .../ic_vaccination_incomplete_final.xml | 15 --- .../layout/fragment_vaccination_details.xml | 2 +- .../res/layout/fragment_vaccination_list.xml | 24 +--- .../layout/vaccination_list_immunity_card.xml | 17 +++ .../vaccination_list_incomplete_top_card.xml | 30 ----- ...d.xml => vaccination_list_qrcode_card.xml} | 0 .../res/values-de/vaccination_strings.xml | 25 ++--- .../main/res/values/vaccination_strings.xml | 19 ++-- 19 files changed, 276 insertions(+), 230 deletions(-) delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListCertificateCardItemVH.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt delete mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListIncompleteTopCardItemVH.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt delete mode 100644 Corona-Warn-App/src/main/res/drawable/ic_vaccination_incomplete_final.xml create mode 100644 Corona-Warn-App/src/main/res/layout/vaccination_list_immunity_card.xml delete mode 100644 Corona-Warn-App/src/main/res/layout/vaccination_list_incomplete_top_card.xml rename Corona-Warn-App/src/main/res/layout/{vaccination_list_certificate_card.xml => vaccination_list_qrcode_card.xml} (100%) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt index 7392f5fe7..52a1bd6dd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt @@ -24,6 +24,7 @@ object TimeAndDateExtensions { private const val MS_TO_SECONDS = 1000 private val dayFormatter = DateTimeFormat.forPattern("dd.MM.yyyy") + private val dayFormatter2DigitYear = DateTimeFormat.forPattern("dd.MM.yy") fun getCurrentHourUTC(): Int = DateTime(Instant.now(), DateTimeZone.UTC).hourOfDay().get() @@ -117,6 +118,16 @@ object TimeAndDateExtensions { * Returns a readable date String with the format "dd.MM.yyyy" like 23.05.1989 of a LocalDate */ fun LocalDate.toDayFormat() = toString(dayFormatter) + + /** + * Returns a readable date String with the format "dd.MM.yy" like 23.05.89 of an Instant + */ + fun Instant.toShortDayFormat() = toString(dayFormatter2DigitYear) + + /** + * Returns a readable date String with the format "dd.MM.yy" like 23.05.89 of an LocalDate + */ + fun LocalDate.toShortDayFormat() = toString(dayFormatter2DigitYear) } typealias HourInterval = Long diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPerson.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPerson.kt index 51a1a760d..9b0a15fd8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPerson.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPerson.kt @@ -37,6 +37,11 @@ data class VaccinatedPerson( val dateOfBirth: LocalDate get() = vaccinationCertificates.first().dateOfBirth + val getMostRecentVaccinationCertificate: VaccinationCertificate + get() = vaccinationCertificates.maxByOrNull { it.vaccinatedAt } ?: throw IllegalStateException( + "Every Vaccinated Person needs to have at least one vaccinationCertificate" + ) + fun getVaccinationStatus(nowUTC: Instant = Instant.now()): Status { val newestFullDose = vaccinationCertificates .filter { it.doseNumber == it.totalSeriesOfDoses } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragment.kt index 00741c2e2..dfbb9884f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragment.kt @@ -101,12 +101,12 @@ class VaccinationDetailsFragment : Fragment(R.layout.fragment_vaccination_detail ) // QrCode details qrCodeCard.title.text = getString( - R.string.vaccination_qr_code_card_title, + R.string.vaccination_qrcode_card_title, certificate.doseNumber, certificate.totalSeriesOfDoses ) qrCodeCard.subtitle.text = getString( - R.string.vaccination_qr_code_card_subtitle, + R.string.vaccination_qrcode_card_subtitle, certificate.vaccinatedAt.toString(format), certificate.expiresAt.toString(format) ) 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 5e6de31fa..58d6e8635 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 @@ -3,15 +3,17 @@ package de.rki.coronawarnapp.vaccination.ui.list import android.os.Bundle import android.view.View import android.widget.LinearLayout -import android.widget.Toast import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.net.toUri -import androidx.core.view.isVisible import androidx.fragment.app.Fragment +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.imageview.ShapeableImageView import de.rki.coronawarnapp.R 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.lists.diffutil.update @@ -66,27 +68,38 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au is VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen -> doNavigate( VaccinationListFragmentDirections.actionVaccinationListFragmentToVaccinationQrCodeScanFragment() ) + is VaccinationListViewModel.Event.NavigateToQrCodeFullScreen -> { + val navigatorExtras = + binding.recyclerViewVaccinationList.layoutManager?.findViewByPosition(event.positionInList) + ?.run { + val image = findViewById<ShapeableImageView>(R.id.image) + FragmentNavigatorExtras(image to image.transitionName) + } + findNavController().navigate( + R.id.action_global_qrCodeFullScreenFragment, + QrCodeFullScreenFragmentArgs(event.qrCode).toBundle(), + null, + navigatorExtras + ) + } } } registerNewVaccinationButton.setOnClickListener { viewModel.onRegisterNewVaccinationClick() } - - refreshButton.setOnClickListener { - Toast.makeText(requireContext(), "TODO \uD83D\uDEA7", Toast.LENGTH_LONG).show() - } } } private fun FragmentVaccinationListBinding.bindViews(uiState: VaccinationListViewModel.UiState) = with(uiState) { - val isVaccinationComplete = vaccinationStatus == VaccinatedPerson.Status.COMPLETE - setToolbarOverlay(isVaccinationComplete) + val hasImmunity = uiState.vaccinationStatus == VaccinatedPerson.Status.IMMUNITY + + setToolbarOverlay() adapter.update(listItems) - val background = if (isVaccinationComplete) { + val background = if (hasImmunity) { R.drawable.vaccination_compelete_gradient } else { R.drawable.vaccination_incomplete @@ -94,29 +107,24 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au expandedImage.setImageResource(background) - subtitle.isVisible = isVaccinationComplete - appBarLayout.onOffsetChange { titleAlpha, subtitleAlpha -> title.alpha = titleAlpha subtitle.alpha = subtitleAlpha } } - private fun setToolbarOverlay(isVaccinationComplete: Boolean) { - - // subtitle is only visible when vaccination is complete - val bottomTextView = if (isVaccinationComplete) binding.subtitle else binding.title + private fun setToolbarOverlay() { val deviceWidth = requireContext().resources.displayMetrics.widthPixels val layoutParamsRecyclerView: CoordinatorLayout.LayoutParams = binding.recyclerViewVaccinationList.layoutParams as (CoordinatorLayout.LayoutParams) - val textParams = bottomTextView.layoutParams as (LinearLayout.LayoutParams) + val textParams = binding.subtitle.layoutParams as (LinearLayout.LayoutParams) - val divider = if (isVaccinationComplete) 2 else 3 + val divider = 2 textParams.bottomMargin = (deviceWidth / divider) - 24 /* 24 is space between screen border and Card */ - bottomTextView.requestLayout() + binding.subtitle.requestLayout() val behavior: AppBarLayout.ScrollingViewBehavior = layoutParamsRecyclerView.behavior as (AppBarLayout.ScrollingViewBehavior) 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 a8dbcba0f..f1bbfe430 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 @@ -1,32 +1,38 @@ package de.rki.coronawarnapp.vaccination.ui.list import android.content.Context +import android.graphics.Bitmap import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import dagger.assisted.Assisted import dagger.assisted.AssistedFactory 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.di.AppContext import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.COMPLETE import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.vaccination.core.repository.ValueSetsRepository import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListIncompleteTopCardItemVH.VaccinationListIncompleteTopCardItem +import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem 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.flow.Flow import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.transform class VaccinationListViewModel @AssistedInject constructor( vaccinationRepository: VaccinationRepository, valueSetsRepository: ValueSetsRepository, @AppContext context: Context, + private val qrCodeGenerator: QrCodeGenerator, @Assisted private val personIdentifierCodeSha256: String ) : CWAViewModel() { @@ -40,45 +46,80 @@ class VaccinationListViewModel @AssistedInject constructor( vaccinatedPersonSet.single { it.identifier.codeSHA256 == personIdentifierCodeSha256 } } - val uiState: LiveData<UiState> = vaccinatedPersonFlow.map { vaccinatedPerson -> + private val vaccinationQrCodeFlow: Flow<Bitmap?> = vaccinatedPersonFlow.transform { + // emit null initially, so that the UI can show the list with a loading indicator for the qrcode + // immediately ... + emit(null) + // ... and after the QR code was generated, it is emitted + emit(qrCodeGenerator.createQrCode(it.getMostRecentVaccinationCertificate.vaccinationQrCodeString)) + } + + val uiState: LiveData<UiState> = combine(vaccinatedPersonFlow, vaccinationQrCodeFlow) { vaccinatedPerson, qrCode -> UiState( - listItems = assembleItemList(vaccinatedPerson = vaccinatedPerson), + listItems = assembleItemList(vaccinatedPerson = vaccinatedPerson, qrCode), vaccinationStatus = vaccinatedPerson.getVaccinationStatus() ) }.catch { // TODO Error Handling in an upcoming subtask }.asLiveData() - private fun assembleItemList(vaccinatedPerson: VaccinatedPerson) = mutableListOf<VaccinationListItem>().apply { - if (vaccinatedPerson.getVaccinationStatus() == COMPLETE) { - // Tbd what to show on complete vaccination - the proof certificate is now obsolete - } else { - add(VaccinationListIncompleteTopCardItem) - } - add( - VaccinationListNameCardItem( - fullName = "${vaccinatedPerson.firstName} ${vaccinatedPerson.lastName}", - dayOfBirth = vaccinatedPerson.dateOfBirth.toDayFormat() + private fun assembleItemList(vaccinatedPerson: VaccinatedPerson, qrCode: Bitmap?) = + mutableListOf<VaccinationListItem>().apply { + + val vaccinationCertificate = vaccinatedPerson.getMostRecentVaccinationCertificate + + add( + VaccinationListQrCodeCardItem( + qrCode = qrCode, + doseNumber = vaccinationCertificate.doseNumber, + totalSeriesOfDoses = vaccinationCertificate.totalSeriesOfDoses, + vaccinatedAt = vaccinatedPerson.getMostRecentVaccinationCertificate.vaccinatedAt, + expiresAt = vaccinatedPerson.getMostRecentVaccinationCertificate.expiresAt, + onQrCodeClick = { + events.postValue( + Event.NavigateToQrCodeFullScreen( + qrCode = vaccinatedPerson.getMostRecentVaccinationCertificate.vaccinationQrCodeString, + positionInList = 0 + ) + ) + } + ) ) - ) - vaccinatedPerson.vaccinationCertificates.forEach { vaccinationCertificate -> - with(vaccinationCertificate) { - add( - VaccinationListVaccinationCardItem( - vaccinationCertificateId = certificateId, - doseNumber = doseNumber.toString(), - totalSeriesOfDoses = totalSeriesOfDoses.toString(), - vaccinatedAt = vaccinatedAt.toDayFormat(), - vaccinationStatus = vaccinatedPerson.getVaccinationStatus(), - isFinalVaccination = doseNumber == totalSeriesOfDoses, - onCardClick = { certificateId -> - events.postValue(Event.NavigateToVaccinationCertificateDetails(certificateId)) - } - ) + + add( + VaccinationListNameCardItem( + fullName = vaccinatedPerson.fullName, + dayOfBirth = vaccinatedPerson.dateOfBirth.toDayFormat() ) + ) + + if (vaccinatedPerson.getVaccinationStatus() == VaccinatedPerson.Status.COMPLETE) { + val timeUntilImmunity = vaccinatedPerson.getTimeUntilImmunity() + if (timeUntilImmunity != null) { + add( + VaccinationListImmunityInformationCardItem(timeUntilImmunity) + ) + } + } + + vaccinatedPerson.vaccinationCertificates.forEach { vaccinationCertificate -> + with(vaccinationCertificate) { + add( + VaccinationListVaccinationCardItem( + vaccinationCertificateId = certificateId, + doseNumber = doseNumber, + totalSeriesOfDoses = totalSeriesOfDoses, + vaccinatedAt = vaccinatedAt.toDayFormat(), + vaccinationStatus = vaccinatedPerson.getVaccinationStatus(), + isFinalVaccination = doseNumber == totalSeriesOfDoses, + onCardClick = { certificateId -> + events.postValue(Event.NavigateToVaccinationCertificateDetails(certificateId)) + } + ) + ) + } } - } - }.toList() + }.toList() fun onRegisterNewVaccinationClick() { events.postValue(Event.NavigateToVaccinationQrCodeScanScreen) @@ -92,6 +133,7 @@ class VaccinationListViewModel @AssistedInject constructor( sealed class Event { data class NavigateToVaccinationCertificateDetails(val vaccinationCertificateId: String) : Event() object NavigateToVaccinationQrCodeScanScreen : Event() + data class NavigateToQrCodeFullScreen(val qrCode: String, val positionInList: Int) : Event() } @AssistedFactory diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/VaccinationListAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/VaccinationListAdapter.kt index e152edbe9..0bca114ae 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/VaccinationListAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/VaccinationListAdapter.kt @@ -11,12 +11,12 @@ import de.rki.coronawarnapp.util.lists.modular.ModularAdapter import de.rki.coronawarnapp.util.lists.modular.mods.DataBinderMod import de.rki.coronawarnapp.util.lists.modular.mods.StableIdMod import de.rki.coronawarnapp.util.lists.modular.mods.TypedVHCreatorMod -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListCertificateCardItemVH -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListCertificateCardItemVH.VaccinationListCertificateCardItem -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListIncompleteTopCardItemVH -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListIncompleteTopCardItemVH.VaccinationListIncompleteTopCardItem +import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH +import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem +import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH +import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem @@ -31,17 +31,17 @@ class VaccinationListAdapter : listOf( StableIdMod(data), DataBinderMod<VaccinationListItem, ItemVH<VaccinationListItem, ViewBinding>>(data), - TypedVHCreatorMod({ data[it] is VaccinationListIncompleteTopCardItem }) { - VaccinationListIncompleteTopCardItemVH(it) - }, TypedVHCreatorMod({ data[it] is VaccinationListNameCardItem }) { VaccinationListNameCardItemVH(it) }, + TypedVHCreatorMod({ data[it] is VaccinationListImmunityInformationCardItem }) { + VaccinationListImmunityInformationCardItemVH(it) + }, TypedVHCreatorMod({ data[it] is VaccinationListVaccinationCardItem }) { VaccinationListVaccinationCardItemVH(it) }, - TypedVHCreatorMod({ data[it] is VaccinationListCertificateCardItem }) { - VaccinationListCertificateCardItemVH(it) + TypedVHCreatorMod({ data[it] is VaccinationListQrCodeCardItem }) { + VaccinationListQrCodeCardItemVH(it) } ) ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListCertificateCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListCertificateCardItemVH.kt deleted file mode 100644 index 1ccc74b28..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListCertificateCardItemVH.kt +++ /dev/null @@ -1,46 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder - -import android.graphics.Bitmap -import android.view.ViewGroup -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.VaccinationListCertificateCardBinding -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.VaccinationListCertificateCardItemVH.VaccinationListCertificateCardItem - -class VaccinationListCertificateCardItemVH(parent: ViewGroup) : - VaccinationListAdapter.ItemVH<VaccinationListCertificateCardItem, VaccinationListCertificateCardBinding>( - layoutRes = R.layout.vaccination_list_certificate_card, - parent = parent - ) { - - override val viewBinding: Lazy<VaccinationListCertificateCardBinding> = lazy { - VaccinationListCertificateCardBinding.bind(itemView) - } - - override val onBindData: VaccinationListCertificateCardBinding.( - item: VaccinationListCertificateCardItem, - payloads: List<Any> - ) -> Unit = - { item, _ -> - when (item.qrCode) { - null -> progressBar.show() - else -> { - image.setImageBitmap(item.qrCode) - progressBar.hide() - } - } - subtitle.text = context.getString( - R.string.vaccination_list_certificate_card_subtitle, - item.remainingValidityInDays - ) - } - - data class VaccinationListCertificateCardItem( - val qrCode: Bitmap?, - val remainingValidityInDays: Int - ) : - VaccinationListItem { - override val stableId = this.hashCode().toLong() - } -} 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 new file mode 100644 index 000000000..f6824b79b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt @@ -0,0 +1,35 @@ +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.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 +import org.joda.time.Duration + +class VaccinationListImmunityInformationCardItemVH(parent: ViewGroup) : + VaccinationListAdapter.ItemVH<VaccinationListImmunityInformationCardItem, VaccinationListImmunityCardBinding>( + layoutRes = R.layout.vaccination_list_immunity_card, + parent = parent + ) { + + override val viewBinding: Lazy<VaccinationListImmunityCardBinding> = lazy { + VaccinationListImmunityCardBinding.bind(itemView) + } + + override val onBindData: VaccinationListImmunityCardBinding + .(item: VaccinationListImmunityInformationCardItem, payloads: List<Any>) -> Unit = { item, _ -> + val daysUntilImmunity = item.timeUntilImmunity.standardDays.toInt() + body.text = + context.resources.getQuantityString( + R.plurals.vaccination_list_immunity_card_body, + daysUntilImmunity, + daysUntilImmunity + ) + } + + data class VaccinationListImmunityInformationCardItem(val timeUntilImmunity: Duration) : VaccinationListItem { + override val stableId = VaccinationListImmunityInformationCardItem::class.java.name.hashCode().toLong() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListIncompleteTopCardItemVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListIncompleteTopCardItemVH.kt deleted file mode 100644 index dd9ec7bba..000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListIncompleteTopCardItemVH.kt +++ /dev/null @@ -1,27 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder - -import android.view.ViewGroup -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.VaccinationListIncompleteTopCardBinding -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.VaccinationListIncompleteTopCardItemVH.VaccinationListIncompleteTopCardItem - -class VaccinationListIncompleteTopCardItemVH(parent: ViewGroup) : - VaccinationListAdapter.ItemVH<VaccinationListIncompleteTopCardItem, VaccinationListIncompleteTopCardBinding>( - layoutRes = R.layout.vaccination_list_incomplete_top_card, - parent = parent - ) { - - override val viewBinding: Lazy<VaccinationListIncompleteTopCardBinding> = lazy { - VaccinationListIncompleteTopCardBinding.bind(itemView) - } - - override val onBindData: VaccinationListIncompleteTopCardBinding - .(item: VaccinationListIncompleteTopCardItem, payloads: List<Any>) -> Unit = { _, _ -> // NOOP - } - - object VaccinationListIncompleteTopCardItem : VaccinationListItem { - override val stableId = this.hashCode().toLong() - } -} 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 new file mode 100644 index 000000000..9510850ad --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt @@ -0,0 +1,62 @@ +package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder + +import android.graphics.Bitmap +import android.view.ViewGroup +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.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 +import org.joda.time.Instant +import org.joda.time.LocalDate + +class VaccinationListQrCodeCardItemVH(parent: ViewGroup) : + VaccinationListAdapter.ItemVH<VaccinationListQrCodeCardItem, VaccinationListQrcodeCardBinding>( + layoutRes = R.layout.vaccination_list_qrcode_card, + parent = parent + ) { + + override val viewBinding: Lazy<VaccinationListQrcodeCardBinding> = lazy { + VaccinationListQrcodeCardBinding.bind(itemView) + } + + override val onBindData: VaccinationListQrcodeCardBinding + .(item: VaccinationListQrCodeCardItem, payloads: List<Any>) -> Unit = + { item, _ -> + when (item.qrCode) { + null -> progressBar.isVisible = true + else -> { + image.setImageBitmap(item.qrCode) + progressBar.isVisible = false + image.setOnClickListener { + item.onQrCodeClick.invoke() + } + } + } + title.text = context.getString( + R.string.vaccination_qrcode_card_title, + item.doseNumber, + item.totalSeriesOfDoses + ) + subtitle.text = + context.getString( + R.string.vaccination_qrcode_card_subtitle, + item.vaccinatedAt.toShortDayFormat(), + item.expiresAt.toShortDayFormat() + ) + } + + data class VaccinationListQrCodeCardItem( + val qrCode: Bitmap?, + val doseNumber: Int, + val totalSeriesOfDoses: Int, + val vaccinatedAt: LocalDate, + val expiresAt: Instant, + val onQrCodeClick: () -> Unit + ) : + VaccinationListItem { + override val stableId = this.hashCode().toLong() + } +} 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 e2f158b36..80b9dc19f 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 @@ -42,24 +42,16 @@ class VaccinationListVaccinationCardItemVH( ) val iconRes = when (vaccinationStatus) { - INCOMPLETE -> { - if (isFinalVaccination) { - R.drawable.ic_vaccination_incomplete_final - } else { - R.drawable.ic_vaccination_incomplete - } + INCOMPLETE, COMPLETE -> { + R.drawable.ic_vaccination_incomplete } - COMPLETE -> { + IMMUNITY -> { if (isFinalVaccination) { R.drawable.ic_vaccination_complete_final } else { R.drawable.ic_vaccination_complete } } - IMMUNITY -> { - // TODO - R.drawable.ic_vaccination_complete_final - } } vaccinationIcon.setImageResource(iconRes) } @@ -67,8 +59,8 @@ class VaccinationListVaccinationCardItemVH( data class VaccinationListVaccinationCardItem( val vaccinationCertificateId: String, - val doseNumber: String, - val totalSeriesOfDoses: String, + val doseNumber: Int, + val totalSeriesOfDoses: Int, val vaccinatedAt: String, val vaccinationStatus: VaccinatedPerson.Status, val isFinalVaccination: Boolean, diff --git a/Corona-Warn-App/src/main/res/drawable/ic_vaccination_incomplete_final.xml b/Corona-Warn-App/src/main/res/drawable/ic_vaccination_incomplete_final.xml deleted file mode 100644 index ea2e7488d..000000000 --- a/Corona-Warn-App/src/main/res/drawable/ic_vaccination_incomplete_final.xml +++ /dev/null @@ -1,15 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="88dp" - android:height="95dp" - android:viewportWidth="88" - android:viewportHeight="95"> - <path - android:pathData="M0,4C0,1.7909 1.7909,0 4,0H84C86.2091,0 88,1.7909 88,4V91C88,93.2091 86.2091,95 84,95H4C1.7909,95 0,93.2091 0,91V4Z" - android:fillColor="#616F7E"/> - <path - android:pathData="M44.5,21L23,30.8182V45.5455C23,59.1682 32.1733,71.9073 44.5,75C56.8267,71.9073 66,59.1682 66,45.5455V30.8182L44.5,21Z" - android:fillColor="#ffffff"/> - <path - android:pathData="M32.525,46.7909L29,50.2335L39,60L59,40.4671L55.475,37L39,53.0902L32.525,46.7909Z" - android:fillColor="#616F7E"/> -</vector> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_vaccination_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_vaccination_details.xml index 9768d421c..ac3c0c517 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_vaccination_details.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_vaccination_details.xml @@ -107,7 +107,7 @@ <include android:id="@+id/qr_code_card" - layout="@layout/vaccination_list_certificate_card" + layout="@layout/vaccination_list_qrcode_card" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="24dp" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_vaccination_list.xml b/Corona-Warn-App/src/main/res/layout/fragment_vaccination_list.xml index ec70295fc..072daf279 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_vaccination_list.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_vaccination_list.xml @@ -13,7 +13,7 @@ android:layout_height="0dp" android:layout_marginBottom="12dp" android:nestedScrollingEnabled="true" - app:layout_constraintBottom_toTopOf="@id/refresh_button" + app:layout_constraintBottom_toTopOf="@id/register_new_vaccination_button" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> @@ -55,7 +55,7 @@ android:text="@string/vaccination_list_title" android:textColor="@android:color/white" android:textSize="20sp" - android:textStyle="bold"/> + android:textStyle="bold" /> <TextView android:id="@+id/subtitle" @@ -67,7 +67,7 @@ android:text="@string/vaccination_list_complete_vaccination_subtitle" android:textColor="@android:color/white" android:textSize="18sp" - android:visibility="visible"/> + android:visibility="visible" /> </LinearLayout> @@ -104,26 +104,14 @@ android:id="@+id/recycler_view_vaccination_list" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="1" + android:clipToPadding="false" + android:paddingTop="24dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_behavior="@string/appbar_scrolling_view_behavior" - tools:listitem="@layout/vaccination_list_incomplete_top_card" /> + tools:listitem="@layout/vaccination_list_qrcode_card" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> - <Button - android:id="@+id/refresh_button" - style="@style/buttonPrimary" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/spacing_normal" - android:layout_marginEnd="@dimen/spacing_normal" - android:layout_marginBottom="8dp" - android:text="@string/vaccination_list_refresh_button" - app:layout_constraintBottom_toTopOf="@id/register_new_vaccination_button" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent"/> - <Button android:id="@+id/register_new_vaccination_button" style="@style/buttonPrimary" diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_list_immunity_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_list_immunity_card.xml new file mode 100644 index 000000000..44c0c8ece --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/vaccination_list_immunity_card.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Card.Vaccination" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="24dp" + android:layout_marginTop="8dp" + android:orientation="vertical" + android:padding="16dp"> + + <TextView + android:id="@+id/body" + style="@style/body2" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + +</FrameLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_list_incomplete_top_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_list_incomplete_top_card.xml deleted file mode 100644 index b5238b92d..000000000 --- a/Corona-Warn-App/src/main/res/layout/vaccination_list_incomplete_top_card.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - style="@style/Card.Vaccination" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="24dp" - android:layout_marginTop="16dp" - android:orientation="vertical" - android:paddingHorizontal="16dp" - android:paddingTop="24dp" - android:paddingBottom="12dp"> - - <TextView - android:id="@+id/top_card_title" - style="@style/headline4Bold" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/vaccination_list_top_card_title" - android:textSize="30sp"/> - - <TextView - android:id="@+id/top_card_subtitle" - style="@style/body2Medium" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:text="@string/vaccination_list_top_card_subtitle" - android:textSize="18sp" /> - -</LinearLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_list_certificate_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_list_qrcode_card.xml similarity index 100% rename from Corona-Warn-App/src/main/res/layout/vaccination_list_certificate_card.xml rename to Corona-Warn-App/src/main/res/layout/vaccination_list_qrcode_card.xml 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 215e69d41..9529309ba 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 @@ -23,9 +23,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">Impfung %1$d von %2$d</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qr_code_card_title">Impfzertifikat %1$d von %2$d</string> + <string name="vaccination_qrcode_card_title">Impfzertifikat %1$d von %2$d</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qr_code_card_subtitle">Geimpft %1$s - gültig bis %2$s</string> + <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--> @@ -37,22 +37,21 @@ <!-- XTXT: Vaccination List title--> <string name="vaccination_list_title">Digitaler Impfnachweis</string> - <!-- XTXT: Vaccination List complete vaccination subtitle--> - <string name="vaccination_list_complete_vaccination_subtitle">SARS-CoV-2-Impfschutz</string> - <!-- XTXT: Vaccination List top card title--> - <string name="vaccination_list_top_card_title">SARS-CoV-2 Impfung</string> <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_top_card_subtitle">Unvollständiger Impfschutz</string> - <!-- XTXT: Vaccination List name card subtitle--> <string name="vaccination_list_name_card_subtitle">geboren %1$s</string> <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_vaccination_card_title">Impfung %1$s von %2$s</string> + <string name="vaccination_list_vaccination_card_title">Impfung %1$d von %2$d</string> <!-- XTXT: Vaccination List top card subtitle--> <string name="vaccination_list_vaccination_card_subtitle">durchgeführt am %1$s</string> - <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_certificate_card_title">COVID-19-Prüfzertifikat</string> - <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_certificate_card_subtitle">Noch %1$d Tage gültig</string> + <!-- XTXT: Vaccination List immunity information card body--> + <plurals name="vaccination_list_immunity_card_body"> + <item quantity="one">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tag vollständig.</item> + <item quantity="other">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="zero">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="two">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="few">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="many">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + </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 --> 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 4b7d2be48..62730ced6 100644 --- a/Corona-Warn-App/src/main/res/values/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values/vaccination_strings.xml @@ -23,9 +23,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">Impfung %1$d von %2$d</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qr_code_card_title">Impfzertifikat %1$d von %2$d</string> + <string name="vaccination_qrcode_card_title">Impfzertifikat %1$d von %2$d</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qr_code_card_subtitle">Geimpft %1$s - gültig bis %2$s</string> + <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--> @@ -46,13 +46,18 @@ <!-- XTXT: Vaccination List name card subtitle--> <string name="vaccination_list_name_card_subtitle">geboren %1$s</string> <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_vaccination_card_title">Impfung %1$s von %2$s</string> + <string name="vaccination_list_vaccination_card_title">Impfung %1$d von %2$d</string> <!-- XTXT: Vaccination List top card subtitle--> <string name="vaccination_list_vaccination_card_subtitle">durchgeführt am %1$s</string> - <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_certificate_card_title">COVID-19-Prüfzertifikat</string> - <!-- XTXT: Vaccination List top card subtitle--> - <string name="vaccination_list_certificate_card_subtitle">Noch %1$d Tage gültig</string> + <!-- XTXT: Vaccination List immunity information card body--> + <plurals name="vaccination_list_immunity_card_body"> + <item quantity="one">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tag vollständig.</item> + <item quantity="other">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="zero">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="two">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="few">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + <item quantity="many">Sie haben nun alle Impfungen erhalten. Allerdings ist der Impfschutz erst in %1$d Tagen vollständig.</item> + </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 --> -- GitLab