diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index 948ff9a9f433ca51ceebe8bfe1726be49a643aec..f8ffb33b2ae96d524de4addad7bae3bf4e6d66ec 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -312,7 +312,6 @@ dependencies { androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutineVersion" // ANDROID STANDARD - def nav_version = "2.3.3" implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.core:core-ktx:1.5.0' implementation 'com.google.android.material:material:1.3.0' diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentTest.kt similarity index 95% rename from Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentTest.kt rename to Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentTest.kt index c19839d46d1dbf9d72b428dec85bf4179944c579..5cb63237dc3c20ba83b91e3c3bc65fd2d2f2615e 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.details +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.details import android.content.Context import android.graphics.Bitmap @@ -13,7 +13,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -86,8 +86,7 @@ class VaccinationDetailsFragmentTest : BaseUITest() { private fun vaccinationDetailsData(complete: Boolean): MutableLiveData<VaccinationDetails> { val formatter = DateTimeFormat.forPattern("dd.MM.yyyy") val mockCertificate = mockk<VaccinationCertificate>().apply { - every { firstName } returns "Max" - every { lastName } returns "Mustermann" + every { fullName } returns "Max Mustermann" every { dateOfBirth } returns LocalDate.parse("01.02.1976", formatter) every { vaccinatedAt } returns LocalDate.parse("18.02.2021", formatter) every { vaccineTypeName } returns "Comirnaty (mRNA)" diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentTest.kt similarity index 86% rename from Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentTest.kt rename to Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentTest.kt index a017ec7b73ddda132d9d8e5cd549a2c5c30e99bf..a35f99170a4d003c5a82a909b239ac50a9d0daf5 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.list +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list import android.content.Context import android.graphics.BitmapFactory @@ -11,16 +11,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.COMPLETE +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.IMMUNITY +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.INCOMPLETE +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat -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 -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.INCOMPLETE -import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem -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 io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt index 5d57baaa46585ec091a6c244c33f6753adb3bdc4..e12a5aa4f0fd3a606a469d5119c59b0ec0ce277f 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt @@ -9,6 +9,9 @@ import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestNegative import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestPending import de.rki.coronawarnapp.coronatest.type.pcr.SubmissionStatePCR.TestPositive import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.ImmuneVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.VaccinationCard import de.rki.coronawarnapp.risk.RiskState import de.rki.coronawarnapp.submission.ui.homecards.PcrTestErrorCard import de.rki.coronawarnapp.submission.ui.homecards.PcrTestInvalidCard @@ -31,9 +34,6 @@ import de.rki.coronawarnapp.tracing.ui.homecards.TracingDisabledCard import de.rki.coronawarnapp.tracing.ui.homecards.TracingFailedCard import de.rki.coronawarnapp.tracing.ui.homecards.TracingProgressCard import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.ui.cards.ImmuneVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.VaccinationCard import io.mockk.every import io.mockk.mockk import org.joda.time.Duration diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..68b8a1b80a3d8ae83fad80d050cced8549a2e6b0 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentTest.kt @@ -0,0 +1,116 @@ +package de.rki.coronawarnapp.ui.submission.covidcertificate + +import androidx.lifecycle.MutableLiveData +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.swipeUp +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.ext.junit.runners.AndroidJUnit4 +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.coronatest.qrcode.CoronaTestQRCode +import de.rki.coronawarnapp.ui.submission.ApiRequestState +import de.rki.coronawarnapp.ui.submission.qrcode.QrCodeRegistrationStateProcessor +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.joda.time.Instant +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import testhelpers.BaseUITest +import testhelpers.Screenshot +import testhelpers.launchFragmentInContainer2 +import testhelpers.takeScreenshot + +@RunWith(AndroidJUnit4::class) +class RequestCovidCertificateFragmentTest : BaseUITest() { + + @MockK lateinit var viewModel: RequestCovidCertificateViewModel + + @Before + fun setup() { + MockKAnnotations.init(this, relaxed = true) + + every { viewModel.birthDate } returns MutableLiveData(null) + every { viewModel.registrationState } returns MutableLiveData( + QrCodeRegistrationStateProcessor.RegistrationState(ApiRequestState.IDLE) + ) + + setupMockViewModel( + object : RequestCovidCertificateViewModel.Factory { + override fun create( + coronaTestQrCode: CoronaTestQRCode, + coronaTestConsent: Boolean, + deleteOldTest: Boolean + ): RequestCovidCertificateViewModel = viewModel + } + ) + } + + @Test + fun launch_fragment_pcr() { + val args = RequestCovidCertificateFragmentArgs( + CoronaTestQRCode.PCR(qrCodeGUID = "GUID") + ).toBundle() + launchFragmentInContainer2<RequestCovidCertificateFragment>(fragmentArgs = args) + } + + @Test + fun launch_fragment_rat() { + val args = RequestCovidCertificateFragmentArgs( + CoronaTestQRCode.RapidAntigen(hash = "hash", createdAt = Instant.EPOCH) + ).toBundle() + launchFragmentInContainer2<RequestCovidCertificateFragment>(fragmentArgs = args) + } + + @Test + @Screenshot + fun capture_fragment_pcr() { + val args = RequestCovidCertificateFragmentArgs( + CoronaTestQRCode.PCR(qrCodeGUID = "GUID") + ).toBundle() + launchFragmentInContainer2<RequestCovidCertificateFragment>(fragmentArgs = args) + takeScreenshot<RequestCovidCertificateFragment>("pcr") + + onView(withId(R.id.scrollview)).perform(swipeUp()) + takeScreenshot<RequestCovidCertificateFragment>("pcr_2") + } + + @Test + @Screenshot + fun capture_fragment_pcr_birthdate_dialog() { + val args = RequestCovidCertificateFragmentArgs( + CoronaTestQRCode.PCR(qrCodeGUID = "GUID") + ).toBundle() + launchFragmentInContainer2<RequestCovidCertificateFragment>(fragmentArgs = args) + onView(withId(R.id.date_input_edit)).perform(click()) + takeScreenshot<RequestCovidCertificateFragment>("date_picker") + } + + @Test + @Screenshot + fun capture_fragment_rat() { + val args = RequestCovidCertificateFragmentArgs( + CoronaTestQRCode.RapidAntigen(hash = "hash", createdAt = Instant.EPOCH) + ).toBundle() + launchFragmentInContainer2<RequestCovidCertificateFragment>(fragmentArgs = args) + takeScreenshot<RequestCovidCertificateFragment>("rat") + + onView(withId(R.id.scrollview)).perform(swipeUp()) + takeScreenshot<RequestCovidCertificateFragment>("rat_2") + } + + @After + fun teardown() { + clearAllViewModels() + } +} + +@Module +abstract class RequestCovidCertificateFragmentTestModule { + @ContributesAndroidInjector + abstract fun requestCovidCertificateFragment(): RequestCovidCertificateFragment +} diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/vaccination/VaccinationConsentFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/vaccination/VaccinationConsentFragmentTest.kt index 280bb1d9cb34dc1f474d92b548a24b4acdaee37c..3ef969a112b80105a768b6bf8eb7eb70a25d099f 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/vaccination/VaccinationConsentFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/vaccination/VaccinationConsentFragmentTest.kt @@ -3,9 +3,9 @@ package de.rki.coronawarnapp.ui.vaccination import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment -import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragmentArgs -import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentViewModel +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragment +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragmentArgs +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentViewModel import io.mockk.MockKAnnotations import io.mockk.impl.annotations.MockK import org.junit.After diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt index 937486ea384b8b2e3835c2dcc74aee533ff7a657..c8d1be3d5f7627a65ec5a00f7024e585f794c9af 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/FragmentTestModuleRegistrar.kt @@ -3,6 +3,8 @@ package testhelpers import dagger.Module import de.rki.coronawarnapp.bugreporting.DebugLogTestModule import de.rki.coronawarnapp.bugreporting.DebugLogUploadTestModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragmentTestModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragmentTestModule import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryDayFragmentTestModule import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryEditLocationsFragmentTestModule import de.rki.coronawarnapp.ui.contactdiary.ContactDiaryEditPersonsFragmentTestModule @@ -35,10 +37,9 @@ import de.rki.coronawarnapp.ui.submission.SubmissionTestResultTestAvailableModul import de.rki.coronawarnapp.ui.submission.SubmissionTestResultTestModule import de.rki.coronawarnapp.ui.submission.SubmissionTestResultTestNegativeModule import de.rki.coronawarnapp.ui.submission.SubmissionYourConsentFragmentTestModule +import de.rki.coronawarnapp.ui.submission.covidcertificate.RequestCovidCertificateFragmentTestModule import de.rki.coronawarnapp.ui.tracing.TracingDetailsFragmentTestTestModule import de.rki.coronawarnapp.ui.vaccination.VaccinationConsentFragmentTestModule -import de.rki.coronawarnapp.vaccination.ui.details.VaccinationDetailsFragmentTestModule -import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragmentTestModule @Module( includes = [ @@ -88,7 +89,8 @@ import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragmentTestModul // Vaccination VaccinationDetailsFragmentTestModule::class, VaccinationConsentFragmentTestModule::class, - VaccinationListFragmentTestModule::class + VaccinationListFragmentTestModule::class, + RequestCovidCertificateFragmentTestModule::class, ] ) class FragmentTestModuleRegistrar diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt index 7c18b62ce80dbb7d1966fd375d7b6a97519545de..f3b5ff6b4c25822fefc0c919ead8596cab32bbb3 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragment.kt @@ -5,6 +5,8 @@ import android.os.Bundle import android.view.View import android.widget.RadioButton import android.widget.RadioGroup +import androidx.core.text.buildSpannedString +import androidx.core.text.color import androidx.core.view.ViewCompat import androidx.core.view.children import androidx.fragment.app.Fragment @@ -13,6 +15,7 @@ import com.google.android.material.snackbar.Snackbar import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.FragmentTestDebugoptionsBinding import de.rki.coronawarnapp.test.menu.ui.TestMenuItem +import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat import de.rki.coronawarnapp.util.di.AutoInject import de.rki.coronawarnapp.util.ui.observe2 import de.rki.coronawarnapp.util.ui.viewBinding @@ -66,13 +69,14 @@ class DebugOptionsFragment : Fragment(R.layout.fragment_test_debugoptions), Auto it.isChecked = it.text == state.current.rawKey } - environmentCdnurlDownload.text = "Download CDN:\n${state.urlDownload}" - environmentCdnurlSubmission.text = "Submission CDN:\n${state.urlSubmission}" - environmentCdnurlVerification.text = "Verification CDN:\n${state.urlVerification}" - environmentUrlDatadonation.text = "DataDonation:\n${state.urlDataDonation}" - environmentUrlLogUpload.text = "LogUpload:\n${state.urlLogUpload}" - environmentPubkeyCrowdnotifier.text = "CrowdNotifierPubKey:\n${state.pubKeyCrowdNotifier}" - environmentPubkeyAppconfig.text = "AppConfigPubKey:\n${state.pubKeyAppConfig}" + environmentCdnurlDownload.text = "Download CDN" styleTo state.urlDownload + environmentCdnurlSubmission.text = "Submission CDN" styleTo state.urlSubmission + environmentCdnurlVerification.text = "Verification CDN" styleTo state.urlVerification + environmentUrlDatadonation.text = "DataDonation" styleTo state.urlDataDonation + environmentUrlLogUpload.text = "LogUpload" styleTo state.urlLogUpload + environmentPubkeyCrowdnotifier.text = "CrowdNotifierPubKey" styleTo state.pubKeyCrowdNotifier + environmentPubkeyAppconfig.text = "AppConfigPubKey" styleTo state.pubKeyAppConfig + environmentDccServerUrl.text = "DccServerUrl" styleTo state.dccServerUrl } } vm.environmentStateChange.observe2(this) { @@ -80,6 +84,13 @@ class DebugOptionsFragment : Fragment(R.layout.fragment_test_debugoptions), Auto } } + private infix fun String.styleTo(value: String) = buildSpannedString { + val color = requireContext().getColorCompat(R.color.colorAccent) + append("${this@styleTo}:") + appendLine() + color(color) { append(value) } + } + private fun showSnackBar(message: String) { Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG).show() } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt index c9b907793ab6812a252f519c71a9bee66af2387a..7ef52de717b849e5bc03688bacddf2be2e57eba6 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/EnvironmentState.kt @@ -12,6 +12,7 @@ data class EnvironmentState( val urlLogUpload: String, val pubKeyCrowdNotifier: String, val pubKeyAppConfig: String, + val dccServerUrl: String, ) { companion object { internal fun EnvironmentSetup.toEnvironmentState() = EnvironmentState( @@ -24,6 +25,7 @@ data class EnvironmentState( urlLogUpload = logUploadServerUrl, pubKeyCrowdNotifier = crowdNotifierPublicKey, pubKeyAppConfig = appConfigPublicKey, + dccServerUrl = dccServerUrl, ) } } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt index 12d682335609b9558ff9502c6ede71b33cf31f14..61cfe815eb6208a57c604b5f411f2f6ac365c1f0 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/deltaonboarding/ui/DeltaOnboardingFragmentViewModel.kt @@ -5,13 +5,13 @@ import androidx.lifecycle.asLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.presencetracing.TraceLocationSettings import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings class DeltaOnboardingFragmentViewModel @AssistedInject constructor( private val settings: CWASettings, diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragment.kt deleted file mode 100644 index c7d115bc504008facadbaf809f25495c639438a7..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -package de.rki.coronawarnapp.test.greencertificate - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentTestGreenCertificateBinding -import de.rki.coronawarnapp.test.menu.ui.TestMenuItem -import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.ui.doNavigate -import de.rki.coronawarnapp.util.ui.viewBinding -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider -import de.rki.coronawarnapp.util.viewmodel.cwaViewModels -import javax.inject.Inject - -@SuppressLint("SetTextI18n") -class GreenCertificateTestFragment : Fragment(R.layout.fragment_test_green_certificate), AutoInject { - - @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel: GreenCertificateTestFragmentViewModel by cwaViewModels { viewModelFactory } - private val binding by viewBinding<FragmentTestGreenCertificateBinding>() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.apply { - detailsScreen.setOnClickListener { - doNavigate( - GreenCertificateTestFragmentDirections - .actionGreenCertificateTestFragmentToGreenCertificateDetailsFragment() - ) - } - } - } - - companion object { - val MENU_ITEM = TestMenuItem( - title = "Green Certificate", - description = "View & Control green certificate related features.", - targetId = R.id.greenCertificateTestFragment - ) - } -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentModule.kt deleted file mode 100644 index c7bfa9d620f592e5a6df51de37c426b1b06528ae..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package de.rki.coronawarnapp.test.greencertificate - -import dagger.Binds -import dagger.Module -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 GreenCertificateTestFragmentModule { - @Binds - @IntoMap - @CWAViewModelKey(GreenCertificateTestFragmentViewModel::class) - abstract fun testVaccinationFragment( - factory: GreenCertificateTestFragmentViewModel.Factory - ): CWAViewModelFactory<out CWAViewModel> -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentViewModel.kt deleted file mode 100644 index 274a2af66555634d9fd53d8ee3547903573b2ce2..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/greencertificate/GreenCertificateTestFragmentViewModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package de.rki.coronawarnapp.test.greencertificate - -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.util.coroutine.DispatcherProvider -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class GreenCertificateTestFragmentViewModel @AssistedInject constructor( - dispatcherProvider: DispatcherProvider, -) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<GreenCertificateTestFragmentViewModel> -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt index 915453e1673da71ace23199073150cb764cb4150..5481b8257082656430d2bc3b06d3bab62401cc68 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/menu/ui/TestMenuFragmentViewModel.kt @@ -11,7 +11,6 @@ import de.rki.coronawarnapp.test.crash.ui.SettingsCrashReportFragment import de.rki.coronawarnapp.test.datadonation.ui.DataDonationTestFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment -import de.rki.coronawarnapp.test.greencertificate.GreenCertificateTestFragment import de.rki.coronawarnapp.test.hometestcards.ui.HomeTestCardsFragment import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment import de.rki.coronawarnapp.test.playground.ui.PlaygroundFragment @@ -22,7 +21,6 @@ import de.rki.coronawarnapp.test.tasks.ui.TestTaskControllerFragment import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.ui.VaccinationTestFragment class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() { @@ -43,8 +41,6 @@ class TestMenuFragmentViewModel @AssistedInject constructor() : CWAViewModel() { PresenceTracingTestFragment.MENU_ITEM, HomeTestCardsFragment.MENU_ITEM, CoronaTestTestFragment.MENU_ITEM, - VaccinationTestFragment.MENU_ITEM, - GreenCertificateTestFragment.MENU_ITEM, ).let { MutableLiveData(it) } } val showTestScreenEvent = SingleLiveEvent<TestMenuItem>() diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt index 7827f307ab521991b40f3be03f38cc38119b3225..65aa2e6524a9f0a56a2ae9c9dff8d3e590c2e4fe 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/ui/main/MainActivityTestModule.kt @@ -16,8 +16,6 @@ import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragment import de.rki.coronawarnapp.test.debugoptions.ui.DebugOptionsFragmentModule import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaOnboardingFragmentModule import de.rki.coronawarnapp.test.deltaonboarding.ui.DeltaonboardingFragment -import de.rki.coronawarnapp.test.greencertificate.GreenCertificateTestFragment -import de.rki.coronawarnapp.test.greencertificate.GreenCertificateTestFragmentModule import de.rki.coronawarnapp.test.hometestcards.ui.HomeTestCardsFragment import de.rki.coronawarnapp.test.hometestcards.ui.HomeTestCardsFragmentModule import de.rki.coronawarnapp.test.keydownload.ui.KeyDownloadTestFragment @@ -36,8 +34,6 @@ import de.rki.coronawarnapp.test.submission.ui.SubmissionTestFragment import de.rki.coronawarnapp.test.submission.ui.SubmissionTestFragmentModule import de.rki.coronawarnapp.test.tasks.ui.TestTaskControllerFragment import de.rki.coronawarnapp.test.tasks.ui.TestTaskControllerFragmentModule -import de.rki.coronawarnapp.vaccination.ui.VaccinationTestFragment -import de.rki.coronawarnapp.vaccination.ui.VaccinationTestFragmentModule @Module abstract class MainActivityTestModule { @@ -89,10 +85,4 @@ abstract class MainActivityTestModule { @ContributesAndroidInjector(modules = [CoronaTestTestFragmentModule::class]) abstract fun coronaTest(): CoronaTestTestFragment - - @ContributesAndroidInjector(modules = [VaccinationTestFragmentModule::class]) - abstract fun vaccinationTest(): VaccinationTestFragment - - @ContributesAndroidInjector(modules = [GreenCertificateTestFragmentModule::class]) - abstract fun greenCertificateTestFragment(): GreenCertificateTestFragment } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt deleted file mode 100644 index 97cc21c5f6e96f4c5176c7c834cd97305b93ec87..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt +++ /dev/null @@ -1,54 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui - -import android.annotation.SuppressLint -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentTestVaccinationBinding -import de.rki.coronawarnapp.test.menu.ui.TestMenuItem -import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.ui.doNavigate -import de.rki.coronawarnapp.util.ui.viewBinding -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider -import de.rki.coronawarnapp.util.viewmodel.cwaViewModels -import javax.inject.Inject - -@SuppressLint("SetTextI18n") -class VaccinationTestFragment : Fragment(R.layout.fragment_test_vaccination), AutoInject { - - @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val vm: VaccinationTestFragmentViewModel by cwaViewModels { viewModelFactory } - - private val binding: FragmentTestVaccinationBinding by viewBinding() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.openVaccinationDetailsIncomplete.setOnClickListener { - doNavigate( - VaccinationTestFragmentDirections - .actionVaccinationTestFragmentToVaccinationDetailsFragment( - "05930482748454836478695764787840" - ) - ) - } - - binding.openVaccinationDetailsComplete.setOnClickListener { - doNavigate( - VaccinationTestFragmentDirections - .actionVaccinationTestFragmentToVaccinationDetailsFragment( - "05930482748454836478695764787841" - ) - ) - } - } - - companion object { - val MENU_ITEM = TestMenuItem( - title = "Vaccination", - description = "View & Control vaccination related features.", - targetId = R.id.vaccinationTestFragment - ) - } -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentModule.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentModule.kt deleted file mode 100644 index fb24039c60b2b4cfbea5e2a91e7d9672922eb806..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui - -import dagger.Binds -import dagger.Module -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 VaccinationTestFragmentModule { - @Binds - @IntoMap - @CWAViewModelKey(VaccinationTestFragmentViewModel::class) - abstract fun testVaccinationFragment( - factory: VaccinationTestFragmentViewModel.Factory - ): CWAViewModelFactory<out CWAViewModel> -} diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentViewModel.kt deleted file mode 100644 index b9e4d4498f23b25c08f5e004052c04cab18e71de..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragmentViewModel.kt +++ /dev/null @@ -1,15 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui - -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.util.coroutine.DispatcherProvider -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory - -class VaccinationTestFragmentViewModel @AssistedInject constructor( - dispatcherProvider: DispatcherProvider, -) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<VaccinationTestFragmentViewModel> -} diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml index 165e76a5873ac28d6015f1d63ec99802ff2c4a4f..c29c720b6e742330b7614283234a1a2c75a51ca5 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_debugoptions.xml @@ -55,12 +55,12 @@ </androidx.constraintlayout.widget.ConstraintLayout> <LinearLayout - android:orientation="vertical" android:id="@+id/environment_container" style="@style/Card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="@dimen/spacing_tiny"> + android:layout_margin="@dimen/spacing_tiny" + android:orientation="vertical"> <TextView android:id="@+id/environment_title" @@ -109,6 +109,14 @@ android:layout_marginTop="4dp" tools:text="LogUpload: ?" /> + <TextView + android:id="@+id/environment_dcc_server_url" + style="@style/TextAppearance.MaterialComponents.Caption" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + tools:text="Dcc Server Url: ?" /> + <TextView android:id="@+id/environment_pubkey_appconfig" style="@style/TextAppearance.MaterialComponents.Caption" diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_green_certificate.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_green_certificate.xml deleted file mode 100644 index b35ae07c4ee791a7744cd05a5c8b6917343b1e60..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_green_certificate.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:ignore="HardcodedText"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="8dp" - android:orientation="vertical" - android:paddingBottom="32dp"> - - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Request DCC screen" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/details_screen" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="Details screen" /> - - </LinearLayout> -</androidx.core.widget.NestedScrollView> diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml deleted file mode 100644 index 36066d516e9ac6475d02e86438e9d657c04bd1c7..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:ignore="HardcodedText"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="8dp" - android:orientation="vertical" - android:paddingBottom="32dp"> - - <androidx.constraintlayout.widget.ConstraintLayout - style="@style/Card" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_margin="@dimen/spacing_tiny" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:orientation="vertical"> - - <TextView - android:id="@+id/textView2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="Vaccinations" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/open_vaccination_details_complete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:text="Vaccination details - complete" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/textView2" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/open_vaccination_details_incomplete" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:text="Vaccination details - incomplete" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/open_vaccination_details_complete" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - </LinearLayout> -</androidx.core.widget.NestedScrollView> diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml index 0e531f26af962639a18c07bc84e222c19160dc9c..e8d813f071f07a78cf8989f8140d3d4a18148d65 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml @@ -55,12 +55,6 @@ <action android:id="@+id/action_test_menu_fragment_to_coronaTestTestFragment" app:destination="@id/coronaTestTestFragment" /> - <action - android:id="@+id/action_test_menu_fragment_to_vaccinationTestFragment" - app:destination="@id/vaccinationTestFragment" /> - <action - android:id="@+id/action_test_menu_fragment_to_greenCertificateTestFragment" - app:destination="@id/greenCertificateTestFragment" /> </fragment> <fragment @@ -168,40 +162,5 @@ android:name="de.rki.coronawarnapp.test.coronatest.ui.CoronaTestTestFragment" android:label="CoronaTestTestFragment" tools:layout="@layout/fragment_test_coronatest" /> - <fragment - android:id="@+id/vaccinationTestFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.VaccinationTestFragment" - android:label="VaccinationTestFragment" - tools:layout="@layout/fragment_test_vaccination"> - - <action - android:id="@+id/action_vaccinationTestFragment_to_vaccinationDetailsFragment" - app:destination="@id/vaccinationDetailsFragment" /> - - </fragment> - - <fragment - android:id="@+id/vaccinationDetailsFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.details.VaccinationDetailsFragment" - android:label="fragment_vaccination_details" - tools:layout="@layout/fragment_vaccination_details"> - <argument - android:name="vaccinationCertificateId" - app:argType="string" /> - </fragment> - <fragment - android:id="@+id/greenCertificateTestFragment" - android:name="de.rki.coronawarnapp.test.greencertificate.GreenCertificateTestFragment" - android:label="GreenCertificateTestFragment" - tools:layout="@layout/fragment_test_green_certificate" > - <action - android:id="@+id/action_greenCertificateTestFragment_to_greenCertificateDetailsFragment" - app:destination="@id/greenCertificateDetailsFragment" /> - </fragment> - <fragment - android:id="@+id/greenCertificateDetailsFragment" - android:name="de.rki.coronawarnapp.greencertificate.ui.certificates.details.GreenCertificateDetailsFragment" - android:label="GreenCertificateDetailsFragment" - tools:layout="@layout/fragment_greencertificate_details" /> </navigation> diff --git a/Corona-Warn-App/src/main/assets/privacy_de.html b/Corona-Warn-App/src/main/assets/privacy_de.html index 34d84e3ce1d59b9210ee3fff6299de20fb1dec52..b6d195dc142220347427bb120dd9b985a8ed482b 100644 --- a/Corona-Warn-App/src/main/assets/privacy_de.html +++ b/Corona-Warn-App/src/main/assets/privacy_de.html @@ -7,7 +7,7 @@ <p> Folgende Themen werden behandelt: </p> -<ul> +<ol> <li><strong>1. Wer ist Herausgeber der Corona-Warn-App?</strong></li> <li><strong>2. Ist die Nutzung der App freiwillig?</strong></li> <li><strong>3. Auf welcher Rechtsgrundlage werden Ihre Daten verarbeitet?</strong></li> @@ -22,7 +22,7 @@ <li><strong>12. Wie können Sie Ihr Einverständnis zurücknehmen?</strong></li> <li><strong>13. Welche weiteren Datenschutzrechte haben Sie?</strong></li> <li><strong>14. Datenschutzbeauftragter und Kontakt</strong></li> -</ul> +</ol> <p> Damit dieser Text für alle Nutzer verständlich ist, bemühen wir uns um eine einfache und möglichst untechnische Darstellung. @@ -50,24 +50,41 @@ </h2> <p> Die Nutzung der App ist freiwillig. Es ist allein Ihre Entscheidung, ob Sie die App - installieren, welche App-Funktionen Sie nutzen und ob Sie Daten mit anderen teilen. Alle - App-Funktionen, die eine Weitergabe Ihrer personenbezogenen Daten an das RKI oder an andere - Nutzer erfordern, holen vorher Ihr ausdrückliches Einverständnis ein. Falls Sie ein - Einverständnis nicht erteilen oder nachträglich zurücknehmen, entstehen Ihnen keine Nachteile. - + installieren, welche App-Funktionen Sie nutzen und ob Sie Daten mit anderen teilen. + Grundsätzlich holen die Haupt-Funktionen der App, die eine Weitergabe Ihrer personenbezogenen + Daten an das RKI oder an andere Nutzer erfordern, vorher Ihr ausdrückliches Einverständnis ein. + Im Zusammenhang mit den offiziellen digitalen COVID-Zertifikaten (COVID-Testzertifikat und + COVID-Impfzertifikat) gibt es neue gesetzliche Grundlagen für die Erstellung der Nachweise. In + diesem Zusammenhang ist daher keine zusätzliche Einwilligung erforderlich. Die Zertifikate + werden aber nur erstellt, wenn Sie dies wünschen. Die Anforderung und Verwendung der digitalen + COVID-Zertifikate ist freiwillig. +</p> +<p> + Falls Sie ein Einverständnis nicht erteilen oder nachträglich zurücknehmen oder keine digitalen + COVID-Zertifikate anfordern, entstehen Ihnen keine Nachteile. </p> <h2> 3. Auf welcher Rechtsgrundlage werden Ihre Daten verarbeitet? </h2> <p> - Ihre Daten werden vom RKI grundsätzlich nur verarbeitet, wenn Sie zuvor Ihr ausdrückliches - Einverständnis erteilt haben. Die Rechtsgrundlage ist Art. 6 Abs. 1 S. 1 lit. a DSGVO sowie im - Falle von Gesundheitsdaten Art. 9 Abs. 2 lit. a DSGVO. Sie können ein einmal erteiltes - Einverständnis jederzeit wieder zurücknehmen (sogenanntes Widerrufsrecht). Weitere Informationen - zu Ihrem Widerrufsrecht finden Sie unter Punkt 12. Die Verarbeitung von Zugriffsdaten für die - Bereitstellung der täglichen Statistiken (siehe hierzu Punkt 6.e.) erfolgt im Rahmen der - Information der Öffentlichkeit durch das RKI gem. § 4 Abs. 4 BGA-NachfG auf Basis von Art. 6 - Abs. 1 lit e. DSGVO i.V.m § 3 BDSG. + Ihre Daten werden im Zusammenhang mit Risiko-Ermittlung und der Warnung Anderer vom RKI + grundsätzlich nur verarbeitet, wenn Sie zuvor Ihr ausdrückliches Einverständnis erteilt haben. + Die Rechtsgrundlage ist Art. 6 Abs. 1 S. 1 lit. a DSGVO sowie im Falle von Gesundheitsdaten Art. + 9 Abs. 2 lit. a DSGVO. Sie können ein einmal erteiltes Einverständnis jederzeit wieder + zurücknehmen (sogenanntes Widerrufsrecht). Weitere Informationen zu Ihrem Widerrufsrecht finden + Sie unter Punkt 12. +</p> +<p> + Im Zusammenhang mit den offiziellen digitalen COVID-Zertifikaten (COVID-Testzertifikat und + COVID-Impfzertifikat) ist die Datenverarbeitung gesetzlich geregelt. Die Erstellung und + Bescheinigung von Impfzertifikaten erfolgt auf Basis von Art. 9 Abs. 2 lit. g DSGVO i.V.m § 22 + Abs. 5 IfSG. Die Erstellung und Bescheinigung von Testzertifikaten erfolgt auf Basis von Art. 9 + Abs. 2 lit. g DSGVO i.V.m § 22 Abs. 7 IfSG. +</p> +<p> + Die Verarbeitung von Zugriffsdaten für die Bereitstellung der täglichen Statistiken (siehe + hierzu Punkt 6 f.) erfolgt im Rahmen der Information der Öffentlichkeit durch das RKI gem. § 4 + Abs. 4 BGA-NachfG auf Basis von Art. 6 Abs. 1 lit e. DSGVO i.V.m § 3 BDSG. </p> <h2> 4. An wen richtet sich die App? @@ -85,18 +102,20 @@ verarbeitet werden. Das bedeutet, dass das System bei der Risiko-Ermittlung, der Warnung anderer und dem Abruf des Testergebnisses keine Daten erfassen muss, die es dem RKI oder anderen Nutzern ermöglichen, auf Ihre Identität, Ihren Namen, Ihren Standort oder andere persönliche Details zu - schließen. Eine Ausnahme gilt nur für die Funktion „Schnelltest-Ergebnis nachweisen“, mit der - Sie eine auf Ihren Namen ausgestellte Bestätigung für negative Schnelltest-Ergebnisse anzeigen - lassen können (siehe hierzu Punkt 6 c.) sowie die Funktion zum Anlegen eines - „Schnelltest-Profils“, mit der Sie einer Teststelle die zur Durchführung eines Schnelltests - erforderlichen Daten bereitstellen können (siehe hierzu Punkt 6 d.). Die App verzichtet - standardmäßig auf die Auswertung Ihres Nutzungsverhaltens durch Analyse-Tools. Nur wenn Sie - ausdrücklich der freiwilligen Datenspende zustimmen oder einen Fehlerbericht aufzeichnen und mit - dem RKI teilen, werden bestimmte Daten über Ihre Nutzung der App an das RKI übermittelt (siehe - hierzu Punkt 5 i. und Punkt 5 k.). + schließen. +</p> +<p> + Ausnahmen gelten für die Funktionen „Schnelltest-Ergebnis nachweisen“, mit der Sie eine auf + Ihren Namen ausgestellte Bestätigung für negative Schnelltest-Ergebnisse anzeigen lassen können + (siehe hierzu Punkt 6 c.), die Funktion zum Anlegen eines „Schnelltest-Profils“, mit der Sie + einer Teststelle die zur Durchführung eines Schnelltests erforderlichen Daten bereitstellen + können (siehe hierzu Punkt 6 d.) und wenn Sie digitale COVID-Zertifikate in der App hinzufügen. +</p> +<p> Die App verzichtet standardmäßig auf die Auswertung Ihres Nutzungsverhaltens durch - Analyse-Tools. Nur wenn Sie ausdrücklich der freiwilligen Datenspende zustimmen, werden - bestimmte Daten über Ihre Nutzung der App an das RKI übermittelt (siehe hierzu Punkt 5 h.). + Analyse-Tools. Nur wenn Sie ausdrücklich der freiwilligen Datenspende zustimmen oder einen + Fehlerbericht aufzeichnen und mit dem RKI teilen, werden bestimmte Daten über Ihre Nutzung der + App an das RKI übermittelt (siehe hierzu Punkt 5 j. und Punkt 5 l.). </p> <p> Die von der App verarbeiteten Daten lassen sich den folgenden Kategorien @@ -262,7 +281,7 @@ (Bezeichnung des Events/Ortes, Adresse/Ort, typische Aufenthaltsdauer und ggf. Startzeit des Events) sowie die Check-in-Zeit auf Ihrem Smartphone gespeichert. Zudem leitet Ihre App anhand der im QR-Code enthaltenen Informationen eine dem Event eindeutig zuordenbare verschlüsselte - Kennung (im Folgenden: Event-ID) ab. Aus der Event-ID ergeben sich keine Rückschlüssel auf das + Kennung (im Folgenden: Event-ID) ab. Aus der Event-ID ergeben sich keine Rückschlüsse auf das Event oder den Ort. Wenn Sie sich in der App auschecken oder nach Ablauf der vom Gastgeber voreingestellten Zeit automatisch ausgecheckt werden, wird der Auscheck-Zeitpunkt auf Ihrem Smartphone gespeichert. @@ -315,7 +334,8 @@ wenn Sie Angaben zum Beginn von eventuellen Corona-Symptomen machen oder </li> <li> - wenn Sie digitale Impfzertifikate in der App hinzufügen. + wenn Sie digitale COVID-Zertifikate (Impfzertifikate oder Testzertifikate) in der App + hinzufügen. </li> </ul> <p> @@ -348,13 +368,14 @@ </p> <h3> - h. Daten über Ihre Corona-Impfung + h. Daten über Ihre Corona-Impfung (Daten im COVID-Impfzertifikat) </h3> <p> - Sie können Ihre Impfzertifikate in der App hinzufügen, die Sie bei der Impfung erhalten haben. - Die Anforderung des digitalen Impfzertifikats ist freiwillig. Sollten Sie dieses Angebot - wahrnehmen, erhalten Sie bei der Impfung einen Ausdruck mit einem QR-Code. In diesem sind die - folgenden Daten zu Ihrer Corona-Impfung kodiert: + Sie können Ihre offiziellen Impfzertifikate (digitale COVID-Impfzertifikate) in der App + hinzufügen, die Sie bei der Impfung erhalten haben. Die Anforderung des digitalen + Impfzertifikats ist freiwillig. Sollten Sie dieses Angebot wahrnehmen, erhalten Sie bei der + Impfung einen Ausdruck mit einem QR-Code. In diesem sind die folgenden Daten zu Ihrer + Corona-Impfung kodiert: </p> <ul> <li> @@ -375,7 +396,28 @@ scannen. Diese Daten wurden zuvor bei Ihrer Impfung erhoben. </p> <h3> - i. Nutzungsdaten (Datenspende) + i. Daten im COVID-Testzertifikat +</h3> +<p> + Sie können über die App offizielle Testzertifikate (digitale COVID-Testzertifikate) anfordern. + Die Anforderung des digitalen Testzertifikats ist freiwillig und erfolgt nur, wenn ein negatives + Testergebnis vorliegt. Sie erhalten dann ihr Testzertifikat mit einem QR-Code in der App. In + diesem sind die folgenden Daten zu Ihrem Testergebnis enthalten: +</p> +<ul> + <li>Daten zu Ihrer Person (Name, Vorname, Geburtsdatum)</li> + <li>Informationen zum Test (Krankheit, Art des Tests, Produkt, Hersteller)</li> + <li>Informationen zur Testdurchführung (Datum und Uhrzeit des Tests, Ort an dem sich das + Testzentrum befindet) + </li> + <li>Testergebnis</li> + <li>Eindeutige Kennung des Testzertifikats.</li> +</ul> +<p> + Die Daten werden in der App gespeichert, sobald Ihr Testergebnis vorliegt. +</p> +<h3> + j. Nutzungsdaten (Datenspende) </h3> <p> @@ -434,15 +476,6 @@ Zusätzlich können Sie weitere optionale Angaben zu Ihrer Region sowie zu Ihrer Altersgruppe machen, die zusammen mit den Nutzungsdaten an das RKI übermittelt werden. </p> -<ul> - <li>Angaben zum Modell und der Version Ihres Smartphones und zur Version Ihrer App sowie dem - verwendeten Betriebssystem. - </li> -</ul> -<p> - Zusätzlich können Sie weitere freiwilligen Angaben zu Ihrer Region sowie zu Ihrer Altersgruppe - machen, die zusammen mit den Nutzungsdaten an das RKI übermittelt werden. -</p> <p> Das RKI wird die Nutzungsdaten und eventuelle optionale Angaben zu anonymisierten Statistiken zusammenfassen und auswerten, um die Wirksamkeit und Funktionsweise der App zu bewerten und @@ -454,13 +487,13 @@ unter Punkt 5 l. und Punkt 11). </p> <h3> - j. Teilnahme an einer Befragung + k. Teilnahme an einer Befragung </h3> <p> Einigen Nutzern wird in der App die Teilnahme an einer Befragung des RKI angeboten. In der Regel wird das Angebot zur Teilnahme an der Befragung abhängig von bestimmten in der App registrierten - Ereignissen sein (z. B. der Anzeige eines erhöhten Risikos). Mit der Teilnahme an der - Befragung helfen Sie dem RKI, die Wirksamkeit der App zu bewerten, die App zu verbessern und + Ereignissen sein (z.B. der Anzeige eines erhöhten Risikos). Mit der Teilnahme an der Befragung + helfen Sie dem RKI, die Wirksamkeit der App zu bewerten, die App zu verbessern und beispielsweise zu verstehen, ob und wie Warnungen über die App dabei helfen, weitere Ansteckungen zu verhindern. </p> @@ -469,11 +502,11 @@ Befragung teilnehmen möchten und Daten hierfür an das RKI übermittelt werden sollen. Die Befragungen finden auf einer Webseite außerhalb der App statt, auf die Sie weitergeleitet werden. Die Teilnahme an einer Befragung setzt die Bestätigung der Echtheit Ihrer App voraus - (Beachten Sie bitte die weitere Informationen hierzu unter Punkt 5 j. und Punkt 11). + (Beachten Sie bitte die weiteren Informationen hierzu unter Punkt 5 l. und Punkt 11). </p> <h3> - k. Inhalte der Fehlerberichte + l. Inhalte der Fehlerberichte </h3> <p> Um den technischen Support der App bei der Fehleranalyse zu unterstützen, können Sie in der App @@ -489,7 +522,7 @@ <li> zur Risiko-Ermittlung (z.B. zur Funktionsweise der Verarbeitung der Begegnungsdaten, der Berechnung des Ansteckungsrisikos, der Aktualisierung der Positiv-Listen, der - Anzeige des errechneten Risikostatus + Anzeige des errechneten Risikostatus, </li> <li> zum Abruf und der Anzeige von Testergebnissen und @@ -510,9 +543,9 @@ <p> Der Fehlerbericht enthält jedoch keine Informationen über QR-Codes für die Testregistrierung, das in Ihrer App gespeicherte Token (siehe dazu unten Punkt 6 b. unter „Testergebnis abrufen“), - Schnelltest-Ergebnisse, Impfzertifikate und Einträge in Ihrem Kontakt-Tagebuch. Der - Fehlerbericht enthält auch nicht Ihren Namen oder andere Angaben, mit denen Sie vom RKI - identifiziert werden können. + Schnelltest-Ergebnisse, digitale COVID-Impf- und Testzertifikate und Einträge in Ihrem + Kontakt-Tagebuch. Der Fehlerbericht enthält auch nicht Ihren Namen oder andere Angaben, mit + denen Sie vom RKI identifiziert werden können. </p> <p> Sie können die Aufzeichnung des Fehlerberichts jederzeit stoppen und den Fehlerbericht löschen. @@ -527,11 +560,11 @@ Die Erstellung und Ãœbersendung eines Fehlerberichts an das RKI ist freiwillig. Sie entscheiden selbst darüber, ob Sie einen Fehlerbericht aufzeichnen möchten und diesen an den technischen Support der App übersenden. Die Ãœbersendung des Fehlerberichts setzt die Bestätigung der - Echtheit Ihrer App voraus (Beachten Sie bitte die weiteren Informationen hierzu unter Punkt 5 l. + Echtheit Ihrer App voraus (Beachten Sie bitte die weiteren Informationen hierzu unter Punkt 5 m. und Punkt 11). </p> <h3> - l. Bestätigung der Echtheit Ihrer App + m. Bestätigung der Echtheit Ihrer App </h3> <p> Einige Funktionen der App setzen voraus, dass vorab die Echtheit Ihrer App geprüft und gegenüber @@ -543,9 +576,9 @@ werden Daten an Apple übermittelt). Die Kennung enthält Informationen über die Version Ihres Smartphones und die Version der App. Möglicherweise kann der Anbieter Ihres Betriebssystems anhand der Kennung auf Ihre Identität schließen und nachvollziehen, dass die Echtheitsprüfung - Ihres Smartphones stattgefunden hat. Weitere Angaben aus der App, z. B. Begegnungsdaten, - erhält der Anbieter Ihres Betriebssystems nicht. Die Anbieter des Betriebssystems nutzen die - Kennung, um die Echtheit Ihrer App gegenüber dem RKI zu bestätigen. Die Nutzung der Funktion zur + Ihres Smartphones stattgefunden hat. Weitere Angaben aus der App, z.B. Begegnungsdaten, erhält + der Anbieter Ihres Betriebssystems nicht. Die Anbieter des Betriebssystems nutzen die Kennung, + um die Echtheit Ihrer App gegenüber dem RKI zu bestätigen. Die Nutzung der Funktion zur Bestätigung der Echtheit ist freiwillig. Wenn Sie mit der Bestätigung der Echtheit Ihrer App nicht einverstanden sind, kann es jedoch sein, dass Ihnen andere Funktionen der App nicht zur Verfügung stehen. @@ -576,7 +609,7 @@ </p> <p> Die Zufalls-IDs und Event-IDs in den Positiv-Listen enthalten zusätzlich einen - Ãœbertragungsrisiko-Wert und eine Angabe zur Art der Diagnose (siehe hierzu Punkt 6 e.). + Ãœbertragungsrisiko-Wert und eine Angabe zur Art der Diagnose (siehe hierzu Punkt 6 e.). </p> <p> Die App gibt die Zufalls-IDs aus der Positiv-Liste an das COVID-19-Benachrichtigungssystem @@ -691,7 +724,10 @@ zu der gehashten Kennzahl tatsächlich ein positives Testergebnis vorliegt. Sofern dies bestätigt wird, erzeugt das Serversystem die TAN und übermittelt sie an die App. Eine Kopie der TAN verbleibt auf dem Serversystem. - +</p> +<p> + Die Testergebnisse werden auch in Ihrem Kontakt-Tagebuch hinterlegt. Sie können diese dort + jederzeit wieder entfernen. </p> <h3> c. Nachweis des Schnelltest-Ergebnisses @@ -727,11 +763,15 @@ Schnelltestdaten (Kennzahl, Testzeitpunkt) werden gelöscht, sobald das positive Schnelltest-Ergebnis nicht mehr in der App angezeigt wird. </p> +<p> + Die Schnelltest-Ergebnisse werden auch in Ihrem Kontakt-Tagebuch hinterlegt. Sie können diese + dort jederzeit wieder entfernen. +</p> <h3> d. Schnelltest-Profil </h3> <p> - Das Schnelltest-Profil bietet Ihnen die Möglichkeit die Datenerfassung in teilnehmenden + Das Schnelltest-Profil bietet Ihnen die Möglichkeit, die Datenerfassung in teilnehmenden Teststellen zu beschleunigen. Sie können hierfür Angaben zu Ihrer Person in Ihrem Schnelltest-Profil in der App speichern und diese in Ihren persönlichen QR-Code umwandeln, der alle eingegebenen Daten enthält. In der Teststelle können Sie den QR-Code Ihres @@ -857,7 +897,7 @@ </p> <h3> - h. Digitaler Impfnachweis + h. Digitaler Impfnachweis (COVID-Impfzertifikat) </h3> <p> @@ -892,7 +932,49 @@ dass die Daten ausgelesen werden. </p> <h3> - i. Datenspende + i. Digitaler Testnachweis (COVID-Testzertifikat) + +</h3> +<p> + Die App erlaubt es Ihnen, Ihre Testzertifikate zu speichern und in digitaler Form bei sich zu + führen. Bei Bedarf können Sie dann die App nutzen, um in den gesetzlich vorgesehenen Fällen + nachzuweisen, dass Sie negativ getestet wurden. +</p> +<p> + Ein Testzertifikat können Sie in der App anfordern, wenn Sie einen Test registrieren. Scannen + Sie hierfür den QR-Code, den Sie beim Test erhalten haben. Die App liest die Daten zu Ihrem Test + aus dem QR-Code aus und erhält das Testergebnis vom Server. Wenn Sie einen PCR-Test gemacht + haben, wird der Abruf des Ergebnisses und des Testzertifikats zusätzlich mit Ihrem Geburtsdatum + gesichert. Aus dem Geburtsdatum wird ein Sicherheitscode erstellt (in Form eines sog. + Hashwertes) und mit dem Server des RKI abgeglichen. Auf diese Weise wird sichergestellt, dass + andere Personen nicht Ihr Testergebnis abrufen können. +</p> +<p> + Die Daten zum Testzertifikat speichert die App in einem gesicherten Bereich Ihres Smartphones. + Wenn Sie ein positives Testergebnis abgerufen haben, wird kein Testzertifikat erstellt. +</p> +<p> + Auf dem Homescreen wird Ihr aktuelles Testzertifikat in der App angezeigt. Zum Nachweis können + Sie den in der App dargestellten QR-Code des digitalen Testzertifikats vorzeigen. Mit der + offiziellen Prüf-App kann dieser QR-Code gescannt werden, die Daten im Testzertifikat (siehe + Punkt 5 i.) werden dabei ausgelesen. In der Prüf-App wird dann angezeigt, ob Sie negativ + getestet sind und wann der Test durchgeführt wurde. Zusätzlich werden auch Ihr Name und Ihr + Geburtsdatum angezeigt, damit die prüfende Person diese Angaben mit Ihrem offiziellen + Ausweisdokument abgleichen kann. +</p> +<p> + Bitte beachten Sie, dass die Verwendung der Funktion zum Anfordern und Speichern Ihrer + COVID-Testzertifikate in der App freiwillig ist. Sie können in den gesetzlich vorgesehenen + Fällen den Nachweis Ihres Testergebnisses auch auf andere Weise erbringen. +</p> +<p> + Bitte beachten Sie, dass der QR-Code Ihres digitalen Testzertifikates Ihre Daten enthält. Zeigen + Sie diesen nur vor, wenn Sie Ihr negatives Testergebnis nachweisen möchten. Stellen Sie die + Testzertifikate und QR-Codes niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten + ausgelesen werden. +</p> +<h3> + j. Datenspende </h3> <p> @@ -916,11 +998,11 @@ </li> <li> Informationen und Hilfestellungen zur App ermöglichen – Es soll möglich werden, zu erkennen, - ob es z. B. bei der Nutzung der App im Zusammenhang mit bestimmten Testeinrichtungen - und Laboren oder in bestimmten Regionen zu Problemen kommt. Dies kann festgestellt werden, - wenn aufgrund der Datenspende auffällt, dass in bestimmten Regionen Testergebnisse verspätet - zur Verfügung stehen. So können die zuständigen Gesundheitsbehörden auch gezielt auf - mögliche technische Störungen hingewiesen werden. + ob es z.B. bei der Nutzung der App im Zusammenhang mit bestimmten Testeinrichtungen und + Laboren oder in bestimmten Regionen zu Problemen kommt. Dies kann festgestellt werden, wenn + aufgrund der Datenspende auffällt, dass in bestimmten Regionen Testergebnisse verspätet zur + Verfügung stehen. So können die zuständigen Gesundheitsbehörden auch gezielt auf mögliche + technische Störungen hingewiesen werden. </li> <li> Verbesserung der Statistiken über den Pandemieverlauf – Die Daten können Aufschluss über die @@ -934,7 +1016,7 @@ oder wen Sie getroffen haben. </p> <h3> - j. Fehlerberichte + k. Fehlerberichte </h3> <p> Das RKI ist bemüht, eine fehlerfreie App anzubieten. Aufgrund der großen Anzahl verschiedener @@ -951,7 +1033,7 @@ Offenlegung Ihrer Identität (z. B. per E-Mail) mitteilen. </p> <h3> - k. Befragungen + l. Befragungen </h3> <p> Befragungen finden auf einer Webseite außerhalb der App statt, auf die Sie weitergeleitet @@ -960,7 +1042,7 @@ Befragung auf der Befragungs-Webseite beschrieben. </p> <h3> - l. Bestätigung der Echtheit Ihrer App + m. Bestätigung der Echtheit Ihrer App </h3> <p> Zur Bestätigung der Echtheit Ihrer App wird eine Funktion des Betriebssystems Ihres Smartphones @@ -1023,7 +1105,7 @@ festgelegt (Durchführungsbeschluss (EU) 2020/1023 vom 15. Juli 2020, abrufbar unter <a href="https://eur-lex.europa.eu/eli/dec_impl/2020/1023/oj"> https://eur-lex.europa.eu/eli/dec_impl/2020/1023/oj - </a>. Für die Verarbeitung der in den Positiv-Listen enthaltenen Angaben (Zufalls-IDs und + </a>). Für die Verarbeitung der in den Positiv-Listen enthaltenen Angaben (Zufalls-IDs und eventuelle Angaben zum Symptombeginn) auf den Austausch-Servern zur Ermöglichung der länderübergreifenden Risiko-Ermittlung und Warnung ist das RKI danach mit den jeweils zuständigen Gesundheitsbehörden der teilnehmenden Länder gemeinsam verantwortlich. @@ -1049,8 +1131,7 @@ title="Follow link" > https://www.coronawarn.app/de/faq/#interoperability_countries - </a> - . + </a>. </p> <h2> @@ -1170,15 +1251,15 @@ </h3> <p> Die Positiv-Listen werden nach 14 Tagen aus dem App-Speicher gelöscht. Event-Daten im Bereich - „Meine Check-Ins“ werden nach 14 Tagen automatisch gelöscht. Alternativ können Sie Einträge im - Bereich „Meine Check-Ins“ jederzeit händisch löschen. Das für Sie ermittelte Ansteckungsrisiko + „Meine Check-ins“ werden nach 14 Tagen automatisch gelöscht. Alternativ können Sie Einträge im + Bereich „Meine Check-ins“ jederzeit händisch löschen. Das für Sie ermittelte Ansteckungsrisiko (z. B. „niedriges Risiko“) wird nach jeder Aktualisierung, spätestens aber nach 14 Tagen aus dem App-Speicher gelöscht. Sofern Sie ein positives Testergebnis abgerufen haben, wird das Token im App-Speicher gelöscht, sobald Sie eine Warnung auslösen. Die Einträge im Kontakt-Tagebuch bleiben für 16 Tage auf Ihrem Smartphone gespeichert und werden dann automatisch gelöscht. Sie können diese Einträge jederzeit auch vorzeitig selbst löschen. Bitte beachten Sie, dass beim Einchecken zu einem Event oder an einem Ort übernommene Einträge im Kontakt-Tagebuch auch nach - der Löschung des zugehörigen Check-Ins dort noch gespeichert sind. Wenn Sie Ihr + der Löschung des zugehörigen Check-ins dort noch gespeichert sind. Wenn Sie Ihr Schnelltest-Profil einmal angelegt haben, bleibt dieses so lange in der App gespeichert, bis Sie es selbst wieder löschen. Wenn Sie Impfzertifikate gescannt haben, bleiben auch diese so lange in der App gespeichert, bis Sie sie selbst wieder löschen. @@ -1237,21 +1318,16 @@ EU-Länder haben die jeweils zuständigen nationalen Gesundheitsbehörden die EU-Kommission als Auftragsverarbeiter beauftragt. Der Austausch-Server für länderübergreifende Warnungen zwischen der Corona-Warn-App und der schweizerischen Corona-App wird vom Bundesamt für Gesundheit der - Schweizerischen Eidgenossenschaft in Abstimmung mit dem RKI betrieben und gewartet. -</p> -<p> - Mit dem Betrieb und der Wartung eines Teils der technischen Infrastruktur - der App (z. B. Serversysteme, Hotline) hat das RKI die T-Systems - International GmbH und die SAP Deutschland SE & Co. KG beauftragt, die - als Auftragsverarbeiter des RKI tätig werden. Diese Unternehmen sind von - der EU-Kommission zudem als Unterauftragsverarbeiter mit der technischen - Bereitstellung und Verwaltung des gemeinsam betriebenen Warnsystems der - teilnehmenden Länder beauftragt. Im Ãœbrigen gibt das RKI Ihre Daten, die im Zusammenhang mit der - Nutzung der - App erhoben werden, nur an Dritte weiter, soweit das RKI rechtlich dazu - verpflichtet ist oder die Weitergabe im Falle von Angriffen auf die - technische Infrastruktur der App zur Rechts- oder Strafverfolgung - erforderlich ist. Eine Weitergabe durch das RKI in anderen Fällen erfolgt grundsätzlich nicht. + Schweizerischen Eidgenossenschaft in Abstimmung mit dem RKI betrieben und gewartet. Mit dem + Betrieb und der Wartung eines Teils der technischen Infrastruktur der App (z.B. Serversysteme, + Hotline) hat das RKI die T-Systems International GmbH und die SAP Deutschland SE & Co. KG + beauftragt, die als Auftragsverarbeiter des RKI tätig werden. Diese Unternehmen sind von der + EU-Kommission zudem als Unterauftragsverarbeiter mit der technischen Bereitstellung und + Verwaltung des gemeinsam betriebenen Warnsystems der teilnehmenden Länder beauftragt. Im Ãœbrigen + gibt das RKI Ihre Daten, die im Zusammenhang mit der Nutzung der App erhoben werden, nur an + Dritte weiter, soweit das RKI rechtlich dazu verpflichtet ist oder die Weitergabe im Falle von + Angriffen auf die technische Infrastruktur der App zur Rechts- oder Strafverfolgung erforderlich + ist. Eine Weitergabe durch das RKI in anderen Fällen erfolgt grundsätzlich nicht. </p> <h2> 11. Werden Ihre Daten in Länder außerhalb der EU übermittelt? @@ -1456,6 +1532,5 @@ <a href="mailto:datenschutz@rki.de">datenschutz@rki.de</a>. </p> <p> - Stand: 28.05.2021 + Stand: 17.06.2021 </p> - diff --git a/Corona-Warn-App/src/main/assets/privacy_en.html b/Corona-Warn-App/src/main/assets/privacy_en.html index 2d69b43027d732d97a4ecafbda1b0875d7634323..63fcebb108630872c2db99bd5763648caf8cd69e 100644 --- a/Corona-Warn-App/src/main/assets/privacy_en.html +++ b/Corona-Warn-App/src/main/assets/privacy_en.html @@ -6,7 +6,7 @@ <p> It covers the following topics: </p> -<ul> +<ol> <li><strong>1. Who is the Corona-Warn-App published by?</strong></li> <li><strong>2. Is using the app voluntary?</strong></li> <li><strong>3. On what legal basis is your data processed?</strong></li> @@ -21,7 +21,7 @@ <li><strong>12. How can you withdraw your consent?</strong></li> <li><strong>13. What other rights do you have under data protection law?</strong></li> <li><strong>14. Data protection officer and contact</strong></li> -</ul> +</ol> <p> To make sure that this text can be understood by all users, we have made every effort to make it simple and as non-technical as possible. @@ -52,25 +52,41 @@ </h2> <p> Using the app is voluntary. It is entirely up to you whether you install the app, which of the - app’s features you use, and whether you share data with others. All of the app’s features that - require the transfer of your personal data to the RKI or to other users will obtain your express - consent in advance. If you do not give your consent or if you subsequently withdraw it, this - will not result in any disadvantages for you. - + app’s features you use, and whether you share data with others. As a matter of principle, all of + the app’s main features that require the transfer of your personal data to the RKI or to other + users will obtain your express consent in advance. In the context of the official digital COVID + certificates (COVID test certificate and COVID vaccination certificate), new legal requirements + apply to the creation of the certificates. For this reason, no additional consent is required in + this context. However, the certificates will only be created if you wish for this to happen. + Requesting and using digital COVID certificates is voluntary. +</p> +<p> + If you do not give your consent, if you subsequently withdraw it, or if you do not request any + digital COVID certificates, this will not result in any disadvantages for you. </p> <h2> 3. On what legal basis is your data processed? </h2> <p> - The RKI will only process your data if you have given your express consent beforehand. The legal - basis is Art. 6(1) Sentence 1(a) GDPR and, in the case of health data, Art. 9(2)(a) GDPR. After - giving your consent, you can withdraw it at any time (so-called right of withdrawal). Please - refer to Section 12 for further information about your right of withdrawal. On the basis of Art. - 6(1) Sentence 1(e) GDPR in conjunction with Sect. 3 of the German Federal Data Protection Act - (BDSG), the processing of access data for the provision of daily statistics (see Section 6 e.) - is performed as part of the RKI’s duty to inform the public pursuant to Sect. 4(4) of the Act on - Successor Agencies to the Federal Health Agency (BGA-NachfG). + As a matter of principle, the RKI will only process your data for the purposes of exposure + logging and warning others if you have given your express consent beforehand. The legal basis is + Art. 6(1) Sentence 1(a) GDPR and, in the case of health data, Art. 9(2)(a) GDPR. After giving + your consent, you can withdraw it at any time (so-called right of withdrawal). Please refer to + Section 12 for further information about your right of withdrawal. +</p> +<p> + In the context of the official digital COVID certificates (COVID test certificate and COVID + vaccination certificate), data processing is regulated by law. The creation and confirmation of + vaccination certificates is based on Art. 9(2)(g) GDPR in conjunction with Sect. 22(5) of the + Infection Protection Act (IfSG). The creation and confirmation of test certificates is based on + Art. 9(2)(g) GDPR in conjunction with Sect. 22(7) IfSG. +</p> +<p> + On the basis of Art. 6(1) Sentence 1(e) GDPR in conjunction with Sect. 3 of the German Federal + Data Protection Act (BDSG), the processing of access data for the provision of daily statistics + (see Section 6 f.) is performed as part of the RKI’s duty to inform the public pursuant to Sect. + 4(4) of the Act on Successor Agencies to the Federal Health Agency (BGA-NachfG). </p> <h2> 4. Who is the app aimed at? @@ -95,10 +111,17 @@ rapid test profile, which allows you to provide a testing point with the data required to perform a rapid test (see Section 6 d.). </p> +<p> + Exceptions apply to the feature for proving a rapid test result, which allows you to display a + confirmation issued in your name for negative rapid test results (see Section 6 c.), the feature + for creating a rapid test profile, which allows you to provide a testing point with the data + required to perform a rapid test (see Section 6 d.) and if you add digital COVID certificates in + the app. +</p> <p> The app refrains by default from using analysis tools to evaluate the way you use it. Only if you expressly agree to voluntarily share data or to record an error report and share it with the - RKI (see Sections 5 i. and 5 k.), will certain data about your use of the app be transmitted to + RKI (see Sections 5 j. and 5 l.), will certain data about your use of the app be transmitted to the RKI. </p> @@ -153,9 +176,9 @@ </h3> <p> - As soon as you enable your iPhone’s or your Android smartphone’s COVID-19 + As soon as you enable your iPhone’s or your Android smartphone’s COVID exposure notification system (which is called “Exposure Notifications†or - “COVID-19 Exposure Notifications†respectively), your smartphone transmits + “COVID Exposure Notifications†respectively), your smartphone transmits so-called exposure data via Bluetooth, which other smartphones in your vicinity can record. Your smartphone, in turn, also receives the exposure data of other smartphones. The exposure data transmitted by your smartphone @@ -194,7 +217,7 @@ smartphones of other app users. </p> <p> - Please note: the COVID-19 exposure notification system functionality is + Please note: the COVID exposure notification system functionality is part of your operating system. The providers responsible for this system are therefore Apple (if you have an iPhone) and Google (if you have an Android smartphone). In this respect, the data processing is subject to @@ -207,7 +230,7 @@ <ul> <li> If you have an Android smartphone, you can find information from Google - on your device by going to “Settings†> “Google†> “COVID-19 + on your device by going to “Settings†> “Google†> “COVID exposure notifications†and tapping on “Learn moreâ€. </li> <li> @@ -268,7 +291,7 @@ stored on your smartphone. </p> <p> - An entry will also be created in your contact journal by default. Sections 5 g. and 6.g. explain + An entry will also be created in your contact journal by default. Sections 5 g. and 6 g. explain this in more detail. If you do not want to create an entry in your contact journal for an event or place, you can simply switch off this feature using the corresponding toggle switch. </p> @@ -314,7 +337,8 @@ If you provide information about the onset of any coronavirus symptoms or </li> <li> - If you add digital vaccination certificates in the app. + If you add digital COVID certificates (vaccination certificates or test certificates) in the + app. </li> </ul> <p> @@ -345,14 +369,15 @@ </p> <h3> - h. Data about your COVID-19 vaccination + h. Data about your COVID vaccination (data in the COVID vaccination certificate) </h3> <p> - In the app, it is possible to add your vaccination certificates that you received when you were - vaccinated. Requesting a digital vaccination certificate is voluntary. If you choose to use this - service, you will receive a printout with a QR code at the time of your vaccination. This will - contain the following data about your COVID-19 vaccination in encoded form: + In the app, it is possible to add your official vaccination certificates (digital COVID + certificates) that you received when you were vaccinated. Requesting a digital vaccination + certificate is voluntary. If you choose to use this service, you will receive a printout with a + QR code at the time of your vaccination. This will contain the following data about your COVID + vaccination in encoded form: </p> <ul> <li> @@ -373,7 +398,28 @@ certificate. This data will have been collected previously at the time of your vaccination. </p> <h3> - i. Usage Data (data sharing) + i. Data in the COVID test certificate +</h3> +<p> + You request official test certificates (digital COVID test certificates) through the app. + Requesting a digital test certificate is voluntary and only possible if a negative test result + is available. You will then receive your test certificate with a QR code in the app. This + contains the following data about your test result: +</p> +<ul> + <li>Personal data (last name, first name, date of birth)</li> + <li>Information about the test (disease, type of test, product, manufacturer)</li> + <li>Information about the testing procedure (date and time of the test, location of the testing + centre) + </li> + <li>Test result</li> + <li>Unique test certificate identifier.</li> +</ul> +<p> + The data is stored in the app as soon as your test result is available. +</p> +<h3> + j. Usage Data (data sharing) </h3> <p> @@ -403,7 +449,7 @@ the relevant test registration </li> <li>Whether you have used the feature for warning others and, if so, which step you reached in - the process (e.g. the part that asks about your symptoms) + the process (e.g. the part that asks about your symptoms). </li> </ul> <p> @@ -434,10 +480,10 @@ <p> Participation in data sharing is voluntary. To enable the data sharing feature, the authenticity of your app first needs to be confirmed (please note the further information about this under - Sections 5 l. and 11). + Sections 5 m. and 11). </p> <h3> - j. Participation in a survey + k. Participation in a survey </h3> <p> Some app users are offered to participate in a survey by the RKI. This offer to participate in @@ -451,11 +497,11 @@ in a survey and whether data should be transmitted to the RKI for this purpose. The surveys take place on a website outside of the app, which you will be redirected to. To enable participation in a survey, the authenticity of your app first needs to be confirmed (please note the further - information about this in Sections 5 j. and 11). + information about this in Sections 5 m. and 11). </p> <h3> - k. Contents of the error reports + l. Contents of the error reports </h3> <p> To assist the app’s technical support team with error analysis, you can record an error report @@ -463,7 +509,7 @@ </p> <ul> <li> - the steps you take in the app + the steps you take in the app, </li> <li> the technical steps and processes as well as status messages involving @@ -489,9 +535,10 @@ </p> <p> However, the error report does not contain information about QR codes for test registration, the - token stored in your app (see “Retrieving a test result†in Section 6 b. below) rapid test - results, vaccination certificates and entries in your contact journal. Furthermore, the error - report does not contain your name or other information with which the RKI can identify you. + token stored in your app (see “Retrieving a test result†in Section 6 b. below), rapid test + results, digital COVID vaccination and test certificates and entries in your contact journal. + Furthermore, the error report does not contain your name or other information with which the RKI + can identify you. </p> <p> You can stop recording the error report and delete the error report at any time. If you choose @@ -506,10 +553,10 @@ Creating and sending an error report to the RKI is voluntary. You decide yourself whether you want to record an error report and send it to the app’s technical support team. To send the error report, the authenticity of your app first needs to be confirmed (please note the further - information about this in Sections 5 l. and 11). + information about this in Sections 5 m. and 11). </p> <h3> - l. Confirmation of the authenticity of your app + m. Confirmation of the authenticity of your app </h3> <p> Before you can use some of the app’s features, the authenticity of your app first needs to be @@ -532,7 +579,7 @@ </h2> <h3> - a. Exposure logging + a. </h3> <p> @@ -609,7 +656,9 @@ separate consent to your test result being sent. It is not possible to display test results from testing facilities that are not connected to the app’s server system. If you have not received a QR code, then you cannot use this feature either. - +</p> +<p> + The test results are also stored in your contact journal. You can delete them there at any time </p> <p> <u>Scanning the QR code</u> @@ -698,6 +747,10 @@ data (code, time you were tested) will be deleted as soon as the positive rapid test result is no longer displayed in the app. </p> +<p> + The rapid test results are also stored in your contact journal. You can delete them there at any + time. +</p> <h3> d. Rapid test profile @@ -799,10 +852,9 @@ <p> The app automatically receives the daily statistics that appear in the app via the server system. This generates access data. Websites linked in the app, such as - <a href="http://www.bundesregierung.de/">www.bundesregierung.de</a>, are - opened and displayed in your smartphone’s standard browser (Android - smartphones) or within the app (iPhones). Which data is processed in this context depends on the - respective providers of the websites accessed. + <a href="http://www.bundesregierung.de/">www.bundesregierung.de</a>, are opened and displayed in + your standard browser (Android smartphones) or within the app (iPhones). Which data is processed + in this context depends on the respective providers of the websites accessed. </p> <h3> @@ -821,7 +873,7 @@ </p> <h3> - h. Digital vaccination certificate + h. Digital vaccination certificate (COVID vaccination certificate) </h3> <p> The app allows you to save your vaccination certificates and keep them with you in digital form. @@ -837,8 +889,8 @@ Fourteen days after receiving the last vaccination dose required, the home screen in the app will show that you are fully vaccinated. To prove this, you can show the QR code from your digital vaccination certificate, which will be displayed in the app. The official verification - app can be used to scan this QR code and read the information about your COVID-19 vaccination. - The verification app will then show whether you are fully vaccinated. In addition, your name and + app can be used to scan this QR code and read the information about your COVID vaccination. The + verification app will then show whether you are fully vaccinated. In addition, your name and date of birth will also be displayed so that the person verifying your vaccination status can check this information against your official identification document. </p> @@ -848,13 +900,51 @@ do this in other ways. </p> <p> - Please note that the QR code from your digital vaccination certificate contains your COVID-19 + Please note that the QR code from your digital vaccination certificate contains your COVID vaccination details. You should only show it if you wish to prove your vaccination status. Do not provide vaccination certificates or QR codes to anyone if you do not want the data to be read. </p> <h3> - i. Data sharing + i. Digital test certificate (COVID test certificate) +</h3> +<p> + The app allows you to save your test certificates and keep them with you in digital form. If + necessary, you can then use the app to prove that you have tested negative if required by law +</p> +<p> + You can request a test certificate in the app when you register a test. To do this, scan the QR + code you received during the test. The app will read the information about your test from the QR + code and receive the test result from the server. If you have taken a PCR test, retrieving the + result and the test certificate is additionally secured by means of your date of birth. A + security code is generated from your date of birth (in the form of what’s known as a hash value) + and compared with the RKI server. This ensures that no one else can retrieve your test result. +</p> +<p> + The app stores the test certificate data in a secure area on your smartphone. If you retrieved a + positive test result, no test certificate will be generated. +</p> +<p> + Your current test certificate is displayed on the app’s home screen. To prove this, you can show + the QR code from your digital test certificate, which will be displayed in the app. The official + verification app can be used to scan this QR code and read the information in the test + certificate (see Section 5 i.). The verification app will then show whether you tested negative + and when the test was performed. In addition, your name and date of birth will also be displayed + so that the person verifying your vaccination status can check this information against your + official identification document. +</p> +<p> + Please note that using the feature for requesting and saving your COVID test certificates in the + app is voluntary. In cases where you are required to do so by law, there are other ways to prove + your test result. +</p> +<p> + Please note that the QR code from your digital test certificate contains your data. You should + only show it if you wish to prove your negative test result. Do not provide test certificates or + QR codes to anyone if you do not want the data to be read. +</p> +<h3> + j. Data sharing </h3> <p> Data sharing is an additional feature of the app. The usage data and other voluntary information @@ -896,7 +986,7 @@ </p> <h3> - j. Error reports + k. Error reports </h3> <p> The RKI strives to offer a bug-free app. However, due to the large number of different systems @@ -912,7 +1002,7 @@ (e.g. by email), this may reveal information about your identity. </p> <h3> - k. Surveys + l. Surveys </h3> <p> The surveys take place on a website outside of the app, which you will be redirected to. The app @@ -921,7 +1011,7 @@ </p> <h3> - l. Confirmation of the authenticity of your app + m. Confirmation of the authenticity of your app </h3> <p> @@ -970,7 +1060,7 @@ Each server system merges the positive lists received in this way with its own positive list, which allows the exposure logging feature to also take into account possible exposures involving users of another coronavirus - app (see point 6 e.) The other participating countries proceed in the same + app (see Section 6 e.) The other participating countries proceed in the same way with the positive lists provided by the RKI. </p> @@ -1136,8 +1226,8 @@ time. Please note that if entries are added to the contact journal when you check in at an event or place, these will still be stored there even after you delete the associated check-in. Once you have created your rapid test profile, it will be stored in the app until you delete it - yourself. Once you have scanned your vaccination certificates, these will also be stored in the - app until you delete them yourself. + yourself. Once you have scanned your COVID vaccination or test certificates, these will also be + stored in the app until you delete them yourself. </p> <h3> @@ -1226,7 +1316,7 @@ </p> <p> - The EU has issued an adequacy decision for Switzerland, which determines the adequacy of the level of data protection in the country (Art. 45 GDPR). In addition, please note that users can retrieve the latest positive lists regardless of where they are (even if they are abroad on holiday or on a business trip, for example). In addition, the confirmation of the authenticity of your app may involve the transfer of data + In addition, the confirmation of the authenticity of your app may involve the transfer of data to a country outside the EU. The identifier generated by your smartphone, which contains information about the version of your smartphone and the app, will be transmitted to the provider of your smartphone’s operating system (Apple or Google). This may result in data being @@ -1256,7 +1346,7 @@ </p> <h3> - a. Consent to “exposure logging†+ a. Consent to "exposure logging" </h3> <p> @@ -1392,7 +1482,7 @@ </ul> <p> You also have these data protection rights vis-à -vis the health authorities responsible for data - processing in the countries participating in the exchange server, insofar as you have + processing in the countries participating in the exchange servers, insofar as you have transmitted your random IDs from recent days to warn other people (see Section 7). </p> @@ -1420,5 +1510,5 @@ 13353 Berlin, or by emailing <a href="mailto:datenschutz@rki.de">datenschutz@rki.de</a>. </p> <p> - Last amended: 28 May 2021 -</p> + Last amended: 17 June 2021 +</p> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/assets/privacy_tr.html b/Corona-Warn-App/src/main/assets/privacy_tr.html index 1bf1c47d818fef8153a98ab5305c240b96401ef8..9458a6179c7ec17323366c45db95e628bd40c3d3 100644 --- a/Corona-Warn-App/src/main/assets/privacy_tr.html +++ b/Corona-Warn-App/src/main/assets/privacy_tr.html @@ -7,7 +7,7 @@ <p> Burada aÅŸağıdaki konular ele alınmaktadır: </p> -<ul> +<ol> <li><strong>1. Corona-Warn-App’ın yayımcısı kimdir?</strong></li> <li><strong>2. Uygulamanın kullanılması isteÄŸe baÄŸlı mı?</strong></li> <li><strong>3. Verileriniz iÅŸlenmesinde hangi yasal dayanaklar söz konusudur?</strong></li> @@ -22,7 +22,7 @@ <li><strong>12. VerdiÄŸiniz rıza beyanını nasıl geri alabilirsiniz?</strong></li> <li><strong>13. BaÅŸka hangi veri koruma haklarına sahipsiniz?</strong></li> <li><strong>14. Veri koruma görevlisi ve iletiÅŸim</strong></li> -</ul> +</ol> <p> Bu metnin tüm kullanıcılar için anlaşılabilir olması amacıyla, mümkün olduÄŸunca basit ve teknik terimler içermeyen bir metin hazırladık. @@ -49,23 +49,41 @@ <p> Uygulamanın kullanılması isteÄŸe baÄŸlıdır. Uygulamayı yüklemeniz, Uygulamanın hangi iÅŸlevlerini kullanmanız ve verileri diÄŸer kiÅŸilerle paylaÅŸmanız noktasında yalnızca siz karar verirsiniz. - KiÅŸisel verilerinizin RKI’ye veya diÄŸer kullanıcılara aktarılmasını gerektiren Uygulamanın tüm - ana iÅŸlevleri, sizden önceden açıkça rızanızı vermenizi gerektirir. Rızanızı vermezseniz veya - sonradan bu rızayı geri alırsanız, bu durum sizin için bir sakınca doÄŸurmaz. + KiÅŸisel verilerinizin RKI’ye veya diÄŸer kullanıcılara aktarılmasını gerektiren Uygulamanın temel + iÅŸlevleri için sizden önceden açıkça rızanızı vermenizi istiyoruz. Resmi dijital Covid + sertifikalarına (Covid test sertifikası ve Covid aşı sertifikası) iliÅŸkin kanıtların saÄŸlanması + için yeni yasal dayanaklar söz konusudur. Bu baÄŸlamda sizin ayrıca bir rıza beyanında bulunmanız + gerekmemektedir. Ancak bu sertifikalar yalnızca sizin talebiniz doÄŸrultusunda oluÅŸturulmaktadır. + Dijital Covid test sertifikalarını talep etmek ve kullanmak isteÄŸe baÄŸlıdır. +</p> +<p> + Bu konuda rızanızı vermezseniz, sonradan bu rızayı geri alırsanız veya dijital Covid sertifikası + talep etmezseniz, bu durum sizin için bir sakınca doÄŸurmaz. </p> <h2> 3. Verileriniz iÅŸlenmesinde hangi yasal dayanaklar söz konusudur? </h2> <p> - Esas itibariyle yalnızca siz daha önce açık bir ÅŸekilde rıza beyanında bulunmuÅŸsanız, - verileriniz ancak o zaman RKI tarafından iÅŸlenir. Buradaki yasal dayanak, GVKT (Genel Veri - Koruma Tüzüğü) madde 6, fıkra 1, cümle 1, bent a ve saÄŸlık verileri durumundaki yasal dayanak - ise GVKT madde 9, fıkra 2, bent a’dır. Daha önce vermiÅŸ olduÄŸunuz rızanızı, istediÄŸi zaman geri - alabilirsiniz (geri alma hakkı). Onayınız geri alma hakkı ile ilgili ayrıntılı bilgileri Madde - 12’de bulabilirsiniz. Günlük istatistiklerin saÄŸlanması için eriÅŸim verilerinin iÅŸlenmesi (bkz. - Madde 6 f.), GVKT madde 6, fıkra 1, bent e ile baÄŸlantılı olarak BGA-NachfG (Federal SaÄŸlık - Kurumu Halef KuruluÅŸları hakkında Kanun) madde 4, fıkra 4 uyarınca RKI tarafından toplumun - bilgilendirilmesi kapsamında gerçekleÅŸir. + Esas itibariyle yalnızca siz daha önce açıkça rıza beyanında bulunmuÅŸsanız, verileriniz ancak o + zaman RKI tarafından risk deÄŸerlendirmesi ile baÄŸlantılı olarak iÅŸlenir. Buradaki yasal dayanak, + GVKT (Genel Veri Koruma Tüzüğü) madde 6, fıkra 1, cümle 1, bent a ve saÄŸlık verileri durumundaki + yasal dayanak ise GVKT madde 9, fıkra 2, bent a’dır. Daha önce vermiÅŸ olduÄŸunuz rızanızı, + istediÄŸi zaman geri alabilirsiniz (geri alma hakkı). +</p> +<p> + Resmi dijital Covid sertifikalarına (Covid test sertifikası ve Covid aşı sertifikası) iliÅŸkin + veri iÅŸleme süreçleri yasalar çerçevesinde düzenlenmiÅŸtir. Aşı sertifikalarının oluÅŸturulması ve + belgelendirilmesi, GVKT (Genel Veri Koruma Tüzüğü) madde 9, fıkra 2, bent g ile baÄŸlantılı + olarak IfSG (Enfeksiyondan Korunma Yasası) madde 22, fıkra 5’e dayanmaktadır. Test + sertifikalarının oluÅŸturulması ve belgelendirilmesi, GVKT madde 9, fıkra 2, bent g ile + baÄŸlantılı olarak IfSG madde 22, fıkra 7’ye dayanmaktadır. +</p> +<p> + Onayınız geri alma hakkı ile ilgili ayrıntılı bilgileri Madde 12’de bulabilirsiniz. Günlük + istatistiklerin saÄŸlanması için eriÅŸim verilerinin iÅŸlenmesi (bkz. Madde 6 f), GVKT madde 6, + fıkra 1, bent e ile baÄŸlantılı olarak BGA-NachfG (Federal SaÄŸlık Kurumu Halef KuruluÅŸları + hakkında Kanun) madde 4, fıkra 4 uyarınca RKI tarafından toplumun bilgilendirilmesi kapsamında + gerçekleÅŸir. </p> <h2> 4. Uygulama kimleri hedefler? @@ -81,19 +99,20 @@ programlanmıştır. Bu demektir ki sistem, risk deÄŸerlendirmesi, diÄŸer kiÅŸilerin uyarılması ve test sonucunun alınması için, RKI’nin veya diÄŸer kullanıcıların sizin kimliÄŸinizi, adınızı, konumunuzu veya diÄŸer kiÅŸisel bilgilerinizi öğrenmesine olanak tanıyan verileri toplamamaktadır. - Kendi adınıza düzenlenen negatif hızlı test sonucu için onayı görüntüleyebileceÄŸiniz (bkz. Madde - 6c) “hızlı test sonucunu kanıtlama†fonksiyonu için istisna ve test merkezinde hızlı testin - uygulanması için gereken verilerin hazırlandığı “hızlı test profiliâ€oluÅŸturma fonksiyonu söz - konusudur (bunun için bkz. Madde 6d.). </p> - +<p> + Bu baÄŸlamda istisnalar yalnızca, kendi adınıza düzenlenen negatif hızlı test sonucuna iliÅŸkin + onayı görüntüleyebileceÄŸiniz “Hızlı test sonucunu kanıtla†iÅŸlevinde (bkz. Madde 6 c), bir test + merkezinde hızlı testin uygulanması için gereken verilerin hazırlandığı “Hızlı test profili†+ oluÅŸturma iÅŸlevinde (bunun için bkz. Madde 6 d) ve bunların yanı sıra Uygulamaya dijital Covid + sertifikaları eklediÄŸinizde söz konusudur. +</p> <p> Dolayısıyla Uygulama, standart olarak analiz araçları üzerinden kullanıcı davranışınızın herhangi bir deÄŸerlendirmesini yapmaz. Sadece isteÄŸe baÄŸlı veri bağışına açıkça onay vermeniz veya bir hata raporu kaydetmeniz ve bunu RKI’ye iletmeniz durumunda, Uygulama kullanımınıza - iliÅŸkin belirli veriler, RKI’ye aktarılacaktır (bkz. Madde 5 i ve Madde 5 k.). + iliÅŸkin belirli veriler, RKI’ye aktarılacaktır (bkz. Madde 5 j ve Madde 5 l). </p> - <p> Uygulama tarafından iÅŸlenen veriler aÅŸağıdaki kategorilerde sınıflandırılabilir: </p> @@ -295,7 +314,8 @@ Olası Korona semptomlarının baÅŸlangıcına iliÅŸkin beyanda bulunursanız veya </li> <li> - Uygulamaya dijital aşı sertifikaları eklediÄŸinizde. + Uygulamaya dijital Covid sertifikaları (aşı sertifikaları veya test sertifikaları) + eklediÄŸinizde. </li> </ul> <p> @@ -326,12 +346,12 @@ </p> <h3> - h. Korona aşılarınıza iliÅŸkin veriler + h. Korona aşılarınıza iliÅŸkin veriler (Covid aşı sertifikasındaki veriler) </h3> <p> - Aşı olduÄŸunuzda size saÄŸlanan aşı sertifikalarınızı Uygulamaya ekleyebilirsiniz. Dijital aşı - sertifikasını talep etmeniz isteÄŸe baÄŸlıdır. Bu olanaktan yararlanmak isterseniz, aşı olduktan - sonra size QR kodlu bir çıktı verilir. Bu kod, Korona aşılamanızla ilgili ÅŸu verileri içerir: + Aşı olduÄŸunuzda size saÄŸlanan resmi aşı sertifikalarınızı (dijital Covid aşı sertifikaları) + Uygulamaya ekleyebilirsiniz. Bu olanaktan yararlanmak isterseniz, aşı olduktan sonra size QR + kodlu bir çıktı verilir. Bu kod, Korona aşılamanızla ilgili ÅŸu verileri içerir </p> <ul> <li> @@ -353,7 +373,27 @@ Uygulamaya kaydedilir. </p> <h3> - i. Kullanım verileri (veri bağışı) + i. Covid test sertifikasındaki veriler +</h3> +<p> + Uygulama üzerinden resmi test sertifikaları (dijital Covid test sertifikaları) talep + edebilirsiniz. Dijital test sertifikası talep etmek isteÄŸe baÄŸlıdır ve yalnızca test sonucu + negatifse gerçekleÅŸir. Bu durumda test sertifikanız, bir QR kod ile birlikte Uygulamaya + gönderilir. Burada test sonucunuza iliÅŸkin ÅŸu veriler bulunmaktadır: +</p> +<ul> + <li>KiÅŸisel veriler (adı, soyadı, doÄŸum tarihi)</li> + <li>Testle ilgili bilgiler (hastalık, test türü, ürün, üretici)</li> + <li>Testin yapılışına iliÅŸkin veriler (testin tarihi ve saati, test merkezinin bulunduÄŸu yer) + </li> + <li>Test sonucu</li> + <li>Test sertifikasının benzersiz kimlik kodu.</li> +</ul> +<p> + Bu veriler, test sonucunuz hazır olur olmaz Uygulamaya kaydedilir. +</p> +<h3> + j. Kullanım verileri (veri bağışı) </h3> <p> Veri bağışını etkinleÅŸtirdiÄŸinizde Uygulama, Uygulama kullanımınıza iliÅŸkin belirli verileri @@ -406,15 +446,15 @@ <p> RKI, Uygulamanın etki gücünü ve iÅŸlevselliÄŸini deÄŸerlendirmek ve pandemi hakkında yeni çıkarımlar elde etmek için, kullanım verilerini olası opsiyonel veriler ile birleÅŸtirecek ve - anonimleÅŸtirilmiÅŸ istatistikler olarak deÄŸerlendirecektir + anonimleÅŸtirilmiÅŸ istatistikler olarak deÄŸerlendirecektir. </p> <p> Veri bağışına katılım, gönüllü olarak gerçekleÅŸmektedir. Veri bağışının etkinleÅŸtirilmesi, Uygulamanızın orijinalliÄŸinin doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için - bkz. Madde 5 l. ve 11.). + bkz. Madde 5 m. ve Madde 11). </p> <h3> - j. Ankete katılım + k. Ankete katılım </h3> <p> @@ -429,10 +469,10 @@ Ankete katılım isteÄŸe baÄŸlıdır. Bir ankete katılmaya ve kullanım verilerinin RKI’ye aktarılmasına, siz kendiniz karar verirsiniz. Bu anketler, yönlendirileceÄŸiniz Uygulama dışındaki bir web sitesinde gerçekleÅŸtirilir. Ankete katılım, Uygulamanızın orijinalliÄŸinin - doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için bkz. Madde 5 j. ve 11.). + doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için bkz. Madde 5 m. ve Madde 11). </p> <h3> - k. Hata raporlarının içerikleri + l. Hata raporlarının içerikleri </h3> <p> Hata analizi ile ilgili olarak Uygulama teknik desteÄŸine yardımcı olmak için Uygulamanızda hata @@ -468,9 +508,9 @@ </p> <p> Ancak hata raporu, test kaydı QR kodları ile Uygulamanızda depolanan belirteç, hızlı test - sonuçları, aşı sertifikaları ve temas günlüğünüzdeki veri giriÅŸlerine iliÅŸkin herhangi bir bilgi - içermez. Hata raporu, adınızı ve RKI tarafından kimliÄŸinizin tanımlanmasını saÄŸlayacak diÄŸer - verileri içermez. + sonuçları, dijital Covid aşı ve test sertifikaları ve temas günlüğünüzdeki veri giriÅŸlerine + iliÅŸkin herhangi bir bilgi içermez. Hata raporu, adınızı ve RKI tarafından kimliÄŸinizin + tanımlanmasını saÄŸlayacak diÄŸer verileri içermez. </p> <p> Ä°stediÄŸiniz zaman hata raporunu kaydını durdurabilir ve hata raporunu silebilirsiniz. Hata @@ -488,7 +528,7 @@ doÄŸrulanmasını gerektirir (bu konuyla ilgili daha fazla bilgi için bkz. Madde 5 l ve Madde 11). </p> <h3> - l. Uygulamanızın orijinalliÄŸinin doÄŸrulanması + m. Uygulamanızın orijinalliÄŸinin doÄŸrulanması </h3> <p> @@ -531,21 +571,28 @@ </p> <p> Pozitif listedeki rastgele kimlik numaralarını ve olay kimliklerini, ek olarak bir taşıma riski - deÄŸeri ve tanı tipi hakkında bilgi de içerir (bkz. Madde 6 e.). + deÄŸeri ve tanı tipi hakkında bilgi de içerir (bkz. Madde 6 e). </p> <p> - Uygulama pozitif listeden aldığı bu rastgele kimlik numaralarını, COVID-19 bildirim sistemine - aktarır ve bu sistem bu kodları, karşılaÅŸmalarınızda kaydedilen rastgele kimlik numaraları ile - karşılaÅŸtırır. COVID-19 bildirim sistemi bir eÅŸleÅŸme saptarsa, söz konusu maruz kalma için - kaydedilen maruz kalma verilerini Uygulamaya aktarır. Bu maruz kalma verileri ve pozitif - listedeki bilgiler (taşıma riski deÄŸeri, semptomların baÅŸlangıcına iliÅŸkin bilgiler) enfeksiyon - riskinizi belirlemek için Uygulama tarafından deÄŸerlendirmeye alınır. Bu veriler için olan - deÄŸerlendirme kuralları, RKI’nin güncel bilimsel bulgularına (örn. temas süresinin enfeksiyon - riski üzerindeki etkisi) dayanmaktadır. Yeni bulgular olması durumunda RKI, Uygulamanın - deÄŸerlendirme ayarları üzerinden bu deÄŸerlendirme kurallarını güncelleyebilmektedir. Bu durumda, - yeni deÄŸerlendirme ayarları pozitif listeyle birlikte Uygulamaya aktarılır. + Uygulama pozitif listeden aldığı bu rastgele kimlik numaralarını ve olay kimliklerini, COVID-19 + bildirim sistemine aktarır ve bu sistem bu kodları, karşılaÅŸmalarınızda kaydedilen rastgele + kimlik numaraları ile karşılaÅŸtırır. COVID-19 bildirim sistemi bir eÅŸleÅŸme saptarsa, söz konusu + maruz kalma için kaydedilen maruz kalma verilerini Uygulamaya aktarır. +</p> +<p> + Bundan baÅŸka Uygulama, Korona testi pozitif çıkan kullanıcılarla aynı anda bir olayda veya + konumda bulunmuÅŸ olmanızı saptamak için pozitif listedeki olay kimliklerini giriÅŸ + denetimlerinizin olay kimlikleriyle karşılaÅŸtırır. +</p> +<p> + Bu maruz kalma verileri, olay kimlikleri (iliÅŸkili giriÅŸ ve çıkış saatleri de dahil) ve pozitif + listelerdeki bilgiler (taşıma riski deÄŸeri, semptomların baÅŸlangıcına iliÅŸkin bilgiler) + enfeksiyon riskinizi belirlemek için Uygulama tarafından deÄŸerlendirmeye alınır. Bu veriler için + olan deÄŸerlendirme kuralları, RKI’nin güncel bilimsel bulgularına (örn. temas süresinin + enfeksiyon riski üzerindeki etkisi) dayanmaktadır. Yeni bulgular olması durumunda RKI, + Uygulamanın deÄŸerlendirme ayarları üzerinden bu deÄŸerlendirme kurallarını güncelleyebilmektedir. + Bu durumda, yeni deÄŸerlendirme ayarları pozitif listeler ile birlikte Uygulamaya aktarılır. </p> - <p> Enfeksiyon riski, yalnızca Uygulama içinde hesaplanır ve COVID-19 bildirim sistemine veya diÄŸer alıcılara (RKI, Almanya’daki diÄŸer saÄŸlık kurumları veya diÄŸer ülkeler, Apple, Google ve diÄŸer @@ -555,12 +602,15 @@ <p> Sizin için bir enfeksiyon riski hesaplanırsa, bu deÄŸer Uygulamada görüntülenecektir. Görüntülenen bu risk deÄŸeri yüksek ise, bunun anlamı, sizin Korona testi pozitif çıkan ve bir - uyarı tetikleyen diÄŸer kullanıcılarla bir veya daha çok kez karşılaÅŸmış olmanızdır. Son 14 gün - için hesaplanan risk, iletiÅŸim günlüğünün takvim görünümünde görüntülenir. Lütfen riskin - kaynağıyla ilgili yanlış çıkarımlar yapmaktan kaçının: Belli bir gün için hesaplanan ve + uyarı tetikleyen diÄŸer kullanıcılarla bir veya daha çok kez karşılaÅŸmış veya bu tür kullanıcılar + ile aynı anda bir olay veya bir konuma giriÅŸ yapmış olmanızdır. +</p> +<p> + Son 14 gün için hesaplanan risk, iletiÅŸim günlüğünün takvim görünümünde görüntülenir. Lütfen + riskin kaynağıyla ilgili yanlış çıkarımlar yapmaktan kaçının: Belli bir gün için hesaplanan ve görüntülenen bir risk, tanımadığınız kullanıcılarla farkına varmadığınız bir karşılaÅŸmaya kadar - geri gidebilir ve mutlaka iletiÅŸim günlüğüne girdiÄŸiniz kiÅŸiler veya yerlerle ilgili olması - gerekmez. + geri gidebilir ve mutlaka iletiÅŸim günlüğüne girdiÄŸiniz kiÅŸiler, konumlar veya olaylar ile + ilgili olması gerekmez. </p> <h3> b. Test sonucu çağırın @@ -630,6 +680,10 @@ mevcudiyetini doÄŸrulamasını ister. Ä°lgili doÄŸrulama alındığında, sunucu sistemi TAN’ı oluÅŸturur ve bunu uygulamaya iletir. Bu TAN’ın bir kopyası sunucu sisteminde kalır. </p> +<p> + Test sonuçları ayrıca temas günlüğünüzde de saklanır. Bunları istediÄŸiniz zaman oradan + kaldırabilirsiniz. +</p> <h3> c. Hızlı test sonucunun kanıtı </h3> @@ -661,6 +715,10 @@ Uygulamadan hemen silinir. DiÄŸer hızlı test verileriniz Uygulamada poztif çıkan hızlı test sonucu görüntülenmediÄŸi anda (kodlar, test zamanı) silinir. </p> +<p> + Hızlı test sonuçları ayrıca temas günlüğünüzde de saklanır. Bunları istediÄŸiniz zaman oradan + kaldırabilirsiniz. +</p> <h3> d. Hızlı test profili </h3> @@ -780,7 +838,7 @@ enfeksiyonların önüne geçebilir. </p> <h3> - h. Dijital aşı sertifikası + h. Dijital aşı sertifikası (Covid aşı sertifikası) </h3> <p> Uygulama, aşı sertifikalarınızı kaydetmenizi ve dijital olarak yanınızda bulundurmanızı mümkün @@ -812,13 +870,51 @@ okunmasını istemiyorsanız, aşı sertifikasını ve QR kodu hiç kimseye göstermeyin. </p> <h3> - i. Veri bağışı + i. Dijital test kanıtı (Covid test sertifikası) +</h3> +<p> + Uygulama, test sertifikalarınızı kaydetmenizi ve dijital olarak yanınızda bulundurmanızı mümkün + kılar. Gerekli olduÄŸunda, yasal açıdan zorunlu durumlarda, yaptırdığınız testin negatif + çıktığını kanıtlamak için Uygulamayı kullanabilirsiniz. +</p> +<p> + Bir testi kaydettiÄŸinizde, Uygulamada bir test sertifikası talep edebilirsiniz. Bunun için, test + iÅŸlemi sırasında aldığınız QR kodu tarayın. Uygulama, testinize iliÅŸkin verileri QR kod + üzerinden okur ve test sonucunu ilgili sunucudan alır. Bir PCR testi yaptırdığınızda, ilgili + sonuç alınıp, test sertifikası ve ayrıca doÄŸum tarihinizle birlikte kaydedilir. DoÄŸum tarihinden + bir güvenlik kodu (hash deÄŸeri ÅŸeklinde) oluÅŸturulur ve bu deÄŸer, RKI sunucusu ile + karşılaÅŸtırılır. Bu sayede diÄŸer kiÅŸilerin test sonucunuzu çağırmasının önüne geçilir. +</p> +<p> + Uygulama, aldığı test sertifikası verilerini akıllı telefonunuzun güvenli bir alanına kaydeder. + Test sonucunuz pozitif çıkmışsa, herhangi bir test sertifikası oluÅŸturulmaz. +</p> +<p> + Uygulamadaki ana ekranda güncel test sertifikanız görüntülenir. Uygulamadaki dijital test + sertifikasının QR kodu gerektiÄŸinde kanıt olarak gösterebilirsiniz. Bu QR kod, resmi kontrol + uygulamasıyla taranabilir ve test sertifikasındaki veriler buradan okunabilir (Bkz. Madde 5 i). + Daha sonra kontrol uygulamasında ne zaman test yaptırdığınız ve test sonucunuzun negatif çıkıp + çıkmadığı görüntülenir. Ayrıca adınız ve doÄŸum tarihiniz de görüntülenir, bu sayede kontrol eden + kiÅŸi bu bilgileri resmi kimlik belgenizdeki bilgiler ile karşılaÅŸtırabilir. +</p> +<p> + Uygulamadaki Covid test sertifikalarınızı talep etme ve kaydetme iÅŸlevlerini kullanmanın isteÄŸe + baÄŸlı olduÄŸunu lütfen göz önünde bulundurun. Yasal açıdan zorunlu durumlarda test sonucunuzu + diÄŸer yöntemlerle de kanıtlayabilirsiniz. +</p> +<p> + Dijital test sertifikanızdaki QR kodun veriler içerdiÄŸini lütfen unutmayın. Yalnızca negatif + test sonucunu kanıtlamak istediÄŸinizde bunu gösterin. Söz konusu verilerin okunmasını + istemiyorsanız, test sertifikasını ve QR kodu hiç kimseye göstermeyin. +</p> +<h3> + j. Veri bağışı </h3> <p> - Veri bağışı, Uygulamanın ek bir iÅŸlevidir. Veri bağışı kapsamında RKI’ye aktarılan kullanım - verileri ve diÄŸer isteÄŸe baÄŸlı veriler, Uygulamanın etki gücünü ölçmek ve aÅŸağıda sıralanan - iyileÅŸtirmeleri saÄŸlamak üzere deÄŸerlendirmeler yapmak üzere kullanılır: + Veri bağışı kapsamında RKI’ye aktarılan kullanım verileri ve diÄŸer isteÄŸe baÄŸlı veriler, + Uygulamanın etki gücünü ölçmek ve aÅŸağıda sıralanan iyileÅŸtirmeleri saÄŸlamak üzere + deÄŸerlendirmeler yapmak üzere kullanılır: </p> <ul> <li> @@ -839,7 +935,7 @@ tesisler ve laboratuvarlarla baÄŸlantılı olarak veya belirli coÄŸrafi bölgelerde Uygulamanın kullanımıyla ilgili olası sorunların varlığını saptamak hedeflenir. Böylece veri bağışı nedeniyle örneÄŸin, test sonuçlarının belirli coÄŸrafi bölgelerde geç elde edileceÄŸinin - farkına varılabilmektedir. Bu sayede sorumlu saÄŸlık kurumu yetkilileri de olası teknik + farkına varmak mümkün olabilir. Bu sayede sorumlu saÄŸlık kurumu yetkilileri de olası teknik arızalar hakkında bilgilendirilebilmektedir. </li> <li> @@ -854,7 +950,7 @@ öğrenmez. </p> <h3> - j. Hata raporları + k. Hata raporları </h3> <p> @@ -872,7 +968,7 @@ çıkarabileceÄŸini göz önünde bulundurun. </p> <h3> - k. Anketler + l. Anketler </h3> <p> @@ -881,7 +977,7 @@ amaçları, anket web sitesinde anketteki bilgilerde yer almaktadır. </p> <h3> - l. Uygulamanızın orijinalliÄŸinin doÄŸrulanması + m. Uygulamanızın orijinalliÄŸinin doÄŸrulanması </h3> <p> @@ -900,8 +996,7 @@ DiÄŸer ülkelerdeki resmi Korona uygulamalarının kullanıcılarının da uyarılmasını için RKI, bu ülkelerdeki sorumlu merciler ve kurumlar (bundan böyle: <strong>saÄŸlık kurumları</strong>) ile birlikte uyarı mesajlarının sınır ötesi deÄŸiÅŸimine yönelik merkezi uyarı sunucuları(bundan - böyle: <strong>veri deÄŸiÅŸim sunucusu</strong>) kurmuÅŸtur. Veri deÄŸiÅŸim sunucusu, elektronik - saÄŸlık hizmetleri için üye devletler arasında kurulmuÅŸ olan ağın dijital altyapısını kullanır. + böyle: <strong>veri deÄŸiÅŸim sunucusu</strong>) kurmuÅŸtur. </p> <ul> <li>Avrupa BirliÄŸi üye devletlerinden katılımcı ülkelerin veri deÄŸiÅŸim sunucusu, üye devletler @@ -939,16 +1034,16 @@ Kararı (AB) 2020/1023, bu karara <a href="https://eur-lex.europa.eu/eli/dec_impl/2020/1023/oj"> https://eur-lex.europa.eu/eli/dec_impl/2020/1023/oj - </a>. adresinden eriÅŸebilirsiniz. RKI ve katılımcı ülkelerin yetkili saÄŸlık kurumları, sınır - ötesi risk deÄŸerlendirmesi ve uyarısı saÄŸlamak üzere veri deÄŸiÅŸim sunucularında bulunan - pozitif listelerde yer alan verilerin (rastgele kimlik numaraları ve semptomların + </a>). adresinden eriÅŸebilirsiniz. RKI ve katılımcı ülkelerin yetkili saÄŸlık kurumları, + sınır ötesi risk deÄŸerlendirmesi ve uyarısı saÄŸlamak üzere veri deÄŸiÅŸim sunucularında + bulunan pozitif listelerde yer alan verilerin (rastgele kimlik numaraları ve semptomların baÅŸlangıcına iliÅŸkin olası bilgiler) iÅŸlenmesinden müştereken sorumludur. </li> <li> Ä°sviçre ile ortaklaÅŸa iÅŸletilen veri deÄŸiÅŸim sunucusunun çalışması ve veri alışveriÅŸi, RKI ve Ä°sviçre arasında özel bir anlaÅŸmada düzenlenmekte olup, ayrıntıları <a href="https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/WarnApp/Warn_App.html"> - https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/WarnApp/Warn_App.html</a> + https://www.rki.de/DE/Content/InfAZ/N/Neuartiges_Coronavirus/WarnApp/Warn_App.html</a>. adresinden eriÅŸilebilir. Veri deÄŸiÅŸim sunucusunun teknik iÅŸletimi, Ä°sviçre Federal SaÄŸlık Dairesi tarafından gerçekleÅŸtirilmektedir. Pozitif listelerin saklanması, saÄŸlanması ve daha sonra silinmesinden, RKI ve Ä°sviçre Federal Halk SaÄŸlığı Dairesi müştereken sorumludur. @@ -1074,8 +1169,8 @@ silinir. Ancak bu verileri istediÄŸiniz zaman, daha erken de silebilirsiniz. Bir olaya veya bir konuma giriÅŸ denetimi yaptırdığınızda, ilgili giriÅŸ denetimi kaydı silindikten sonra bile temas günlüğündeki verilerin hâlâ orada saklı kaldıklarını lütfen unutmayın. Bir kez hızlı test - profili oluÅŸturduysanız bu, siz tekrar silene kadar uygulamada kalır. Aşı sertifikalarını - taradıysanız, bu veriler siz onları silene kadar Uygulamada kayıtlı kalacaktır. + profili oluÅŸturduysanız bu, siz tekrar silene kadar uygulamada kalır. Covid aşı ve test + sertifikalarını taradıysanız, bu veriler siz onları silene kadar Uygulamada kayıtlı kalacaktır. </p> <h3> b. Sunucu sistemlerindeki veriler @@ -1083,7 +1178,8 @@ <p> Pozitif listeler, 14 gün sonra tüm sunucu sistemlerinden (deÄŸiÅŸim sunucuları dahil) silinir. - DiÄŸer tüm veriler en geç 21 gün sonra silinir. + Veri bağışı kapsamında ve Uygulamanızın orijinalliÄŸinin doÄŸrulanması için aktarılan veriler + haricinde diÄŸer tüm veriler en geç 21 gün sonra silinir. </p> <h3> c. Veri bağışı @@ -1328,5 +1424,5 @@ DiÄŸer insanları uyarmak için test sonucunuzun ( (daha doÄŸrusu, son 14 güne görevlisi), Nordufer 20,13353 Berlin veya e-posta yoluyla: <a href="mailto:datenschutz@rki.de">datenschutz@rki.de</a>. </p> <p> - Baskı 28.05.2021 + Baskı 17.06.2021 </p> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt index b6a78237228f270bc1fd9ec93efd44eb389e0f0c..e9b71a70568681e1083261c7ff6011b1fd4b81e7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/CoronaWarnApplication.kt @@ -21,6 +21,8 @@ import de.rki.coronawarnapp.coronatest.type.pcr.execution.PCRResultScheduler import de.rki.coronawarnapp.coronatest.type.pcr.notification.PCRTestResultAvailableNotificationService import de.rki.coronawarnapp.coronatest.type.rapidantigen.execution.RAResultScheduler import de.rki.coronawarnapp.coronatest.type.rapidantigen.notification.RATTestResultAvailableNotificationService +import de.rki.coronawarnapp.covidcertificate.test.core.execution.TestCertificateRetrievalScheduler +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.VaccinationUpdateScheduler import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler import de.rki.coronawarnapp.exception.reporting.ErrorReportReceiver @@ -42,7 +44,6 @@ import de.rki.coronawarnapp.util.device.ForegroundState import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.ApplicationComponent import de.rki.coronawarnapp.util.hasAPILevel -import de.rki.coronawarnapp.vaccination.core.execution.VaccinationUpdateScheduler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -83,6 +84,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector { @Inject lateinit var pcrTestResultAvailableNotificationService: PCRTestResultAvailableNotificationService @Inject lateinit var raTestResultAvailableNotificationService: RATTestResultAvailableNotificationService @Inject lateinit var vaccinationUpdateScheduler: VaccinationUpdateScheduler + @Inject lateinit var testCertificateRetrievalScheduler: TestCertificateRetrievalScheduler @AppScope @Inject lateinit var appScope: CoroutineScope @@ -138,6 +140,7 @@ class CoronaWarnApplication : Application(), HasAndroidInjector { Timber.v("Setting up test result available notification services.") pcrTestResultAvailableNotificationService.setup() raTestResultAvailableNotificationService.setup() + testCertificateRetrievalScheduler.setup() Timber.v("Setting up vaccination data update scheduler.") vaccinationUpdateScheduler.setup() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt index d076c040865e4500f5022287c34febb3fe5b2857..f1377d38f70d387cd484bca2f4b4a2193232c7d8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt @@ -7,6 +7,7 @@ import de.rki.coronawarnapp.appconfig.download.AppConfigApiV2 import de.rki.coronawarnapp.appconfig.mapping.AnalyticsConfigMapper import de.rki.coronawarnapp.appconfig.mapping.CWAConfigMapper import de.rki.coronawarnapp.appconfig.mapping.CoronaTestConfigMapper +import de.rki.coronawarnapp.appconfig.mapping.CovidCertificateConfigMapper import de.rki.coronawarnapp.appconfig.mapping.ExposureDetectionConfigMapper import de.rki.coronawarnapp.appconfig.mapping.ExposureWindowRiskCalculationConfigMapper import de.rki.coronawarnapp.appconfig.mapping.KeyDownloadParametersMapper @@ -96,6 +97,10 @@ class AppConfigModule { fun coronaTestConfigMapper(mapper: CoronaTestConfigMapper): CoronaTestConfig.Mapper = mapper + @Provides + fun covidCertificateConfigMapper(mapper: CovidCertificateConfigMapper): + CovidCertificateConfig.Mapper = mapper + companion object { private val HTTP_TIMEOUT_APPCONFIG = Duration.standardSeconds(10) private const val DEFAULT_CACHE_SIZE = 2 * 1024 * 1024L // 5MB diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/CovidCertificateConfig.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/CovidCertificateConfig.kt new file mode 100644 index 0000000000000000000000000000000000000000..620b5e4ab2cbfb2432a70b889c27fd47b3385711 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/CovidCertificateConfig.kt @@ -0,0 +1,16 @@ +package de.rki.coronawarnapp.appconfig + +import de.rki.coronawarnapp.appconfig.mapping.ConfigMapper +import org.joda.time.Duration + +interface CovidCertificateConfig { + + val testCertificate: TestCertificate + + interface TestCertificate { + val waitAfterPublicKeyRegistration: Duration + val waitForRetry: Duration + } + + interface Mapper : ConfigMapper<CovidCertificateConfig> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigMapping.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigMapping.kt index d0d3790269465d69bc2423d9ea0175395f2275d4..aaac8ae61dc174a5b5e618200dee68fd1c165b9a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigMapping.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigMapping.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.appconfig.mapping import de.rki.coronawarnapp.appconfig.AnalyticsConfig import de.rki.coronawarnapp.appconfig.CWAConfig import de.rki.coronawarnapp.appconfig.CoronaTestConfig +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig import de.rki.coronawarnapp.appconfig.KeyDownloadConfig @@ -24,4 +25,5 @@ interface ConfigMapping : val logUpload: LogUploadConfig val presenceTracing: PresenceTracingConfig val coronaTestParameters: CoronaTestConfig + val covidCertificateParameters: CovidCertificateConfig } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParser.kt index ced74a1e8d5cd4bc74b31b88e94c3a0c30466333..b29c6b17d9e056b96b9a563e6d91ea493564954d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParser.kt @@ -4,6 +4,7 @@ import dagger.Reusable import de.rki.coronawarnapp.appconfig.AnalyticsConfig import de.rki.coronawarnapp.appconfig.CWAConfig import de.rki.coronawarnapp.appconfig.CoronaTestConfig +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig import de.rki.coronawarnapp.appconfig.KeyDownloadConfig @@ -14,6 +15,7 @@ import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") @Reusable class ConfigParser @Inject constructor( private val cwaConfigMapper: CWAConfig.Mapper, @@ -25,6 +27,7 @@ class ConfigParser @Inject constructor( private val logUploadConfigMapper: LogUploadConfig.Mapper, private val presenceTracingConfigMapper: PresenceTracingConfig.Mapper, private val coronaTestConfigMapper: CoronaTestConfig.Mapper, + private val covidCertificateConfigMapper: CovidCertificateConfig.Mapper, ) { fun parse(configBytes: ByteArray): ConfigMapping = try { @@ -39,7 +42,8 @@ class ConfigParser @Inject constructor( analytics = analyticsConfigMapper.map(it), logUpload = logUploadConfigMapper.map(it), presenceTracing = presenceTracingConfigMapper.map(it), - coronaTestParameters = coronaTestConfigMapper.map(it) + coronaTestParameters = coronaTestConfigMapper.map(it), + covidCertificateParameters = covidCertificateConfigMapper.map(it), ) } } catch (e: Exception) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..e0ca90bd9242a383e173be4d47e3336a786c2079 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapper.kt @@ -0,0 +1,59 @@ +package de.rki.coronawarnapp.appconfig.mapping + +import dagger.Reusable +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig +import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid +import de.rki.coronawarnapp.server.protocols.internal.v2.DgcParameters +import org.joda.time.Duration +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class CovidCertificateConfigMapper @Inject constructor() : CovidCertificateConfig.Mapper { + override fun map(rawConfig: AppConfigAndroid.ApplicationConfigurationAndroid): CovidCertificateConfig { + if (!rawConfig.hasDgcParameters()) { + Timber.w("Config has no DCC parameters.") + return CovidCertificateConfigContainer() + } + + return CovidCertificateConfigContainer( + testCertificate = rawConfig.dgcParameters.mapCovidCertificateConfig() + ) + } + + private fun DgcParameters.DGCParameters.mapCovidCertificateConfig(): CovidCertificateConfig.TestCertificate { + if (!this.hasTestCertificateParameters()) { + Timber.w("DCC config has no test certificate parameters.") + return TestCertificateConfigContainer() + } + return with(testCertificateParameters) { + TestCertificateConfigContainer( + waitAfterPublicKeyRegistration = waitAfterPublicKeyRegistrationInSeconds.let { + if (it !in 0..60) { + Timber.e("Invalid value for waitAfterPublicKeyRegistration: %s", it) + TestCertificateConfigContainer().waitAfterPublicKeyRegistration + } else { + Duration.standardSeconds(it.toLong()) + } + }, + waitForRetry = waitForRetryInSeconds.let { + if (it !in 0..60) { + Timber.e("Invalid value for waitForRetryInSeconds: %s", it) + TestCertificateConfigContainer().waitForRetry + } else { + Duration.standardSeconds(it.toLong()) + } + } + ) + } + } + + data class CovidCertificateConfigContainer( + override val testCertificate: CovidCertificateConfig.TestCertificate = TestCertificateConfigContainer() + ) : CovidCertificateConfig + + data class TestCertificateConfigContainer( + override val waitAfterPublicKeyRegistration: Duration = Duration.standardSeconds(10), + override val waitForRetry: Duration = Duration.standardSeconds(10), + ) : CovidCertificateConfig.TestCertificate +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/DefaultConfigMapping.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/DefaultConfigMapping.kt index 220c854e09f792a985f353684c937fd5b0f655a1..fe11403406d419a66895c4b9d78987d7448b9cd0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/DefaultConfigMapping.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/DefaultConfigMapping.kt @@ -3,6 +3,7 @@ package de.rki.coronawarnapp.appconfig.mapping import de.rki.coronawarnapp.appconfig.AnalyticsConfig import de.rki.coronawarnapp.appconfig.CWAConfig import de.rki.coronawarnapp.appconfig.CoronaTestConfig +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig import de.rki.coronawarnapp.appconfig.KeyDownloadConfig @@ -22,6 +23,7 @@ data class DefaultConfigMapping( override val logUpload: LogUploadConfig, override val presenceTracing: PresenceTracingConfig, override val coronaTestParameters: CoronaTestConfig, + override val covidCertificateParameters: CovidCertificateConfig, ) : ConfigMapping, CWAConfig by cwaConfig, KeyDownloadConfig by keyDownloadConfig, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensor.kt index 83c22418c7b6b6abe8cb5eaca26ac82b059009c4..8ea9f61d764bd44df49652abd76dce8ee9e8ba1c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensor.kt @@ -3,8 +3,9 @@ package de.rki.coronawarnapp.bugreporting.censors.vaccination import dagger.Reusable import de.rki.coronawarnapp.bugreporting.censors.BugCensor import de.rki.coronawarnapp.bugreporting.censors.BugCensor.CensorContainer -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateData +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 import java.util.LinkedList import javax.inject.Inject @@ -35,7 +36,7 @@ class CertificateQrCodeCensor @Inject constructor() : BugCensor { newMessage = censorNameData(nameData, newMessage) - vaccinationDatas.forEach { data -> + payload.let { data -> newMessage = censorVaccinationData(data, newMessage) } } @@ -45,7 +46,7 @@ class CertificateQrCodeCensor @Inject constructor() : BugCensor { } private fun censorVaccinationData( - vaccinationData: VaccinationDGCV1.VaccinationData, + vaccinationData: VaccinationDccV1.VaccinationData, message: CensorContainer ): CensorContainer { var newMessage = message @@ -76,8 +77,8 @@ class CertificateQrCodeCensor @Inject constructor() : BugCensor { ) newMessage = newMessage.censor( - vaccinationData.countryOfVaccination, - "vaccinationData/countryOfVaccination" + vaccinationData.certificateCountry, + "vaccinationData/certificateCountry" ) newMessage = newMessage.censor( @@ -100,7 +101,7 @@ class CertificateQrCodeCensor @Inject constructor() : BugCensor { return newMessage } - private fun censorNameData(nameData: VaccinationDGCV1.NameData, message: CensorContainer): CensorContainer { + private fun censorNameData(nameData: Dcc.NameData, message: CensorContainer): CensorContainer { var newMessage = message nameData.familyName?.let { fName -> @@ -146,8 +147,8 @@ class CertificateQrCodeCensor @Inject constructor() : BugCensor { fun clearQRCodeStringToCensor() = synchronized(qrCodeStringsToCensor) { qrCodeStringsToCensor.clear() } - private val certsToCensor = LinkedList<VaccinationCertificateData>() - fun addCertificateToCensor(cert: VaccinationCertificateData) = synchronized(certsToCensor) { + private val certsToCensor = LinkedList<DccData<VaccinationDccV1>>() + fun addCertificateToCensor(cert: DccData<VaccinationDccV1>) = synchronized(certsToCensor) { certsToCensor.apply { if (contains(cert)) return@apply addFirst(cert) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt index d0c63e34ebdf841d46c6b1cbb8eb799512251180..cf20181d55891f596a560624e7f60d46c3628413 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/storage/entity/ContactDiaryCoronaTestEntity.kt @@ -48,7 +48,7 @@ fun Map.Entry<CoronaTestGUID, CoronaTest>.asTestResultEntity(): ContactDiaryCoro testType = if (type == CoronaTest.Type.PCR) PCR else ANTIGEN, result = if (isPositive) POSITIVE else NEGATIVE, time = when (this) { - is RACoronaTest -> testedAt + is RACoronaTest -> testTakenAt else -> registeredAt } ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt index b4295fc63485450d62cca4ca0ddcdd2552c533fe..1445d927e4f2eed79b3ba219c0708249eb33ee07 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestModule.kt @@ -5,8 +5,8 @@ import dagger.Module import dagger.multibindings.IntoSet import de.rki.coronawarnapp.coronatest.server.VerificationModule import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor -import de.rki.coronawarnapp.coronatest.type.pcr.PCRProcessor -import de.rki.coronawarnapp.coronatest.type.rapidantigen.RAProcessor +import de.rki.coronawarnapp.coronatest.type.pcr.PCRTestProcessor +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RATestProcessor @Module( includes = [VerificationModule::class] @@ -16,12 +16,12 @@ abstract class CoronaTestModule { @Binds @IntoSet abstract fun pcrProcessor( - processor: PCRProcessor + processor: PCRTestProcessor ): CoronaTestProcessor @Binds @IntoSet abstract fun ratProcessor( - processor: RAProcessor + processor: RATestProcessor ): CoronaTestProcessor } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt index c0815c4f3ca43e16e2bab4cc6146b0e488909d99..9af108afeaf5050149af78eed0d289005709cea8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/CoronaTestRepository.kt @@ -224,6 +224,14 @@ class CoronaTestRepository @Inject constructor( } } + suspend fun markDccAsCreated(identifier: TestIdentifier, created: Boolean) { + Timber.tag(TAG).i("markDccAsCreated(identifier=%s, created=%b)", identifier, created) + + modifyTest(identifier) { processor, before -> + processor.markDccCreated(before, created) + } + } + companion object { const val TAG = "CoronaTestRepository" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt index 703886f8eb57475d09d92de7a2779394d92972ac..2265512e021fba44f390d1bc7eb90bd4d79d4e7a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/qrcode/RapidAntigenQrCodeExtractor.kt @@ -147,15 +147,9 @@ class RapidAntigenQrCodeExtractor @Inject constructor() : QrCodeExtractor<Corona private fun requireValidHash() { val isQrCodeWithPersonalData = firstName != null && lastName != null && dateOfBirth != null - val rawBuilder = StringBuilder( + val generatedHash = "${raw.dateOfBirth}#${raw.firstName}#${raw.lastName}#${raw.timestamp}#${raw.testid}#${raw.salt}" - ) - if (raw.dgc != null) { - val asInt = if (raw.dgc == true) 1 else 0 - rawBuilder.append("#$asInt") - } - - val generatedHash = rawBuilder.toString().toSHA256() + .toSHA256() if (isQrCodeWithPersonalData && !generatedHash.equals(hash, true)) { throw InvalidQRCodeException("Generated hash doesn't match QRCode hash") } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt index f516a6e5e67ed5e7f74e72661b2c4411b60e5b9e..824a468b746938f0b30c3c532cc7f00800ad8cd4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTest.kt @@ -43,7 +43,7 @@ interface CoronaTest { // Is the digital green certificate supported by the point of care that issued the test val isDccSupportedByPoc: Boolean - // Has the user given consent to us obtaining the DGC + // Has the user given consent to us obtaining the DCC val isDccConsentGiven: Boolean // Has the corresponding entry been created in the test certificate storage diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt index 68b69445ce7604d310952cb757277f46d3ff4883..32351e107021eafe1e4fbd2ad37d2979f7980a2b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/CoronaTestProcessor.kt @@ -24,4 +24,6 @@ interface CoronaTestProcessor { suspend fun updateSubmissionConsent(test: CoronaTest, consented: Boolean): CoronaTest suspend fun updateResultNotification(test: CoronaTest, sent: Boolean): CoronaTest + + suspend fun markDccCreated(test: CoronaTest, created: Boolean): CoronaTest } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRTestProcessor.kt similarity index 95% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRTestProcessor.kt index a4e0346e2479e26e990de87ed99e6fa993ff00d9..58bfe8a28d16fd55ab3d07efd251fd7f4458e288 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRTestProcessor.kt @@ -37,7 +37,7 @@ import timber.log.Timber import javax.inject.Inject @Reusable -class PCRProcessor @Inject constructor( +class PCRTestProcessor @Inject constructor( private val timeStamper: TimeStamper, private val submissionService: CoronaTestService, private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector, @@ -129,6 +129,7 @@ class PCRProcessor @Inject constructor( registrationToken = response.registrationToken, testResult = testResult, testResultReceivedAt = determineReceivedDate(null, testResult), + isDccConsentGiven = request.isDccConsentGiven, ) } @@ -245,6 +246,13 @@ class PCRProcessor @Inject constructor( return test.copy(isResultAvailableNotificationSent = sent) } + override suspend fun markDccCreated(test: CoronaTest, created: Boolean): CoronaTest { + Timber.tag(TAG).v("markDccCreated(test=%s, created=%b)", test, created) + test as PCRCoronaTest + + return test.copy(isDccDataSetCreated = created) + } + companion object { private val FINAL_STATES = setOf(PCR_POSITIVE, PCR_NEGATIVE, PCR_REDEEMED) internal const val TAG = "PCRProcessor" @@ -269,7 +277,7 @@ private fun CoronaTestResult.toValidatedResult(): CoronaTestResult { return if (isValid) { this } else { - Timber.tag(PCRProcessor.TAG).e("Server returned invalid PCR testresult $this") + Timber.tag(PCRTestProcessor.TAG).e("Server returned invalid PCR testresult $this") PCR_INVALID } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RATestProcessor.kt similarity index 95% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RATestProcessor.kt index c2b3911c61d37aa919b21a2cdd1d1ddd8c17f37f..075881497a8447e524264062768309f02867f60d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RATestProcessor.kt @@ -21,7 +21,6 @@ import de.rki.coronawarnapp.coronatest.server.VerificationServer import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.CoronaTestProcessor import de.rki.coronawarnapp.coronatest.type.CoronaTestService -import de.rki.coronawarnapp.coronatest.type.common.DateOfBirthKey import de.rki.coronawarnapp.coronatest.type.isOlderThan21Days import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector import de.rki.coronawarnapp.datadonation.analytics.modules.testresult.AnalyticsTestResultCollector @@ -36,7 +35,7 @@ import timber.log.Timber import javax.inject.Inject @Reusable -class RAProcessor @Inject constructor( +class RATestProcessor @Inject constructor( private val timeStamper: TimeStamper, private val submissionService: CoronaTestService, private val analyticsKeySubmissionCollector: AnalyticsKeySubmissionCollector, @@ -56,13 +55,9 @@ class RAProcessor @Inject constructor( analyticsKeySubmissionCollector.reset(type) analyticsTestResultCollector.clear(type) - val dateOfBirthKey = if (request.isDccConsentGiven && request.dateOfBirth != null) { - DateOfBirthKey(request.registrationIdentifier, request.dateOfBirth) - } else null - val serverRequest = RegistrationRequest( key = request.registrationIdentifier, - dateOfBirthKey = dateOfBirthKey, + dateOfBirthKey = null, type = VerificationKeyType.GUID ) @@ -102,6 +97,7 @@ class RAProcessor @Inject constructor( dateOfBirth = request.dateOfBirth, sampleCollectedAt = sampleCollectedAt, isDccSupportedByPoc = request.isDccSupportedByPoc, + isDccConsentGiven = request.isDccConsentGiven, ) } @@ -224,6 +220,13 @@ class RAProcessor @Inject constructor( return test.copy(isResultAvailableNotificationSent = sent) } + override suspend fun markDccCreated(test: CoronaTest, created: Boolean): CoronaTest { + Timber.tag(TAG).v("markDccCreated(test=%s, created=%b)", test, created) + test as RACoronaTest + + return test.copy(isDccDataSetCreated = created) + } + companion object { private val FINAL_STATES = setOf(RAT_POSITIVE, RAT_NEGATIVE, RAT_REDEEMED) internal const val TAG = "RapidAntigenProcessor" @@ -248,7 +251,7 @@ private fun CoronaTestResult.toValidatedResult(): CoronaTestResult { return if (isValid) { this } else { - Timber.tag(RAProcessor.TAG).e("Server returned invalid RapidAntigen testresult $this") + Timber.tag(RATestProcessor.TAG).e("Server returned invalid RapidAntigen testresult $this") RAT_INVALID } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..f99403437330ace983a3c86c592dac778b2710eb --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/DigitalCovidCertificateModule.kt @@ -0,0 +1,15 @@ +package de.rki.coronawarnapp.covidcertificate + +import dagger.Module +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateServerModule +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationModule +import de.rki.coronawarnapp.covidcertificate.valueset.CertificateValueSetModule + +@Module( + includes = [ + CertificateValueSetModule::class, + TestCertificateServerModule::class, + VaccinationModule::class, + ] +) +abstract class DigitalCovidCertificateModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CertificatePersonIdentifier.kt similarity index 53% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CertificatePersonIdentifier.kt index aea20ab9b2e412ec1f7862b0bb8f12f8d1c710e3..3148343d4e36ea6d868b1aef3547109cd29658ed 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CertificatePersonIdentifier.kt @@ -1,14 +1,13 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.common.certificate +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_DOB_MISMATCH +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_NAME_MISMATCH +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException import de.rki.coronawarnapp.util.HashExtensions.toSHA256 -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateQRCode import org.joda.time.LocalDate import timber.log.Timber -data class VaccinatedPersonIdentifier( +data class CertificatePersonIdentifier( val dateOfBirth: LocalDate, val lastNameStandardized: String, val firstNameStandardized: String? @@ -32,28 +31,18 @@ data class VaccinatedPersonIdentifier( code.toSHA256() } - fun requireMatch(other: VaccinatedPersonIdentifier) { + fun requireMatch(other: CertificatePersonIdentifier) { if (lastNameStandardized != other.lastNameStandardized) { Timber.d("Family name does not match, got ${other.lastNameStandardized}, expected $lastNameStandardized") - throw InvalidHealthCertificateException(ErrorCode.VC_NAME_MISMATCH) + throw InvalidVaccinationCertificateException(VC_NAME_MISMATCH) } if (firstNameStandardized != other.firstNameStandardized) { Timber.d("Given name does not match, got ${other.firstNameStandardized}, expected $firstNameStandardized") - throw InvalidHealthCertificateException(ErrorCode.VC_NAME_MISMATCH) + throw InvalidVaccinationCertificateException(VC_NAME_MISMATCH) } if (dateOfBirth != other.dateOfBirth) { Timber.d("Date of birth does not match, got ${other.dateOfBirth}, expected $dateOfBirth") - throw InvalidHealthCertificateException(ErrorCode.VC_DOB_MISMATCH) + throw InvalidVaccinationCertificateException(VC_DOB_MISMATCH) } } } - -val VaccinationDGCV1.personIdentifier: VaccinatedPersonIdentifier - get() = VaccinatedPersonIdentifier( - dateOfBirth = dateOfBirth, - lastNameStandardized = nameData.familyNameStandardized, - firstNameStandardized = nameData.givenNameStandardized - ) - -val VaccinationCertificateQRCode.personIdentifier: VaccinatedPersonIdentifier - get() = parsedData.certificate.personIdentifier diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt new file mode 100644 index 0000000000000000000000000000000000000000..bd58201d2ebc0e8e4968d8dff2fa241679b874c0 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/CwaCovidCertificate.kt @@ -0,0 +1,29 @@ +package de.rki.coronawarnapp.covidcertificate.common.certificate + +import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString +import org.joda.time.Instant +import org.joda.time.LocalDate + +/** + * For use with the UI + */ +interface CwaCovidCertificate { + // Header + val issuer: String + val issuedAt: Instant + val expiresAt: Instant + + val qrCode: QrCodeString + + val firstName: String? + + val lastName: String + val fullName: String + val dateOfBirth: LocalDate + + val personIdentifier: CertificatePersonIdentifier + + val certificateIssuer: String + val certificateCountry: String + val certificateId: String +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/Dcc.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/Dcc.kt new file mode 100644 index 0000000000000000000000000000000000000000..8d8c4b030802099b01ee409d6f422c46f96b1c2c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/Dcc.kt @@ -0,0 +1,50 @@ +package de.rki.coronawarnapp.covidcertificate.common.certificate + +import com.google.gson.annotations.SerializedName +import org.joda.time.LocalDate + +interface Dcc<PayloadType : Dcc.Payload> { + data class NameData( + @SerializedName("fn") internal val familyName: String?, + @SerializedName("fnt") internal val familyNameStandardized: String, + @SerializedName("gn") internal val givenName: String?, + @SerializedName("gnt") internal val givenNameStandardized: String?, + ) { + val firstName: String? + get() = if (givenName.isNullOrBlank()) givenNameStandardized else givenName + + val lastName: String + get() = if (familyName.isNullOrBlank()) familyNameStandardized else familyName + + val fullName: String + get() = when { + firstName.isNullOrBlank() -> lastName + else -> "$firstName $lastName" + } + } + + val version: String + val nameData: NameData + val dob: String + + val dateOfBirth: LocalDate + get() = LocalDate.parse(dob) + + val payloads: List<PayloadType> + val payload: PayloadType + get() = payloads.single() + + val personIdentifier: CertificatePersonIdentifier + get() = CertificatePersonIdentifier( + dateOfBirth = dateOfBirth, + lastNameStandardized = nameData.familyNameStandardized, + firstNameStandardized = nameData.givenNameStandardized + ) + + interface Payload { + val targetId: String + val certificateCountry: String + val certificateIssuer: String + val uniqueCertificateIdentifier: String + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccData.kt new file mode 100644 index 0000000000000000000000000000000000000000..89a2aac6b817b69cd06a50167364ef15651ba2f4 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccData.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.covidcertificate.common.certificate + +data class DccData<CertT : Dcc<*>>( + val header: DccHeader, + val certificate: CertT, +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccHeader.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccHeader.kt new file mode 100644 index 0000000000000000000000000000000000000000..4964d707431c1bb29a0fb79e88e4b14d4383d19e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/certificate/DccHeader.kt @@ -0,0 +1,9 @@ +package de.rki.coronawarnapp.covidcertificate.common.certificate + +import org.joda.time.Instant + +data class DccHeader( + val issuer: String, + val issuedAt: Instant, + val expiresAt: Instant, +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccCoseDecoder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccCoseDecoder.kt new file mode 100644 index 0000000000000000000000000000000000000000..51773c92558ee0ef541cc8dc95ebe284dad85e8d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccCoseDecoder.kt @@ -0,0 +1,59 @@ +package de.rki.coronawarnapp.covidcertificate.common.decoder + +import com.upokecenter.cbor.CBORObject +import de.rki.coronawarnapp.covidcertificate.cryptography.AesCryptography +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.AES_DECRYPTION_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_COSE_MESSAGE_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_COSE_TAG_INVALID +import timber.log.Timber +import javax.inject.Inject + +class DccCoseDecoder @Inject constructor( + private val aesEncryptor: AesCryptography +) { + + fun decode(input: RawCOSEObject): CBORObject = try { + val messageObject = CBORObject.DecodeFromBytes(input).validate() + val content = messageObject[2].GetByteString() + CBORObject.DecodeFromBytes(content) + } catch (e: InvalidHealthCertificateException) { + throw e + } catch (e: Throwable) { + Timber.e(e) + throw InvalidHealthCertificateException(HC_COSE_MESSAGE_INVALID) + } + + fun decryptMessage(input: RawCOSEObject, decryptionKey: ByteArray): RawCOSEObject = try { + val messageObject = CBORObject.DecodeFromBytes(input).validate() + val content = messageObject[2].GetByteString() + val decrypted = content.decrypt(decryptionKey) + messageObject[2] = CBORObject.FromObject(decrypted) + messageObject.EncodeToBytes() + } catch (e: InvalidHealthCertificateException) { + throw e + } catch (e: Throwable) { + Timber.e(e) + throw InvalidHealthCertificateException(HC_COSE_MESSAGE_INVALID) + } + + private fun ByteArray.decrypt(decryptionKey: ByteArray) = try { + aesEncryptor.decrypt( + decryptionKey = decryptionKey, + encryptedData = this + ) + } catch (e: Throwable) { + Timber.e(e) + throw InvalidHealthCertificateException(AES_DECRYPTION_FAILED) + } + + private fun CBORObject.validate(): CBORObject { + if (size() != 4) { + throw InvalidHealthCertificateException(HC_COSE_MESSAGE_INVALID) + } + if (!HasTag(18)) { + throw InvalidHealthCertificateException(HC_COSE_TAG_INVALID) + } + return this + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeaderParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccHeaderParser.kt similarity index 51% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeaderParser.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccHeaderParser.kt index e9368782021d3b05e0d79f38b5982e17bb8c28ed..2bac92a71c8847e5cd20d3d8cd572cbb315aa20e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeaderParser.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/DccHeaderParser.kt @@ -1,28 +1,30 @@ -package de.rki.coronawarnapp.vaccination.core.certificate +package de.rki.coronawarnapp.covidcertificate.common.decoder import com.upokecenter.cbor.CBORObject import dagger.Reusable -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_EXP -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_ISS +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccHeader +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_EXP +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_ISS import org.joda.time.Instant import javax.inject.Inject @Reusable -class HealthCertificateHeaderParser @Inject constructor() { +class DccHeaderParser @Inject constructor() { - fun parse(map: CBORObject): CoseCertificateHeader = try { - val issuer: String = map[keyIssuer]?.AsString() ?: throw InvalidHealthCertificateException(VC_HC_CWT_NO_ISS) + fun parse(map: CBORObject): DccHeader = try { + val issuer: String = map[keyIssuer]?.AsString() ?: throw InvalidHealthCertificateException(HC_CWT_NO_ISS) val issuedAt: Instant = map[keyIssuedAt]?.run { Instant.ofEpochSecond(AsNumber().ToInt64Checked()) - } ?: throw InvalidHealthCertificateException(VC_HC_CWT_NO_ISS) + } ?: throw InvalidHealthCertificateException(HC_CWT_NO_ISS) val expiresAt: Instant = map[keyExpiresAt]?.run { Instant.ofEpochSecond(AsNumber().ToInt64Checked()) - } ?: throw InvalidHealthCertificateException(VC_HC_CWT_NO_EXP) + } ?: throw InvalidHealthCertificateException(HC_CWT_NO_EXP) - HealthCertificateHeader( + DccHeader( issuer = issuer, issuedAt = issuedAt, expiresAt = expiresAt, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/RawCOSEObject.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/RawCOSEObject.kt new file mode 100644 index 0000000000000000000000000000000000000000..233796803fd4c90fd7e02f5ef7d3af35d0d08395 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/decoder/RawCOSEObject.kt @@ -0,0 +1,3 @@ +package de.rki.coronawarnapp.covidcertificate.common.decoder + +typealias RawCOSEObject = ByteArray diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/DccQrCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/DccQrCode.kt new file mode 100644 index 0000000000000000000000000000000000000000..9d1527dd0afb14850f7abd32e41edfcdde9421af --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/DccQrCode.kt @@ -0,0 +1,16 @@ +package de.rki.coronawarnapp.covidcertificate.common.qrcode + +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData + +interface DccQrCode<DccT : Dcc<*>> { + val qrCode: QrCodeString + val data: DccData<DccT> + + val personIdentifier: CertificatePersonIdentifier + get() = data.certificate.personIdentifier + + val uniqueCertificateIdentifier: String + get() = data.certificate.payload.uniqueCertificateIdentifier +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/QrCodeString.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/QrCodeString.kt new file mode 100644 index 0000000000000000000000000000000000000000..9c8208fe1c4978bfc8f5f4e941228ca16d1ac329 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/qrcode/QrCodeString.kt @@ -0,0 +1,3 @@ +package de.rki.coronawarnapp.covidcertificate.common.qrcode + +typealias QrCodeString = String diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptography.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptography.kt index 80ad40d4c48bd224b6cf62eef0dbce49b9ac0379..ac294903083ca20e2b70d57069d63223fc9d7c18 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptography.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptography.kt @@ -1,16 +1,25 @@ package de.rki.coronawarnapp.covidcertificate.cryptography -import dagger.Reusable -import okio.ByteString +import com.google.android.gms.common.util.Hex +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec import javax.inject.Inject -@Reusable class AesCryptography @Inject constructor() { fun decrypt( decryptionKey: ByteArray, - encryptedData: ByteString - ): ByteArray { - throw NotImplementedError() + encryptedData: ByteArray + ): ByteArray = with(Cipher.getInstance(TRANSFORMATION)) { + val keySpec = SecretKeySpec(decryptionKey, ALGORITHM) + init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec) + doFinal(encryptedData) } + + private val ivParameterSpec + get() = IvParameterSpec(Hex.stringToBytes("00000000000000000000000000000000")) } + +private const val ALGORITHM = "AES" +private const val TRANSFORMATION = "AES/CBC/PKCS5Padding" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/ErrorMessage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/ErrorMessage.kt new file mode 100644 index 0000000000000000000000000000000000000000..5ecf0593827d5792b950265dc93e22783894578a --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/ErrorMessage.kt @@ -0,0 +1,12 @@ +package de.rki.coronawarnapp.covidcertificate.exception + +import de.rki.coronawarnapp.R + +const val ERROR_MESSAGE_GENERIC = R.string.errors_generic_text_unknown_error_cause +const val ERROR_MESSAGE_TRY_AGAIN = R.string.error_tc_try_again +const val ERROR_MESSAGE_DCC_NOT_SUPPORTED_BY_LAB = R.string.error_tc_dcc_not_supported_by_lab +const val ERROR_MESSAGE_NO_NETWORK = R.string.error_tc_no_network +const val ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE = R.string.error_tc_e2e_error_call_hotline +const val ERROR_MESSAGE_TRY_AGAIN_DCC_NOT_AVAILABLE_YET = R.string.error_tc_try_again_dcc_not_available_yet +const val ERROR_MESSAGE_CLIENT_ERROR_CALL_HOTLINE = R.string.error_tc_client_error_call_hotline +const val ERROR_MESSAGE_DCC_EXPIRED = R.string.error_tc_dcc_expired diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidHealthCertificateException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidHealthCertificateException.kt new file mode 100644 index 0000000000000000000000000000000000000000..9a85a5416babe7fab493a861ffb35bb737753397 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidHealthCertificateException.kt @@ -0,0 +1,52 @@ +package de.rki.coronawarnapp.covidcertificate.exception + +import android.content.Context +import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException +import de.rki.coronawarnapp.util.HasHumanReadableError +import de.rki.coronawarnapp.util.HumanReadableError +import de.rki.coronawarnapp.util.ui.CachedString +import de.rki.coronawarnapp.util.ui.LazyString + +@Suppress("MaxLineLength") +open class InvalidHealthCertificateException( + val errorCode: ErrorCode +) : HasHumanReadableError, InvalidQRCodeException(errorCode.message) { + enum class ErrorCode( + val message: String + ) { + HC_BASE45_DECODING_FAILED("Base45 decoding failed."), + HC_BASE45_ENCODING_FAILED("Base45 encoding failed."), + HC_ZLIB_DECOMPRESSION_FAILED("Zlib decompression failed."), + HC_ZLIB_COMPRESSION_FAILED("Zlib compression failed."), + HC_COSE_TAG_INVALID("COSE tag invalid."), + HC_COSE_MESSAGE_INVALID("COSE message invalid."), + HC_CBOR_DECODING_FAILED("CBOR decoding failed."), + VC_NO_VACCINATION_ENTRY("Vaccination certificate missing."), + NO_TEST_ENTRY("Test certificate missing."), + VC_PREFIX_INVALID("Prefix invalid."), + VC_STORING_FAILED("Storing failed."), + JSON_SCHEMA_INVALID("Json schema invalid."), + VC_NAME_MISMATCH("Name does not match."), + VC_ALREADY_REGISTERED("Certificate already registered."), + VC_DOB_MISMATCH("Date of birth does not match."), + HC_CWT_NO_DGC("Dgc missing."), + HC_CWT_NO_EXP("Expiration date missing."), + HC_CWT_NO_HCERT("Health certificate missing."), + HC_CWT_NO_ISS("Issuer missing."), + + AES_DECRYPTION_FAILED("AES decryption failed"), + RSA_DECRYPTION_FAILED("RSA decryption failed."), + RSA_KP_GENERATION_FAILED("RSA key pair generation failed."), + } + + open val errorMessage: LazyString + get() = CachedString { context -> + context.getString(ERROR_MESSAGE_GENERIC) + } + + override fun toHumanReadableError(context: Context): HumanReadableError { + return HumanReadableError( + description = errorMessage.get(context) + " ($errorCode)" + ) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidTestCertificateException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidTestCertificateException.kt new file mode 100644 index 0000000000000000000000000000000000000000..29cec2e55dc52617bfd9e1fc1f5459e800356fac --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidTestCertificateException.kt @@ -0,0 +1,37 @@ +package de.rki.coronawarnapp.covidcertificate.exception + +import android.content.Context +import de.rki.coronawarnapp.util.HumanReadableError +import de.rki.coronawarnapp.util.ui.CachedString +import de.rki.coronawarnapp.util.ui.LazyString + +class InvalidTestCertificateException(errorCode: ErrorCode) : InvalidHealthCertificateException(errorCode) { + override fun toHumanReadableError(context: Context): HumanReadableError { + return HumanReadableError( + description = errorMessage.get(context) + " ($errorCode)" + ) + } + + override val errorMessage: LazyString + get() = when (errorCode) { + ErrorCode.AES_DECRYPTION_FAILED, + ErrorCode.RSA_DECRYPTION_FAILED, + ErrorCode.HC_COSE_MESSAGE_INVALID, + ErrorCode.HC_COSE_TAG_INVALID -> CachedString { context -> + context.getString(ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE) + } + + ErrorCode.RSA_KP_GENERATION_FAILED -> CachedString { context -> + context.getString(ERROR_MESSAGE_TRY_AGAIN) + } + + ErrorCode.HC_BASE45_DECODING_FAILED, + ErrorCode.HC_BASE45_ENCODING_FAILED, + ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED, + ErrorCode.HC_ZLIB_COMPRESSION_FAILED -> CachedString { context -> + context.getString(ERROR_MESSAGE_CLIENT_ERROR_CALL_HOTLINE) + } + + else -> super.errorMessage + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidVaccinationCertificateException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidVaccinationCertificateException.kt new file mode 100644 index 0000000000000000000000000000000000000000..4254cd9b3c11eeb6427b40c4e0d182cddcc7d9c2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/InvalidVaccinationCertificateException.kt @@ -0,0 +1,61 @@ +package de.rki.coronawarnapp.covidcertificate.exception + +import android.content.Context +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.util.HumanReadableError +import de.rki.coronawarnapp.util.ui.CachedString +import de.rki.coronawarnapp.util.ui.LazyString + +class InvalidVaccinationCertificateException(errorCode: ErrorCode) : InvalidHealthCertificateException(errorCode) { + override fun toHumanReadableError(context: Context): HumanReadableError { + var errorCodeString = errorCode.toString() + errorCodeString = if (errorCodeString.startsWith(PREFIX_VC)) errorCodeString else PREFIX_VC + errorCodeString + return HumanReadableError( + description = errorMessage.get(context) + " ($errorCodeString)" + ) + } + + override val errorMessage: LazyString + get() = when (errorCode) { + ErrorCode.VC_PREFIX_INVALID, + ErrorCode.HC_BASE45_DECODING_FAILED, + ErrorCode.HC_CBOR_DECODING_FAILED, + ErrorCode.HC_COSE_MESSAGE_INVALID, + ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED, + ErrorCode.HC_COSE_TAG_INVALID, + ErrorCode.HC_CWT_NO_DGC, + ErrorCode.HC_CWT_NO_EXP, + ErrorCode.HC_CWT_NO_HCERT, + ErrorCode.HC_CWT_NO_ISS, + ErrorCode.JSON_SCHEMA_INVALID, + -> CachedString { context -> + context.getString(ERROR_MESSAGE_VC_INVALID) + } + + ErrorCode.VC_NO_VACCINATION_ENTRY -> CachedString { context -> + context.getString(ERROR_MESSAGE_VC_NOT_YET_SUPPORTED) + } + + ErrorCode.VC_STORING_FAILED -> CachedString { context -> + context.getString(ERROR_MESSAGE_VC_SCAN_AGAIN) + } + + ErrorCode.VC_NAME_MISMATCH, + ErrorCode.VC_DOB_MISMATCH -> CachedString { context -> + context.getString(ERROR_MESSAGE_VC_DIFFERENT_PERSON) + } + + ErrorCode.VC_ALREADY_REGISTERED -> CachedString { context -> + context.getString(ERROR_MESSAGE_VC_ALREADY_REGISTERED) + } + else -> super.errorMessage + } +} + +private const val PREFIX_VC = "VC_" + +private const val ERROR_MESSAGE_VC_INVALID = R.string.error_vc_invalid +private const val ERROR_MESSAGE_VC_NOT_YET_SUPPORTED = R.string.error_vc_not_yet_supported +private const val ERROR_MESSAGE_VC_SCAN_AGAIN = R.string.error_vc_scan_again +private const val ERROR_MESSAGE_VC_DIFFERENT_PERSON = R.string.error_vc_different_person +private const val ERROR_MESSAGE_VC_ALREADY_REGISTERED = R.string.error_vc_already_registered diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/TestCertificateServerException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/TestCertificateServerException.kt new file mode 100644 index 0000000000000000000000000000000000000000..6cbe138c4d7109ece44e02e86542fdd94b06176a --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/exception/TestCertificateServerException.kt @@ -0,0 +1,99 @@ +package de.rki.coronawarnapp.covidcertificate.exception + +import android.content.Context +import androidx.annotation.StringRes +import de.rki.coronawarnapp.util.HasHumanReadableError +import de.rki.coronawarnapp.util.HumanReadableError +import de.rki.coronawarnapp.util.ui.CachedString +import de.rki.coronawarnapp.util.ui.LazyString + +class TestCertificateServerException( + val errorCode: ErrorCode +) : HasHumanReadableError, Exception(errorCode.message) { + + override fun toHumanReadableError(context: Context): HumanReadableError { + return HumanReadableError( + description = errorMessage.get(context) + ) + } + + val errorMessage: LazyString + get() = CachedString { context -> + context.getString(errorCode.stringRes) + } + + enum class ErrorCode( + val message: String, + @StringRes val stringRes: Int + ) { + DCC_COMP_202( + "DCC Components request failed with error 202: DCC pending.", + ERROR_MESSAGE_TRY_AGAIN_DCC_NOT_AVAILABLE_YET + ), + DCC_COMP_400( + "DCC Components request failed with error 400: Bad request (e.g. wrong format of registration token)", + ERROR_MESSAGE_CLIENT_ERROR_CALL_HOTLINE + ), + DCC_COMP_404( + "DCC Components request failed with error 404: Registration token does not exist.", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + DCC_COMP_410( + "DCC Components request failed with error 410: DCC already cleaned up.", + ERROR_MESSAGE_DCC_EXPIRED + ), + DCC_COMP_412( + "DCC Components request failed with error 412: Test result not yet received", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + DCC_COMP_500( + "DCC Test Certificate Components failed with error 500: Internal server error.", + ERROR_MESSAGE_TRY_AGAIN + ), + DCC_COMP_500_LAB_INVALID_RESPONSE( + "DCC Components failed with error 500: Lab Invalid response", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + DCC_COMP_500_SIGNING_CLIENT_ERROR( + "DCC Components failed with error 500: Signing client error", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + DCC_COMP_500_SIGNING_SERVER_ERROR( + "DCC Components failed with error 500: Signing server error", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + DCC_COMP_NO_NETWORK( + "DCC Test Certificate Components failed due to no network connection.", + ERROR_MESSAGE_NO_NETWORK + ), + PKR_400( + "Public Key Registration failed with 400: " + + "Bad request (e.g. wrong format of registration token or public key).", + ERROR_MESSAGE_CLIENT_ERROR_CALL_HOTLINE + ), + PKR_403( + "Public Key Registration failed with 403: Registration token is not allowed to issue a DCC.", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + PKR_404( + "Public Key Registration failed with 404: Registration token does not exist.", + ERROR_MESSAGE_E2E_ERROR_CALL_HOTLINE + ), + PKR_409( + "Public Key Registration failed with 409: Registration token is already assigned to a public key.", + ERROR_MESSAGE_GENERIC + ), + PKR_500( + "Public Key Registration failed with 500: Internal server error.", + ERROR_MESSAGE_TRY_AGAIN + ), + PKR_FAILED( + "Private key request failed.", + ERROR_MESSAGE_TRY_AGAIN + ), + PKR_NO_NETWORK( + "Private key request failed due to no network connection.", + ERROR_MESSAGE_NO_NETWORK + ), + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateData.kt deleted file mode 100644 index 8858296812f1cd3fe089cf7ac88f8c599a3c8e00..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.rki.coronawarnapp.covidcertificate.test - -import de.rki.coronawarnapp.vaccination.core.certificate.CoseCertificateHeader - -data class TestCertificateData( - val header: CoseCertificateHeader, - val certificate: TestCertificateDccV1, -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateDccV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateDccV1.kt deleted file mode 100644 index d66086dfe7b5fb8b64b64ee9fba7362cc5a8006b..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateDccV1.kt +++ /dev/null @@ -1,48 +0,0 @@ -package de.rki.coronawarnapp.covidcertificate.test - -import com.google.gson.annotations.SerializedName -import org.joda.time.Instant -import org.joda.time.LocalDate - -data class TestCertificateDccV1( - @SerializedName("ver") val version: String, - @SerializedName("nam") val nameData: NameData, - @SerializedName("dob") val dob: String, - @SerializedName("v") val testCertificateData: List<TestCertificateData>, -) { - data class NameData( - @SerializedName("fn") val familyName: String?, - @SerializedName("fnt") val familyNameStandardized: String, - @SerializedName("gn") val givenName: String?, - @SerializedName("gnt") val givenNameStandardized: String?, - ) - - data class TestCertificateData( - // Disease or agent targeted, e.g. "tg": "840539006" - @SerializedName("tg") val targetId: String, - // Vaccine or prophylaxis, e.g. "vp": "1119349007" - @SerializedName("vp") val vaccineId: String, - // Vaccine medicinal product,e.g. "mp": "EU/1/20/1528", - @SerializedName("mp") val medicalProductId: String, - // Marketing Authorization Holder, e.g. "ma": "ORG-100030215", - @SerializedName("ma") val marketAuthorizationHolderId: String, - // Date/Time of Sample Collection (required) - // "sc": "2021-04-13T14:20:00+00:00", - @SerializedName("sc") val sampleCollectedAt: Instant, - // Date/Time of Test Result - // "dr": "2021-04-13T14:40:01+00:00", - @SerializedName("dr") val testResultAt: Instant, - // Testing Center (required) - // "tc": "GGD Fryslân, L-Heliconweg", - @SerializedName("tc") val testCenter: String, - // Country of Vaccination, e.g. "co": "NL" - @SerializedName("co") val countryOfVaccination: String, - // Certificate Issuer, e.g. "is": "Ministry of Public Health, Welfare and Sport", - @SerializedName("is") val certificateIssuer: String, - // Unique Certificate Identifier, e.g. "ci": "urn:uvci:01:NL:PlA8UWS60Z4RZXVALl6GAZ" - @SerializedName("ci") val uniqueCertificateIdentifier: String - ) - - val dateOfBirth: LocalDate - get() = LocalDate.parse(dob) -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCode.kt deleted file mode 100644 index 6cc65a07b63d365aed9841ef0618609961f02c0c..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCode.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.covidcertificate.test - -data class TestCertificateQRCode( - val qrCode: String, - val testCertificateData: TestCertificateData, -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCodeExtractor.kt deleted file mode 100644 index e5c34fc642af841c670cdb405349fb6201a439b1..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateQRCodeExtractor.kt +++ /dev/null @@ -1,26 +0,0 @@ -package de.rki.coronawarnapp.covidcertificate.test - -import dagger.Reusable -import okio.ByteString -import javax.inject.Inject - -@Reusable -class TestCertificateQRCodeExtractor @Inject constructor() { - - /** - * May throw an **[InvalidHealthCertificateException]** - */ - fun extract( - decryptionKey: ByteArray, - encryptedCoseComponents: ByteString, - ): TestCertificateData { - throw NotImplementedError() - } - - /** - * May throw an **[InvalidHealthCertificateException]** - */ - fun extract(qrCode: String): TestCertificateQRCode { - throw NotImplementedError() - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt new file mode 100644 index 0000000000000000000000000000000000000000..b4589236afa4541e4266755cd1a88cd6ab5022b6 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificate.kt @@ -0,0 +1,27 @@ +package de.rki.coronawarnapp.covidcertificate.test.core + +import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate +import org.joda.time.Instant + +interface TestCertificate : CwaCovidCertificate { + + /** + * Disease or agent targeted (required) + */ + val targetName: String + val testType: String + val testResult: String + + /** + * NAA Test Name (only for PCR tests, but not required) + */ + val testName: String? + + /** + * RAT Test name and manufacturer (only for RAT tests, but not required) + */ + val testNameAndManufactor: String? + val sampleCollectedAt: Instant + val testResultAt: Instant? + val testCenter: String +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..ed383c7d1fa83bb75448b86cb217054caeea4445 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateRepository.kt @@ -0,0 +1,268 @@ +package de.rki.coronawarnapp.covidcertificate.test.core + +import de.rki.coronawarnapp.bugreporting.reportProblem +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.test.core.storage.PCRCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.RACertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateContainer +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateProcessor +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateStorage +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository +import de.rki.coronawarnapp.util.coroutine.AppScope +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.flow.HotDataFlow +import de.rki.coronawarnapp.util.flow.combine +import de.rki.coronawarnapp.util.mutate +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.plus +import kotlinx.coroutines.withContext +import timber.log.Timber +import java.util.UUID +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +@Suppress("LongParameterList") +class TestCertificateRepository @Inject constructor( + @AppScope private val appScope: CoroutineScope, + private val dispatcherProvider: DispatcherProvider, + private val storage: TestCertificateStorage, + private val qrCodeExtractor: TestCertificateQRCodeExtractor, + private val processor: TestCertificateProcessor, + valueSetsRepository: ValueSetsRepository, +) { + + private val internalData: HotDataFlow<Map<TestCertificateIdentifier, TestCertificateContainer>> = HotDataFlow( + loggingTag = TAG, + scope = appScope + dispatcherProvider.Default, + sharingBehavior = SharingStarted.Eagerly, + ) { + storage.testCertificates + .map { + TestCertificateContainer( + data = it, + qrCodeExtractor = qrCodeExtractor + ) + } + .map { it.identifier to it } + .toMap().also { + Timber.tag(TAG).v("Restored TestCertificate data: %s", it) + } + } + + val certificates: Flow<Set<TestCertificateWrapper>> = combine( + internalData.data, + valueSetsRepository.latestTestCertificateValueSets + ) { certMap, valueSets -> + certMap.values.map { container -> + TestCertificateWrapper( + valueSets = valueSets, + container = container, + ) + }.toSet() + } + + init { + internalData.data + .onStart { Timber.tag(TAG).d("Observing TestCertificateContainer data.") } + .onEach { entrySets -> + val values = entrySets.values + Timber.tag(TAG).v("TestCertificateContainer data changed: %s", values) + storage.testCertificates = values.map { it.data }.toSet() + } + .catch { + it.reportProblem(TAG, "Failed to snapshot TestCertificateContainer data to storage.") + throw it + } + .launchIn(appScope + dispatcherProvider.Default) + } + + /** + * Will create a new test certificate entry. + * Automation via [de.rki.coronawarnapp.coronatest.type.common.TestCertificateRetrievalScheduler] will kick in. + * + * Throws an exception if there already is a test certificate entry for this test + * or this is not a valid test (no consent, not supported by PoC). + */ + suspend fun requestCertificate(test: CoronaTest): TestCertificateContainer { + Timber.tag(TAG).d("requestCertificate(test.identifier=%s)", test.identifier) + + val newData = internalData.updateBlocking { + if (values.any { it.registrationToken == test.registrationToken }) { + Timber.tag(TAG).e("Certificate entry already exists for %s", test.identifier) + throw IllegalArgumentException("A certificate was already created for this ${test.identifier}") + } + if (!test.isDccSupportedByPoc) { + throw IllegalArgumentException("DCC is not supported by PoC for this test: ${test.identifier}") + } + if (!test.isDccConsentGiven) { + throw IllegalArgumentException("DCC was not given for this test: ${test.identifier}") + } + + val identifier = UUID.randomUUID().toString() + + val data = when (test.type) { + CoronaTest.Type.PCR -> PCRCertificateData( + identifier = identifier, + registeredAt = test.registeredAt, + registrationToken = test.registrationToken, + ) + CoronaTest.Type.RAPID_ANTIGEN -> RACertificateData( + identifier = identifier, + registeredAt = test.registeredAt, + registrationToken = test.registrationToken, + ) + } + val container = TestCertificateContainer( + data = data, + qrCodeExtractor = qrCodeExtractor, + ) + Timber.tag(TAG).d("Adding test certificate entry: %s", container) + mutate { this[container.identifier] = container } + } + + return newData.values.single { it.registrationToken == test.registrationToken } + } + + /** + * If [error] is NULL, then [certificateContainer] will be the refreshed entry. + * If [error] is not NULL, then [certificateContainer] is the latest version before the exception occured. + * Due to refresh being a multiple process, some steps can successed, while others fail. + */ + data class RefreshResult( + val certificateContainer: TestCertificateContainer, + val error: Exception? = null, + ) + + /** + * The refresh call checks each certificate entry for public keys and certificate state. + * It will be triggered via TestCertificateRetrievalScheduler. + * After requestCertificate, calling refresh often enough should yield a certificate eventually. + * + * This returns a set of [RefreshResult], one for each refreshed test certificate entry. + * If you specify an identifier, then the set will only contain a single element. + * + * [refresh] itself will NOT throw an exception. + */ + suspend fun refresh(identifier: TestCertificateIdentifier? = null): Set<RefreshResult> { + Timber.tag(TAG).d("refresh(identifier=%s)", identifier) + + val refreshCallResults = mutableMapOf<TestCertificateIdentifier, RefreshResult>() + + val workedOnIds = mutableSetOf<TestCertificateIdentifier>() + + internalData.updateBlocking { + val toRefresh = values + .filter { it.identifier == identifier || identifier == null } // Targets of our refresh + .filter { !it.isUpdatingData && it.isCertificateRetrievalPending } // Those that need refreshing + + mutate { + toRefresh.forEach { + workedOnIds.add(it.identifier) + this[it.identifier] = it.copy(isUpdatingData = true) + } + } + } + + internalData.updateBlocking { + Timber.tag(TAG).d("Checking for unregistered public keys.") + + val refreshedCerts = values + .filter { workedOnIds.contains(it.identifier) } // Refresh targets + .filter { !it.isPublicKeyRegistered } // Targets of this step + .map { cert -> + withContext(dispatcherProvider.IO) { + try { + val updatedData = processor.registerPublicKey(cert.data) + RefreshResult(cert.copy(data = updatedData)) + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to register public key for %s", cert) + RefreshResult(cert, e) + } + } + } + + refreshedCerts.forEach { + refreshCallResults[it.certificateContainer.identifier] = it + } + + mutate { + refreshedCerts + .filter { it.error == null } + .map { it.certificateContainer } + .forEach { this[it.identifier] = it } + } + } + + internalData.updateBlocking { + Timber.tag(TAG).d("Checking for pending certificates.") + + val refreshedCerts = values + .filter { workedOnIds.contains(it.identifier) } // Refresh targets + .filter { it.isPublicKeyRegistered && it.isCertificateRetrievalPending } // Targets of this step + .map { cert -> + withContext(dispatcherProvider.IO) { + try { + val updatedData = processor.obtainCertificate(cert.data) + RefreshResult(cert.copy(data = updatedData)) + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Failed to retrieve certificate components for %s", cert) + RefreshResult(cert, e) + } + } + } + + refreshedCerts.forEach { + refreshCallResults[it.certificateContainer.identifier] = it + } + + mutate { + refreshedCerts + .filter { it.error == null } + .map { it.certificateContainer } + .forEach { this[it.identifier] = it } + } + } + + internalData.updateBlocking { + val certs = values.filter { workedOnIds.contains(it.identifier) } + + mutate { + certs.forEach { + this[it.identifier] = it.copy(isUpdatingData = false) + } + } + } + + return refreshCallResults.values.toSet() + } + + /** + * [deleteCertificate] does not throw an exception, if the deletion target already does not exist. + */ + suspend fun deleteCertificate(identifier: TestCertificateIdentifier) { + Timber.tag(TAG).d("deleteTestCertificate(identifier=%s)", identifier) + internalData.updateBlocking { + mutate { + remove(identifier) + } + } + } + + suspend fun clear() { + Timber.tag(TAG).i("clear()") + internalData.updateBlocking { emptyMap() } + } + + companion object { + private val TAG = TestCertificateRepository::class.simpleName!! + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateWrapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateWrapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..2be76074c81765443b9c52167bf4d028cceadaab --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/TestCertificateWrapper.kt @@ -0,0 +1,23 @@ +package de.rki.coronawarnapp.covidcertificate.test.core + +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateContainer +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets + +data class TestCertificateWrapper( + private val valueSets: TestCertificateValueSets, + private val container: TestCertificateContainer +) { + + val identifier: TestCertificateIdentifier = container.identifier + + val isCertificateRetrievalPending = container.isCertificateRetrievalPending + + val isUpdatingData = container.isUpdatingData + + val registeredAt = container.registeredAt + + val testCertificate: TestCertificate? by lazy { + container.toTestCertificate(valueSets) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccParser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccParser.kt new file mode 100644 index 0000000000000000000000000000000000000000..7f4cfe2d8aad8a8f55c4bbb8639dcb93accf78b1 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccParser.kt @@ -0,0 +1,69 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.certificate + +import com.google.gson.Gson +import com.upokecenter.cbor.CBORObject +import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_DGC +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_HCERT +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.JSON_SCHEMA_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.NO_TEST_ENTRY +import de.rki.coronawarnapp.covidcertificate.exception.InvalidTestCertificateException +import de.rki.coronawarnapp.util.serialization.BaseGson +import de.rki.coronawarnapp.util.serialization.fromJson +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class TestDccParser @Inject constructor( + @BaseGson private val gson: Gson, +) { + fun parse(map: CBORObject): TestDccV1 = try { + map[keyHCert]?.run { + this[keyEuDgcV1]?.run { + toCertificate() + } ?: throw InvalidTestCertificateException(HC_CWT_NO_DGC) + } ?: throw InvalidTestCertificateException(HC_CWT_NO_HCERT) + } catch (e: InvalidTestCertificateException) { + throw e + } catch (e: Throwable) { + throw InvalidTestCertificateException(HC_CBOR_DECODING_FAILED) + } + + @Suppress("UNNECESSARY_NOT_NULL_ASSERTION") + private fun TestDccV1.validate(): TestDccV1 { + if (payloads.isNullOrEmpty()) { + throw InvalidTestCertificateException(NO_TEST_ENTRY) + } + // check for non null (Gson does not enforce it) & force date parsing + require(version.isNotBlank()) + require(nameData.familyNameStandardized.isNotBlank()) + dateOfBirth + payload.let { + it.testResultAt + it.sampleCollectedAt + require(it.certificateIssuer.isNotBlank()) + require(it.certificateCountry.isNotBlank()) + require(it.targetId.isNotBlank()) + require(it.testCenter.isNotBlank()) + require(it.testResult.isNotBlank()) + require(it.testType.isNotBlank()) + } + return this + } + + private fun CBORObject.toCertificate() = try { + val json = ToJSONString() + gson.fromJson<TestDccV1>(json).validate() + } catch (e: InvalidTestCertificateException) { + throw e + } catch (e: Throwable) { + Timber.e(e) + throw InvalidTestCertificateException(JSON_SCHEMA_INVALID) + } + + companion object { + private val keyEuDgcV1 = CBORObject.FromObject(1) + private val keyHCert = CBORObject.FromObject(-260) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccV1.kt new file mode 100644 index 0000000000000000000000000000000000000000..c105167986ab66c556716285921ab8978fddef9b --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/certificate/TestDccV1.kt @@ -0,0 +1,45 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.certificate + +import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc +import org.joda.time.Instant + +data class TestDccV1( + @SerializedName("ver") override val version: String, + @SerializedName("nam") override val nameData: Dcc.NameData, + @SerializedName("dob") override val dob: String, + @SerializedName("t") override val payloads: List<TestCertificateData>, +) : Dcc<TestDccV1.TestCertificateData> { + + data class TestCertificateData( + // Disease or agent targeted, e.g. "tg": "840539006" + @SerializedName("tg") override val targetId: String, + // Type of Test (required) eg "LP217198-3" + @SerializedName("tt") val testType: String, + // Test Result (required) e. g. "tr": "260415000" + @SerializedName("tr") val testResult: String, + // NAA Test Name (only for PCR tests, but not required) "nm": "Roche LightCycler qPCR", + @SerializedName("nm") val testName: String? = null, + // RAT Test name and manufacturer (only for RAT tests, but not required) + @SerializedName("ma") val testNameAndManufactor: String? = null, + // Date/Time of Sample Collection (required) "sc": "2021-04-13T14:20:00+00:00" + @SerializedName("sc") val sc: String, + // Date/Time of Test Result "dr": "2021-04-13T14:40:01+00:00", + @SerializedName("dr") val dr: String? = null, + // Testing Center (required) "tc": "GGD Fryslân, L-Heliconweg", + @SerializedName("tc") val testCenter: String, + // Country of Test (required) + @SerializedName("co") override val certificateCountry: String, + // Certificate Issuer, e.g. "is": "Ministry of Public Health, Welfare and Sport", + @SerializedName("is") override val certificateIssuer: String, + // Unique Certificate Identifier, e.g. "ci": "urn:uvci:01:NL:PlA8UWS60Z4RZXVALl6GAZ" + @SerializedName("ci") override val uniqueCertificateIdentifier: String + ) : Dcc.Payload { + + val testResultAt: Instant? + get() = dr?.let { Instant.parse(it) } + + val sampleCollectedAt: Instant + get() = Instant.parse(sc) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalScheduler.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalScheduler.kt new file mode 100644 index 0000000000000000000000000000000000000000..2ad3ee6454b7fa8be7038f7c5b0fd0bb5654db43 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalScheduler.kt @@ -0,0 +1,118 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.execution + +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.common.ResultScheduler +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.util.coroutine.AppScope +import de.rki.coronawarnapp.util.device.ForegroundState +import de.rki.coronawarnapp.util.flow.combine +import de.rki.coronawarnapp.worker.BackgroundConstants +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import timber.log.Timber +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TestCertificateRetrievalScheduler @Inject constructor( + @AppScope private val appScope: CoroutineScope, + private val workManager: WorkManager, + private val certificateRepo: TestCertificateRepository, + private val testRepo: CoronaTestRepository, + private val foregroundState: ForegroundState, +) : ResultScheduler( + workManager = workManager +) { + private val processedNewCerts = mutableSetOf<String>() + + private val creationTrigger = testRepo.coronaTests + .map { tests -> + tests + .filter { it.isDccSupportedByPoc } // Only those that support it + .filter { it.isNegative } // Certs only to proof negative state + .filter { it.isDccConsentGiven && !it.isDccDataSetCreated } // Consent and doesn't exist already? + } + .distinctUntilChanged() + + private val refreshTrigger = combine( + certificateRepo.certificates, + foregroundState.isInForeground, + ) { certificates, isForeground -> + + val hasNewCert = certificates.any { + val isNew = !processedNewCerts.contains(it.identifier) + if (isNew) processedNewCerts.add(it.identifier) + isNew + } + + val hasWorkToDo = certificates.any { it.isCertificateRetrievalPending && !it.isUpdatingData } + Timber.tag(TAG).v("shouldPollDcc? hasNewCert=$hasNewCert, hasWorkTodo=$hasWorkToDo, foreground=$isForeground") + (isForeground || hasNewCert) && hasWorkToDo + } + + fun setup() { + Timber.tag(TAG).i("setup() - TestCertificateRetrievalScheduler") + + // Create a certificate entry for each viable test that has none + creationTrigger + .onEach { testsWithoutCert -> + Timber.tag(TAG).d("State change: testsWithoutCert=$testsWithoutCert") + testsWithoutCert.forEach { test -> + val cert = certificateRepo.requestCertificate(test) + Timber.tag(TAG).v("Certificate was created: %s", cert) + testRepo.markDccAsCreated(test.identifier, created = true) + } + } + .catch { Timber.tag(TAG).e(it, "Creation trigger failed.") } + .launchIn(appScope) + + // For each change to the set of existing certificates, check if we need to refresh/load data + refreshTrigger + .onEach { checkCerts -> + Timber.tag(TAG).d("State change: checkCerts=$checkCerts") + if (checkCerts) scheduleWorker() + } + .catch { Timber.tag(TAG).e(it, "Refresh trigger failed.") } + .launchIn(appScope) + } + + internal suspend fun scheduleWorker() { + Timber.tag(TAG).i("scheduleWorker()") + + if (isScheduled(WORKER_ID)) { + Timber.tag(TAG).d("Worker already queued, skipping requeue.") + } + + Timber.tag(TAG).d("enqueueUniqueWork PCR_TESTRESULT_WORKER_UNIQUEUNAME") + workManager.enqueueUniqueWork( + WORKER_ID, + ExistingWorkPolicy.KEEP, + buildWorkerRequest() + ) + } + + private fun buildWorkerRequest() = + OneTimeWorkRequestBuilder<TestCertificateRetrievalWorker>() + .setConstraints( + Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build() + ) + .setBackoffCriteria(BackoffPolicy.LINEAR, BackgroundConstants.KIND_DELAY, TimeUnit.MINUTES) + .build() + + companion object { + private const val WORKER_ID = "TestCertificateRetrievalWorker" + + private val TAG = TestCertificateRetrievalScheduler::class.simpleName!! + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalWorker.kt new file mode 100644 index 0000000000000000000000000000000000000000..8dc80c437b24c3db1d710c14ad741a62e763f214 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/execution/TestCertificateRetrievalWorker.kt @@ -0,0 +1,51 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.execution + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory +import de.rki.coronawarnapp.worker.BackgroundConstants +import timber.log.Timber + +class TestCertificateRetrievalWorker @AssistedInject constructor( + @Assisted val context: Context, + @Assisted workerParams: WorkerParameters, + private val testCertificateRepository: TestCertificateRepository, +) : CoroutineWorker(context, workerParams) { + + override suspend fun doWork(): Result { + Timber.tag(TAG).d("$id: doWork() started. Run attempt: $runAttemptCount") + + if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { + Timber.tag(TAG).d("$id doWork() failed after $runAttemptCount attempts. Aborting...") + return Result.failure() + } + + return try { + Timber.tag(TAG).v("Refreshing test certificates.") + val results = testCertificateRepository.refresh() + + if (results.any { it.error != null }) { + Timber.tag(TAG).w("Some test certificates failed refresh, will retry.") + Result.retry() + } else { + Timber.tag(TAG).d("No errors during test certificate refresh :).") + Result.success() + } + } catch (e: Exception) { + Timber.tag(TAG).e(e, "Test result retrieval worker failed.") + Result.retry() + } + } + + @AssistedFactory + interface Factory : InjectedWorkerFactory<TestCertificateRetrievalWorker> + + companion object { + private val TAG = TestCertificateRetrievalWorker::class.java.simpleName + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCode.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e20630680c30cd7107d81348e6ece36cc82fcdd --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCode.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.qrcode + +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.qrcode.DccQrCode +import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccV1 + +data class TestCertificateQRCode( + override val qrCode: QrCodeString, + override val data: DccData<TestDccV1>, +) : DccQrCode<TestDccV1> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..51a8de2f1f8bc7087001278a0005b77fed23af5e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractor.kt @@ -0,0 +1,133 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.qrcode + +import com.upokecenter.cbor.CBORObject +import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccCoseDecoder +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccHeaderParser +import de.rki.coronawarnapp.covidcertificate.common.decoder.RawCOSEObject +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_BASE45_ENCODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_COSE_MESSAGE_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_COMPRESSION_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidTestCertificateException +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccParser +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccV1 +import de.rki.coronawarnapp.util.compression.deflate +import de.rki.coronawarnapp.util.compression.inflate +import de.rki.coronawarnapp.util.encoding.Base45Decoder +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class TestCertificateQRCodeExtractor @Inject constructor( + private val coseDecoder: DccCoseDecoder, + private val headerParser: DccHeaderParser, + private val bodyParser: TestDccParser, +) { + + /** + * May throw an **[InvalidTestCertificateException]** + */ + fun extract( + decryptionKey: ByteArray, + rawCoseObjectEncrypted: ByteArray, + ): TestCertificateQRCode { + val rawCoseObject = rawCoseObjectEncrypted.decrypt(decryptionKey) + return TestCertificateQRCode( + data = rawCoseObject.decode(), + qrCode = rawCoseObject.encode() + ) + } + + /** + * May throw an **[InvalidTestCertificateException]** + */ + fun extract(qrCode: String) = TestCertificateQRCode( + data = qrCode.extract(), + qrCode = qrCode + ) + + private fun RawCOSEObject.decrypt(decryptionKey: ByteArray): RawCOSEObject = try { + coseDecoder.decryptMessage( + input = this, + decryptionKey = decryptionKey + ) + } catch (e: InvalidHealthCertificateException) { + throw InvalidTestCertificateException(e.errorCode) + } catch (e: Throwable) { + Timber.e(e, HC_COSE_MESSAGE_INVALID.toString()) + throw InvalidTestCertificateException(HC_COSE_MESSAGE_INVALID) + } + + private fun String.extract(): DccData<TestDccV1> = + removePrefix(PREFIX) + .decodeBase45() + .decompress() + .decode() + + private fun RawCOSEObject.encode(): String { + return PREFIX + compress().encodeBase45() + } + + private fun RawCOSEObject.decode(): DccData<TestDccV1> = try { + coseDecoder.decode(this).parse() + } catch (e: InvalidHealthCertificateException) { + throw InvalidTestCertificateException(e.errorCode) + } catch (e: Throwable) { + Timber.e(e, HC_COSE_MESSAGE_INVALID.toString()) + throw InvalidTestCertificateException(HC_COSE_MESSAGE_INVALID) + } + + private fun CBORObject.parse(): DccData<TestDccV1> = try { + DccData( + header = headerParser.parse(this), + certificate = bodyParser.parse(this) + ).also { + Timber.v("Parsed test certificate for %s", it.certificate.nameData.givenNameStandardized) + } + } catch (e: InvalidHealthCertificateException) { + throw InvalidTestCertificateException(e.errorCode) + } catch (e: Throwable) { + Timber.e(e, HC_CBOR_DECODING_FAILED.toString()) + throw InvalidTestCertificateException(HC_CBOR_DECODING_FAILED) + } + + private fun String.decodeBase45(): ByteArray = try { + Base45Decoder.decode(this) + } catch (e: Throwable) { + Timber.e(e, HC_BASE45_DECODING_FAILED.toString()) + throw InvalidTestCertificateException(HC_BASE45_DECODING_FAILED) + } + + private fun ByteArray.encodeBase45(): String = try { + Base45Decoder.encode(this) + } catch (e: Throwable) { + Timber.e(e, HC_BASE45_ENCODING_FAILED.toString()) + throw InvalidTestCertificateException(HC_BASE45_ENCODING_FAILED) + } + + private fun RawCOSEObject.compress(): ByteArray = try { + this.deflate() + } catch (e: Throwable) { + Timber.e(e, HC_ZLIB_COMPRESSION_FAILED.toString()) + throw InvalidTestCertificateException(HC_ZLIB_COMPRESSION_FAILED) + } + + private fun ByteArray.decompress(): RawCOSEObject = try { + this.inflate(sizeLimit = DEFAULT_SIZE_LIMIT) + } catch (e: Throwable) { + Timber.e(e, HC_ZLIB_DECOMPRESSION_FAILED.toString()) + throw InvalidTestCertificateException(HC_ZLIB_DECOMPRESSION_FAILED) + } + + companion object { + private const val PREFIX = "HC1:" + + // Zip bomb + private const val DEFAULT_SIZE_LIMIT = 1024L * 1024 * 10L // 10 MB + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateApiV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateApiV1.kt new file mode 100644 index 0000000000000000000000000000000000000000..e8b7558e353f28b0b91f9c1117a887e1a7ec91f6 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateApiV1.kt @@ -0,0 +1,41 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.server + +import com.google.gson.annotations.SerializedName +import retrofit2.Response +import retrofit2.http.Body +import retrofit2.http.POST + +interface TestCertificateApiV1 { + + data class PublicKeyUploadRequest( + @SerializedName("registrationToken") val registrationToken: String, + @SerializedName("publicKey") val publicKey: String + ) + + @POST("/version/v1/publicKey") + suspend fun sendPublicKey( + @Body requestBody: PublicKeyUploadRequest + ): Response<Unit> + + data class ComponentsRequest( + @SerializedName("registrationToken") val registrationToken: String, + ) + + data class ComponentsResponse( + @SerializedName("dek") val dek: String? = null, + @SerializedName("dcc") val dcc: String? = null, + @SerializedName("reason") val errorReason: String? = null + ) { + enum class Reason(val errorString: String) { + SIGNING_CLIENT_ERROR("SIGNING_CLIENT_ERROR"), + SIGNING_SERVER_ERROR("SIGNING_SERVER_ERROR"), + LAB_INVALID_RESPONSE("LAB_INVALID_RESPONSE"), + INTERNAL("INTERNAL") + } + } + + @POST("/version/v1/dcc") + suspend fun getComponents( + @Body requestBody: ComponentsRequest + ): Response<ComponentsResponse?> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateComponents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateComponents.kt new file mode 100644 index 0000000000000000000000000000000000000000..c0f71f4803763d2fb3c29b4664c8ff7f9d185792 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateComponents.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.server + +data class TestCertificateComponents( + val dataEncryptionKeyBase64: String, + val encryptedCoseTestCertificateBase64: String, +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServer.kt new file mode 100644 index 0000000000000000000000000000000000000000..d0d7a71319fa7698aa5d3e2bd2746f4d7f9ef519 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServer.kt @@ -0,0 +1,114 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.server + +import dagger.Lazy +import dagger.Reusable +import de.rki.coronawarnapp.coronatest.type.RegistrationToken +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_202 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_400 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_404 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_410 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_412 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_500 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_500_LAB_INVALID_RESPONSE +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_500_SIGNING_CLIENT_ERROR +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_500_SIGNING_SERVER_ERROR +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.DCC_COMP_NO_NETWORK +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_400 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_403 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_404 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_409 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_500 +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException.ErrorCode.PKR_NO_NETWORK +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateApiV1.ComponentsResponse.Reason.INTERNAL +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateApiV1.ComponentsResponse.Reason.LAB_INVALID_RESPONSE +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateApiV1.ComponentsResponse.Reason.SIGNING_CLIENT_ERROR +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateApiV1.ComponentsResponse.Reason.SIGNING_SERVER_ERROR +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import de.rki.coronawarnapp.util.network.NetworkStateProvider +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class TestCertificateServer @Inject constructor( + private val dccApi: Lazy<TestCertificateApiV1>, + private val dispatcherProvider: DispatcherProvider, + private val networkStateProvider: NetworkStateProvider +) { + + private val api: TestCertificateApiV1 + get() = dccApi.get() + + @Throws(TestCertificateServerException::class) + suspend fun registerPublicKeyForTest( + testRegistrationToken: RegistrationToken, + publicKey: RSAKey.Public, + ): Unit = withContext(dispatcherProvider.IO) { + Timber.tag(TAG).v("registerPublicKeyForTest(token=%s, key=%s)", testRegistrationToken, publicKey) + if (!isInternetAvailable()) { + throw TestCertificateServerException(PKR_NO_NETWORK) + } + try { + val response = api.sendPublicKey( + requestBody = TestCertificateApiV1.PublicKeyUploadRequest( + registrationToken = testRegistrationToken, + publicKey = publicKey.base64 + ) + ) + when (response.code()) { + 400 -> throw TestCertificateServerException(PKR_400) + 403 -> throw TestCertificateServerException(PKR_403) + 404 -> throw TestCertificateServerException(PKR_404) + 409 -> throw TestCertificateServerException(PKR_409) + 500 -> throw TestCertificateServerException(PKR_500) + } + } catch (e: Exception) { + Timber.tag(TAG).w(e, "registerPublicKeyForTest failed") + throw TestCertificateServerException(PKR_FAILED) + } + } + + @Throws(TestCertificateServerException::class) + suspend fun requestCertificateForTest( + testRegistrationToken: RegistrationToken, + ): TestCertificateComponents = withContext(dispatcherProvider.IO) { + Timber.tag(TAG).v("requestCertificateForTest(token=%s)", testRegistrationToken) + if (!isInternetAvailable()) { + throw TestCertificateServerException(DCC_COMP_NO_NETWORK) + } + val response = api.getComponents( + requestBody = TestCertificateApiV1.ComponentsRequest(testRegistrationToken) + ) + when (response.code()) { + 202 -> throw TestCertificateServerException(DCC_COMP_202) + 400 -> throw TestCertificateServerException(DCC_COMP_400) + 404 -> throw TestCertificateServerException(DCC_COMP_404) + 410 -> throw TestCertificateServerException(DCC_COMP_410) + 412 -> throw TestCertificateServerException(DCC_COMP_412) + 500 -> when (response.body()?.errorReason) { + INTERNAL.errorString -> throw TestCertificateServerException(DCC_COMP_500) + SIGNING_CLIENT_ERROR.errorString -> + throw TestCertificateServerException(DCC_COMP_500_SIGNING_CLIENT_ERROR) + SIGNING_SERVER_ERROR.errorString -> + throw TestCertificateServerException(DCC_COMP_500_SIGNING_SERVER_ERROR) + LAB_INVALID_RESPONSE.errorString -> + throw TestCertificateServerException(DCC_COMP_500_LAB_INVALID_RESPONSE) + } + } + val result = response.body()!! // throw exception? + TestCertificateComponents( + dataEncryptionKeyBase64 = result.dek!!, + encryptedCoseTestCertificateBase64 = result.dcc!! + ) + } + + private suspend fun isInternetAvailable() = networkStateProvider.networkState.first().isInternetAvailable + + companion object { + private const val TAG = "CovidCertificateServer" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServerModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServerModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..4949f5161ede63238719035c55f7975a5c3d92e2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/server/TestCertificateServerModule.kt @@ -0,0 +1,49 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.server + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import de.rki.coronawarnapp.environment.covidcertificate.DCCHttpClient +import de.rki.coronawarnapp.environment.covidcertificate.DCCServerUrl +import okhttp3.OkHttpClient +import okhttp3.ResponseBody +import retrofit2.Converter +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.lang.reflect.Type + +@Module +class TestCertificateServerModule { + + /** + * Handles DCC server 202 "retry later" response with 0-byte bodies. + */ + private val nullConverter = object : Converter.Factory() { + fun factoryRef() = this + override fun responseBodyConverter( + type: Type, + annotations: Array<out Annotation>, + retrofit: Retrofit + ) = object : Converter<ResponseBody, Any?> { + val nextConverter = retrofit.nextResponseBodyConverter<Any?>(factoryRef(), type, annotations) + + override fun convert(value: ResponseBody): Any? { + return if (value.contentLength() != 0L) nextConverter.convert(value) else null + } + } + } + + @Reusable + @Provides + fun apiV1( + @DCCHttpClient httpClient: OkHttpClient, + @DCCServerUrl url: String, + gsonConverterFactory: GsonConverterFactory + ): TestCertificateApiV1 = Retrofit.Builder() + .client(httpClient) + .baseUrl(url) + .addConverterFactory(nullConverter) + .addConverterFactory(gsonConverterFactory) + .build() + .create(TestCertificateApiV1::class.java) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/PCRCertificateData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/PCRCertificateData.kt new file mode 100644 index 0000000000000000000000000000000000000000..118bf5bc112a3fc4399a49210542b1f6069c7256 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/PCRCertificateData.kt @@ -0,0 +1,52 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.RegistrationToken +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import okio.ByteString +import org.joda.time.Instant + +data class PCRCertificateData internal constructor( + @SerializedName("identifier") + override val identifier: String, + + @SerializedName("registrationToken") + override val registrationToken: RegistrationToken, + + @SerializedName("registeredAt") + override val registeredAt: Instant, + + @SerializedName("publicKeyRegisteredAt") + override val publicKeyRegisteredAt: Instant? = null, + + @SerializedName("rsaPublicKey") + override val rsaPublicKey: RSAKey.Public? = null, + + @SerializedName("rsaPrivateKey") + override val rsaPrivateKey: RSAKey.Private? = null, + + @SerializedName("certificateReceivedAt") + override val certificateReceivedAt: Instant? = null, + + @SerializedName("encryptedDataEncryptionkey") + override val encryptedDataEncryptionkey: ByteString? = null, + + @SerializedName("encryptedDccCose") + override val encryptedDccCose: ByteString? = null, + + @SerializedName("testCertificateQrCode") + override val testCertificateQrCode: String? = null, +) : StoredTestCertificateData { + + // Otherwise GSON unsafes reflection to create this class, and sets the LAZY to null + @Suppress("unused") + constructor() : this( + identifier = "", + registrationToken = "", + registeredAt = Instant.EPOCH + ) + + override val type: CoronaTest.Type + get() = CoronaTest.Type.PCR +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/RACertificateData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/RACertificateData.kt new file mode 100644 index 0000000000000000000000000000000000000000..dfcb82bad80c8f2000c505ce3929fb62dc6f1f64 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/RACertificateData.kt @@ -0,0 +1,52 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.RegistrationToken +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import okio.ByteString +import org.joda.time.Instant + +data class RACertificateData( + @SerializedName("identifier") + override val identifier: String, + + @SerializedName("registrationToken") + override val registrationToken: RegistrationToken, + + @SerializedName("registeredAt") + override val registeredAt: Instant, + + @SerializedName("publicKeyRegisteredAt") + override val publicKeyRegisteredAt: Instant? = null, + + @SerializedName("rsaPublicKey") + override val rsaPublicKey: RSAKey.Public? = null, + + @SerializedName("rsaPrivateKey") + override val rsaPrivateKey: RSAKey.Private? = null, + + @SerializedName("certificateReceivedAt") + override val certificateReceivedAt: Instant? = null, + + @SerializedName("encryptedDataEncryptionkey") + override val encryptedDataEncryptionkey: ByteString? = null, + + @SerializedName("encryptedDccCose") + override val encryptedDccCose: ByteString? = null, + + @SerializedName("testCertificateQrCode") + override val testCertificateQrCode: String? = null, +) : StoredTestCertificateData { + + // Otherwise GSON unsafes reflection to create this class, and sets the LAZY to null + @Suppress("unused") + constructor() : this( + identifier = "", + registrationToken = "", + registeredAt = Instant.EPOCH + ) + + override val type: CoronaTest.Type + get() = CoronaTest.Type.RAPID_ANTIGEN +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/StoredTestCertificateData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/StoredTestCertificateData.kt new file mode 100644 index 0000000000000000000000000000000000000000..131c18e26608fb530f939f6cccc0cbb49128133d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/StoredTestCertificateData.kt @@ -0,0 +1,21 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.coronatest.type.RegistrationToken +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import okio.ByteString +import org.joda.time.Instant + +interface StoredTestCertificateData { + val identifier: TestCertificateIdentifier + val registrationToken: RegistrationToken + val type: CoronaTest.Type + val registeredAt: Instant + val publicKeyRegisteredAt: Instant? + val rsaPublicKey: RSAKey.Public? + val rsaPrivateKey: RSAKey.Private? + val certificateReceivedAt: Instant? + val encryptedDataEncryptionkey: ByteString? + val encryptedDccCose: ByteString? + val testCertificateQrCode: String? +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt new file mode 100644 index 0000000000000000000000000000000000000000..972fcc5db5ee6c333fb06819ec23d70eb719632c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateContainer.kt @@ -0,0 +1,99 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccV1 +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets +import org.joda.time.Instant +import org.joda.time.LocalDate +import java.util.Locale + +data class TestCertificateContainer( + internal val data: StoredTestCertificateData, + private val qrCodeExtractor: TestCertificateQRCodeExtractor, + val isUpdatingData: Boolean = false, +) : StoredTestCertificateData by data { + + @delegate:Transient + private val certificateData: DccData<TestDccV1> by lazy { + data.testCertificateQrCode!!.let { qrCodeExtractor.extract(it).data } + } + + val isPublicKeyRegistered: Boolean + get() = data.publicKeyRegisteredAt != null + + val isCertificateRetrievalPending: Boolean + get() = data.certificateReceivedAt == null + + val certificateId: String? + get() { + if (isCertificateRetrievalPending) return null + return certificateData.certificate.payload.uniqueCertificateIdentifier + } + + fun toTestCertificate( + valueSet: TestCertificateValueSets?, + userLocale: Locale = Locale.getDefault(), + ): TestCertificate? { + if (isCertificateRetrievalPending) return null + + val header = certificateData.header + val certificate = certificateData.certificate + val testCertificate = certificate.payload + + return object : TestCertificate { + override val personIdentifier: CertificatePersonIdentifier + get() = certificate.personIdentifier + + override val firstName: String? + get() = certificate.nameData.firstName + + override val lastName: String + get() = certificate.nameData.lastName + + override val fullName: String + get() = certificate.nameData.fullName + + override val dateOfBirth: LocalDate + get() = certificate.dateOfBirth + + override val targetName: String + get() = valueSet?.getDisplayText(testCertificate.targetId) ?: testCertificate.targetId + override val testType: String + get() = valueSet?.getDisplayText(testCertificate.testType) ?: testCertificate.testType + override val testResult: String + get() = valueSet?.getDisplayText(testCertificate.testResult) ?: testCertificate.testResult + override val testName: String? + get() = testCertificate.testName?.let { valueSet?.getDisplayText(it) ?: it } + override val testNameAndManufactor: String? + get() = testCertificate.testNameAndManufactor?.let { valueSet?.getDisplayText(it) ?: it } + override val sampleCollectedAt: Instant + get() = testCertificate.sampleCollectedAt + override val testResultAt: Instant? + get() = testCertificate.testResultAt + override val testCenter: String + get() = testCertificate.testCenter + + override val certificateIssuer: String + get() = header.issuer + override val certificateCountry: String + get() = Locale(userLocale.language, testCertificate.certificateCountry.uppercase()) + .getDisplayCountry(userLocale) + override val certificateId: String + get() = testCertificate.uniqueCertificateIdentifier + + override val issuer: String + get() = header.issuer + override val issuedAt: Instant + get() = header.issuedAt + override val expiresAt: Instant + get() = header.expiresAt + + override val qrCode: QrCodeString + get() = data.testCertificateQrCode!! + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateIdentifier.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateIdentifier.kt new file mode 100644 index 0000000000000000000000000000000000000000..9de185a474a05502f427bab0f19b5f0bdad703e5 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateIdentifier.kt @@ -0,0 +1,3 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +typealias TestCertificateIdentifier = String diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateProcessor.kt new file mode 100644 index 0000000000000000000000000000000000000000..4d7e14c25ec6582142e155f6fd4a0cd1fa090f44 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateProcessor.kt @@ -0,0 +1,156 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import dagger.Reusable +import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidTestCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.TestCertificateServerException +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateComponents +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateServer +import de.rki.coronawarnapp.util.TimeStamper +import de.rki.coronawarnapp.util.encryption.rsa.RSACryptography +import de.rki.coronawarnapp.util.encryption.rsa.RSAKeyPairGenerator +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.first +import okio.ByteString.Companion.decodeBase64 +import org.joda.time.Duration +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class TestCertificateProcessor @Inject constructor( + private val timeStamper: TimeStamper, + private val certificateServer: TestCertificateServer, + private val rsaKeyPairGenerator: RSAKeyPairGenerator, + private val rsaCryptography: RSACryptography, + private val appConfigProvider: AppConfigProvider, + private val qrCodeExtractor: TestCertificateQRCodeExtractor, +) { + + /** + * Register the public key with the server, a shortwhile later, + * the test certificate components should be available, via [obtainCertificate]. + */ + internal suspend fun registerPublicKey( + data: StoredTestCertificateData + ): StoredTestCertificateData { + Timber.tag(TAG).d("registerPublicKey(cert=%s)", data) + + if (data.publicKeyRegisteredAt != null) { + Timber.tag(TAG).d("Public key is already registered for %s", data) + return data + } + + val rsaKeyPair = try { + rsaKeyPairGenerator.generate() + } catch (e: Throwable) { + throw InvalidTestCertificateException(InvalidHealthCertificateException.ErrorCode.RSA_KP_GENERATION_FAILED) + } + + certificateServer.registerPublicKeyForTest( + testRegistrationToken = data.registrationToken, + publicKey = rsaKeyPair.publicKey, + ) + Timber.tag(TAG).i("Public key successfully registered for %s", data) + + val nowUTC = timeStamper.nowUTC + + return when (data.type) { + CoronaTest.Type.PCR -> (data as PCRCertificateData).copy( + publicKeyRegisteredAt = nowUTC, + rsaPublicKey = rsaKeyPair.publicKey, + rsaPrivateKey = rsaKeyPair.privateKey, + ) + CoronaTest.Type.RAPID_ANTIGEN -> (data as RACertificateData).copy( + publicKeyRegisteredAt = nowUTC, + rsaPublicKey = rsaKeyPair.publicKey, + rsaPrivateKey = rsaKeyPair.privateKey, + ) + } + } + + /** + * Try to obtain the actual certificate. + * PublicKey registration and certificate retrieval are two steps, because if we manage to register our public key, + * but fail to get the certificate, we are still one step further. + * + * The server does not immediately return the test certificate components after registering the public key. + */ + internal suspend fun obtainCertificate( + data: StoredTestCertificateData + ): StoredTestCertificateData { + Timber.tag(TAG).d("requestCertificate(cert=%s)", data) + + if (data.publicKeyRegisteredAt == null) { + throw IllegalStateException("Public key is not registered yet.") + } + + if (data.certificateReceivedAt != null) { + Timber.tag(TAG).d("Dcc has already been retrieved for %s", data) + return data + } + + val certConfig = appConfigProvider.currentConfig.first().covidCertificateParameters.testCertificate + + val nowUTC = timeStamper.nowUTC + val certAvailableAt = data.publicKeyRegisteredAt!!.plus(certConfig.waitAfterPublicKeyRegistration) + val certAvailableIn = Duration(nowUTC, certAvailableAt) + + if (certAvailableIn > Duration.ZERO && certAvailableIn <= certConfig.waitAfterPublicKeyRegistration) { + Timber.tag(TAG) + .d("Delaying certificate retrieval by %d ms", certAvailableIn.millis) + delay(certAvailableIn.millis) + } + + val executeRequest: suspend () -> TestCertificateComponents = { + certificateServer.requestCertificateForTest(testRegistrationToken = data.registrationToken) + } + + val components = try { + executeRequest() + } catch (e: TestCertificateServerException) { + if (e.errorCode == TestCertificateServerException.ErrorCode.DCC_COMP_202) { + delay(certConfig.waitForRetry.millis) + executeRequest() + } else { + throw e + } + } + Timber.tag(TAG) + .i("Test certificate components successfully request for %s: %s", data, components) + + val encryptionKey = try { + rsaCryptography.decrypt( + toDecrypt = components.dataEncryptionKeyBase64.decodeBase64()!!, + privateKey = data.rsaPrivateKey!! + ) + } catch (e: Throwable) { + Timber.tag(TAG).e(e, "RSA_DECRYPTION_FAILED") + throw InvalidTestCertificateException(InvalidHealthCertificateException.ErrorCode.RSA_DECRYPTION_FAILED) + } + + val extractedData = qrCodeExtractor.extract( + decryptionKey = encryptionKey.toByteArray(), + rawCoseObjectEncrypted = components.encryptedCoseTestCertificateBase64.decodeBase64()!!.toByteArray() + ) + + val nowUtc = timeStamper.nowUTC + + return when (data.type) { + CoronaTest.Type.PCR -> (data as PCRCertificateData).copy( + testCertificateQrCode = extractedData.qrCode, + certificateReceivedAt = nowUtc, + ) + CoronaTest.Type.RAPID_ANTIGEN -> (data as RACertificateData).copy( + testCertificateQrCode = extractedData.qrCode, + certificateReceivedAt = nowUtc, + ) + } + } + + companion object { + private val TAG = TestCertificateProcessor::class.simpleName!! + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateStorage.kt new file mode 100644 index 0000000000000000000000000000000000000000..087486024444e89c955a9d87d4ba1b3ed5123e0c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/core/storage/TestCertificateStorage.kt @@ -0,0 +1,99 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.storage + +import android.content.Context +import androidx.core.content.edit +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.ContainerPostProcessor +import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.serialization.BaseGson +import timber.log.Timber +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class TestCertificateStorage @Inject constructor( + @AppContext val context: Context, + @BaseGson val baseGson: Gson, + private val containerPostProcessor: ContainerPostProcessor, +) { + + private val prefs by lazy { + context.getSharedPreferences("coronatest_certificate_localdata", Context.MODE_PRIVATE) + } + + private val gson by lazy { + baseGson.newBuilder().apply { + registerTypeAdapter(CoronaTestResult::class.java, CoronaTestResult.GsonAdapter()) + registerTypeAdapterFactory(containerPostProcessor) + }.create() + } + + private val typeTokenPCR by lazy { + object : TypeToken<Set<PCRCertificateData>>() {}.type + } + + private val typeTokenRA by lazy { + object : TypeToken<Set<RACertificateData>>() {}.type + } + + var testCertificates: Collection<StoredTestCertificateData> + get() { + Timber.tag(TAG).d("load()") + + val pcrCertContainers: Set<PCRCertificateData> = run { + val raw = prefs.getString(PKEY_DATA_PCR, null) ?: return@run emptySet() + gson.fromJson<Set<PCRCertificateData>>(raw, typeTokenPCR).onEach { + Timber.tag(TAG).v("PCR loaded: %s", it) + requireNotNull(it.identifier) + requireNotNull(it.type) { "PCR type should not be null, GSON footgun." } + } + } + + val raCerts: Set<RACertificateData> = run { + val raw = prefs.getString(PKEY_DATA_RA, null) ?: return@run emptySet() + gson.fromJson<Set<RACertificateData>>(raw, typeTokenRA).onEach { + Timber.tag(TAG).v("RA loaded: %s", it) + requireNotNull(it.identifier) + requireNotNull(it.type) { "RA type should not be null, GSON footgun." } + } + } + + return (pcrCertContainers + raCerts).also { + Timber.tag(TAG).v("Loaded %d certificates.", it.size) + } + } + set(value) { + Timber.tag(TAG).d("save(testCertificates=%s)", value) + prefs.edit { + value.filter { it.type == CoronaTest.Type.PCR }.run { + if (isNotEmpty()) { + val raw = gson.toJson(this, typeTokenPCR) + Timber.tag(TAG).v("PCR storing: %s", raw) + putString(PKEY_DATA_PCR, raw) + } else { + Timber.tag(TAG).v("No PCR certificates available, clearing.") + remove(PKEY_DATA_PCR) + } + } + value.filter { it.type == CoronaTest.Type.RAPID_ANTIGEN }.run { + if (isNotEmpty()) { + val raw = gson.toJson(this, typeTokenRA) + Timber.tag(TAG).v("RA storing: %s", raw) + putString(PKEY_DATA_RA, raw) + } else { + Timber.tag(TAG).v("No RA certificates available, clearing.") + remove(PKEY_DATA_RA) + } + } + } + } + + companion object { + private const val TAG = "TestCertificateStorage" + private const val PKEY_DATA_RA = "testcertificate.data.ra" + private const val PKEY_DATA_PCR = "testcertificate.data.pcr" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesAdapter.kt similarity index 61% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesAdapter.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesAdapter.kt index aabec57cb42dd9b9404422c3bfa085c03cf1b7cf..41a700c645df7214a93cbec367d066dbf862b589 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesAdapter.kt @@ -1,9 +1,16 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates +package de.rki.coronawarnapp.covidcertificate.test.ui import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem +import de.rki.coronawarnapp.covidcertificate.test.ui.cards.CovidTestCertificateCard +import de.rki.coronawarnapp.covidcertificate.test.ui.cards.CovidTestCertificateErrorCard +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.CreateVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.HeaderInfoVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.ImmuneVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.NoCovidTestCertificatesCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.VaccinationCard import de.rki.coronawarnapp.util.lists.BindableVH import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffer @@ -11,11 +18,6 @@ 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.cards.BottomInfoVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.CreateVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.HeaderInfoVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.ImmuneVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.VaccinationCard class CertificatesAdapter : ModularAdapter<CertificatesAdapter.CertificatesItemVH<CertificatesItem, ViewBinding>>(), @@ -36,7 +38,11 @@ class CertificatesAdapter : }, TypedVHCreatorMod({ data[it] is CreateVaccinationCard.Item }) { CreateVaccinationCard(it) }, TypedVHCreatorMod({ data[it] is HeaderInfoVaccinationCard.Item }) { HeaderInfoVaccinationCard(it) }, - TypedVHCreatorMod({ data[it] is BottomInfoVaccinationCard.Item }) { BottomInfoVaccinationCard(it) }, + TypedVHCreatorMod({ data[it] is NoCovidTestCertificatesCard.Item }) { NoCovidTestCertificatesCard(it) }, + TypedVHCreatorMod({ data[it] is CovidTestCertificateCard.Item }) { CovidTestCertificateCard(it) }, + TypedVHCreatorMod({ data[it] is CovidTestCertificateErrorCard.Item }) { + CovidTestCertificateErrorCard(it) + }, ) ) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..96263715b079593f273d3a19bddbcd80c0d736a2 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragment.kt @@ -0,0 +1,99 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui + +import android.os.Bundle +import android.view.View +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DefaultItemAnimator +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragment +import de.rki.coronawarnapp.databinding.FragmentCertificatesBinding +import de.rki.coronawarnapp.util.DialogHelper +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator +import de.rki.coronawarnapp.util.lists.diffutil.update +import de.rki.coronawarnapp.util.ui.doNavigate +import de.rki.coronawarnapp.util.ui.findNestedGraph +import de.rki.coronawarnapp.util.ui.observe2 +import de.rki.coronawarnapp.util.ui.viewBinding +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModels +import javax.inject.Inject + +class CertificatesFragment : Fragment(R.layout.fragment_certificates), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val viewModel by cwaViewModels<CertificatesViewModel> { viewModelFactory } + private val binding by viewBinding<FragmentCertificatesBinding>() + + private val certificatesAdapter = CertificatesAdapter() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.apply { + setupMenu(mainTracing) + recyclerView.apply { + itemAnimator = DefaultItemAnimator() + addItemDecoration(TopBottomPaddingDecorator(topPadding = R.dimen.spacing_tiny)) + adapter = certificatesAdapter + } + } + + viewModel.screenItems.observe2(this) { items -> certificatesAdapter.update(items) } + viewModel.events.observe2(this) { event -> + when (event) { + is CertificatesFragmentEvents.GoToVaccinationList -> findNavController().navigate( + VaccinationListFragment.navigationUri(event.personIdentifierCodeSha256) + ) + is CertificatesFragmentEvents.OpenVaccinationRegistrationGraph -> { + findNestedGraph(R.id.vaccination_nav_graph).startDestination = R.id.vaccinationQrCodeScanFragment + doNavigate(CertificatesFragmentDirections.actionCertificatesFragmentToVaccinationNavGraph()) + } + is CertificatesFragmentEvents.GoToCovidCertificateDetailScreen -> { + doNavigate( + CertificatesFragmentDirections + .actionCertificatesFragmentToCovidCertificateDetailsFragment(event.identifier) + ) + } + is CertificatesFragmentEvents.ShowRefreshErrorCertificateDialog -> { + val dialog = DialogHelper.DialogInstance( + context = requireContext(), + title = R.string.test_certificate_refresh_dialog_title, + message = event.error.localizedMessage, + positiveButton = R.string.test_certificate_refresh_dialog_confirm_button, + cancelable = false + ) + DialogHelper.showDialog(dialog) + } + is CertificatesFragmentEvents.ShowDeleteErrorCertificateDialog -> { + val dialog = DialogHelper.DialogInstance( + context = requireContext(), + title = R.string.test_certificate_delete_dialog_title, + message = R.string.test_certificate_delete_dialog_body, + positiveButton = R.string.test_certificate_delete_dialog_confirm_button, + negativeButton = R.string.test_certificate_delete_dialog_cancel_button, + cancelable = false, + positiveButtonFunction = { + viewModel.deleteTestCertificate(event.identifier) + } + ) + DialogHelper.showDialog(dialog) + } + } + } + } + + private fun setupMenu(toolbar: Toolbar) = toolbar.apply { + setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_information -> { + doNavigate(CertificatesFragmentDirections.actionCertificatesFragmentToConsentFragment()) + true + } + else -> onOptionsItemSelected(it) + } + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentEvents.kt new file mode 100644 index 0000000000000000000000000000000000000000..259f0feb5c669db1f99a271c1ffc7d11a7869061 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentEvents.kt @@ -0,0 +1,17 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui + +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier + +sealed class CertificatesFragmentEvents { + + data class OpenVaccinationRegistrationGraph(val registrationAcknowledged: Boolean) : CertificatesFragmentEvents() + + data class GoToVaccinationList(val personIdentifierCodeSha256: String) : CertificatesFragmentEvents() + + data class GoToCovidCertificateDetailScreen(val identifier: String) : CertificatesFragmentEvents() + + data class ShowRefreshErrorCertificateDialog(val error: Exception) : CertificatesFragmentEvents() + + data class ShowDeleteErrorCertificateDialog(val identifier: TestCertificateIdentifier) : + CertificatesFragmentEvents() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentModule.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentModule.kt index a0e9ec938fef274d033efae607e075cc3050bb38..ae38d73a53186cf059b1c83015771378e1db27d7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates +package de.rki.coronawarnapp.covidcertificate.test.ui import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..374c269b475ecea16b6f5fe41bc4f6896b73937e --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CertificatesViewModel.kt @@ -0,0 +1,131 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui + +import androidx.lifecycle.LiveData +import androidx.lifecycle.asLiveData +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateWrapper +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier +import de.rki.coronawarnapp.covidcertificate.test.ui.cards.CovidTestCertificateCard +import de.rki.coronawarnapp.covidcertificate.test.ui.cards.CovidTestCertificateErrorCard +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.CreateVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.HeaderInfoVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.ImmuneVaccinationCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.NoCovidTestCertificatesCard +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards.VaccinationCard +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory +import kotlinx.coroutines.flow.combine + +class CertificatesViewModel @AssistedInject constructor( + vaccinationRepository: VaccinationRepository, + private val vaccinationSettings: VaccinationSettings, + private val testCertificateRepository: TestCertificateRepository +) : CWAViewModel() { + + val events = SingleLiveEvent<CertificatesFragmentEvents>() + + private fun refreshTestCertificate(identifier: TestCertificateIdentifier) { + launch { + val error = testCertificateRepository.refresh(identifier).mapNotNull { it.error }.singleOrNull() + if (error != null) { + events.postValue(CertificatesFragmentEvents.ShowRefreshErrorCertificateDialog(error)) + } + } + } + + fun deleteTestCertificate(identifier: TestCertificateIdentifier) { + launch { + testCertificateRepository.deleteCertificate(identifier) + } + } + + val screenItems: LiveData<List<CertificatesItem>> = + vaccinationRepository.vaccinationInfos + .combine(testCertificateRepository.certificates) { vaccinatedPersons, certificates -> + mutableListOf<CertificatesItem>().apply { + add(HeaderInfoVaccinationCard.Item) + if (vaccinatedPersons.isEmpty()) { + add( + CreateVaccinationCard.Item( + onClickAction = { + CertificatesFragmentEvents.OpenVaccinationRegistrationGraph( + vaccinationSettings.registrationAcknowledged + ).run { events.postValue(this) } + } + ) + ) + } else { + addAll(vaccinatedPersons.toCertificateItems()) + } + + if (certificates.isEmpty()) { + add(NoCovidTestCertificatesCard.Item) + } else { + addAll(certificates.toCertificateItems()) + } + } + }.asLiveData() + + private fun Set<VaccinatedPerson>.toCertificateItems(): List<CertificatesItem> = map { vaccinatedPerson -> + when (vaccinatedPerson.getVaccinationStatus()) { + VaccinatedPerson.Status.COMPLETE, + VaccinatedPerson.Status.INCOMPLETE -> VaccinationCard.Item( + vaccinatedPerson = vaccinatedPerson, + onClickAction = { + CertificatesFragmentEvents.GoToVaccinationList( + vaccinatedPerson.identifier.codeSHA256 + ).run { events.postValue(this) } + } + ) + VaccinatedPerson.Status.IMMUNITY -> ImmuneVaccinationCard.Item( + vaccinatedPerson = vaccinatedPerson, + onClickAction = { + CertificatesFragmentEvents.GoToVaccinationList( + vaccinatedPerson.identifier.codeSHA256 + ).run { events.postValue(this) } + } + ) + } + } + + private fun Collection<TestCertificateWrapper>.toCertificateItems(): List<CertificatesItem> = map { certificate -> + if (certificate.isCertificateRetrievalPending) { + CovidTestCertificateErrorCard.Item( + testDate = certificate.registeredAt, + isUpdatingData = certificate.isUpdatingData, + onRetryAction = { + refreshTestCertificate(certificate.identifier) + }, + onDeleteAction = { + events.postValue( + CertificatesFragmentEvents.ShowDeleteErrorCertificateDialog( + certificate.identifier + ) + ) + } + ) + } else { + CovidTestCertificateCard.Item( + testDate = certificate.registeredAt, + testPerson = + certificate.testCertificate?.firstName + " " + + certificate.testCertificate?.lastName, + onClickAction = { + CertificatesFragmentEvents.GoToCovidCertificateDetailScreen( + certificate.identifier + ).run { events.postValue(this) } + } + ) + } + } + + @AssistedFactory + interface Factory : SimpleCWAViewModelFactory<CertificatesViewModel> +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateUIModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..8bc95ef2ad4b954ec34eb1f43dc84f68180aab3d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/CovidCertificateUIModule.kt @@ -0,0 +1,16 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment +import de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsModule + +@Module +abstract class CovidCertificateUIModule { + + @ContributesAndroidInjector(modules = [CertificatesFragmentModule::class]) + abstract fun certificatesFragment(): CertificatesFragment + + @ContributesAndroidInjector(modules = [CovidCertificateDetailsModule::class]) + abstract fun certificateDetailsFragment(): CovidCertificateDetailsFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidCertificateTestItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidCertificateTestItem.kt new file mode 100644 index 0000000000000000000000000000000000000000..bc27a92dd94416a5e9e1f45b22b9f384af8f8856 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidCertificateTestItem.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.cards + +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem +import org.joda.time.Instant + +interface CovidCertificateTestItem : CertificatesItem { + val testDate: Instant + + override val stableId: Long + get() = testDate.hashCode().toLong() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateCard.kt new file mode 100644 index 0000000000000000000000000000000000000000..791baaf5ab8d8625f9aa814583dd4d9975107aad --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateCard.kt @@ -0,0 +1,45 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.cards + +import android.view.ViewGroup +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.databinding.CovidTestSuccessCardBinding +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortDayFormat +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortTimeFormat +import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer +import org.joda.time.Instant + +class CovidTestCertificateCard(parent: ViewGroup) : + CertificatesAdapter.CertificatesItemVH<CovidTestCertificateCard.Item, CovidTestSuccessCardBinding>( + R.layout.home_card_container_layout, + parent + ) { + + override val viewBinding = lazy { + CovidTestSuccessCardBinding.inflate(layoutInflater, itemView.findViewById(R.id.card_container), true) + } + + override val onBindData: CovidTestSuccessCardBinding.( + item: Item, + payloads: List<Any> + ) -> Unit = { item, payloads -> + val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item + testTime.text = context.getString( + R.string.test_certificate_time, + curItem.testDate.toShortDayFormat(), + curItem.testDate.toShortTimeFormat(), + ) + + personName.text = curItem.testPerson + + itemView.setOnClickListener { curItem.onClickAction(curItem) } + } + + data class Item( + override val testDate: Instant, + val testPerson: String, + val onClickAction: (Item) -> Unit, + ) : CovidCertificateTestItem, HasPayloadDiffer { + override fun diffPayload(old: Any, new: Any): Any? = if (old::class == new::class) new else null + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateErrorCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateErrorCard.kt new file mode 100644 index 0000000000000000000000000000000000000000..3377a129fe47ba8d15b4ac6677af453871b3e028 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/cards/CovidTestCertificateErrorCard.kt @@ -0,0 +1,63 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.cards + +import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isInvisible +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.databinding.CovidTestErrorCardBinding +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortDayFormat +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortTimeFormat +import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer +import org.joda.time.Instant + +class CovidTestCertificateErrorCard(parent: ViewGroup) : + CertificatesAdapter.CertificatesItemVH<CovidTestCertificateErrorCard.Item, CovidTestErrorCardBinding>( + R.layout.home_card_container_layout, + parent + ) { + + override val viewBinding = lazy { + CovidTestErrorCardBinding.inflate(layoutInflater, itemView.findViewById(R.id.card_container), true) + } + + override val onBindData: CovidTestErrorCardBinding.( + item: Item, + payloads: List<Any> + ) -> Unit = { item, _ -> + + testTime.text = context.getString( + R.string.test_certificate_time, + item.testDate.toShortDayFormat(), + item.testDate.toShortTimeFormat(), + ) + + retryButton.setOnClickListener { + item.onRetryAction(item) + } + + if (item.isUpdatingData) { + refreshStatus.isGone = false + progressBar.show() + retryButton.isInvisible = true + deleteButton.isInvisible = true + body.text = context.getString(R.string.test_certificate_error_label_refreshing) + } else { + refreshStatus.isGone = true + progressBar.hide() + retryButton.isInvisible = false + deleteButton.isInvisible = false + body.text = context.getString(R.string.test_certificate_error_label) + } + deleteButton.setOnClickListener { item.onDeleteAction(item) } + } + + data class Item( + override val testDate: Instant, + val onRetryAction: (Item) -> Unit, + val onDeleteAction: (Item) -> Unit, + val isUpdatingData: Boolean, + ) : CovidCertificateTestItem, HasPayloadDiffer { + override fun diffPayload(old: Any, new: Any): Any? = if (old::class == new::class) new else null + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..ae32afd1905d6b97daf944cccf12e4eb8487214a --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsFragment.kt @@ -0,0 +1,172 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.details + +import android.graphics.Bitmap +import android.os.Bundle +import android.view.View +import android.widget.LinearLayout +import androidx.coordinatorlayout.widget.CoordinatorLayout +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 de.rki.coronawarnapp.R +import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate +import de.rki.coronawarnapp.databinding.FragmentCovidCertificateDetailsBinding +import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs +import de.rki.coronawarnapp.ui.view.onOffsetChange +import de.rki.coronawarnapp.util.DialogHelper +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortDayFormat +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toShortTimeFormat +import de.rki.coronawarnapp.util.di.AutoInject +import de.rki.coronawarnapp.util.setUrl +import de.rki.coronawarnapp.util.ui.popBackStack +import de.rki.coronawarnapp.util.ui.viewBinding +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider +import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted +import javax.inject.Inject + +class CovidCertificateDetailsFragment : Fragment(R.layout.fragment_covid_certificate_details), AutoInject { + + @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory + private val binding by viewBinding<FragmentCovidCertificateDetailsBinding>() + private val args by navArgs<CovidCertificateDetailsFragmentArgs>() + private val viewModel: CovidCertificateDetailsViewModel by cwaViewModelsAssisted( + factoryProducer = { viewModelFactory }, + constructorCall = { factory, _ -> + factory as CovidCertificateDetailsViewModel.Factory + factory.create( + testCertificateIdentifier = args.testCertificateIdentifier + ) + } + ) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) = with(binding) { + qrCodeCard.title.setText(R.string.detail_green_certificate_card_title) + appBarLayout.onOffsetChange { titleAlpha, subtitleAlpha -> + title.alpha = titleAlpha + subtitle.alpha = subtitleAlpha + } + + bindTravelNoticeViews() + bindToolbar() + setToolbarOverlay() + + viewModel.qrCode.observe(viewLifecycleOwner) { onQrCodeReady(it) } + viewModel.errors.observe(viewLifecycleOwner) { onError(it) } + viewModel.events.observe(viewLifecycleOwner) { onNavEvent(it) } + viewModel.covidCertificate.observe(viewLifecycleOwner) { it?.let { onCertificateReady(it) } } + } + + private fun FragmentCovidCertificateDetailsBinding.onCertificateReady( + testCertificate: TestCertificate + ) { + qrCodeCard.subtitle.text = getString( + R.string.detail_green_certificate_card_subtitle, + testCertificate.sampleCollectedAt.toShortDayFormat(), + testCertificate.sampleCollectedAt.toShortTimeFormat(), + ) + name.text = testCertificate.run { "$firstName $lastName" } + birthDate.text = testCertificate.dateOfBirth.toDayFormat() + diseaseType.text = testCertificate.targetName + testType.text = testCertificate.testType + testName.text = testCertificate.testName + testManufacturer.text = testCertificate.testNameAndManufactor + testDate.text = "%s %s".format( + testCertificate.sampleCollectedAt.toShortDayFormat(), + testCertificate.sampleCollectedAt.toShortTimeFormat() + ) + testResult.text = testCertificate.testResult + testCenter.text = testCertificate.testCenter + certificateCountry.text = testCertificate.certificateCountry + certificateIssuer.text = testCertificate.certificateIssuer + certificateId.text = testCertificate.certificateId + } + + private fun FragmentCovidCertificateDetailsBinding.onQrCodeReady(bitmap: Bitmap?) { + qrCodeCard.apply { + image.setImageBitmap(bitmap) + progressBar.hide() + bitmap?.let { image.setOnClickListener { viewModel.openFullScreen() } } + } + } + + private fun FragmentCovidCertificateDetailsBinding.onError(error: Throwable) { + qrCodeCard.progressBar.hide() + error.toErrorDialogBuilder(requireContext()).show() + } + + private fun FragmentCovidCertificateDetailsBinding.onNavEvent(event: CovidCertificateDetailsNavigation) { + when (event) { + CovidCertificateDetailsNavigation.Back -> popBackStack() + is CovidCertificateDetailsNavigation.FullQrCode -> findNavController().navigate( + R.id.action_global_qrCodeFullScreenFragment, + QrCodeFullScreenFragmentArgs(event.qrCodeText).toBundle(), + null, + FragmentNavigatorExtras(qrCodeCard.image to qrCodeCard.image.transitionName) + ) + } + } + + private fun FragmentCovidCertificateDetailsBinding.bindTravelNoticeViews() { + if (travelNoticeGerman.text == + getString(R.string.green_certificate_attribute_certificate_travel_notice_german) + ) { + travelNoticeGerman.setUrl( + R.string.green_certificate_attribute_certificate_travel_notice_german, + R.string.green_certificate_travel_notice_link_de, + R.string.green_certificate_travel_notice_link_de + ) + } + + if (travelNoticeEnglish.text == + getString(R.string.green_certificate_attribute_certificate_travel_notice_english) + ) { + travelNoticeEnglish.setUrl( + R.string.green_certificate_attribute_certificate_travel_notice_english, + R.string.green_certificate_travel_notice_link_en, + R.string.green_certificate_travel_notice_link_en + ) + } + } + + private fun FragmentCovidCertificateDetailsBinding.bindToolbar() = toolbar.apply { + setNavigationOnClickListener { popBackStack() } + setOnMenuItemClickListener { + when (it.itemId) { + R.id.menu_covid_certificate_delete -> { + DialogHelper.showDialog(deleteTestConfirmationDialog) + true + } + else -> onOptionsItemSelected(it) + } + } + } + + private fun setToolbarOverlay() { + val width = requireContext().resources.displayMetrics.widthPixels + val params: CoordinatorLayout.LayoutParams = binding.scrollView.layoutParams as (CoordinatorLayout.LayoutParams) + + val textParams = binding.subtitle.layoutParams as (LinearLayout.LayoutParams) + textParams.bottomMargin = (width / 3) + 170 + binding.subtitle.requestLayout() + + val behavior: AppBarLayout.ScrollingViewBehavior = params.behavior as (AppBarLayout.ScrollingViewBehavior) + behavior.overlayTop = (width / 3) + 170 + } + + private val deleteTestConfirmationDialog by lazy { + DialogHelper.DialogInstance( + requireActivity(), + R.string.green_certificate_details_dialog_remove_test_title, + R.string.green_certificate_details_dialog_remove_test_message, + R.string.green_certificate_details_dialog_remove_test_button_positive, + R.string.green_certificate_details_dialog_remove_test_button_negative, + positiveButtonFunction = { + viewModel.onDeleteTestConfirmed() + } + ) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsModule.kt similarity index 62% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsModule.kt index 363a483bf8bd6ba41eb6fb1e408055eb3bda9189..2846c57e371822be705c0cd4be3d455a752b4814 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates.details +package de.rki.coronawarnapp.covidcertificate.test.ui.details import dagger.Binds import dagger.Module @@ -8,12 +8,12 @@ import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey @Module -abstract class GreenCertificateDetailsModule { +abstract class CovidCertificateDetailsModule { @Binds @IntoMap - @CWAViewModelKey(GreenCertificateDetailsViewModel::class) + @CWAViewModelKey(CovidCertificateDetailsViewModel::class) abstract fun greenCertificateDetailsFragment( - factory: GreenCertificateDetailsViewModel.Factory + factory: CovidCertificateDetailsViewModel.Factory ): CWAViewModelFactory<out CWAViewModel> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsNavigation.kt new file mode 100644 index 0000000000000000000000000000000000000000..b064ccfdaeecd4a2ae2c6b472e7aed57ce1a9304 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsNavigation.kt @@ -0,0 +1,6 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.details + +sealed class CovidCertificateDetailsNavigation { + object Back : CovidCertificateDetailsNavigation() + data class FullQrCode(val qrCodeText: String) : CovidCertificateDetailsNavigation() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsViewModel.kt new file mode 100644 index 0000000000000000000000000000000000000000..62e1bef00fac4029c59d563f8108cf8db1d58a03 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/details/CovidCertificateDetailsViewModel.kt @@ -0,0 +1,66 @@ +package de.rki.coronawarnapp.covidcertificate.test.ui.details + +import android.graphics.Bitmap +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateIdentifier +import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.viewmodel.CWAViewModel +import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory +import kotlinx.coroutines.flow.map +import timber.log.Timber + +class CovidCertificateDetailsViewModel @AssistedInject constructor( + dispatcherProvider: DispatcherProvider, + @Assisted private val testCertificateIdentifier: TestCertificateIdentifier, + private val qrCodeGenerator: QrCodeGenerator, + private val testCertificateRepository: TestCertificateRepository +) : CWAViewModel(dispatcherProvider) { + + private var qrCodeText: String? = null + private val bitmapStateData = MutableLiveData<Bitmap>() + val qrCode: LiveData<Bitmap> = bitmapStateData + val events = SingleLiveEvent<CovidCertificateDetailsNavigation>() + val errors = SingleLiveEvent<Throwable>() + val covidCertificate = testCertificateRepository.certificates.map { certificates -> + certificates.find { it.identifier == testCertificateIdentifier }?.testCertificate + .also { generateQrCode(it) } + }.asLiveData(dispatcherProvider.Default) + + fun onClose() = events.postValue(CovidCertificateDetailsNavigation.Back) + + fun openFullScreen() = qrCodeText?.let { events.postValue(CovidCertificateDetailsNavigation.FullQrCode(it)) } + + fun onDeleteTestConfirmed() = launch { + Timber.d("Removing Test Certificate=$testCertificateIdentifier") + testCertificateRepository.deleteCertificate(testCertificateIdentifier) + events.postValue(CovidCertificateDetailsNavigation.Back) + } + + private fun generateQrCode(testCertificate: TestCertificate?) = launch { + try { + bitmapStateData.postValue( + testCertificate?.let { certificate -> + qrCodeGenerator.createQrCode(certificate.qrCode.also { qrCodeText = it }) + } + ) + } catch (e: Exception) { + Timber.d(e, "generateQrCode failed for covidCertificate=%s", testCertificateIdentifier) + bitmapStateData.postValue(null) + errors.postValue(e) + } + } + + @AssistedFactory + interface Factory : CWAViewModelFactory<CovidCertificateDetailsViewModel> { + fun create(testCertificateIdentifier: TestCertificateIdentifier): CovidCertificateDetailsViewModel + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/items/CertificatesItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/items/CertificatesItem.kt similarity index 58% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/items/CertificatesItem.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/items/CertificatesItem.kt index c0d0ec32ca135bd6dd1740a4f2a0668b1555c92d..ec7c5198c6016e25bee488d6bedbf3fd5af6db63 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/items/CertificatesItem.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/test/ui/items/CertificatesItem.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates.items +package de.rki.coronawarnapp.covidcertificate.test.ui.items import de.rki.coronawarnapp.util.lists.HasStableId 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/covidcertificate/vaccination/core/VaccinatedPerson.kt similarity index 76% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPerson.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinatedPerson.kt index 597a623db6757ffc4bf574373ec8851176610a52..942fe20bd7096d0eb936facb7a8e867abfc99c8c 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/covidcertificate/vaccination/core/VaccinatedPerson.kt @@ -1,19 +1,20 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinatedPersonData +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinatedPersonData -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet import org.joda.time.Duration import org.joda.time.Instant import org.joda.time.LocalDate data class VaccinatedPerson( internal val data: VaccinatedPersonData, - private val valueSet: VaccinationValueSet?, + private val valueSet: VaccinationValueSets?, val isUpdatingData: Boolean = false, val lastError: Throwable? = null, ) { - val identifier: VaccinatedPersonIdentifier + val identifier: CertificatePersonIdentifier get() = data.identifier val vaccinationCertificates: Set<VaccinationCertificate> by lazy { @@ -23,17 +24,8 @@ data class VaccinatedPerson( val vaccineName: String get() = vaccinationCertificates.first().vaccineTypeName - val firstName: String? - get() = vaccinationCertificates.first().firstName - - val lastName: String - get() = vaccinationCertificates.first().lastName - val fullName: String - get() = when { - firstName == null -> lastName - else -> "$firstName $lastName" - } + get() = vaccinationCertificates.first().fullName val dateOfBirth: LocalDate get() = vaccinationCertificates.first().dateOfBirth diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt new file mode 100644 index 0000000000000000000000000000000000000000..21efe369b7c65a85c67f367f07f5a799308a7647 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationCertificate.kt @@ -0,0 +1,16 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core + +import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate +import org.joda.time.LocalDate + +interface VaccinationCertificate : CwaCovidCertificate { + + val vaccinatedAt: LocalDate + + val vaccineTypeName: String + val vaccineManufacturer: String + val medicalProductName: String + + val doseNumber: Int + val totalSeriesOfDoses: Int +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationException.kt similarity index 62% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationException.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationException.kt index 4963bf1bd500fddac6397d6da57a8f06a5d8eaf5..da1c1911f63931bd18a6bb237bdd51f455053989 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationException.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core open class VaccinationException( cause: Throwable?, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationModule.kt similarity index 62% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationModule.kt index e8c20bd7126a10e2af24b8d262145783ddaf83a1..047534e6edbe942345297ea3a1ddd14b9d68a28a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationModule.kt @@ -1,19 +1,14 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task.VaccinationUpdateTask import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskFactory import de.rki.coronawarnapp.task.TaskTypeKey -import de.rki.coronawarnapp.vaccination.core.execution.task.VaccinationUpdateTask -import de.rki.coronawarnapp.vaccination.core.server.VaccinationServerModule -@Module( - includes = [ - VaccinationServerModule::class - ] -) +@Module abstract class VaccinationModule { @Binds @IntoMap diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationPreferences.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationPreferences.kt similarity index 91% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationPreferences.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationPreferences.kt index 1c0597590793b93d1a4979249a0992f0f48fe738..dfef6623a2aa8e1a5f5f1a5c2daee99dec73666f 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationPreferences.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationPreferences.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core import android.content.Context import de.rki.coronawarnapp.util.di.AppContext diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationSettings.kt similarity index 85% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationSettings.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationSettings.kt index 1fe9dbda18b8e4178555e078f2e1b6ba55c5d239..ae1f8443addc2c02f6620c2ee25488636b32456e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationSettings.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationSettings.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core import javax.inject.Inject import javax.inject.Singleton diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1.kt similarity index 56% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1.kt index 0fca7a0101289b434bd8069951df51e84a323327..3583458aa0c5211d4524647a6c449e81b5bd6af6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1.kt @@ -1,24 +1,19 @@ -package de.rki.coronawarnapp.vaccination.core.certificate +package de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate import com.google.gson.annotations.SerializedName +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc import org.joda.time.LocalDate -data class VaccinationDGCV1( - @SerializedName("ver") val version: String, - @SerializedName("nam") val nameData: NameData, - @SerializedName("dob") val dob: String, - @SerializedName("v") val vaccinationDatas: List<VaccinationData>, -) { - data class NameData( - @SerializedName("fn") val familyName: String?, - @SerializedName("fnt") val familyNameStandardized: String, - @SerializedName("gn") val givenName: String?, - @SerializedName("gnt") val givenNameStandardized: String?, - ) +data class VaccinationDccV1( + @SerializedName("ver") override val version: String, + @SerializedName("nam") override val nameData: Dcc.NameData, + @SerializedName("dob") override val dob: String, + @SerializedName("v") override val payloads: List<VaccinationData>, +) : Dcc<VaccinationDccV1.VaccinationData> { data class VaccinationData( // Disease or agent targeted, e.g. "tg": "840539006" - @SerializedName("tg") val targetId: String, + @SerializedName("tg") override val targetId: String, // Vaccine or prophylaxis, e.g. "vp": "1119349007" @SerializedName("vp") val vaccineId: String, // Vaccine medicinal product,e.g. "mp": "EU/1/20/1528", @@ -32,16 +27,13 @@ data class VaccinationDGCV1( // Date of Vaccination, e.g. "dt" : "2021-04-21" @SerializedName("dt") val dt: String, // Country of Vaccination, e.g. "co": "NL" - @SerializedName("co") val countryOfVaccination: String, + @SerializedName("co") override val certificateCountry: String, // Certificate Issuer, e.g. "is": "Ministry of Public Health, Welfare and Sport", - @SerializedName("is") val certificateIssuer: String, + @SerializedName("is") override val certificateIssuer: String, // Unique Certificate Identifier, e.g. "ci": "urn:uvci:01:NL:PlA8UWS60Z4RZXVALl6GAZ" - @SerializedName("ci") val uniqueCertificateIdentifier: String - ) { + @SerializedName("ci") override val uniqueCertificateIdentifier: String + ) : Dcc.Payload { val vaccinatedAt: LocalDate get() = LocalDate.parse(dt) } - - val dateOfBirth: LocalDate - get() = LocalDate.parse(dob) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1Parser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1Parser.kt new file mode 100644 index 0000000000000000000000000000000000000000..c2547d460f2ef7a0b2e45edffaf604877e506464 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/certificate/VaccinationDccV1Parser.kt @@ -0,0 +1,69 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate + +import com.google.gson.Gson +import com.upokecenter.cbor.CBORObject +import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_DGC +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_HCERT +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.JSON_SCHEMA_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_NO_VACCINATION_ENTRY +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException +import de.rki.coronawarnapp.util.serialization.BaseGson +import de.rki.coronawarnapp.util.serialization.fromJson +import javax.inject.Inject + +@Reusable +class VaccinationDccV1Parser @Inject constructor( + @BaseGson private val gson: Gson +) { + + fun parse(map: CBORObject): VaccinationDccV1 = try { + map[keyHCert]?.run { + this[keyEuDgcV1]?.run { + toCertificate() + } ?: throw InvalidVaccinationCertificateException(HC_CWT_NO_DGC) + } ?: throw InvalidVaccinationCertificateException(HC_CWT_NO_HCERT) + } catch (e: InvalidHealthCertificateException) { + throw e + } catch (e: Throwable) { + throw InvalidVaccinationCertificateException(HC_CBOR_DECODING_FAILED) + } + + @Suppress("UNNECESSARY_NOT_NULL_ASSERTION") + private fun VaccinationDccV1.validate(): VaccinationDccV1 { + if (payloads.isNullOrEmpty()) { + throw InvalidVaccinationCertificateException(VC_NO_VACCINATION_ENTRY) + } + // check for non null (Gson does not enforce it) & force date parsing + require(version.isNotBlank()) + require(nameData.familyNameStandardized.isNotBlank()) + dateOfBirth + payload.let { + it.vaccinatedAt + require(it.certificateIssuer.isNotBlank()) + require(it.certificateCountry.isNotBlank()) + require(it.marketAuthorizationHolderId.isNotBlank()) + require(it.medicalProductId.isNotBlank()) + require(it.targetId.isNotBlank()) + require(it.doseNumber > 0) + require(it.totalSeriesOfDoses > 0) + } + return this + } + + private fun CBORObject.toCertificate() = try { + val json = ToJSONString() + gson.fromJson<VaccinationDccV1>(json).validate() + } catch (e: InvalidVaccinationCertificateException) { + throw e + } catch (e: Throwable) { + throw InvalidVaccinationCertificateException(JSON_SCHEMA_INVALID) + } + + companion object { + private val keyEuDgcV1 = CBORObject.FromObject(1) + private val keyHCert = CBORObject.FromObject(-260) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateScheduler.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateScheduler.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateScheduler.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateScheduler.kt index 5393642f6da98b91c3e16e73b3260c7bedb1a8cc..b58242c8e03bbe328feac51024972f609794d76a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateScheduler.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateScheduler.kt @@ -1,8 +1,11 @@ -package de.rki.coronawarnapp.vaccination.core.execution +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.WorkInfo import androidx.work.WorkManager +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task.VaccinationUpdateTask +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker.VaccinationUpdateWorkerRequestBuilder +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.TaskFactory import de.rki.coronawarnapp.task.common.DefaultTaskRequest @@ -11,9 +14,6 @@ import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.await import de.rki.coronawarnapp.util.device.ForegroundState -import de.rki.coronawarnapp.vaccination.core.execution.task.VaccinationUpdateTask -import de.rki.coronawarnapp.vaccination.core.execution.worker.VaccinationUpdateWorkerRequestBuilder -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTask.kt similarity index 93% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTask.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTask.kt index fdb14d5e90df4e916c192a18b504460112397c4e..77d919a9f8cab99b00c50ecbdf82667d0a6511cb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTask.kt @@ -1,13 +1,13 @@ -package de.rki.coronawarnapp.vaccination.core.execution.task +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.bugreporting.reportProblem +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskFactory +import de.rki.coronawarnapp.task.common.DefaultProgress import de.rki.coronawarnapp.task.common.Finished import de.rki.coronawarnapp.task.common.Started -import de.rki.coronawarnapp.task.common.DefaultProgress -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import org.joda.time.Duration diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorker.kt similarity index 92% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorker.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorker.kt index a22d1c1f885925005fb62c74e54bb99ccbe13302..e308b3789ba3093974520ea8500d42b40d7bbbc1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorker.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core.execution.worker +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker import android.content.Context import androidx.work.CoroutineWorker @@ -7,12 +7,12 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.bugreporting.reportProblem +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task.VaccinationUpdateTask import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.TaskFactory import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.task.submitBlocking import de.rki.coronawarnapp.util.worker.InjectedWorkerFactory -import de.rki.coronawarnapp.vaccination.core.execution.task.VaccinationUpdateTask import timber.log.Timber class VaccinationUpdateWorker @AssistedInject constructor( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt similarity index 93% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt index 62b491fe6415cd54eef28cf5891720c64a2e1b02..c97b9f6bf087a5e3c795d958195057a06ff842a6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerRequestBuilder.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core.execution.worker +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker import androidx.work.BackoffPolicy import androidx.work.Constraints diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationCertificateQRCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationCertificateQRCode.kt new file mode 100644 index 0000000000000000000000000000000000000000..ad88fb5911610916e6ad6a1c1b2abf2493c2c5bb --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationCertificateQRCode.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode + +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.qrcode.DccQrCode +import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 + +data class VaccinationCertificateQRCode( + override val qrCode: QrCodeString, + override val data: DccData<VaccinationDccV1> +) : DccQrCode<VaccinationDccV1> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt new file mode 100644 index 0000000000000000000000000000000000000000..362c7f7a621ca4628383e5c97f0cf286b405cea3 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt @@ -0,0 +1,83 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode + +import de.rki.coronawarnapp.bugreporting.censors.vaccination.CertificateQrCodeCensor +import de.rki.coronawarnapp.coronatest.qrcode.QrCodeExtractor +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccCoseDecoder +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccHeaderParser +import de.rki.coronawarnapp.covidcertificate.common.decoder.RawCOSEObject +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1Parser +import de.rki.coronawarnapp.util.compression.inflate +import de.rki.coronawarnapp.util.encoding.Base45Decoder +import timber.log.Timber +import javax.inject.Inject + +class VaccinationQRCodeExtractor @Inject constructor( + private val coseDecoder: DccCoseDecoder, + private val headerParser: DccHeaderParser, + private val bodyParser: VaccinationDccV1Parser, +) : QrCodeExtractor<VaccinationCertificateQRCode> { + + override fun canHandle(rawString: String): Boolean = rawString.startsWith(PREFIX) + + override fun extract(rawString: String): VaccinationCertificateQRCode { + CertificateQrCodeCensor.addQRCodeStringToCensor(rawString) + + val parsedData = rawString + .removePrefix(PREFIX) + .decodeBase45() + .decompress() + .parse() + + return VaccinationCertificateQRCode( + qrCode = rawString, + data = parsedData, + ) + } + + private fun String.decodeBase45(): ByteArray = try { + Base45Decoder.decode(this) + } catch (e: Throwable) { + Timber.e(e) + throw InvalidVaccinationCertificateException(HC_BASE45_DECODING_FAILED) + } + + private fun ByteArray.decompress(): RawCOSEObject = try { + this.inflate(sizeLimit = DEFAULT_SIZE_LIMIT) + } catch (e: Throwable) { + Timber.e(e) + throw InvalidVaccinationCertificateException(HC_ZLIB_DECOMPRESSION_FAILED) + } + + fun RawCOSEObject.parse(): DccData<VaccinationDccV1> = try { + Timber.v("Parsing COSE for vaccination certificate.") + val cbor = coseDecoder.decode(this) + + DccData( + header = headerParser.parse(cbor), + certificate = bodyParser.parse(cbor) + ).also { + CertificateQrCodeCensor.addCertificateToCensor(it) + }.also { + Timber.v("Parsed vaccination certificate for %s", it.certificate.nameData.familyNameStandardized) + } + } catch (e: InvalidHealthCertificateException) { + throw InvalidVaccinationCertificateException(e.errorCode) + } catch (e: Throwable) { + Timber.e(e) + throw InvalidVaccinationCertificateException(HC_CBOR_DECODING_FAILED) + } + + companion object { + private const val PREFIX = "HC1:" + + // Zip bomb + private const val DEFAULT_SIZE_LIMIT = 1024L * 1024 * 10L // 10 MB + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeValidator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeValidator.kt similarity index 70% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeValidator.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeValidator.kt index 3e7f603962d7306ea8bde1da607008559c94c7c1..9fe4b3a577c755f5667297e0d18d4282ed2e65aa 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeValidator.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeValidator.kt @@ -1,9 +1,9 @@ -package de.rki.coronawarnapp.vaccination.core.qrcode +package de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode import dagger.Reusable import de.rki.coronawarnapp.coronatest.qrcode.QrCodeExtractor -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_PREFIX_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_PREFIX_INVALID +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException import timber.log.Timber import javax.inject.Inject @@ -19,7 +19,7 @@ class VaccinationQRCodeValidator @Inject constructor( return findExtractor(rawString) ?.extract(rawString) ?.also { Timber.i("Extracted data from QR code is %s", it) } - ?: throw InvalidHealthCertificateException(VC_PREFIX_INVALID) + ?: throw InvalidVaccinationCertificateException(VC_PREFIX_INVALID) } private fun findExtractor(rawString: String): QrCodeExtractor<VaccinationCertificateQRCode>? { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepository.kt similarity index 79% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepository.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepository.kt index ab3b35f4dfc2e3c48e4f5c94e586fcf66f7e6c6e..187a85d33b612ccf70bd1bd64b16530b1c42bb89 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepository.kt @@ -1,24 +1,24 @@ -package de.rki.coronawarnapp.vaccination.core.repository +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository import de.rki.coronawarnapp.bugreporting.reportProblem +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_ALREADY_REGISTERED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.errors.VaccinationCertificateNotFoundException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinatedPersonData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationContainer +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationStorage +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.toVaccinationContainer +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.flow.HotDataFlow import de.rki.coronawarnapp.util.flow.combine -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.core.VaccinatedPersonIdentifier -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode -import de.rki.coronawarnapp.vaccination.core.personIdentifier -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateQRCode -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractor -import de.rki.coronawarnapp.vaccination.core.repository.errors.VaccinationCertificateNotFoundException -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinatedPersonData -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationContainer -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationStorage -import de.rki.coronawarnapp.vaccination.core.repository.storage.toVaccinationContainer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted @@ -75,7 +75,7 @@ class VaccinationRepository @Inject constructor( val vaccinationInfos: Flow<Set<VaccinatedPerson>> = combine( internalData.data, - valueSetsRepository.latestValueSet + valueSetsRepository.latestVaccinationValueSets ) { personDatas, currentValueSet -> personDatas.map { it.copy(valueSet = currentValueSet) }.toSet() } @@ -101,7 +101,7 @@ class VaccinationRepository @Inject constructor( if (originalPerson.data.vaccinations.any { it.certificateId == qrCode.uniqueCertificateIdentifier }) { Timber.tag(TAG).e("Certificate is already registered: %s", qrCode.uniqueCertificateIdentifier) - throw InvalidHealthCertificateException(ErrorCode.VC_ALREADY_REGISTERED) + throw InvalidVaccinationCertificateException(VC_ALREADY_REGISTERED) } val newCertificate = qrCode.toVaccinationContainer( @@ -132,7 +132,7 @@ class VaccinationRepository @Inject constructor( * Passing null as identifier will refresh all available data, if within constraints. * Throws VaccinatedPersonNotFoundException is you try to refresh a person that is unknown. */ - suspend fun refresh(personIdentifier: VaccinatedPersonIdentifier? = null) { + suspend fun refresh(personIdentifier: CertificatePersonIdentifier? = null) { Timber.tag(TAG).d("refresh(personIdentifier=%s)", personIdentifier) // NOOP diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt new file mode 100644 index 0000000000000000000000000000000000000000..8ad45faa3eef29ea9c59a0d59b97c21a140d0dfb --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt @@ -0,0 +1,10 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.errors + +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationException + +class VaccinatedPersonNotFoundException( + message: String +) : VaccinationException( + message = message, + cause = null +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt new file mode 100644 index 0000000000000000000000000000000000000000..f67ab42fb802d1eaffc117a8f736131a5b8ed63f --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt @@ -0,0 +1,10 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.errors + +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationException + +class VaccinationCertificateNotFoundException( + message: String +) : VaccinationException( + message = message, + cause = null +) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ContainerPostProcessor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ContainerPostProcessor.kt similarity index 77% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ContainerPostProcessor.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ContainerPostProcessor.kt index c4f2debb082773d1baefb0f9483151182d42e0b5..ce35f0c23ab1a56326830e7e2c8590046daa0561 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ContainerPostProcessor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ContainerPostProcessor.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage import com.google.gson.Gson import com.google.gson.TypeAdapter @@ -7,14 +7,14 @@ import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter import dagger.Reusable -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractor import timber.log.Timber import java.io.IOException import javax.inject.Inject @Reusable class ContainerPostProcessor @Inject constructor( - private val qrCodeExtractor: VaccinationQRCodeExtractor, + private val vaccinationQrCodeExtractor: VaccinationQRCodeExtractor, ) : TypeAdapterFactory { override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T> { val delegate = gson.getDelegateAdapter(this, type) @@ -30,7 +30,7 @@ class ContainerPostProcessor @Inject constructor( when (obj) { is VaccinationContainer -> { Timber.v("Injecting VaccinationContainer %s", obj.hashCode()) - obj.qrCodeExtractor = qrCodeExtractor + obj.qrCodeExtractor = vaccinationQrCodeExtractor } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinatedPersonData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinatedPersonData.kt similarity index 52% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinatedPersonData.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinatedPersonData.kt index 06978d4907f11613336a3c1e85929a4228fa8021..ab4663c66ad5b6c44b4a7287ace60be7986d9eb6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinatedPersonData.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinatedPersonData.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage import com.google.gson.annotations.SerializedName -import de.rki.coronawarnapp.vaccination.core.VaccinatedPersonIdentifier +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier data class VaccinatedPersonData( @SerializedName("vaccinationData") val vaccinations: Set<VaccinationContainer> = emptySet() ) { - val identifier: VaccinatedPersonIdentifier + val identifier: CertificatePersonIdentifier get() = vaccinations.first().personIdentifier } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt similarity index 63% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainer.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt index 9694f5f9d6875b21d3075f90a8dfd52068574962..61aa1c59989870f4ec1c28cb53496093cf72040b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainer.kt @@ -1,18 +1,16 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage import androidx.annotation.Keep import com.google.gson.annotations.SerializedName -import de.rki.coronawarnapp.vaccination.core.VaccinatedPersonIdentifier -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate -import de.rki.coronawarnapp.vaccination.core.certificate.CoseCertificateHeader -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 -import de.rki.coronawarnapp.vaccination.core.personIdentifier -import de.rki.coronawarnapp.vaccination.core.qrcode.QrCodeString -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateData -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateQRCode -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractor -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.getDisplayText +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccHeader +import de.rki.coronawarnapp.covidcertificate.common.qrcode.QrCodeString +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets import org.joda.time.Instant import org.joda.time.LocalDate import java.util.Locale @@ -25,43 +23,47 @@ data class VaccinationContainer internal constructor( // Either set by [ContainerPostProcessor] or via [toVaccinationContainer] @Transient lateinit var qrCodeExtractor: VaccinationQRCodeExtractor - @Transient internal var preParsedData: VaccinationCertificateData? = null + @Transient internal var preParsedData: DccData<VaccinationDccV1>? = null // Otherwise GSON unsafes reflection to create this class, and sets the LAZY to null @Suppress("unused") constructor() : this("", Instant.EPOCH) @delegate:Transient - internal val certificateData: VaccinationCertificateData by lazy { - preParsedData ?: qrCodeExtractor.extract(vaccinationQrCode).parsedData + internal val certificateData: DccData<VaccinationDccV1> by lazy { + preParsedData ?: qrCodeExtractor.extract(vaccinationQrCode).data } - val header: CoseCertificateHeader + val header: DccHeader get() = certificateData.header - val certificate: VaccinationDGCV1 + val certificate: VaccinationDccV1 get() = certificateData.certificate - val vaccination: VaccinationDGCV1.VaccinationData - get() = certificate.vaccinationDatas.single() + val vaccination: VaccinationDccV1.VaccinationData + get() = certificate.payload val certificateId: String get() = vaccination.uniqueCertificateIdentifier - val personIdentifier: VaccinatedPersonIdentifier + val personIdentifier: CertificatePersonIdentifier get() = certificate.personIdentifier fun toVaccinationCertificate( - valueSet: VaccinationValueSet?, + valueSet: VaccinationValueSets?, userLocale: Locale = Locale.getDefault(), ) = object : VaccinationCertificate { - override val personIdentifier: VaccinatedPersonIdentifier + override val personIdentifier: CertificatePersonIdentifier get() = certificate.personIdentifier override val firstName: String? - get() = certificate.nameData.givenName + get() = certificate.nameData.firstName + override val lastName: String - get() = certificate.nameData.familyName ?: certificate.nameData.familyNameStandardized + get() = certificate.nameData.lastName + + override val fullName: String + get() = certificate.nameData.fullName override val dateOfBirth: LocalDate get() = certificate.dateOfBirth @@ -87,7 +89,7 @@ data class VaccinationContainer internal constructor( override val certificateCountry: String get() = Locale( userLocale.language, - vaccination.countryOfVaccination.uppercase() + vaccination.certificateCountry.uppercase() ).getDisplayCountry(userLocale) override val certificateId: String @@ -100,7 +102,7 @@ data class VaccinationContainer internal constructor( override val expiresAt: Instant get() = header.expiresAt - override val vaccinationQrCodeString: QrCodeString + override val qrCode: QrCodeString get() = vaccinationQrCode } } @@ -109,9 +111,9 @@ fun VaccinationCertificateQRCode.toVaccinationContainer( scannedAt: Instant, qrCodeExtractor: VaccinationQRCodeExtractor, ) = VaccinationContainer( - vaccinationQrCode = this.qrCodeString, + vaccinationQrCode = this.qrCode, scannedAt = scannedAt, ).apply { this.qrCodeExtractor = qrCodeExtractor - preParsedData = parsedData + preParsedData = data } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorage.kt similarity index 96% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorage.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorage.kt index 1424b943171378f04cc41ee44a398e0705afc95f..0b92bea2a9e707bedccb08b461d7794f6b36dc96 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorage.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorage.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage import android.content.Context import androidx.core.content.edit diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationUIModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..a912530513999c63b31b1dac6b1df6ef02fffa38 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/VaccinationUIModule.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.ui + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragment +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragmentModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragment +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragmentModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragment +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragmentModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanModule + +@Module +abstract class VaccinationUIModule { + + @ContributesAndroidInjector(modules = [VaccinationListFragmentModule::class]) + abstract fun vaccinationListFragment(): VaccinationListFragment + + @ContributesAndroidInjector(modules = [VaccinationDetailsFragmentModule::class]) + abstract fun vaccinationDetailsFragment(): VaccinationDetailsFragment + + @ContributesAndroidInjector(modules = [VaccinationQrCodeScanModule::class]) + abstract fun vaccinationQrCodeScanFragment(): VaccinationQrCodeScanFragment + + @ContributesAndroidInjector(modules = [VaccinationConsentFragmentModule::class]) + abstract fun vaccinationConsentFragment(): VaccinationConsentFragment +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/CreateVaccinationCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/CreateVaccinationCard.kt similarity index 85% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/CreateVaccinationCard.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/CreateVaccinationCard.kt index 7332aa4cee7a388119ecc3240e96daddcc775caf..e45d550ef3b1dfa80d2adfb7197a9c3f6ed9dbc9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/CreateVaccinationCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/CreateVaccinationCard.kt @@ -1,10 +1,10 @@ -package de.rki.coronawarnapp.vaccination.ui.cards +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem import de.rki.coronawarnapp.databinding.VaccinationHomeRegistrationCardBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesAdapter -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer class CreateVaccinationCard(parent: ViewGroup) : diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/HeaderInfoVaccinationCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/HeaderInfoVaccinationCard.kt similarity index 78% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/HeaderInfoVaccinationCard.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/HeaderInfoVaccinationCard.kt index e62941fc9bfffa16aae2b11fa60b9c93682d8ffb..a2026dfa69f6b8ee8061f7a914e5e1b113876b57 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/HeaderInfoVaccinationCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/HeaderInfoVaccinationCard.kt @@ -1,10 +1,10 @@ -package de.rki.coronawarnapp.vaccination.ui.cards +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem import de.rki.coronawarnapp.databinding.VaccinationHeaderInfoCardBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesAdapter -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem class HeaderInfoVaccinationCard(parent: ViewGroup) : CertificatesAdapter.CertificatesItemVH<HeaderInfoVaccinationCard.Item, VaccinationHeaderInfoCardBinding>( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/ImmuneVaccinationCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/ImmuneVaccinationCard.kt similarity index 70% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/ImmuneVaccinationCard.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/ImmuneVaccinationCard.kt index f3bf89f9cefc467bd0020754fd60bc79de4e5a3a..09e83c5d9e14fc1f2d056528405f6dbbdcca238c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/ImmuneVaccinationCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/ImmuneVaccinationCard.kt @@ -1,11 +1,12 @@ -package de.rki.coronawarnapp.vaccination.ui.cards +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson import de.rki.coronawarnapp.databinding.VaccinationHomeImmuneCardBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesAdapter +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson class ImmuneVaccinationCard(parent: ViewGroup) : CertificatesAdapter.CertificatesItemVH<ImmuneVaccinationCard.Item, VaccinationHomeImmuneCardBinding>( @@ -24,6 +25,10 @@ class ImmuneVaccinationCard(parent: ViewGroup) : val curItem = payloads.filterIsInstance<Item>().singleOrNull() ?: item personName.text = curItem.vaccinatedPerson.fullName + vaccinationState.text = context.getString( + R.string.vaccination_card_status_vaccination_complete, + curItem.vaccinatedPerson.getMostRecentVaccinationCertificate.expiresAt.toDayFormat() + ) itemView.setOnClickListener { curItem.onClickAction(item) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/BottomInfoVaccinationCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/NoCovidTestCertificatesCard.kt similarity index 62% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/BottomInfoVaccinationCard.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/NoCovidTestCertificatesCard.kt index c2b5fe49130b2786d5c822e86d5aa31cec82c30e..dae5c0dba1c885464b7000b4135013f5a65b8f91 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/BottomInfoVaccinationCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/NoCovidTestCertificatesCard.kt @@ -1,13 +1,13 @@ -package de.rki.coronawarnapp.vaccination.ui.cards +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem import de.rki.coronawarnapp.databinding.VaccinationBottomInfoCardBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesAdapter -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem -class BottomInfoVaccinationCard(parent: ViewGroup) : - CertificatesAdapter.CertificatesItemVH<BottomInfoVaccinationCard.Item, VaccinationBottomInfoCardBinding>( +class NoCovidTestCertificatesCard(parent: ViewGroup) : + CertificatesAdapter.CertificatesItemVH<NoCovidTestCertificatesCard.Item, VaccinationBottomInfoCardBinding>( R.layout.dashed_line_container_layout, parent ) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationCard.kt similarity index 83% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationCard.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationCard.kt index 7377a6d5d25227896da653c6b120604208792889..911ca7e1e45c52100006acaf2b1cdb2a86418dc5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationCard.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.vaccination.ui.cards +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson import de.rki.coronawarnapp.databinding.VaccinationHomeCardBinding -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesAdapter import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson class VaccinationCard(parent: ViewGroup) : CertificatesAdapter.CertificatesItemVH<VaccinationCard.Item, VaccinationHomeCardBinding>( @@ -32,11 +32,9 @@ class VaccinationCard(parent: ViewGroup) : days, days ) - icon.setImageResource(R.drawable.vaccination_card_icon_complete) } else -> { vaccinationState.setText(R.string.vaccination_card_status_vaccination_incomplete) - icon.setImageResource(R.drawable.vaccination_card_icon_incomplete) } } itemView.setOnClickListener { curItem.onClickAction(item) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationStatusItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationStatusItem.kt new file mode 100644 index 0000000000000000000000000000000000000000..0216d7eea4c3df9009ba8aaf52cd76a67c349c07 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/cards/VaccinationStatusItem.kt @@ -0,0 +1,11 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.cards + +import de.rki.coronawarnapp.covidcertificate.test.ui.items.CertificatesItem +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson + +interface VaccinationStatusItem : CertificatesItem { + val vaccinatedPerson: VaccinatedPerson + + override val stableId: Long + get() = vaccinatedPerson.identifier.hashCode().toLong() +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragment.kt similarity index 97% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragment.kt index b19db038c8926c515f017f2cdff8a8e6436bbd55..49e64766849f48005f1f3d71582d288d475f8c82 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.consent +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent import android.os.Bundle import android.view.View diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragmentModule.kt similarity index 88% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragmentModule.kt index cfb33134fdcaaa9b47f312769c2fb5055fa66e14..e2a7be9f3813590c08dd498e7c8c2c1206a55860 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.consent +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt similarity index 73% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt index 865937c0df0906298bf6d610cf6c658645ca0094..52a1b33fb0c2d747fd9992d5ead645064a7b568c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentNavigationEvent.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.consent +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent sealed class VaccinationConsentNavigationEvent { object NavigateToDataPrivacy : VaccinationConsentNavigationEvent() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentViewModel.kt similarity index 87% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentViewModel.kt index 28327dfb7eacbd5c5aa09b3d2c419802570051b6..0c2c4bafd184936a2b7e9466c9919fa4c713e385 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/consent/VaccinationConsentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/consent/VaccinationConsentViewModel.kt @@ -1,12 +1,12 @@ -package de.rki.coronawarnapp.vaccination.ui.consent +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings class VaccinationConsentViewModel @AssistedInject constructor( private val vaccinationSettings: VaccinationSettings, 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/covidcertificate/vaccination/ui/details/VaccinationDetailsFragment.kt similarity index 96% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragment.kt index b76fd07b7e133d4deb7e7114b2f1e08eae9ecafc..8ea0ed355e85b3fd4167a9cacc2add4d19036a6b 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/covidcertificate/vaccination/ui/details/VaccinationDetailsFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.details +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.details import android.os.Bundle import android.view.View @@ -11,6 +11,7 @@ import androidx.navigation.fragment.navArgs import com.google.android.material.appbar.AppBarLayout import de.rki.coronawarnapp.R import de.rki.coronawarnapp.bugreporting.ui.toErrorDialogBuilder +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate import de.rki.coronawarnapp.databinding.FragmentVaccinationDetailsBinding import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs import de.rki.coronawarnapp.ui.view.onOffsetChange @@ -20,7 +21,6 @@ import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBinding import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider import de.rki.coronawarnapp.util.viewmodel.cwaViewModelsAssisted -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate import org.joda.time.format.DateTimeFormat import javax.inject.Inject @@ -87,7 +87,7 @@ class VaccinationDetailsFragment : Fragment(R.layout.fragment_vaccination_detail private fun FragmentVaccinationDetailsBinding.bindCertificateViews( certificate: VaccinationCertificate ) { - name.text = certificate.run { "$firstName $lastName" } + name.text = certificate.fullName birthDate.text = getString( R.string.vaccination_details_birth_date, certificate.dateOfBirth.toDayFormat() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentModule.kt similarity index 88% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentModule.kt index 62e9906d2577086ae40ee382f6e66fede9fa27f4..f4d86d341dbcca83744f16a35722e99910893fcb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.details +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.details import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsNavigation.kt similarity index 72% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsNavigation.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsNavigation.kt index d1151ff28fdef732ead8dc049bedc638f3f2d871..45d2d1754f6f2c43e364b0f3fabd4807b4857c41 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsNavigation.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsNavigation.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.details +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.details sealed class VaccinationDetailsNavigation { object Back : VaccinationDetailsNavigation() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsViewModel.kt similarity index 87% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsViewModel.kt index 3a1181d329277738c9fe8c4abe76c30403ca9724..9cb494dd22f82ea6a76c3595dc0d393a2e0d289b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/details/VaccinationDetailsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/details/VaccinationDetailsViewModel.kt @@ -1,18 +1,18 @@ -package de.rki.coronawarnapp.vaccination.ui.details +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.details import android.graphics.Bitmap import androidx.lifecycle.asLiveData import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.map import timber.log.Timber @@ -60,8 +60,8 @@ class VaccinationDetailsViewModel @AssistedInject constructor( private fun generateQrCode(certificate: VaccinationCertificate?) = launch { try { mutableStateFlow.value = certificate?.let { - qrCodeText = it.vaccinationQrCodeString - qrCodeGenerator.createQrCode(it.vaccinationQrCodeString) + qrCodeText = it.qrCode + qrCodeGenerator.createQrCode(it.qrCode) } } catch (e: Exception) { Timber.d(e, "generateQrCode failed for vaccinationCertificate=%s", certificate) 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/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt index 9c3768891b87a83378c714760d5f9f1949dc9b9f..b129fd94038631bb80221271c1a3a1d1e2b6fe5c 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/covidcertificate/vaccination/ui/list/VaccinationListFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.list +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list import android.os.Bundle import android.view.View @@ -14,6 +14,12 @@ 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.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.DeleteVaccinationEvent +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateBack +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationCertificateDetails +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter import de.rki.coronawarnapp.databinding.FragmentVaccinationListBinding import de.rki.coronawarnapp.ui.qrcode.fullscreen.QrCodeFullScreenFragmentArgs import de.rki.coronawarnapp.ui.view.onOffsetChange @@ -25,12 +31,6 @@ import de.rki.coronawarnapp.util.ui.popBackStack import de.rki.coronawarnapp.util.ui.viewBinding 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 class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), AutoInject { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentModule.kt similarity index 88% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentModule.kt index 86c2cb094926468e4163fcdc2c637f1d04ac2ca6..bcffd957ae6b59439f17b73c75ee09eb75386cb7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.list +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list import dagger.Binds import dagger.Module 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/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt similarity index 87% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt index a4e200153ccea27b40c5e3422cd31964e594ff8f..9dea2c3500fe560abdba6ef75945cd14967b6087 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/covidcertificate/vaccination/ui/list/VaccinationListViewModel.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.list +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list import android.content.Context import android.graphics.Bitmap @@ -8,6 +8,14 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.util.getLocale +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat import de.rki.coronawarnapp.util.coroutine.AppScope @@ -15,14 +23,6 @@ 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.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.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.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -56,7 +56,7 @@ class VaccinationListViewModel @AssistedInject constructor( // immediately ... emit(null) // ... and after the QR code was generated, it is emitted - emit(qrCodeGenerator.createQrCode(it.getMostRecentVaccinationCertificate.vaccinationQrCodeString)) + emit(qrCodeGenerator.createQrCode(it.getMostRecentVaccinationCertificate.qrCode)) } val uiState: LiveData<UiState> = combine(vaccinatedPersonFlow, vaccinationQrCodeFlow) { vaccinatedPerson, qrCode -> @@ -92,7 +92,7 @@ class VaccinationListViewModel @AssistedInject constructor( onQrCodeClick = { events.postValue( Event.NavigateToQrCodeFullScreen( - qrCode = vaccinatedPerson.getMostRecentVaccinationCertificate.vaccinationQrCodeString, + qrCode = vaccinatedPerson.getMostRecentVaccinationCertificate.qrCode, positionInList = 0 ) ) 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/covidcertificate/vaccination/ui/list/adapter/VaccinationListAdapter.kt similarity index 63% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/VaccinationListAdapter.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/adapter/VaccinationListAdapter.kt index 0bca114ae463ddd046ecd17410c90d4a6a11d794..3fa17a05ab738cfa642fb761ba0c36a475aa1c1e 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/covidcertificate/vaccination/ui/list/adapter/VaccinationListAdapter.kt @@ -1,8 +1,16 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem import de.rki.coronawarnapp.util.lists.BindableVH import de.rki.coronawarnapp.util.lists.HasStableId import de.rki.coronawarnapp.util.lists.diffutil.AsyncDiffUtilAdapter @@ -11,14 +19,6 @@ 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.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 class VaccinationListAdapter : ModularAdapter<VaccinationListAdapter.ItemVH<VaccinationListItem, ViewBinding>>(), 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt similarity index 74% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt index f6824b79b04a1ca22d84eb29a73a7871ff0bc2d0..8f30d3940133de55144206c9b31e1f9d9a4ae4ac 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListImmunityInformationCardItemVH.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListImmunityInformationCardItemVH.VaccinationListImmunityInformationCardItem 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) : 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt similarity index 71% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt index 5cd1501ca2a27854aeedeb23575a690a5938ec3e..f073acb5408a1946401aadb4b707c8c128d807a3 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListNameCardItemVH.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem import de.rki.coronawarnapp.databinding.VaccinationListNameCardBinding -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 class VaccinationListNameCardItemVH(parent: ViewGroup) : VaccinationListAdapter.ItemVH<VaccinationListNameCardItem, VaccinationListNameCardBinding>( 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt similarity index 81% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt index 3c84ae55250f6e9d91255b4326ba5365fc3892ca..11c936aab9ae69672c0a68055770002b906d12a4 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListQrCodeCardItemVH.kt @@ -1,13 +1,13 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder import android.graphics.Bitmap import android.view.ViewGroup import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListQrCodeCardItemVH.VaccinationListQrCodeCardItem import de.rki.coronawarnapp.databinding.IncludeCertificateQrcodeCardBinding 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 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt similarity index 86% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt index 82f2a8388585b70ba8f570710b5728f9f1f91889..5c254efe417d573d3618d90ce26e8ccd91d16d38 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/covidcertificate/vaccination/ui/list/adapter/viewholder/VaccinationListVaccinationCardItemVH.kt @@ -1,19 +1,19 @@ -package de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder import android.view.Gravity import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.recyclerview.widget.RecyclerView import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.COMPLETE +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.IMMUNITY +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson.Status.INCOMPLETE +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListAdapter +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.VaccinationListItem +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem import de.rki.coronawarnapp.databinding.VaccinationListVaccinationCardBinding import de.rki.coronawarnapp.util.list.Swipeable -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 -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.INCOMPLETE -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.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem import java.util.Objects class VaccinationListVaccinationCardItemVH(parent: ViewGroup) : diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt similarity index 98% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt index 48a09d1257473491fbe4bd5167242d3af602a449..ffb9537416eda49f1d4d29d61afed701da3542a7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.scan +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan import android.Manifest import android.content.pm.PackageManager diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt similarity index 89% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt index aa5cd02dfd000d4e78466d6a5e9d5fd85f072ea7..bc581306a761d7bdfd9e46489b4ca2f98e401dc8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.ui.scan +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt similarity index 86% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt index 5f8695e78f98984d521d1009476ec8bb96a6bc21..adbddadf7608994aa37288d4413cd22436cbcb95 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt @@ -1,14 +1,14 @@ -package de.rki.coronawarnapp.vaccination.ui.scan +package de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan import com.journeyapps.barcodescanner.BarcodeResult import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeValidator +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.util.permission.CameraSettings import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeValidator -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import timber.log.Timber class VaccinationQrCodeScanViewModel @AssistedInject constructor( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/CertificateValueSetModule.kt similarity index 82% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/CertificateValueSetModule.kt index 2b3413d14169f086392e7ab3df76a867f2acb57a..dd3100e22d9a3ead782e003f58422054f80c39b1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/CertificateValueSetModule.kt @@ -1,9 +1,11 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset +package de.rki.coronawarnapp.covidcertificate.valueset import android.content.Context import dagger.Module import dagger.Provides import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.valueset.server.CertificateValueSet +import de.rki.coronawarnapp.covidcertificate.valueset.server.CertificateValueSetApiV1 import de.rki.coronawarnapp.environment.download.DownloadCDNHttpClient import de.rki.coronawarnapp.environment.download.DownloadCDNServerUrl import de.rki.coronawarnapp.util.di.AppContext @@ -17,10 +19,10 @@ import java.io.File import java.util.concurrent.TimeUnit @Module -class VaccinationValueSetModule { +class CertificateValueSetModule { @Reusable - @ValueSet + @CertificateValueSet @Provides fun cache( @AppContext context: Context @@ -35,8 +37,8 @@ class VaccinationValueSetModule { fun api( @DownloadCDNHttpClient httpClient: OkHttpClient, @DownloadCDNServerUrl url: String, - @ValueSet cache: Cache - ): VaccinationValueSetApiV1 { + @CertificateValueSet cache: Cache + ): CertificateValueSetApiV1 { val client = httpClient.newBuilder() .addNetworkInterceptor(CacheInterceptor()) .cache(cache) @@ -46,7 +48,7 @@ class VaccinationValueSetModule { .client(client) .baseUrl(url) .build() - .create(VaccinationValueSetApiV1::class.java) + .create(CertificateValueSetApiV1::class.java) } private class CacheInterceptor : Interceptor { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..eae1302048eae9d8c9cf8c89af6dbb08873e8425 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepository.kt @@ -0,0 +1,93 @@ +package de.rki.coronawarnapp.covidcertificate.valueset + +import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.valueset.server.CertificateValueSetServer +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsContainer +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsStorage +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.emptyValueSetsContainer +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.isEmpty +import de.rki.coronawarnapp.util.coroutine.AppScope +import de.rki.coronawarnapp.util.coroutine.DispatcherProvider +import de.rki.coronawarnapp.util.flow.HotDataFlow +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.plus +import timber.log.Timber +import java.util.Locale +import javax.inject.Inject + +@Reusable +class ValueSetsRepository @Inject constructor( + private val certificateValueSetServer: CertificateValueSetServer, + private val valueSetsStorage: ValueSetsStorage, + @AppScope private val scope: CoroutineScope, + dispatcherProvider: DispatcherProvider +) { + + private val internalData: HotDataFlow<ValueSetsContainer> = HotDataFlow( + loggingTag = TAG, + scope = scope, + coroutineContext = dispatcherProvider.IO, + sharingBehavior = SharingStarted.Lazily, + startValueProvider = { + valueSetsStorage.valueSetsContainer.also { Timber.v("Loaded initial value sets %s", it) } + } + ) + + init { + internalData.data + .onStart { Timber.d("Observing value set") } + .onEach { valueSetsStorage.valueSetsContainer = it } + .catch { Timber.e(it, "Storing new value sets failed.") } + .launchIn(scope + dispatcherProvider.IO) + } + + val latestVaccinationValueSets: Flow<VaccinationValueSets> = internalData.data + .map { it.vaccinationValueSets } + + val latestTestCertificateValueSets: Flow<TestCertificateValueSets> = internalData.data + .map { it.testCertificateValueSets } + + fun triggerUpdateValueSet(languageCode: Locale) { + Timber.d("triggerUpdateValueSet(languageCode=%s)", languageCode) + internalData.updateAsync( + onUpdate = { getValueSetFromServer(languageCode = languageCode) ?: this }, + onError = { Timber.e(it, "Updating value sets failed") } + ) + } + + private suspend fun getValueSetFromServer(languageCode: Locale): ValueSetsContainer? { + Timber.v("getValueSetFromServer(languageCode=%s)", languageCode) + var container = certificateValueSetServer.getVaccinationValueSets(languageCode = languageCode) + + if (container.isEmpty()) { + Timber.d( + "Got no value sets from server for %s... Try fallback to value sets for en", + languageCode.language + ) + container = certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + } + + return container + .also { Timber.v("New value sets %s", it) } + } + + suspend fun clear() { + Timber.d("Clearing value sets") + certificateValueSetServer.clear() + internalData.updateBlocking { + Timber.v("Resetting value sets") + emptyValueSetsContainer + } + } +} + +private const val TAG = "ValueSetsRepository" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/DccValueSetMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/DccValueSetMapper.kt new file mode 100644 index 0000000000000000000000000000000000000000..5833e17c1b96a8058775827efdd36a49b8789a9c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/DccValueSetMapper.kt @@ -0,0 +1,44 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.internal + +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.DefaultValueSet +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsContainer +import de.rki.coronawarnapp.server.protocols.internal.dgc.ValueSetsOuterClass +import timber.log.Timber +import java.util.Locale + +internal fun ValueSetsOuterClass.ValueSets.toValueSetsContainer(languageCode: Locale): ValueSetsContainer { + Timber.d("toValueSetsContainer(valueSets=%s, languageCode=%s)", this, languageCode) + return ValueSetsContainer( + vaccinationValueSets = toVaccinationValueSets(languageCode = languageCode), + testCertificateValueSets = toTestCertificateValueSets(languageCode = languageCode) + ).also { Timber.tag(TAG).d("Created %s", it) } +} + +internal fun ValueSetsOuterClass.ValueSets.toVaccinationValueSets(languageCode: Locale): VaccinationValueSets = + VaccinationValueSets( + languageCode = languageCode, + tg = tg.toValueSet(), + vp = vp.toValueSet(), + mp = mp.toValueSet(), + ma = ma.toValueSet() + ) + +internal fun ValueSetsOuterClass.ValueSets.toTestCertificateValueSets(languageCode: Locale): TestCertificateValueSets = + TestCertificateValueSets( + languageCode = languageCode, + tg = tg.toValueSet(), + tt = tcTt.toValueSet(), + ma = tcMa.toValueSet(), + tr = tcTr.toValueSet() + ) + +internal fun ValueSetsOuterClass.ValueSet.toValueSet() = DefaultValueSet(items = itemsList.map { it.toItem() }) + +internal fun ValueSetsOuterClass.ValueSetItem.toItem() = DefaultValueSet.DefaultItem( + key = key, + displayText = displayText +) + +private const val TAG: String = "ValueSetMapper" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/ValueSetInvalidSignatureException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/ValueSetInvalidSignatureException.kt similarity index 80% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/ValueSetInvalidSignatureException.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/ValueSetInvalidSignatureException.kt index 6de24adafb228da191c3f7d076ef1729b8ad3540..5a218d35a9c556aa1fba85f6d4115007e221ae00 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/ValueSetInvalidSignatureException.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/internal/ValueSetInvalidSignatureException.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset.internal +package de.rki.coronawarnapp.covidcertificate.valueset.internal import de.rki.coronawarnapp.exception.reporting.ErrorCodes import de.rki.coronawarnapp.util.security.InvalidSignatureException diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSet.kt new file mode 100644 index 0000000000000000000000000000000000000000..9721ba413f8e50ef7c5bead14159a54a04d2220d --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSet.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.server + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class CertificateValueSet diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetApiV1.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetApiV1.kt similarity index 71% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetApiV1.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetApiV1.kt index d30f63ada3e13187b20aa2952192c35b9ea9df20..b7f560ac677316edd296e62eb4db83e18e9808c6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSetApiV1.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetApiV1.kt @@ -1,11 +1,11 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset +package de.rki.coronawarnapp.covidcertificate.valueset.server import okhttp3.ResponseBody import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path -interface VaccinationValueSetApiV1 { +interface CertificateValueSetApiV1 { @GET("/version/v1/ehn-dgc/{lang}/value-sets") suspend fun getValueSets(@Path("lang") languageCode: String): Response<ResponseBody> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServer.kt similarity index 83% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServer.kt index 5e064692b159c9123f431138dbbc4520a33228f3..8d88f880b6b5b53e205cfd43f35a442ab595b0cb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServer.kt @@ -1,15 +1,16 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset +package de.rki.coronawarnapp.covidcertificate.valueset.server import androidx.annotation.VisibleForTesting import dagger.Lazy import dagger.Reusable +import de.rki.coronawarnapp.covidcertificate.valueset.internal.ValueSetInvalidSignatureException +import de.rki.coronawarnapp.covidcertificate.valueset.internal.toValueSetsContainer +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsContainer import de.rki.coronawarnapp.server.protocols.internal.dgc.ValueSetsOuterClass import de.rki.coronawarnapp.util.ZipHelper.readIntoMap import de.rki.coronawarnapp.util.ZipHelper.unzip import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.security.SignatureValidation -import de.rki.coronawarnapp.vaccination.core.server.valueset.internal.ValueSetInvalidSignatureException -import de.rki.coronawarnapp.vaccination.core.server.valueset.internal.toVaccinationValueSet import kotlinx.coroutines.withContext import okhttp3.Cache import okhttp3.ResponseBody @@ -24,14 +25,14 @@ import javax.inject.Inject * Talks with CWA servers */ @Reusable -class VaccinationServer @Inject constructor( - @ValueSet private val cache: Cache, - private val apiV1: Lazy<VaccinationValueSetApiV1>, +class CertificateValueSetServer @Inject constructor( + @CertificateValueSet private val cache: Cache, + private val apiV1: Lazy<CertificateValueSetApiV1>, private val dispatcherProvider: DispatcherProvider, private val signatureValidation: SignatureValidation ) { - suspend fun getVaccinationValueSets(languageCode: Locale): VaccinationValueSet? = + suspend fun getVaccinationValueSets(languageCode: Locale): ValueSetsContainer? = withContext(dispatcherProvider.Default) { return@withContext try { val response = requestValueSets(languageCode.language) @@ -39,7 +40,7 @@ class VaccinationServer @Inject constructor( val body = requireNotNull(response.body()) { "Body of response was null" } val valueSetsProtobuf = body.parseBody() - valueSetsProtobuf.toVaccinationValueSet(languageCode = languageCode) + valueSetsProtobuf.toValueSetsContainer(languageCode = languageCode) } catch (e: Exception) { Timber.e(e, "Getting vaccination value sets from server failed cause: ${e.message}") null diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/DefaultValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/DefaultValueSet.kt new file mode 100644 index 0000000000000000000000000000000000000000..bf8d268cdc339982862689ee482b7375d6cfe624 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/DefaultValueSet.kt @@ -0,0 +1,15 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class DefaultValueSet( + @SerializedName("items") override val items: List<DefaultItem> = emptyList() +) : ValueSets.ValueSet { + @Keep + data class DefaultItem( + @SerializedName("key") override val key: String, + @SerializedName("displayText") override val displayText: String + ) : ValueSets.ValueSet.Item +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/TestCertificateValueSets.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/TestCertificateValueSets.kt new file mode 100644 index 0000000000000000000000000000000000000000..c412d2656ab71d99f10788fac12cc54404f665ae --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/TestCertificateValueSets.kt @@ -0,0 +1,31 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName +import java.util.Locale + +@Keep +data class TestCertificateValueSets( + @SerializedName("languageCode") override val languageCode: Locale, + @SerializedName("tg") override val tg: DefaultValueSet, + @SerializedName("tt") val tt: DefaultValueSet, // Type of Test + @SerializedName("ma") val ma: DefaultValueSet, // RAT Test name and manufacturer + @SerializedName("tr") val tr: DefaultValueSet, // Test Result +) : ValueSets { + + override val isEmpty: Boolean + get() = tg.items.isEmpty() && tt.items.isEmpty() && ma.items.isEmpty() && tr.items.isEmpty() + + override fun getDisplayText(key: String): String? = + tg.getDisplayText(key) ?: tt.getDisplayText(key) ?: ma.getDisplayText(key) ?: tr.getDisplayText(key) +} + +val emptyTestCertificateValueSets: TestCertificateValueSets by lazy { + TestCertificateValueSets( + languageCode = Locale.ENGLISH, + tg = DefaultValueSet(), + tt = DefaultValueSet(), + ma = DefaultValueSet(), + tr = DefaultValueSet() + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/VaccinationValueSets.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/VaccinationValueSets.kt new file mode 100644 index 0000000000000000000000000000000000000000..c15738bfce2e3881e0afa81052e7ff2aa59cc333 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/VaccinationValueSets.kt @@ -0,0 +1,31 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName +import java.util.Locale + +@Keep +data class VaccinationValueSets( + @SerializedName("languageCode") override val languageCode: Locale, + @SerializedName("tg") override val tg: DefaultValueSet, + @SerializedName("vp") val vp: DefaultValueSet, // Vaccine or prophylaxis + @SerializedName("mp") val mp: DefaultValueSet, // Vaccine medicinal product + @SerializedName("ma") val ma: DefaultValueSet, // Marketing Authorization Holder +) : ValueSets { + + override val isEmpty: Boolean + get() = tg.items.isEmpty() && vp.items.isEmpty() && mp.items.isEmpty() && ma.items.isEmpty() + + override fun getDisplayText(key: String): String? = + tg.getDisplayText(key) ?: vp.getDisplayText(key) ?: mp.getDisplayText(key) ?: ma.getDisplayText(key) +} + +val emptyVaccinationValueSets: VaccinationValueSets by lazy { + VaccinationValueSets( + languageCode = Locale.ENGLISH, + tg = DefaultValueSet(), + vp = DefaultValueSet(), + mp = DefaultValueSet(), + ma = DefaultValueSet() + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSets.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSets.kt new file mode 100644 index 0000000000000000000000000000000000000000..80a7321bcc2fdf9af227ad873358e287be513e20 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSets.kt @@ -0,0 +1,27 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import java.util.Locale + +interface ValueSets { + + val languageCode: Locale + + // Disease or agent targeted + val tg: ValueSet + + val isEmpty: Boolean + + fun getDisplayText(key: String): String? + + interface ValueSet { + val items: List<Item> + + // Use custom item instead of map to allow for future extensions + interface Item { + val key: String + val displayText: String + } + } +} + +fun ValueSets.ValueSet.getDisplayText(key: String): String? = items.find { key == it.key }?.displayText diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsContainer.kt new file mode 100644 index 0000000000000000000000000000000000000000..d1af578833b007c0a41254fb8e07f05676bff20a --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsContainer.kt @@ -0,0 +1,20 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class ValueSetsContainer( + @SerializedName("vaccinationValueSets") val vaccinationValueSets: VaccinationValueSets, + @SerializedName("testCertificateValueSets") val testCertificateValueSets: TestCertificateValueSets +) + +fun ValueSetsContainer?.isEmpty(): Boolean = + (this == null) || vaccinationValueSets.isEmpty && testCertificateValueSets.isEmpty + +val emptyValueSetsContainer: ValueSetsContainer by lazy { + ValueSetsContainer( + vaccinationValueSets = emptyVaccinationValueSets, + testCertificateValueSets = emptyTestCertificateValueSets + ) +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsStorage.kt new file mode 100644 index 0000000000000000000000000000000000000000..9c45cf1edc9529097b92d5db1105bdb94281c7a6 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/valueset/valuesets/ValueSetsStorage.kt @@ -0,0 +1,61 @@ +package de.rki.coronawarnapp.covidcertificate.valueset.valuesets + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import com.google.gson.Gson +import dagger.Reusable +import de.rki.coronawarnapp.util.di.AppContext +import de.rki.coronawarnapp.util.serialization.BaseGson +import de.rki.coronawarnapp.util.serialization.fromJson +import timber.log.Timber +import javax.inject.Inject + +@Reusable +class ValueSetsStorage @Inject constructor( + @AppContext private val context: Context, + @BaseGson private val gson: Gson +) { + + private val prefs: SharedPreferences by lazy { + context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) + .also { removeLeftOver(it) } + } + + var valueSetsContainer: ValueSetsContainer + get() = getValueSet() + set(value) = setValueSet(value) + + private fun getValueSet(): ValueSetsContainer { + Timber.v("Loading value sets container") + val valueSetString = prefs.getString(PKEY_VALUE_SETS_CONTAINER_PREFIX, null) + return when (valueSetString != null) { + true -> gson.fromJson(valueSetString) + else -> emptyValueSetsContainer + }.also { loaded -> Timber.v("Loaded value sets container %s", loaded) } + } + + private fun setValueSet(value: ValueSetsContainer) { + Timber.v("Saving value sets container %s", value) + prefs.edit { + val json = gson.toJson(value, ValueSetsContainer::class.java) + Timber.v("Writing %s to prefs", json) + putString(PKEY_VALUE_SETS_CONTAINER_PREFIX, json) + } + } + + private fun removeLeftOver(prefs: SharedPreferences) { + Timber.v("Checking for leftover and removing it") + if (prefs.contains(PKEY_VALUE_SETS_PREFIX)) { + prefs.edit(commit = true) { + remove(PKEY_VALUE_SETS_PREFIX) + } + } + } + + companion object { + private const val PREF_NAME = "valuesets_localdata" + private const val PKEY_VALUE_SETS_PREFIX = "valueset" + private const val PKEY_VALUE_SETS_CONTAINER_PREFIX = "valuesets_container" + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt index 8ec58e10709d6dbaefb48c8764bb71c417056d1d..33720b0c900ee121e2180a8b747cc481169d8ef3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsTestResultDonor.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.testresult import de.rki.coronawarnapp.coronatest.server.CoronaTestResult +import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.datadonation.analytics.common.isFinal import de.rki.coronawarnapp.datadonation.analytics.common.isPending import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule @@ -16,19 +17,25 @@ import javax.inject.Singleton class AnalyticsPCRTestResultDonor @Inject constructor( testResultSettings: AnalyticsPCRTestResultSettings, timeStamper: TimeStamper, -) : AnalyticsTestResultDonor(testResultSettings, timeStamper) +) : AnalyticsTestResultDonor(testResultSettings, timeStamper) { + override val type = CoronaTest.Type.PCR +} @Singleton class AnalyticsRATestResultDonor @Inject constructor( testResultSettings: AnalyticsRATestResultSettings, timeStamper: TimeStamper, -) : AnalyticsTestResultDonor(testResultSettings, timeStamper) +) : AnalyticsTestResultDonor(testResultSettings, timeStamper) { + override val type = CoronaTest.Type.RAPID_ANTIGEN +} abstract class AnalyticsTestResultDonor( private val testResultSettings: AnalyticsTestResultSettings, private val timeStamper: TimeStamper, ) : DonorModule { + abstract val type: CoronaTest.Type + override suspend fun beginDonation(request: DonorModule.Request): DonorModule.Contribution { val timestampAtRegistration = testResultSettings.testRegisteredAt.value if (timestampAtRegistration == null) { @@ -145,9 +152,14 @@ abstract class AnalyticsTestResultDonor( private fun CoronaTestResult.toPPATestResult(): PpaData.PPATestResult { return when (this) { - CoronaTestResult.PCR_OR_RAT_PENDING -> PpaData.PPATestResult.TEST_RESULT_PENDING + CoronaTestResult.PCR_OR_RAT_PENDING -> when (type) { + CoronaTest.Type.PCR -> PpaData.PPATestResult.TEST_RESULT_PENDING + CoronaTest.Type.RAPID_ANTIGEN -> PpaData.PPATestResult.TEST_RESULT_RAT_PENDING + } CoronaTestResult.PCR_POSITIVE -> PpaData.PPATestResult.TEST_RESULT_POSITIVE CoronaTestResult.PCR_NEGATIVE -> PpaData.PPATestResult.TEST_RESULT_NEGATIVE + CoronaTestResult.RAT_NEGATIVE -> PpaData.PPATestResult.TEST_RESULT_RAT_NEGATIVE + CoronaTestResult.RAT_POSITIVE -> PpaData.PPATestResult.TEST_RESULT_RAT_POSITIVE else -> PpaData.PPATestResult.TEST_RESULT_UNKNOWN } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt index b99710e6d4a6bd5a07a5bb7d08931aecf4594746..10fa9b9785e9ac1ca074fc3e5bf2700212bb26f8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentModule.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.environment import dagger.Module import de.rki.coronawarnapp.environment.bugreporting.BugReportingServerModule +import de.rki.coronawarnapp.environment.covidcertificate.DCCModule import de.rki.coronawarnapp.environment.datadonation.DataDonationCDNModule import de.rki.coronawarnapp.environment.download.DownloadCDNModule import de.rki.coronawarnapp.environment.submission.SubmissionCDNModule @@ -13,7 +14,8 @@ import de.rki.coronawarnapp.environment.verification.VerificationCDNModule SubmissionCDNModule::class, VerificationCDNModule::class, DataDonationCDNModule::class, - BugReportingServerModule::class + BugReportingServerModule::class, + DCCModule::class ] ) class EnvironmentModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt index d44e8e88ef378eaae6e78f7c4e73591be7c08e93..bc62e8baba75f858aa927329b247a50ce0c694e6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/EnvironmentSetup.kt @@ -7,6 +7,7 @@ import com.google.gson.JsonObject import com.google.gson.JsonPrimitive import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DATA_DONATION +import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DCC import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.DOWNLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.LOG_UPLOAD import de.rki.coronawarnapp.environment.EnvironmentSetup.EnvKey.SAFETYNET_API_KEY @@ -37,7 +38,8 @@ class EnvironmentSetup @Inject constructor( DATA_DONATION("DATA_DONATION_CDN_URL"), LOG_UPLOAD("LOG_UPLOAD_SERVER_URL"), SAFETYNET_API_KEY("SAFETYNET_API_KEY"), - CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY") + CROWD_NOTIFIER_PUBLIC_KEY("CROWD_NOTIFIER_PUBLIC_KEY"), + DCC("DCC_SERVER_URL"), } enum class Type(val rawKey: String) { @@ -135,6 +137,9 @@ class EnvironmentSetup @Inject constructor( val logUploadServerUrl: String get() = getEnvironmentValue(LOG_UPLOAD).asString + val dccServerUrl: String + get() = getEnvironmentValue(DCC).asString + companion object { private const val PKEY_CURRENT_ENVINROMENT = "environment.current" } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt new file mode 100644 index 0000000000000000000000000000000000000000..d34bb8687820f5fb0f22bf0ae6920be1e883e809 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCHttpClient.kt @@ -0,0 +1,8 @@ +package de.rki.coronawarnapp.environment.covidcertificate + +import javax.inject.Qualifier + +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DCCHttpClient diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt new file mode 100644 index 0000000000000000000000000000000000000000..47821bb169e4569cea0d18e5ab100e245fba7f1c --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCModule.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.environment.covidcertificate + +import dagger.Module +import dagger.Provides +import dagger.Reusable +import de.rki.coronawarnapp.environment.BaseEnvironmentModule +import de.rki.coronawarnapp.environment.EnvironmentSetup +import de.rki.coronawarnapp.http.HttpClientDefault +import okhttp3.OkHttpClient +import javax.inject.Singleton + +@Module +class DCCModule : BaseEnvironmentModule() { + + @Reusable + @DCCHttpClient + @Provides + fun dccHttpClient(@HttpClientDefault defaultHttpClient: OkHttpClient): OkHttpClient = + defaultHttpClient.newBuilder().build() + + @Singleton + @DCCServerUrl + @Provides + fun dccServerUrl(environment: EnvironmentSetup): String { + val url = environment.dccServerUrl + return requireValidUrl(url) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/ValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt similarity index 53% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/ValueSet.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt index 1300c045c0d8f448781ddcaa327bb7f2959fe09f..8c9cffcad317bb4eb1dad008c63d6e1264ddc804 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/ValueSet.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/environment/covidcertificate/DCCServerUrl.kt @@ -1,8 +1,8 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset +package de.rki.coronawarnapp.environment.covidcertificate import javax.inject.Qualifier @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) -annotation class ValueSet +annotation class DCCServerUrl diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/GreenCertificateUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/GreenCertificateUIModule.kt deleted file mode 100644 index 62cacdaf517d4f183508f95368ab68c367e92937..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/GreenCertificateUIModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui - -import dagger.Module -import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesFragment -import de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesFragmentModule -import de.rki.coronawarnapp.greencertificate.ui.certificates.details.GreenCertificateDetailsFragment -import de.rki.coronawarnapp.greencertificate.ui.certificates.details.GreenCertificateDetailsModule - -@Module -abstract class GreenCertificateUIModule { - - @ContributesAndroidInjector(modules = [CertificatesFragmentModule::class]) - abstract fun certificatesFragment(): CertificatesFragment - - @ContributesAndroidInjector(modules = [GreenCertificateDetailsModule::class]) - abstract fun certificateDetailsFragment(): GreenCertificateDetailsFragment -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragment.kt deleted file mode 100644 index b9be3ef8f0c8da5a2f8bc30338d97b99cff70bd6..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragment.kt +++ /dev/null @@ -1,53 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates - -import android.os.Bundle -import android.view.View -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import androidx.recyclerview.widget.DefaultItemAnimator -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentCertificatesBinding -import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.lists.decorations.TopBottomPaddingDecorator -import de.rki.coronawarnapp.util.lists.diffutil.update -import de.rki.coronawarnapp.util.ui.doNavigate -import de.rki.coronawarnapp.util.ui.findNestedGraph -import de.rki.coronawarnapp.util.ui.observe2 -import de.rki.coronawarnapp.util.ui.viewBinding -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider -import de.rki.coronawarnapp.util.viewmodel.cwaViewModels -import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragment -import javax.inject.Inject - -class CertificatesFragment : Fragment(R.layout.fragment_certificates), AutoInject { - - @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val viewModel by cwaViewModels<CertificatesViewModel> { viewModelFactory } - private val binding by viewBinding<FragmentCertificatesBinding>() - - private val certificatesAdapter = CertificatesAdapter() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.recyclerView.apply { - itemAnimator = DefaultItemAnimator() - addItemDecoration(TopBottomPaddingDecorator(topPadding = R.dimen.spacing_tiny)) - adapter = certificatesAdapter - } - - viewModel.screenItems.observe2(this) { items -> certificatesAdapter.update(items) } - viewModel.events.observe2(this) { event -> - when (event) { - is CertificatesFragmentEvents.GoToVaccinationList -> findNavController().navigate( - VaccinationListFragment.navigationUri(event.personIdentifierCodeSha256) - ) - is CertificatesFragmentEvents.OpenVaccinationRegistrationGraph -> { - // TODO: update when certifications info screen is done - findNestedGraph(R.id.vaccination_nav_graph).startDestination = R.id.vaccinationQrCodeScanFragment - doNavigate(CertificatesFragmentDirections.actionCertificatesFragmentToVaccinationNavGraph()) - } - } - } - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentEvents.kt deleted file mode 100644 index 4682805714cb0ae55fca91abc0b7b34f03bc00d0..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesFragmentEvents.kt +++ /dev/null @@ -1,8 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates - -sealed class CertificatesFragmentEvents { - - data class OpenVaccinationRegistrationGraph(val registrationAcknowledged: Boolean) : CertificatesFragmentEvents() - - data class GoToVaccinationList(val personIdentifierCodeSha256: String) : CertificatesFragmentEvents() -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesViewModel.kt deleted file mode 100644 index 41e545fdc82e919188b1f9739227bbaa2fcd7d13..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/CertificatesViewModel.kt +++ /dev/null @@ -1,78 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates - -import androidx.lifecycle.LiveData -import androidx.lifecycle.asLiveData -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository -import de.rki.coronawarnapp.vaccination.ui.cards.BottomInfoVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.CreateVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.HeaderInfoVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.ImmuneVaccinationCard -import de.rki.coronawarnapp.vaccination.ui.cards.VaccinationCard -import kotlinx.coroutines.flow.map - -class CertificatesViewModel @AssistedInject constructor( - vaccinationRepository: VaccinationRepository, - private val vaccinationSettings: VaccinationSettings -) : CWAViewModel() { - - val events = SingleLiveEvent<CertificatesFragmentEvents>() - - // TODO: cards should be adjusted in the following PR - val screenItems: LiveData<List<CertificatesItem>> = - vaccinationRepository.vaccinationInfos.map { vaccinatedPersons -> - mutableListOf<CertificatesItem>().apply { - add(HeaderInfoVaccinationCard.Item) - - vaccinatedPersons.forEach { vaccinatedPerson -> - val card = when (vaccinatedPerson.getVaccinationStatus()) { - VaccinatedPerson.Status.COMPLETE, - VaccinatedPerson.Status.INCOMPLETE -> VaccinationCard.Item( - vaccinatedPerson = vaccinatedPerson, - onClickAction = { - events.postValue( - CertificatesFragmentEvents.GoToVaccinationList( - vaccinatedPerson.identifier.codeSHA256 - ) - ) - } - ) - VaccinatedPerson.Status.IMMUNITY -> ImmuneVaccinationCard.Item( - vaccinatedPerson = vaccinatedPerson, - onClickAction = { - events.postValue( - CertificatesFragmentEvents.GoToVaccinationList( - vaccinatedPerson.identifier.codeSHA256 - ) - ) - } - ) - } - add(card) - } - - add( - CreateVaccinationCard.Item( - onClickAction = { - events.postValue( - CertificatesFragmentEvents.OpenVaccinationRegistrationGraph( - vaccinationSettings.registrationAcknowledged - ) - ) - } - ) - ) - add(BottomInfoVaccinationCard.Item) - } - }.asLiveData() - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<CertificatesViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsFragment.kt deleted file mode 100644 index ff9e0a78ba067f6d66397a33b22fe303151a6c80..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsFragment.kt +++ /dev/null @@ -1,84 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates.details - -import android.os.Bundle -import android.view.View -import android.widget.LinearLayout -import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.fragment.app.Fragment -import com.google.android.material.appbar.AppBarLayout -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.databinding.FragmentGreencertificateDetailsBinding -import de.rki.coronawarnapp.ui.view.onOffsetChange -import de.rki.coronawarnapp.util.di.AutoInject -import de.rki.coronawarnapp.util.setUrl -import de.rki.coronawarnapp.util.ui.popBackStack -import de.rki.coronawarnapp.util.ui.viewBinding -import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider -import de.rki.coronawarnapp.util.viewmodel.cwaViewModels -import javax.inject.Inject - -class GreenCertificateDetailsFragment : Fragment(R.layout.fragment_greencertificate_details), AutoInject { - - @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - private val binding: FragmentGreencertificateDetailsBinding by viewBinding() - private val viewModel: GreenCertificateDetailsViewModel by cwaViewModels { viewModelFactory } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) = - with(binding) { - toolbar.setNavigationOnClickListener { popBackStack() } - - qrCodeCard.title.text = getString(R.string.detail_green_certificate_card_title) - qrCodeCard.subtitle.text = "Test durchgeführt am 12.05.21 18:01" // will be changed - - appBarLayout.onOffsetChange { titleAlpha, subtitleAlpha -> - title.alpha = titleAlpha - subtitle.alpha = subtitleAlpha - } - - if (travelNoticeGerman.text == - requireContext().getString(R.string.green_certificate_attribute_certificate_travel_notice_german) - ) { - travelNoticeGerman.setUrl( - R.string.green_certificate_attribute_certificate_travel_notice_german, - R.string.green_certificate_travel_notice_link_de, - R.string.green_certificate_travel_notice_link_de - ) - } - - if (travelNoticeEnglish.text == - requireContext().getString(R.string.green_certificate_attribute_certificate_travel_notice_english) - ) { - travelNoticeEnglish.setUrl( - R.string.green_certificate_attribute_certificate_travel_notice_english, - R.string.green_certificate_travel_notice_link_en, - R.string.green_certificate_travel_notice_link_en - ) - } - - setToolbarOverlay() - - viewModel.qrCode.observe(viewLifecycleOwner) { - qrCodeCard.image.setImageBitmap(it) - it?.let { - qrCodeCard.image.setOnClickListener { viewModel.openFullScreen() } - qrCodeCard.progressBar.hide() - } - } - - // TODO: Will in the future be called when the data is loaded from the database - viewModel.generateQrCode() - } - - private fun setToolbarOverlay() { - val width = requireContext().resources.displayMetrics.widthPixels - val params: CoordinatorLayout.LayoutParams = binding.scrollView.layoutParams - as (CoordinatorLayout.LayoutParams) - - val textParams = binding.subtitle.layoutParams as (LinearLayout.LayoutParams) - textParams.bottomMargin = (width / 3) + 170 - binding.subtitle.requestLayout() - - val behavior: AppBarLayout.ScrollingViewBehavior = params.behavior as (AppBarLayout.ScrollingViewBehavior) - behavior.overlayTop = (width / 3) + 170 - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsNavigation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsNavigation.kt deleted file mode 100644 index 029b09bdc085eb4cedc5b22bf9b86dabee7e6db1..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsNavigation.kt +++ /dev/null @@ -1,6 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates.details - -sealed class GreenCertificateDetailsNavigation { - object Back : GreenCertificateDetailsNavigation() - data class FullQrCode(val qrCodeText: String) : GreenCertificateDetailsNavigation() -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsViewModel.kt deleted file mode 100644 index c1996855f250724b0466791cdd1de45dcc5a1e05..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/greencertificate/ui/certificates/details/GreenCertificateDetailsViewModel.kt +++ /dev/null @@ -1,42 +0,0 @@ -package de.rki.coronawarnapp.greencertificate.ui.certificates.details - -import android.graphics.Bitmap -import androidx.lifecycle.asLiveData -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator -import de.rki.coronawarnapp.util.coroutine.DispatcherProvider -import de.rki.coronawarnapp.util.ui.SingleLiveEvent -import de.rki.coronawarnapp.util.viewmodel.CWAViewModel -import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import kotlinx.coroutines.flow.MutableStateFlow -import timber.log.Timber - -class GreenCertificateDetailsViewModel @AssistedInject constructor( - private val qrCodeGenerator: QrCodeGenerator, - dispatcherProvider: DispatcherProvider, -) : CWAViewModel(dispatcherProvider) { - - private var qrCodeText: String? = null - private val mutableStateFlow = MutableStateFlow<Bitmap?>(null) - val qrCode = mutableStateFlow.asLiveData(dispatcherProvider.Default) - - val events = SingleLiveEvent<GreenCertificateDetailsNavigation>() - - fun onClose() = events.postValue(GreenCertificateDetailsNavigation.Back) - - fun openFullScreen() = qrCodeText?.let { events.postValue(GreenCertificateDetailsNavigation.FullQrCode(it)) } - - /* TODO: Adapt to Green Certificate */ - fun generateQrCode() = launch { - try { - mutableStateFlow.value = qrCodeGenerator.createQrCode("Sample String") - } catch (e: Exception) { - Timber.d(e, "generateQrCode failed for greenCertificate=%s", "Sample Certificate") - mutableStateFlow.value = null - } - } - - @AssistedFactory - interface Factory : SimpleCWAViewModelFactory<GreenCertificateDetailsViewModel> -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt index a55f59dbd258a45c9ba9252411bed5b2173cbd84..7b05ff4b3a0705dd0e78b0d9893387e8915910b0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/execution/PresenceTracingWarningTask.kt @@ -15,7 +15,6 @@ import de.rki.coronawarnapp.presencetracing.warning.storage.TraceWarningReposito import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskCancellationException import de.rki.coronawarnapp.task.TaskFactory -import de.rki.coronawarnapp.task.common.Finished import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt index 8a5d733cb1be611c57e6d695c6609ade158d1ee4..b174210001f84d9573e95a6f995ac2e2370f57be 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt @@ -19,9 +19,9 @@ import de.rki.coronawarnapp.risk.storage.RiskLevelStorage import de.rki.coronawarnapp.task.Task import de.rki.coronawarnapp.task.TaskCancellationException import de.rki.coronawarnapp.task.TaskFactory +import de.rki.coronawarnapp.task.common.DefaultProgress import de.rki.coronawarnapp.task.common.Finished import de.rki.coronawarnapp.task.common.Started -import de.rki.coronawarnapp.task.common.DefaultProgress import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.device.BackgroundModeStatus import kotlinx.coroutines.flow.Flow diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/additionalinfos/FindDetailsInJournalBox.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/additionalinfos/FindDetailsInJournalBox.kt index 48cf2f853edc29e64603db9296b367b1872eaedc..a87e584ec8a5e3da332bc353616754e10036d71e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/additionalinfos/FindDetailsInJournalBox.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/additionalinfos/FindDetailsInJournalBox.kt @@ -5,11 +5,12 @@ import androidx.annotation.LayoutRes import de.rki.coronawarnapp.R import de.rki.coronawarnapp.databinding.TracingDetailsFindDetailsInJournalBinding import de.rki.coronawarnapp.tracing.ui.details.TracingDetailsAdapter +import de.rki.coronawarnapp.tracing.ui.details.items.additionalinfos.FindDetailsInJournalBox.Item class FindDetailsInJournalBox( parent: ViewGroup, @LayoutRes containerLayout: Int = R.layout.tracing_details_find_details_in_journal -) : TracingDetailsAdapter.DetailsItemVH<FindDetailsInJournalBox.Item, TracingDetailsFindDetailsInJournalBinding>( +) : TracingDetailsAdapter.DetailsItemVH<Item, TracingDetailsFindDetailsInJournalBinding>( containerLayout, parent ) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt index 552d7ccb8691c8d2b08a431b3ac725e87cf89804..b9a628633897316295d23bfff2b14ad59636323c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt @@ -4,8 +4,9 @@ import dagger.Binds import dagger.Module import dagger.android.ContributesAndroidInjector import dagger.multibindings.IntoMap +import de.rki.coronawarnapp.covidcertificate.test.ui.CovidCertificateUIModule +import de.rki.coronawarnapp.covidcertificate.vaccination.ui.VaccinationUIModule import de.rki.coronawarnapp.datadonation.analytics.ui.AnalyticsUIModule -import de.rki.coronawarnapp.greencertificate.ui.GreenCertificateUIModule import de.rki.coronawarnapp.release.NewReleaseInfoFragment import de.rki.coronawarnapp.release.NewReleaseInfoFragmentModule import de.rki.coronawarnapp.tracing.ui.details.TracingDetailsFragmentModule @@ -26,7 +27,6 @@ import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionFragmentModule import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey -import de.rki.coronawarnapp.vaccination.ui.VaccinationUIModule @Module( includes = [ @@ -42,7 +42,7 @@ import de.rki.coronawarnapp.vaccination.ui.VaccinationUIModule PresenceTracingUIModule::class, RATProfileUIModule::class, VaccinationUIModule::class, - GreenCertificateUIModule::class, + CovidCertificateUIModule::class, ] ) abstract class MainActivityModule { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt index 7952b2e729d07b2ad1c0ea5151f75772c7cd3b7d..6fe194da45ba54ed22a245032879f0a93aeb8556 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.asLiveData import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.playbook.BackgroundNoise import de.rki.coronawarnapp.presencetracing.TraceLocationSettings @@ -17,7 +18,6 @@ import de.rki.coronawarnapp.util.device.BackgroundModeStatus import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt index d549a308af695e9e842abd3586f7905127f49fb5..b75e523a1e49a607e4a4d52fe522fb96a5815d89 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentViewModel.kt @@ -20,6 +20,8 @@ import de.rki.coronawarnapp.coronatest.type.pcr.toSubmissionState import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest import de.rki.coronawarnapp.coronatest.type.rapidantigen.SubmissionStateRAT import de.rki.coronawarnapp.coronatest.type.rapidantigen.toSubmissionState +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.statistics.source.StatisticsProvider import de.rki.coronawarnapp.statistics.ui.homecards.StatisticsHomeCard @@ -74,8 +76,6 @@ import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragment.kt similarity index 99% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragment.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragment.kt index 6ada9a9747eacc1581735ae1d68ac13efdf885bd..2635a01a5d5cdcfec0d8b5f48f392d1616777c46 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragment.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.greencertificate +package de.rki.coronawarnapp.ui.submission.covidcertificate import android.os.Bundle import androidx.fragment.app.Fragment diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentModule.kt similarity index 90% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragmentModule.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentModule.kt index 74c2e8f205297db138d98de358174a45ce2da4bc..6bc12c024573270581707f0cf504024d2be50ca2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateFragmentModule.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.greencertificate +package de.rki.coronawarnapp.ui.submission.covidcertificate import dagger.Binds import dagger.Module diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModel.kt similarity index 98% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModel.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModel.kt index e6603079d74c953af578d0f6655f1e588603476f..ff0e272e71fbba2a1b6de47be1defa317be6f66c 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModel.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.greencertificate +package de.rki.coronawarnapp.ui.submission.covidcertificate import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestDccNavEvent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestDccNavEvent.kt similarity index 72% rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestDccNavEvent.kt rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestDccNavEvent.kt index 1bb251b76e2760f8183d4df792b992857304ad8c..d1b3d8c6075c668fb5410864e1a0eed51d37bf46 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestDccNavEvent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestDccNavEvent.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.greencertificate +package de.rki.coronawarnapp.ui.submission.covidcertificate sealed class RequestDccNavEvent object ToDispatcherScreen : RequestDccNavEvent() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt index 2f0c1d50985049173bd6fc25c468936899b70776..fe135a5d3ae934c14066d17ee3cdd426cce8af24 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/viewmodel/SubmissionFragmentModule.kt @@ -8,8 +8,8 @@ import de.rki.coronawarnapp.ui.submission.fragment.SubmissionContactFragment import de.rki.coronawarnapp.ui.submission.fragment.SubmissionDispatcherFragment import de.rki.coronawarnapp.ui.submission.deletionwarning.SubmissionDeletionWarningFragment import de.rki.coronawarnapp.ui.submission.deletionwarning.SubmissionDeletionWarningModule -import de.rki.coronawarnapp.ui.submission.greencertificate.RequestCovidCertificateFragment -import de.rki.coronawarnapp.ui.submission.greencertificate.RequestCovidCertificateFragmentModule +import de.rki.coronawarnapp.ui.submission.covidcertificate.RequestCovidCertificateFragment +import de.rki.coronawarnapp.ui.submission.covidcertificate.RequestCovidCertificateFragmentModule import de.rki.coronawarnapp.ui.submission.qrcode.consent.SubmissionConsentFragment import de.rki.coronawarnapp.ui.submission.qrcode.consent.SubmissionConsentModule import de.rki.coronawarnapp.ui.submission.qrcode.scan.SubmissionQRCodeScanFragment @@ -102,7 +102,7 @@ internal abstract class SubmissionFragmentModule { abstract fun submissionTestResultNoConsentScreen(): SubmissionTestResultNoConsentFragment @ContributesAndroidInjector(modules = [SubmissionResultPositiveOtherWarningNoConsentModule::class]) - abstract fun SubmissionResultPositiveOtherWarningNoConsentScreen(): SubmissionResultPositiveOtherWarningNoConsentFragment + abstract fun submissionResultPositiveOtherWarningNoConsentScreen(): SubmissionResultPositiveOtherWarningNoConsentFragment @ContributesAndroidInjector(modules = [SubmissionDeletionWarningModule::class]) abstract fun submissionDeletionWarningScreen(): SubmissionDeletionWarningFragment @@ -114,5 +114,5 @@ internal abstract class SubmissionFragmentModule { abstract fun submissionTestResultKeysSharedScreen(): SubmissionTestResultKeysSharedFragment @ContributesAndroidInjector(modules = [RequestCovidCertificateFragmentModule::class]) - abstract fun requestGreenCertificateFragment(): RequestCovidCertificateFragment + abstract fun requestCovidCertificateFragment(): RequestCovidCertificateFragment } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt index df7f430bbe48c42f64406e73b64ed417d2750c51..1f6f88ae6974863c3da1262151001e7375c883e6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt @@ -7,6 +7,10 @@ import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationPreferences +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository import de.rki.coronawarnapp.datadonation.analytics.Analytics import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings import de.rki.coronawarnapp.datadonation.survey.SurveySettings @@ -25,9 +29,6 @@ import de.rki.coronawarnapp.storage.TracingSettings import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.SubmissionSettings import de.rki.coronawarnapp.ui.presencetracing.TraceLocationPreferences -import de.rki.coronawarnapp.vaccination.core.repository.ValueSetsRepository -import de.rki.coronawarnapp.vaccination.core.VaccinationPreferences -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber @@ -67,6 +68,7 @@ class DataReset @Inject constructor( private val valueSetsRepository: ValueSetsRepository, private val vaccinationPreferences: VaccinationPreferences, private val vaccinationRepository: VaccinationRepository, + private val testCertificateRepository: TestCertificateRepository, ) { private val mutex = Mutex() @@ -109,6 +111,7 @@ class DataReset @Inject constructor( traceLocationRepository.deleteAllTraceLocations() checkInRepository.clear() coronaTestRepository.clear() + testCertificateRepository.clear() ratProfileSettings.deleteProfile() valueSetsRepository.clear() 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 fb41bfffa07a8878bcf3fbb1f94618534a250e3f..897f618ef8b4b1c654e2aacb7ac839810ad88517 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 @@ -25,6 +25,7 @@ object TimeAndDateExtensions { private val dayFormatter = DateTimeFormat.forPattern("dd.MM.yyyy") private val dayFormatter2DigitYear = DateTimeFormat.forPattern("dd.MM.yy") + private val shortTime = DateTimeFormat.shortTime() fun getCurrentHourUTC(): Int = DateTime(Instant.now(), DateTimeZone.UTC).hourOfDay().get() @@ -119,6 +120,16 @@ object TimeAndDateExtensions { */ fun LocalDate.toDayFormat(): String = toString(dayFormatter) + /** + * Returns a readable time String with the format "hh:mm" like 12:00 of a LocalDate + */ + fun LocalDate.toShortTimeFormat(): String = toString(shortTime) + + /** + * Returns a readable time String with the format "hh:mm" like 12:00 of a LocalDate + */ + fun Instant.toShortTimeFormat(): String = toString(shortTime) + /** * Returns a readable date String with the format "dd.MM.yy" like 23.05.89 of an Instant */ diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/compression/ZLIBCompression.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/compression/ZLIBCompression.kt index d03ef22a3372422c6651b5b12f62c12fdb4fa182..e4ddeec39b3881411dfe311b731491813938b141 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/compression/ZLIBCompression.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/compression/ZLIBCompression.kt @@ -2,6 +2,9 @@ package de.rki.coronawarnapp.util.compression import okio.Buffer import okio.inflate +import timber.log.Timber +import java.io.ByteArrayOutputStream +import java.util.zip.Deflater import java.util.zip.Inflater import javax.inject.Inject @@ -25,8 +28,25 @@ class ZLIBCompression @Inject constructor() { sink.readByteArray() } catch (e: Throwable) { + Timber.e(e) throw InvalidInputException("ZLIB decompression failed.", e) } + + fun compress(input: ByteArray): ByteArray { + val deflater = Deflater() + deflater.setInput(input) + val outputStream = ByteArrayOutputStream(input.size) + deflater.finish() + val buffer = ByteArray(1024) + while (!deflater.finished()) { + val count = deflater.deflate(buffer) + outputStream.write(buffer, 0, count) + } + outputStream.close() + return outputStream.toByteArray() + } } fun ByteArray.inflate(sizeLimit: Long = -1L) = ZLIBCompression().decompress(this, sizeLimit) + +fun ByteArray.deflate() = ZLIBCompression().compress(this) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt index 471f0e5866c704de32490da16012ea6ad77b2081..7582acfcdef9c87909c9489f4c35def8bae162cb 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/di/ApplicationComponent.kt @@ -14,6 +14,7 @@ import de.rki.coronawarnapp.bugreporting.BugReportingSharedModule import de.rki.coronawarnapp.bugreporting.debuglog.DebugLogger import de.rki.coronawarnapp.coronatest.CoronaTestModule import de.rki.coronawarnapp.coronatest.server.VerificationModule +import de.rki.coronawarnapp.covidcertificate.DigitalCovidCertificateModule import de.rki.coronawarnapp.datadonation.DataDonationModule import de.rki.coronawarnapp.diagnosiskeys.DiagnosisKeysModule import de.rki.coronawarnapp.diagnosiskeys.DownloadDiagnosisKeysTaskModule @@ -45,7 +46,6 @@ import de.rki.coronawarnapp.util.encryptionmigration.EncryptionErrorResetTool import de.rki.coronawarnapp.util.security.SecurityModule import de.rki.coronawarnapp.util.serialization.SerializationModule import de.rki.coronawarnapp.util.worker.WorkerBinder -import de.rki.coronawarnapp.vaccination.core.VaccinationModule import javax.inject.Singleton @Singleton @@ -80,7 +80,7 @@ import javax.inject.Singleton SecurityModule::class, PresenceTracingModule::class, CoronaTestModule::class, - VaccinationModule::class, + DigitalCovidCertificateModule::class, ] ) interface ApplicationComponent : AndroidInjector<CoronaWarnApplication> { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encoding/Base45Decoder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encoding/Base45Decoder.kt index 39320eb27363a5bc8ecd9368316c8bde1d3042f7..b8cebf7a46c13f3a9efe04b17735f8402c6dd013 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encoding/Base45Decoder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encoding/Base45Decoder.kt @@ -23,7 +23,6 @@ import java.math.BigInteger * Based on * https://github.com/ehn-digital-green-development/hcert-kotlin/blob/23203fbb71f53524ee643a9df116264f87b5b32a/src/main/kotlin/ehn/techiop/hcert/kotlin/chain/common/Base45Encoder.kt */ -@OptIn(ExperimentalUnsignedTypes::class) object Base45Decoder { private const val alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" private val int45 = BigInteger.valueOf(45) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encryption/rsa/RSAKey.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encryption/rsa/RSAKey.kt index 1018360a12b96322ea1b89ba7d639bd6b3c53065..9e417278c1c23b1beed7ecdc70bf3243a5edac2e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encryption/rsa/RSAKey.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/encryption/rsa/RSAKey.kt @@ -1,8 +1,13 @@ package de.rki.coronawarnapp.util.encryption.rsa +import com.google.gson.TypeAdapter +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonWriter import de.rki.coronawarnapp.util.trimToLength import okio.ByteString +import okio.ByteString.Companion.decodeBase64 import okio.ByteString.Companion.toByteString +import org.json.JSONObject import java.security.Key import java.security.KeyFactory import java.security.PrivateKey @@ -29,6 +34,18 @@ interface RSAKey { get() = KEY_FACTORY.generatePrivate(PKCS8EncodedKeySpec(rawKey.toByteArray())) override fun toString(): String = base64.trimToLength(16) + + class GsonAdapter : TypeAdapter<Private>() { + override fun write(out: JsonWriter, value: Private?) { + if (value == null) out.nullValue() + else out.value(value.rawKey.base64()) + } + + override fun read(reader: JsonReader): Private? = when (reader.peek()) { + JSONObject.NULL -> reader.nextNull().let { null } + else -> Private(reader.nextString().decodeBase64()!!) + } + } } data class Public(override val rawKey: ByteString) : RSAKey { @@ -39,5 +56,17 @@ interface RSAKey { get() = KEY_FACTORY.generatePublic(X509EncodedKeySpec(rawKey.toByteArray())) override fun toString(): String = base64.trimToLength(16) + + class GsonAdapter : TypeAdapter<Public>() { + override fun write(out: JsonWriter, value: Public?) { + if (value == null) out.nullValue() + else out.value(value.rawKey.base64()) + } + + override fun read(reader: JsonReader): Public? = when (reader.peek()) { + JSONObject.NULL -> reader.nextNull().let { null } + else -> Public(reader.nextString().decodeBase64()!!) + } + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/serialization/SerializationModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/serialization/SerializationModule.kt index 1a35bb0361d6c87e14447419bebb714fb747f23b..f7823f6e1f12f46ad430266b8bfb7af1a81416ff 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/serialization/SerializationModule.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/serialization/SerializationModule.kt @@ -5,6 +5,7 @@ import com.google.gson.GsonBuilder import dagger.Module import dagger.Provides import dagger.Reusable +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey import de.rki.coronawarnapp.util.serialization.adapter.ByteArrayAdapter import de.rki.coronawarnapp.util.serialization.adapter.ByteStringBase64Adapter import de.rki.coronawarnapp.util.serialization.adapter.DurationAdapter @@ -27,5 +28,7 @@ class SerializationModule { .registerTypeAdapter(Duration::class.java, DurationAdapter()) .registerTypeAdapter(ByteArray::class.java, ByteArrayAdapter()) .registerTypeAdapter(ByteString::class.java, ByteStringBase64Adapter()) + .registerTypeAdapter(RSAKey.Public::class.java, RSAKey.Public.GsonAdapter()) + .registerTypeAdapter(RSAKey.Private::class.java, RSAKey.Private.GsonAdapter()) .create() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/worker/WorkerBinder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/worker/WorkerBinder.kt index b877317c4215654c733750ac38793022a4f3b563..a80ff22e3751062ae654584672de2d5975996fe7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/worker/WorkerBinder.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/worker/WorkerBinder.kt @@ -7,6 +7,8 @@ import dagger.multibindings.IntoMap import de.rki.coronawarnapp.contactdiary.retention.ContactDiaryRetentionWorker import de.rki.coronawarnapp.coronatest.type.pcr.execution.PCRResultRetrievalWorker import de.rki.coronawarnapp.coronatest.type.rapidantigen.execution.RAResultRetrievalWorker +import de.rki.coronawarnapp.covidcertificate.test.core.execution.TestCertificateRetrievalWorker +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker.VaccinationUpdateWorker import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsPeriodicWorker import de.rki.coronawarnapp.deadman.DeadmanNotificationOneTimeWorker import de.rki.coronawarnapp.deadman.DeadmanNotificationPeriodicWorker @@ -18,7 +20,6 @@ import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOutW import de.rki.coronawarnapp.presencetracing.risk.execution.PresenceTracingWarningWorker import de.rki.coronawarnapp.presencetracing.storage.retention.TraceLocationDbCleanUpPeriodicWorker import de.rki.coronawarnapp.submission.auto.SubmissionWorker -import de.rki.coronawarnapp.vaccination.core.execution.worker.VaccinationUpdateWorker @Module abstract class WorkerBinder { @@ -127,4 +128,11 @@ abstract class WorkerBinder { abstract fun vaccinationUpdateWorker( factory: VaccinationUpdateWorker.Factory ): InjectedWorkerFactory<out ListenableWorker> + + @Binds + @IntoMap + @WorkerKey(TestCertificateRetrievalWorker::class) + abstract fun testCertificateRetrievalWorker( + factory: TestCertificateRetrievalWorker.Factory + ): InjectedWorkerFactory<out ListenableWorker> } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationCertificate.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationCertificate.kt deleted file mode 100644 index 92e96ea0bc9693463b15072b973bc6ab0e940e2d..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinationCertificate.kt +++ /dev/null @@ -1,32 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core - -import de.rki.coronawarnapp.vaccination.core.qrcode.QrCodeString -import org.joda.time.Instant -import org.joda.time.LocalDate - -interface VaccinationCertificate { - val firstName: String? - val lastName: String - - val dateOfBirth: LocalDate - val vaccinatedAt: LocalDate - - val vaccineTypeName: String - val vaccineManufacturer: String - val medicalProductName: String - - val doseNumber: Int - val totalSeriesOfDoses: Int - - val certificateIssuer: String - val certificateCountry: String - val certificateId: String - - val personIdentifier: VaccinatedPersonIdentifier - - val issuer: String - val issuedAt: Instant - val expiresAt: Instant - - val vaccinationQrCodeString: QrCodeString -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/CoseCertificateHeader.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/CoseCertificateHeader.kt deleted file mode 100644 index 2818cf35b5693f525a5acbf55cf977ff4b202556..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/CoseCertificateHeader.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -import org.joda.time.Instant - -interface CoseCertificateHeader { - val issuer: String - val issuedAt: Instant - val expiresAt: Instant -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateCOSEDecoder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateCOSEDecoder.kt deleted file mode 100644 index fbb291dcd3dee0f8be7c67f9d11813b453d6727b..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateCOSEDecoder.kt +++ /dev/null @@ -1,31 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -import com.upokecenter.cbor.CBORObject -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_COSE_MESSAGE_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_COSE_TAG_INVALID -import timber.log.Timber -import javax.inject.Inject - -class HealthCertificateCOSEDecoder @Inject constructor() { - - fun decode(input: RawCOSEObject): CBORObject = try { - val messageObject = CBORObject.DecodeFromBytes(input).validate() - val content = messageObject[2].GetByteString() - CBORObject.DecodeFromBytes(content) - } catch (e: InvalidHealthCertificateException) { - throw e - } catch (e: Throwable) { - Timber.e(e) - throw InvalidHealthCertificateException(HC_COSE_MESSAGE_INVALID) - } - - private fun CBORObject.validate(): CBORObject { - if (size() != 4) { - throw InvalidHealthCertificateException(HC_COSE_MESSAGE_INVALID) - } - if (!HasTag(18)) { - throw InvalidHealthCertificateException(HC_COSE_TAG_INVALID) - } - return this - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeader.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeader.kt deleted file mode 100644 index 87271fc061c37e54f01a9cb6a4750b57df5ed631..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/HealthCertificateHeader.kt +++ /dev/null @@ -1,9 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -import org.joda.time.Instant - -data class HealthCertificateHeader( - override val issuer: String, - override val issuedAt: Instant, - override val expiresAt: Instant, -) : CoseCertificateHeader diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/InvalidHealthCertificateException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/InvalidHealthCertificateException.kt deleted file mode 100644 index 700ab2ddce25b728253bd2d58d46cd0ea9e795c6..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/InvalidHealthCertificateException.kt +++ /dev/null @@ -1,95 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -import android.content.Context -import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.coronatest.qrcode.InvalidQRCodeException -import de.rki.coronawarnapp.util.HasHumanReadableError -import de.rki.coronawarnapp.util.HumanReadableError -import de.rki.coronawarnapp.util.ui.CachedString -import de.rki.coronawarnapp.util.ui.LazyString -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_COSE_MESSAGE_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_COSE_TAG_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_ALREADY_REGISTERED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_DOB_MISMATCH -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_DGC -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_EXP -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_HCERT -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_ISS -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_JSON_SCHEMA_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_NAME_MISMATCH -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_NO_VACCINATION_ENTRY -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_PREFIX_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_STORING_FAILED - -class InvalidHealthCertificateException( - val errorCode: ErrorCode -) : HasHumanReadableError, InvalidQRCodeException(errorCode.message) { - enum class ErrorCode( - val message: String - ) { - HC_BASE45_DECODING_FAILED("Base45 decoding failed."), - HC_ZLIB_DECOMPRESSION_FAILED("Zlib decompression failed."), - HC_COSE_TAG_INVALID("COSE tag invalid."), - HC_COSE_MESSAGE_INVALID("COSE message invalid."), - HC_CBOR_DECODING_FAILED("CBOR decoding failed."), - VC_NO_VACCINATION_ENTRY("Vaccination certificate missing."), - VC_PREFIX_INVALID("Prefix invalid."), - VC_STORING_FAILED("Storing failed."), - VC_JSON_SCHEMA_INVALID("Json schema invalid."), - VC_NAME_MISMATCH("Name does not match."), - VC_ALREADY_REGISTERED("Certificate already registered."), - VC_DOB_MISMATCH("Date of birth does not match."), - VC_HC_CWT_NO_DGC("Dgc missing."), - VC_HC_CWT_NO_EXP("Expiration date missing."), - VC_HC_CWT_NO_HCERT("Health certificate missing."), - VC_HC_CWT_NO_ISS("Issuer missing."), - } - - val errorMessage: LazyString - get() = when (errorCode) { - HC_BASE45_DECODING_FAILED, - HC_CBOR_DECODING_FAILED, - HC_COSE_MESSAGE_INVALID, - HC_ZLIB_DECOMPRESSION_FAILED, - HC_COSE_TAG_INVALID, - VC_PREFIX_INVALID, - VC_HC_CWT_NO_DGC, - VC_HC_CWT_NO_EXP, - VC_HC_CWT_NO_HCERT, - VC_HC_CWT_NO_ISS, - VC_JSON_SCHEMA_INVALID, - -> CachedString { context -> - context.getString(ERROR_MESSAGE_VC_INVALID) - } - VC_NO_VACCINATION_ENTRY -> CachedString { context -> - context.getString(ERROR_MESSAGE_VC_NOT_YET_SUPPORTED) - } - VC_STORING_FAILED -> CachedString { context -> - context.getString(ERROR_MESSAGE_VC_SCAN_AGAIN) - } - VC_NAME_MISMATCH, VC_DOB_MISMATCH -> CachedString { context -> - context.getString(ERROR_MESSAGE_VC_DIFFERENT_PERSON) - } - VC_ALREADY_REGISTERED -> CachedString { context -> - context.getString(ERROR_MESSAGE_ALREADY_REGISTERED) - } - } - - override fun toHumanReadableError(context: Context): HumanReadableError { - var errorCodeString = errorCode.toString() - errorCodeString = if (errorCodeString.startsWith(PREFIX)) errorCodeString else PREFIX + errorCodeString - return HumanReadableError( - description = errorMessage.get(context) + "\n\n$errorCodeString" - ) - } -} - -private const val PREFIX = "VC_" -private const val ERROR_MESSAGE_VC_INVALID = R.string.error_vc_invalid -private const val ERROR_MESSAGE_VC_NOT_YET_SUPPORTED = R.string.error_vc_not_yet_supported -private const val ERROR_MESSAGE_VC_SCAN_AGAIN = R.string.error_vc_scan_again -private const val ERROR_MESSAGE_VC_DIFFERENT_PERSON = R.string.error_vc_different_person -private const val ERROR_MESSAGE_ALREADY_REGISTERED = R.string.error_vc_already_registered diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/RawCOSEObject.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/RawCOSEObject.kt deleted file mode 100644 index 0a13bb0971b82d0456b0539469d6e0e63a16d690..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/RawCOSEObject.kt +++ /dev/null @@ -1,3 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -typealias RawCOSEObject = ByteArray diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1Parser.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1Parser.kt deleted file mode 100644 index e25defbc9cab81c1be8c37c0f6aba41716b14f75..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/certificate/VaccinationDGCV1Parser.kt +++ /dev/null @@ -1,57 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.certificate - -import com.google.gson.Gson -import com.upokecenter.cbor.CBORObject -import dagger.Reusable -import de.rki.coronawarnapp.util.serialization.BaseGson -import de.rki.coronawarnapp.util.serialization.fromJson -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_CBOR_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_DGC -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_HCERT -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_JSON_SCHEMA_INVALID -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_NO_VACCINATION_ENTRY -import javax.inject.Inject - -@Reusable -class VaccinationDGCV1Parser @Inject constructor( - @BaseGson private val gson: Gson -) { - - fun parse(map: CBORObject): VaccinationDGCV1 = try { - val certificate: VaccinationDGCV1 = map[keyHCert]?.run { - this[keyEuDgcV1]?.run { - toCertificate() - } ?: throw InvalidHealthCertificateException(VC_HC_CWT_NO_DGC) - } ?: throw InvalidHealthCertificateException(VC_HC_CWT_NO_HCERT) - - certificate.validate() - } catch (e: InvalidHealthCertificateException) { - throw e - } catch (e: Throwable) { - throw InvalidHealthCertificateException(HC_CBOR_DECODING_FAILED) - } - - private fun VaccinationDGCV1.validate(): VaccinationDGCV1 { - if (vaccinationDatas.isEmpty()) { - throw InvalidHealthCertificateException(VC_NO_VACCINATION_ENTRY) - } - // Force date parsing - dateOfBirth - vaccinationDatas.forEach { - it.vaccinatedAt - } - return this - } - - private fun CBORObject.toCertificate() = try { - val json = ToJSONString() - gson.fromJson<VaccinationDGCV1>(json) - } catch (e: Throwable) { - throw InvalidHealthCertificateException(VC_JSON_SCHEMA_INVALID) - } - - companion object { - private val keyEuDgcV1 = CBORObject.FromObject(1) - private val keyHCert = CBORObject.FromObject(-260) - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateData.kt deleted file mode 100644 index 31da1c88bc2ad193a46614debcbcaea02feae7a6..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateData.kt +++ /dev/null @@ -1,12 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.qrcode - -import de.rki.coronawarnapp.vaccination.core.certificate.CoseCertificateHeader -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 - -/** - * Represents the parsed data from the QR code - */ -data class VaccinationCertificateData( - val header: CoseCertificateHeader, - val certificate: VaccinationDGCV1, -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateQRCode.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateQRCode.kt deleted file mode 100644 index 5d72448a8a526c2954b15ffb67fa244fb20768b5..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationCertificateQRCode.kt +++ /dev/null @@ -1,11 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.qrcode - -data class VaccinationCertificateQRCode( - val qrCodeString: QrCodeString, - val parsedData: VaccinationCertificateData, -) { - val uniqueCertificateIdentifier: String - get() = parsedData.certificate.vaccinationDatas.single().uniqueCertificateIdentifier -} - -typealias QrCodeString = String diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt deleted file mode 100644 index ae3065df399be47ee02e1d6fe5650ad3ea51b2a0..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractor.kt +++ /dev/null @@ -1,74 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.qrcode - -import de.rki.coronawarnapp.bugreporting.censors.vaccination.CertificateQrCodeCensor -import de.rki.coronawarnapp.coronatest.qrcode.QrCodeExtractor -import de.rki.coronawarnapp.util.compression.inflate -import de.rki.coronawarnapp.util.encoding.Base45Decoder -import de.rki.coronawarnapp.vaccination.core.certificate.HealthCertificateCOSEDecoder -import de.rki.coronawarnapp.vaccination.core.certificate.HealthCertificateHeaderParser -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.RawCOSEObject -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1Parser -import timber.log.Timber -import javax.inject.Inject - -class VaccinationQRCodeExtractor @Inject constructor( - private val coseDecoder: HealthCertificateCOSEDecoder, - private val headerParser: HealthCertificateHeaderParser, - private val bodyParser: VaccinationDGCV1Parser, -) : QrCodeExtractor<VaccinationCertificateQRCode> { - - override fun canHandle(rawString: String): Boolean = rawString.startsWith(PREFIX) - - override fun extract(rawString: String): VaccinationCertificateQRCode { - CertificateQrCodeCensor.addQRCodeStringToCensor(rawString) - - val parsedData = rawString - .removePrefix(PREFIX) - .decodeBase45() - .decompress() - .parse() - - return VaccinationCertificateQRCode( - parsedData = parsedData, - qrCodeString = rawString, - ) - } - - private fun String.decodeBase45(): ByteArray = try { - Base45Decoder.decode(this) - } catch (e: Throwable) { - Timber.e(e) - throw InvalidHealthCertificateException(HC_BASE45_DECODING_FAILED) - } - - private fun ByteArray.decompress(): RawCOSEObject = try { - this.inflate(sizeLimit = DEFAULT_SIZE_LIMIT) - } catch (e: Throwable) { - Timber.e(e) - throw InvalidHealthCertificateException(HC_ZLIB_DECOMPRESSION_FAILED) - } - - fun RawCOSEObject.parse(): VaccinationCertificateData { - Timber.v("Parsing COSE for vaccination certificate.") - val cbor = coseDecoder.decode(this) - - return VaccinationCertificateData( - header = headerParser.parse(cbor), - certificate = bodyParser.parse(cbor) - ).also { - CertificateQrCodeCensor.addCertificateToCensor(it) - }.also { - Timber.v("Parsed vaccination certificate for %s", it.certificate.nameData.familyNameStandardized) - } - } - - companion object { - private const val PREFIX = "HC1:" - - // Zip bomb - private const val DEFAULT_SIZE_LIMIT = 1024L * 1024 * 10L // 10 MB - } -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt deleted file mode 100644 index 3c51b6df3aff83058514b6825a8497d602190d8e..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepository.kt +++ /dev/null @@ -1,90 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository - -import dagger.Reusable -import de.rki.coronawarnapp.util.coroutine.AppScope -import de.rki.coronawarnapp.util.coroutine.DispatcherProvider -import de.rki.coronawarnapp.util.flow.HotDataFlow -import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.emptyVaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.isEmpty -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.distinctUntilChangedBy -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.plus -import timber.log.Timber -import java.util.Locale -import javax.inject.Inject - -@Reusable -class ValueSetsRepository @Inject constructor( - private val vaccinationServer: VaccinationServer, - private val valueSetsStorage: ValueSetsStorage, - @AppScope private val scope: CoroutineScope, - dispatcherProvider: DispatcherProvider -) { - - private fun Flow<VaccinationValueSet>.distinctUntilChangedByHash() = distinctUntilChangedBy { it.hashCode() } - - private val internalData: HotDataFlow<VaccinationValueSet> = HotDataFlow( - loggingTag = TAG, - scope = scope, - coroutineContext = dispatcherProvider.IO, - sharingBehavior = SharingStarted.Lazily, - startValueProvider = { - valueSetsStorage.vaccinationValueSet.also { Timber.v("Loaded initial value set %s", it) } - } - ) - - init { - internalData.data - .distinctUntilChangedByHash() - .onStart { Timber.d("Observing value set") } - .onEach { valueSetsStorage.vaccinationValueSet = it } - .catch { Timber.e(it, "Storing new value set failed.") } - .launchIn(scope + dispatcherProvider.IO) - } - - val latestValueSet: Flow<VaccinationValueSet> = internalData.data.distinctUntilChangedByHash() - - fun triggerUpdateValueSet(languageCode: Locale) { - Timber.d("triggerUpdateValueSet(languageCode=%s)", languageCode) - internalData.updateAsync( - onUpdate = { getValueSetFromServer(languageCode = languageCode) ?: this }, - onError = { Timber.e(it, "Updating value set failed") } - ) - } - - private suspend fun getValueSetFromServer(languageCode: Locale): VaccinationValueSet? { - Timber.v("getValueSetFromServer(languageCode=%s)", languageCode) - var valueSet = vaccinationServer.getVaccinationValueSets(languageCode = languageCode) - - if (valueSet.isEmpty()) { - Timber.d( - "Got no value set from server for %s and local value set is empty... Try fallback to value set for en", - languageCode.language - ) - valueSet = vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) - } - - return valueSet - .also { Timber.v("New value set %s", it) } - } - - suspend fun clear() { - Timber.d("Clearing value sets") - vaccinationServer.clear() - internalData.updateBlocking { - Timber.v("Resetting value set to an empty value set") - emptyVaccinationValueSet - } - } -} - -private const val TAG = "ValueSetsRepository" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt deleted file mode 100644 index 34e54c6f088361b3679092e07e728ca63d970a28..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinatedPersonNotFoundException.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository.errors - -import de.rki.coronawarnapp.vaccination.core.VaccinationException - -class VaccinatedPersonNotFoundException( - message: String -) : VaccinationException( - message = message, - cause = null -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt deleted file mode 100644 index 2b3562f9093a0d15fe7e7c812d0de28f299ac056..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/errors/VaccinationCertificateNotFoundException.kt +++ /dev/null @@ -1,10 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository.errors - -import de.rki.coronawarnapp.vaccination.core.VaccinationException - -class VaccinationCertificateNotFoundException( - message: String -) : VaccinationException( - message = message, - cause = null -) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt deleted file mode 100644 index 63c2246e457b96b251dae2590186c4b5e108a214..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorage.kt +++ /dev/null @@ -1,97 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage - -import android.content.Context -import android.content.SharedPreferences -import androidx.annotation.Keep -import androidx.annotation.VisibleForTesting -import androidx.core.content.edit -import com.google.gson.Gson -import com.google.gson.annotations.SerializedName -import dagger.Reusable -import de.rki.coronawarnapp.util.di.AppContext -import de.rki.coronawarnapp.util.serialization.BaseGson -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.emptyVaccinationValueSet -import timber.log.Timber -import java.util.Locale -import javax.inject.Inject - -@Reusable -class ValueSetsStorage @Inject constructor( - @AppContext private val context: Context, - @BaseGson private val gson: Gson -) { - - private val prefs: SharedPreferences by lazy { - context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE) - } - - var vaccinationValueSet: VaccinationValueSet - get() = getValueSet() - set(value) = setValueSet(value) - - private fun getValueSet(): VaccinationValueSet { - Timber.v("Loading value set") - val valueSetString = prefs.getString(PKEY_VALUE_SETS_PREFIX, null) - return when (valueSetString != null) { - true -> gson.fromJson(valueSetString, StoredVaccinationValueSet::class.java) - else -> emptyVaccinationValueSet - }.also { loaded -> Timber.v("Loaded value set %s", loaded) } - } - - private fun setValueSet(value: VaccinationValueSet) { - Timber.v("Saving value set %s", value) - prefs.edit { - val json = gson.toJson(value.toStoredVaccinationValueSet(), StoredVaccinationValueSet::class.java) - Timber.v("Writing %s to prefs", json) - putString(PKEY_VALUE_SETS_PREFIX, json) - } - } - - @VisibleForTesting - fun VaccinationValueSet.toStoredVaccinationValueSet(): StoredVaccinationValueSet = - StoredVaccinationValueSet( - languageCode = languageCode, - vp = vp.toStoredValueSet(), - mp = mp.toStoredValueSet(), - ma = ma.toStoredValueSet() - ) - - @VisibleForTesting - fun VaccinationValueSet.ValueSet.toStoredValueSet(): StoredVaccinationValueSet.StoredValueSet = - StoredVaccinationValueSet.StoredValueSet( - items = items.map { it.toStoredItem() } - ) - - @VisibleForTesting - fun VaccinationValueSet.ValueSet.Item.toStoredItem(): - StoredVaccinationValueSet.StoredValueSet.StoredItem = - StoredVaccinationValueSet.StoredValueSet.StoredItem( - key = key, - displayText = displayText - ) - - @VisibleForTesting - data class StoredVaccinationValueSet( - @SerializedName("languageCode") override val languageCode: Locale, - @SerializedName("vp") override val vp: StoredValueSet, - @SerializedName("mp") override val mp: StoredValueSet, - @SerializedName("ma") override val ma: StoredValueSet - ) : VaccinationValueSet { - - @Keep - data class StoredValueSet( - @SerializedName("items") override val items: List<StoredItem> - ) : VaccinationValueSet.ValueSet { - - @Keep - data class StoredItem( - @SerializedName("key") override val key: String, - @SerializedName("displayText") override val displayText: String - ) : VaccinationValueSet.ValueSet.Item - } - } -} - -private const val PREF_NAME = "valuesets_localdata" -private const val PKEY_VALUE_SETS_PREFIX = "valueset" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/VaccinationServerModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/VaccinationServerModule.kt deleted file mode 100644 index 0b7411416d8d8bd90e5f36241c5f211f6c43ed6f..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/VaccinationServerModule.kt +++ /dev/null @@ -1,11 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.server - -import dagger.Module -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSetModule - -@Module( - includes = [ - VaccinationValueSetModule::class - ] -) -abstract class VaccinationServerModule diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt deleted file mode 100644 index c7635321913b9267d867927ad2359afb41f7968d..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/DefaultVaccinationValueSet.kt +++ /dev/null @@ -1,30 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset - -import java.util.Locale - -data class DefaultVaccinationValueSet( - override val languageCode: Locale, - override val vp: VaccinationValueSet.ValueSet, - override val mp: VaccinationValueSet.ValueSet, - override val ma: VaccinationValueSet.ValueSet -) : VaccinationValueSet { - - data class DefaultValueSet( - override val items: List<VaccinationValueSet.ValueSet.Item> - ) : VaccinationValueSet.ValueSet { - - data class DefaultItem( - override val key: String, - override val displayText: String - ) : VaccinationValueSet.ValueSet.Item - } -} - -internal val emptyVaccinationValueSet: VaccinationValueSet by lazy { - DefaultVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), - mp = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()), - ma = DefaultVaccinationValueSet.DefaultValueSet(items = emptyList()) - ) -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt deleted file mode 100644 index 23d5350fad4c4018dd2691784541febcb8ff42ad..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationValueSet.kt +++ /dev/null @@ -1,29 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset - -import java.util.Locale - -interface VaccinationValueSet { - val languageCode: Locale - val vp: ValueSet - val mp: ValueSet - val ma: ValueSet - - interface ValueSet { - val items: List<Item> - - // Use custom item instead of map to allow for future extensions - interface Item { - val key: String - val displayText: String - } - } -} - -fun VaccinationValueSet.getDisplayText(key: String): String? = - vp.getDisplayText(key) ?: mp.getDisplayText(key) ?: ma.getDisplayText(key) - -fun VaccinationValueSet.ValueSet.getDisplayText(key: String): String? = items.find { key == it.key }?.displayText - -fun VaccinationValueSet?.isEmpty(): Boolean = (this == null) || (vp.isEmpty() && mp.isEmpty() && ma.isEmpty()) - -fun VaccinationValueSet.ValueSet.isEmpty(): Boolean = items.isEmpty() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/VaccinationValueSetMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/VaccinationValueSetMapper.kt deleted file mode 100644 index ad13f40aace7a84a4d73ac99b0d5b836fa8a2185..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/server/valueset/internal/VaccinationValueSetMapper.kt +++ /dev/null @@ -1,28 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset.internal - -import de.rki.coronawarnapp.server.protocols.internal.dgc.ValueSetsOuterClass -import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import timber.log.Timber -import java.util.Locale - -internal fun ValueSetsOuterClass.ValueSets.toVaccinationValueSet(languageCode: Locale): VaccinationValueSet { - Timber.d("toVaccinationValueSet(valueSets=%s, languageCode=%s)", this, languageCode) - return DefaultVaccinationValueSet( - languageCode = languageCode, - vp = vp.toValueSet(), - mp = mp.toValueSet(), - ma = ma.toValueSet() - ).also { Timber.tag(TAG).d("Created %s", it) } -} - -internal fun ValueSetsOuterClass.ValueSet.toValueSet(): VaccinationValueSet.ValueSet = - DefaultVaccinationValueSet.DefaultValueSet(items = itemsList.map { it.toItem() }) - -internal fun ValueSetsOuterClass.ValueSetItem.toItem(): VaccinationValueSet.ValueSet.Item = - DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = key, - displayText = displayText - ) - -private const val TAG: String = "ValueSetMapper" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/VaccinationUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/VaccinationUIModule.kt deleted file mode 100644 index e01ea64a568da58ef96cea1812d585aec6ea2f9c..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/VaccinationUIModule.kt +++ /dev/null @@ -1,28 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui - -import dagger.Module -import dagger.android.ContributesAndroidInjector -import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment -import de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragmentModule -import de.rki.coronawarnapp.vaccination.ui.details.VaccinationDetailsFragment -import de.rki.coronawarnapp.vaccination.ui.details.VaccinationDetailsFragmentModule -import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragment -import de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragmentModule -import de.rki.coronawarnapp.vaccination.ui.scan.VaccinationQrCodeScanFragment -import de.rki.coronawarnapp.vaccination.ui.scan.VaccinationQrCodeScanModule - -@Module -abstract class VaccinationUIModule { - - @ContributesAndroidInjector(modules = [VaccinationListFragmentModule::class]) - abstract fun vaccinationListFragment(): VaccinationListFragment - - @ContributesAndroidInjector(modules = [VaccinationDetailsFragmentModule::class]) - abstract fun vaccinationDetailsFragment(): VaccinationDetailsFragment - - @ContributesAndroidInjector(modules = [VaccinationQrCodeScanModule::class]) - abstract fun vaccinationQrCodeScanFragment(): VaccinationQrCodeScanFragment - - @ContributesAndroidInjector(modules = [VaccinationConsentFragmentModule::class]) - abstract fun vaccinationConsentFragment(): VaccinationConsentFragment -} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationStatusItem.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationStatusItem.kt deleted file mode 100644 index 18a09e0e752c94ff893aeeefac0e0c26ab36643e..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/cards/VaccinationStatusItem.kt +++ /dev/null @@ -1,11 +0,0 @@ -package de.rki.coronawarnapp.vaccination.ui.cards - -import de.rki.coronawarnapp.greencertificate.ui.certificates.items.CertificatesItem -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson - -interface VaccinationStatusItem : CertificatesItem { - val vaccinatedPerson: VaccinatedPerson - - override val stableId: Long - get() = vaccinatedPerson.identifier.hashCode().toLong() -} diff --git a/Corona-Warn-App/src/main/res/drawable/ic_complete_vaccination_shield.xml b/Corona-Warn-App/src/main/res/drawable/ic_complete_vaccination_shield.xml new file mode 100644 index 0000000000000000000000000000000000000000..d37d51142b63f2025174d5a1b3e0392a1f76e606 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_complete_vaccination_shield.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="111dp" + android:height="189dp" + android:viewportWidth="111" + android:viewportHeight="189"> + <path + android:pathData="M75.5,0.75L0.5,28.875V85.969C0.5,133.312 32.469,177.469 75.5,188.25C118.531,177.469 150.5,133.312 150.5,85.969V28.875L75.5,0.75ZM131.75,85.969C131.75,123.469 107.844,158.156 75.5,168.75C43.156,158.156 19.25,123.562 19.25,85.969V41.156L75.5,21.281L131.75,41.156V85.969ZM45.688,81.281L32.375,94.5L65.563,127.688L118.625,74.625L105.406,61.406L65.656,101.156L45.688,81.281Z" + android:fillColor="#007FAD"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_eu_stars.xml b/Corona-Warn-App/src/main/res/drawable/ic_eu_stars.xml new file mode 100644 index 0000000000000000000000000000000000000000..04d246e2d1f25ee0486bbe9181a6b2b7319a6a32 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_eu_stars.xml @@ -0,0 +1,76 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="105dp" + android:height="200dp" + android:viewportWidth="105" + android:viewportHeight="200"> + <path + android:pathData="M112.879,-9L110.99,-3.328H104.902L109.94,0.453L108.051,6.125L112.879,2.554L117.918,6.125L116.028,0.243L120.857,-3.118H114.769L112.879,-9Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M113.356,-9.153L112.888,-10.608L112.405,-9.158L110.63,-3.828H104.902H103.403L104.602,-2.928L109.352,0.637L107.577,5.967L107.077,7.467L108.348,6.527L112.886,3.171L117.629,6.533L118.853,7.4L118.394,5.972L116.618,0.442L121.143,-2.708L122.45,-3.618H120.857H115.133L113.356,-9.153Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M62.496,4.654L60.607,10.326H54.519L59.557,14.107L57.668,19.779L62.496,16.208L67.325,19.779L65.435,13.897L70.474,10.536H64.386L62.496,4.654Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M62.972,4.501L62.505,3.046L62.022,4.496L60.246,9.826H54.519H53.02L54.219,10.726L58.969,14.291L57.193,19.621L56.694,21.122L57.965,20.181L62.496,16.83L67.027,20.181L68.275,21.104L67.801,19.626L66.027,14.104L70.751,10.952L72.124,10.036H70.474H64.75L62.972,4.501Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M25.758,41.626L23.869,47.508H17.781L22.819,51.079L20.93,56.751L25.758,53.18L30.587,56.751L28.697,51.079L33.736,47.508H27.647L25.758,41.626Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M26.234,41.474L25.758,39.992L25.282,41.474L23.504,47.008H17.781H16.211L17.492,47.916L22.228,51.273L20.455,56.593L19.955,58.094L21.227,57.153L25.758,53.802L30.289,57.153L31.561,58.094L31.061,56.593L29.289,51.273L34.025,47.916L35.306,47.008H33.736H28.012L26.234,41.474Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M11.903,92.043L10.013,97.714H3.925L8.754,101.285L6.864,107.167L11.903,103.596L16.731,107.167L14.842,101.285L19.67,97.924H13.792L11.903,92.043Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M12.379,91.89L11.911,90.435L11.428,91.884L9.653,97.214H3.925H2.408L3.628,98.116L8.168,101.474L6.388,107.014L5.929,108.443L7.153,107.575L11.896,104.213L16.434,107.569L17.682,108.492L17.207,107.014L15.431,101.485L19.956,98.335L21.264,97.424H19.67H14.157L12.379,91.89Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M25.548,142.669L23.659,148.341H17.571L22.399,151.912L20.51,157.584L25.548,154.013L30.377,157.584L28.487,151.912L33.316,148.341H27.438L25.548,142.669Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M26.023,142.511L25.548,141.087L25.074,142.511L23.298,147.841H17.571H16.054L17.274,148.743L21.81,152.098L20.035,157.426L19.553,158.875L20.799,157.992L25.542,154.63L30.079,157.986L31.351,158.926L30.851,157.426L29.076,152.098L33.613,148.743L34.833,147.841H33.316H27.798L26.023,142.511Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M62.496,179.641L60.607,185.313H54.519L59.347,188.884L57.458,194.766L62.496,191.195L67.325,194.766L65.435,188.884L70.264,185.313H64.386L62.496,179.641Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M62.971,179.483L62.496,178.059L62.022,179.483L60.246,184.813H54.519H53.002L54.222,185.715L58.762,189.073L56.982,194.613L56.523,196.042L57.747,195.174L62.49,191.812L67.027,195.168L68.275,196.091L67.801,194.613L66.021,189.073L70.561,185.715L71.781,184.813H70.264H64.746L62.971,179.483Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> + <path + android:pathData="M112.879,193.085L110.99,198.757H104.902L109.94,202.328L108.051,208L112.879,204.429L117.918,208L116.028,202.328L120.857,198.757H114.769L112.879,193.085Z" + android:fillColor="#ffffff" + android:fillAlpha="0.2"/> + <path + android:strokeWidth="1" + android:pathData="M113.354,192.927L112.879,191.503L112.405,192.927L110.63,198.257H104.902H103.332L104.613,199.165L109.349,202.522L107.577,207.842L107.077,209.342L108.348,208.402L112.886,205.046L117.629,208.408L118.875,209.291L118.392,207.842L116.617,202.514L121.154,199.159L122.374,198.257H120.857H115.129L113.354,192.927Z" + android:strokeAlpha="0.2" + android:fillColor="#00000000" + android:strokeColor="#ffffff"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_half_white_half_gray_shield.xml b/Corona-Warn-App/src/main/res/drawable/ic_half_white_half_gray_shield.xml new file mode 100644 index 0000000000000000000000000000000000000000..a3cf8929fe0eac92776df2631481582e95bdaf91 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_half_white_half_gray_shield.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="18dp" + android:height="22dp" + android:viewportWidth="18" + android:viewportHeight="22"> + <path + android:pathData="M7.1729,20.9509V2.4925V1.4556L16.5144,4.7739V11.8255L13.9897,17.6326L7.1729,20.9509Z" + android:fillColor="#616F7E"/> + <path + android:pathData="M9.0002,0L0.2002,3.3V9.999C0.2002,15.554 3.9512,20.735 9.0002,22C14.0492,20.735 17.8002,15.554 17.8002,9.999V3.3L9.0002,0ZM15.6002,9.999C15.6002,14.399 12.7952,18.469 9.0002,19.712C5.2052,18.469 2.4002,14.41 2.4002,9.999V4.829L9.0002,2.354L15.6002,4.829V9.999Z" + android:fillColor="#ffffff"/> + <path + android:pathData="M9.0004,20.3417V2.2678V1.2524L1.4866,4.5017V11.4063L3.5173,17.0924L9.0004,20.3417Z" + android:fillColor="#ffffff"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_incomplete_vaccination_shield.xml b/Corona-Warn-App/src/main/res/drawable/ic_incomplete_vaccination_shield.xml new file mode 100644 index 0000000000000000000000000000000000000000..91692016cc895dee0ce5aff3c8d2918290074721 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_incomplete_vaccination_shield.xml @@ -0,0 +1,14 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="111dp" + android:height="188dp" + android:viewportWidth="111" + android:viewportHeight="188"> + <path + android:pathData="M75.7,0.75L0.8,28.837V85.855C0.8,133.136 32.726,177.233 75.7,188C118.674,177.233 150.6,133.136 150.6,85.855V28.837L75.7,0.75ZM131.875,85.855C131.875,123.305 108,157.946 75.7,168.526C43.399,157.946 19.525,123.399 19.525,85.855V41.851L75.7,20.786L131.875,41.851V85.855Z" + android:fillColor="#ffffff" + android:fillAlpha="0.05"/> + <path + android:pathData="M75.7,168.526V20.786L19.525,41.851V85.855C19.525,85.855 18,113 37.049,139.5C53.698,162.66 75.7,168.526 75.7,168.526Z" + android:fillColor="#ffffff" + android:fillAlpha="0.05"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_white_shield_with_checkmark.xml b/Corona-Warn-App/src/main/res/drawable/ic_white_shield_with_checkmark.xml new file mode 100644 index 0000000000000000000000000000000000000000..d6e0f2ac23a1d0171efc9810f6a5e55e0a2c831e --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_white_shield_with_checkmark.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="18dp" + android:height="22dp" + android:viewportWidth="18" + android:viewportHeight="22"> + <path + android:pathData="M9,0L0,4V10C0,15.55 3.84,20.74 9,22C14.16,20.74 18,15.55 18,10V4L9,0Z" + android:fillColor="#ffffff"/> + <path + android:pathData="m5.141,10.1508 l-1.41,1.4968 4,4.2463 8,-8.4926 -1.41,-1.5074 -6.59,6.9958z" + android:fillColor="#007fad"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_complete.xml b/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_complete.xml deleted file mode 100644 index 29d4300db7f6465d9bbf75efd976043b3d8d1414..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_complete.xml +++ /dev/null @@ -1,17 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="78dp" - android:height="72dp" - android:viewportWidth="78" - android:viewportHeight="72"> - <path - android:pathData="M44.2499,0V4.3846H46.4422V6.7139C42.0319,7.1507 38.0498,8.8463 34.7956,11.5096L33.1513,9.8654L34.6586,8.3582L31.5756,5.2752L25.4098,11.4411L28.4927,14.524L29.9999,13.0168L31.6441,14.6611C28.9808,17.9153 27.2852,21.8974 26.8485,26.3077H24.5191V24.1154H20.1345V32.8846H24.5191V30.6923H26.8485C27.2852,35.1369 29.0151,39.1447 31.7126,42.4075L29.9999,43.9832L28.4927,42.476L25.4098,45.5589L31.5756,51.7248L34.6586,48.6418L33.1513,47.1346L34.7956,45.4219C38.0583,48.1023 42.0233,49.8493 46.4422,50.2861V52.6154H44.2499V57H53.0191V52.6154H50.8268V50.2861C55.2457,49.8493 59.2107,48.1023 62.4735,45.4219H62.542L64.1177,47.1346L62.6105,48.6418L65.6934,51.7248L71.8593,45.5589L68.7763,42.476L67.2691,43.9832L65.5564,42.4075C65.5607,42.3989 65.5585,42.386 65.5564,42.3732C65.5543,42.3604 65.5521,42.3475 65.5564,42.3389C68.2368,39.0762 69.9838,35.1112 70.4206,30.6923H72.7499V32.8846H77.1345V24.1154H72.7499V26.3077H70.4206C69.9838,21.8974 68.2882,17.9153 65.6249,14.6611L67.2691,13.0168L68.7763,14.524L71.8593,11.4411L65.6934,5.2752L62.6105,8.3582L64.1177,9.8654L62.4735,11.5096C59.2193,8.8463 55.2371,7.1507 50.8268,6.7139V4.3846H53.0191V0H44.2499ZM48.635,10.9604C58.3462,10.9604 66.1734,18.7876 66.1734,28.4989C66.1734,38.2101 58.3462,46.0373 48.635,46.0373C38.9237,46.0373 31.0965,38.2101 31.0965,28.4989C31.0965,18.7876 38.9237,10.9604 48.635,10.9604ZM39.865,18.9777C39.865,17.1622 41.338,15.6893 43.1535,15.6893C44.969,15.6893 46.4419,17.1622 46.4419,18.9777C46.4419,20.7933 44.969,22.2662 43.1535,22.2662C41.338,22.2662 39.865,20.7933 39.865,18.9777ZM54.1155,15.6893C52.3,15.6893 50.827,17.1622 50.827,18.9777C50.827,20.7933 52.3,22.2662 54.1155,22.2662C55.931,22.2662 57.4039,20.7933 57.4039,18.9777C57.4039,17.1622 55.931,15.6893 54.1155,15.6893ZM34.3846,28.499C34.3846,26.6834 35.8575,25.2105 37.673,25.2105C39.4886,25.2105 40.9615,26.6834 40.9615,28.499C40.9615,30.3145 39.4886,31.7874 37.673,31.7874C35.8575,31.7874 34.3846,30.3145 34.3846,28.499ZM48.635,25.2105C46.8195,25.2105 45.3466,26.6834 45.3466,28.499C45.3466,30.3145 46.8195,31.7874 48.635,31.7874C50.4505,31.7874 51.9235,30.3145 51.9235,28.499C51.9235,26.6834 50.4505,25.2105 48.635,25.2105ZM56.3072,28.499C56.3072,26.6834 57.7802,25.2105 59.5957,25.2105C61.4112,25.2105 62.8841,26.6834 62.8841,28.499C62.8841,30.3145 61.4112,31.7874 59.5957,31.7874C57.7802,31.7874 56.3072,30.3145 56.3072,28.499ZM43.1535,34.7339C41.338,34.7339 39.865,36.2069 39.865,38.0224C39.865,39.8379 41.338,41.3108 43.1535,41.3108C44.969,41.3108 46.4419,39.8379 46.4419,38.0224C46.4419,36.2069 44.969,34.7339 43.1535,34.7339ZM50.827,38.0224C50.827,36.2069 52.3,34.7339 54.1155,34.7339C55.931,34.7339 57.4039,36.2069 57.4039,38.0224C57.4039,39.8379 55.931,41.3108 54.1155,41.3108C52.3,41.3108 50.827,39.8379 50.827,38.0224Z" - android:fillColor="#ffffff" - android:fillAlpha="0.2" - android:fillType="evenOdd"/> - <path - android:pathData="M22,18L0,27.8182V42.5455C0,56.1682 9.3867,68.9073 22,72C34.6133,68.9073 44,56.1682 44,42.5455V27.8182L22,18Z" - android:fillColor="#ffffff"/> - <path - android:pathData="M11.29,43.7909L8,47.2335L17.3333,57L36,37.4671L32.71,34L17.3333,50.0902L11.29,43.7909Z" - android:fillColor="#616F7E"/> -</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_immune.xml b/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_immune.xml deleted file mode 100644 index 47227abb719d617b587e8270164fbde8a3e9aaf8..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_immune.xml +++ /dev/null @@ -1,17 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="78dp" - android:height="72dp" - android:viewportWidth="78" - android:viewportHeight="72"> - <path - android:pathData="M44.2502,0V4.3846H46.4425V6.7139C42.0321,7.1507 38.05,8.8463 34.7958,11.5096L33.1516,9.8654L34.6588,8.3582L31.5759,5.2752L25.41,11.4411L28.4929,14.524L30.0001,13.0168L31.6444,14.6611C28.9811,17.9153 27.2855,21.8974 26.8487,26.3077H24.5194V24.1154H20.1348V32.8846H24.5194V30.6923H26.8487C27.2855,35.1369 29.0153,39.1447 31.7129,42.4075L30.0001,43.9832L28.4929,42.476L25.41,45.5589L31.5759,51.7248L34.6588,48.6418L33.1516,47.1346L34.7958,45.4219C38.0586,48.1023 42.0236,49.8493 46.4425,50.2861V52.6154H44.2502V57H53.0194V52.6154H50.8271V50.2861C55.2459,49.8493 59.2109,48.1023 62.4737,45.4219H62.5422L64.1179,47.1346L62.6107,48.6418L65.6937,51.7248L71.8595,45.5589L68.7766,42.476L67.2694,43.9832L65.5566,42.4075C65.5609,42.3989 65.5588,42.386 65.5566,42.3732C65.5545,42.3604 65.5524,42.3475 65.5566,42.3389C68.2371,39.0762 69.9841,35.1112 70.4208,30.6923H72.7501V32.8846H77.1348V24.1154H72.7501V26.3077H70.4208C69.9841,21.8974 68.2885,17.9153 65.6251,14.6611L67.2694,13.0168L68.7766,14.524L71.8595,11.4411L65.6937,5.2752L62.6107,8.3582L64.1179,9.8654L62.4737,11.5096C59.2195,8.8463 55.2374,7.1507 50.8271,6.7139V4.3846H53.0194V0H44.2502ZM48.6352,10.9604C58.3465,10.9604 66.1737,18.7876 66.1737,28.4989C66.1737,38.2101 58.3465,46.0373 48.6352,46.0373C38.924,46.0373 31.0968,38.2101 31.0968,28.4989C31.0968,18.7876 38.924,10.9604 48.6352,10.9604ZM39.8653,18.9777C39.8653,17.1622 41.3382,15.6893 43.1537,15.6893C44.9692,15.6893 46.4422,17.1622 46.4422,18.9777C46.4422,20.7933 44.9692,22.2662 43.1537,22.2662C41.3382,22.2662 39.8653,20.7933 39.8653,18.9777ZM54.1157,15.6893C52.3002,15.6893 50.8273,17.1622 50.8273,18.9777C50.8273,20.7933 52.3002,22.2662 54.1157,22.2662C55.9312,22.2662 57.4042,20.7933 57.4042,18.9777C57.4042,17.1622 55.9312,15.6893 54.1157,15.6893ZM34.3848,28.499C34.3848,26.6834 35.8578,25.2105 37.6733,25.2105C39.4888,25.2105 40.9618,26.6834 40.9618,28.499C40.9618,30.3145 39.4888,31.7874 37.6733,31.7874C35.8578,31.7874 34.3848,30.3145 34.3848,28.499ZM48.6353,25.2105C46.8198,25.2105 45.3468,26.6834 45.3468,28.499C45.3468,30.3145 46.8198,31.7874 48.6353,31.7874C50.4508,31.7874 51.9237,30.3145 51.9237,28.499C51.9237,26.6834 50.4508,25.2105 48.6353,25.2105ZM56.3075,28.499C56.3075,26.6834 57.7804,25.2105 59.5959,25.2105C61.4114,25.2105 62.8844,26.6834 62.8844,28.499C62.8844,30.3145 61.4114,31.7874 59.5959,31.7874C57.7804,31.7874 56.3075,30.3145 56.3075,28.499ZM43.1537,34.7339C41.3382,34.7339 39.8653,36.2069 39.8653,38.0224C39.8653,39.8379 41.3382,41.3108 43.1537,41.3108C44.9692,41.3108 46.4422,39.8379 46.4422,38.0224C46.4422,36.2069 44.9692,34.7339 43.1537,34.7339ZM50.8273,38.0224C50.8273,36.2069 52.3002,34.7339 54.1157,34.7339C55.9312,34.7339 57.4042,36.2069 57.4042,38.0224C57.4042,39.8379 55.9312,41.3108 54.1157,41.3108C52.3002,41.3108 50.8273,39.8379 50.8273,38.0224Z" - android:fillColor="#ffffff" - android:fillAlpha="0.2" - android:fillType="evenOdd" /> - <path - android:pathData="M22,18L0,27.8182V42.5455C0,56.1682 9.3867,68.9073 22,72C34.6133,68.9073 44,56.1682 44,42.5455V27.8182L22,18Z" - android:fillColor="#ffffff" /> - <path - android:pathData="M11.29,43.7909L8,47.2335L17.3333,57L36,37.4671L32.71,34L17.3333,50.0902L11.29,43.7909Z" - android:fillColor="#007FAD" /> -</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_incomplete.xml b/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_incomplete.xml deleted file mode 100644 index f51cc4182dc3e363ea51e1759488a01cf48f9831..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/res/drawable/vaccination_card_icon_incomplete.xml +++ /dev/null @@ -1,20 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="78dp" - android:height="73dp" - android:viewportWidth="78" - android:viewportHeight="73"> - <path - android:pathData="M44.4167,0V4.3846H46.609V6.7139C42.1987,7.1507 38.2165,8.8463 34.9623,11.5096L33.3181,9.8654L34.8253,8.3582L31.7424,5.2752L25.5765,11.4411L28.6594,14.524L30.1667,13.0168L31.8109,14.6611C29.1476,17.9153 27.452,21.8974 27.0152,26.3077H24.6859V24.1154H20.3013V32.8846H24.6859V30.6923H27.0152C27.452,35.1369 29.1818,39.1447 31.8794,42.4075L30.1667,43.9832L28.6594,42.476L25.5765,45.5589L31.7424,51.7248L34.8253,48.6418L33.3181,47.1346L34.9623,45.4219C38.2251,48.1023 42.1901,49.8493 46.609,50.2861V52.6154H44.4167V57H53.1859V52.6154H50.9936V50.2861C55.4124,49.8493 59.3774,48.1023 62.6402,45.4219H62.7087L64.2844,47.1346L62.7772,48.6418L65.8602,51.7248L72.026,45.5589L68.9431,42.476L67.4359,43.9832L65.7231,42.4075C65.7274,42.3989 65.7253,42.386 65.7231,42.3732C65.721,42.3604 65.7189,42.3475 65.7231,42.3389C68.4036,39.0762 70.1506,35.1112 70.5873,30.6923H72.9166V32.8846H77.3013V24.1154H72.9166V26.3077H70.5873C70.1506,21.8974 68.455,17.9153 65.7916,14.6611L67.4359,13.0168L68.9431,14.524L72.026,11.4411L65.8602,5.2752L62.7772,8.3582L64.2844,9.8654L62.6402,11.5096C59.386,8.8463 55.4039,7.1507 50.9936,6.7139V4.3846H53.1859V0H44.4167ZM48.8017,10.9604C58.513,10.9604 66.3402,18.7876 66.3402,28.4989C66.3402,38.2101 58.513,46.0373 48.8017,46.0373C39.0905,46.0373 31.2633,38.2101 31.2633,28.4989C31.2633,18.7876 39.0905,10.9604 48.8017,10.9604ZM40.0318,18.9777C40.0318,17.1622 41.5047,15.6893 43.3202,15.6893C45.1357,15.6893 46.6087,17.1622 46.6087,18.9777C46.6087,20.7933 45.1357,22.2662 43.3202,22.2662C41.5047,22.2662 40.0318,20.7933 40.0318,18.9777ZM54.2822,15.6893C52.4667,15.6893 50.9938,17.1622 50.9938,18.9777C50.9938,20.7933 52.4667,22.2662 54.2822,22.2662C56.0977,22.2662 57.5707,20.7933 57.5707,18.9777C57.5707,17.1622 56.0977,15.6893 54.2822,15.6893ZM34.5513,28.499C34.5513,26.6834 36.0243,25.2105 37.8398,25.2105C39.6553,25.2105 41.1283,26.6834 41.1283,28.499C41.1283,30.3145 39.6553,31.7874 37.8398,31.7874C36.0243,31.7874 34.5513,30.3145 34.5513,28.499ZM48.8018,25.2105C46.9863,25.2105 45.5133,26.6834 45.5133,28.499C45.5133,30.3145 46.9863,31.7874 48.8018,31.7874C50.6173,31.7874 52.0902,30.3145 52.0902,28.499C52.0902,26.6834 50.6173,25.2105 48.8018,25.2105ZM56.474,28.499C56.474,26.6834 57.9469,25.2105 59.7624,25.2105C61.5779,25.2105 63.0509,26.6834 63.0509,28.499C63.0509,30.3145 61.5779,31.7874 59.7624,31.7874C57.9469,31.7874 56.474,30.3145 56.474,28.499ZM43.3202,34.7339C41.5047,34.7339 40.0318,36.2069 40.0318,38.0224C40.0318,39.8379 41.5047,41.3108 43.3202,41.3108C45.1357,41.3108 46.6087,39.8379 46.6087,38.0224C46.6087,36.2069 45.1357,34.7339 43.3202,34.7339ZM50.9938,38.0224C50.9938,36.2069 52.4667,34.7339 54.2822,34.7339C56.0977,34.7339 57.5707,36.2069 57.5707,38.0224C57.5707,39.8379 56.0977,41.3108 54.2822,41.3108C52.4667,41.3108 50.9938,39.8379 50.9938,38.0224Z" - android:fillColor="#ffffff" - android:fillAlpha="0.2" - android:fillType="evenOdd" /> - <path - android:pathData="M17.167,70V24.5532V22L40.167,30.1702V47.5319L33.9508,61.8298L17.167,70Z" - android:fillColor="#616F7E" /> - <path - android:pathData="M21.6667,18.4167L0,26.5417V43.0355C0,56.7126 9.2354,69.4688 21.6667,72.5834C34.0979,69.4688 43.3333,56.7126 43.3333,43.0355V26.5417L21.6667,18.4167ZM37.9167,43.0355C37.9167,53.8688 31.0104,63.8897 21.6667,66.9501C12.3229,63.8897 5.4167,53.8959 5.4167,43.0355V30.3063L21.6667,24.2126L37.9167,30.3063V43.0355Z" - android:fillColor="#ffffff" /> - <path - android:pathData="M21.667,68.5V24V21.5L3.167,29.5V46.5L8.167,60.5L21.667,68.5Z" - android:fillColor="#ffffff" /> -</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/vaccination_test_success_gradient.xml b/Corona-Warn-App/src/main/res/drawable/vaccination_test_success_gradient.xml new file mode 100644 index 0000000000000000000000000000000000000000..d391deadf06bbbf197fc3fb1022764eb1fa3dbad --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/vaccination_test_success_gradient.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <gradient + android:angle="180" + android:endColor="#2E854B" + android:startColor="#35B55F" /> +</shape> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/covid_test_error_card.xml b/Corona-Warn-App/src/main/res/layout/covid_test_error_card.xml new file mode 100644 index 0000000000000000000000000000000000000000..d71d688299aaac25ba702daa4ca0ec8ee1ccb976 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/covid_test_error_card.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="200dp"> + + <TextView + android:id="@+id/card_title" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginTop="@dimen/card_padding" + android:layout_marginEnd="@dimen/card_padding" + android:focusable="false" + android:text="@string/info_banner_title_1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/vaccination_label" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginEnd="@dimen/card_padding" + android:focusable="false" + android:text="@string/info_banner_title_2" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/card_title" /> + + <TextView + android:id="@+id/body" + style="@style/subtitle" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" + android:layout_marginEnd="@dimen/card_padding" + android:focusable="false" + android:text="@string/test_certificate_error_label" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/vaccination_label" /> + + <TextView + android:id="@+id/test_time" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_mega_tiny" + android:layout_marginEnd="@dimen/card_padding" + android:focusable="false" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/body" + tools:text="Test durchgeführt am 12.04.21, 18:01 Uhr " /> + + <com.google.android.material.progressindicator.CircularProgressIndicator + android:id="@+id/progress_bar" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" + android:layout_marginEnd="@dimen/card_padding" + android:layout_marginBottom="@dimen/spacing_small" + android:indeterminate="true" + android:visibility="gone" + app:indicatorColor="@color/colorAccent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/test_time" + app:layout_constraintBottom_toBottomOf="parent" + app:showAnimationBehavior="inward" + app:trackColor="@android:color/transparent" /> + + <TextView + android:id="@+id/refresh_status" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacing_small" + android:layout_marginEnd="@dimen/card_padding" + android:focusable="false" + android:visibility="gone" + android:text="@string/test_certificate_error_refreshing_status" + app:layout_constraintStart_toEndOf="@id/progress_bar" + app:layout_constraintTop_toTopOf="@id/progress_bar" + app:layout_constraintEnd_toEndOf="parent" + tools:text="@string/test_certificate_error_refreshing_status" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/retry_button" + style="@style/Widget.MaterialComponents.Button.OutlinedButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" + android:layout_marginEnd="@dimen/card_padding" + android:layout_marginBottom="@dimen/spacing_small" + android:text="@string/test_certificate_error_retry_button" + android:textColor="@color/colorAccent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/test_time" + app:strokeColor="@color/colorAccent" /> + + <Button + android:id="@+id/delete_button" + style="@style/buttonLight" + android:backgroundTint="@color/button_main_tracing" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/card_padding" + android:layout_marginEnd="@dimen/card_padding" + android:layout_marginBottom="@dimen/spacing_small" + android:text="@string/test_certificate_error_delete_button" + android:textColor="@color/colorAccent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/retry_button" + app:strokeColor="@color/colorAccent" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/covid_test_success_card.xml b/Corona-Warn-App/src/main/res/layout/covid_test_success_card.xml new file mode 100644 index 0000000000000000000000000000000000000000..985db99698b8ea87746cd30ba4799edbf9d88e4b --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/covid_test_success_card.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/vaccination_test_success_gradient" + android:minHeight="200dp" + tools:ignore="UnusedAttribute"> + + <ImageView + android:id="@+id/icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:importantForAccessibility="no" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_eu_stars" /> + + <TextView + android:id="@+id/title1" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" + android:accessibilityHeading="true" + android:focusable="false" + android:text="@string/info_banner_title_1" + android:textColor="@color/colorTextPrimary1InvertedStable" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/title2" + style="@style/headline5" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/card_padding" + android:accessibilityHeading="true" + android:focusable="false" + android:text="@string/info_banner_title_2" + android:textColor="@color/colorTextPrimary1InvertedStable" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/title1" /> + + <ImageView + android:id="@+id/prc_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="@dimen/card_padding" + android:importantForAccessibility="no" + app:layout_constraintBottom_toBottomOf="@id/test_time" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/test_time" + app:srcCompat="@drawable/ic_risk_details_pcr" + app:tint="@color/colorStableLight" /> + + <TextView + android:id="@+id/person_name" + style="@style/body1" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" + android:accessibilityHeading="true" + android:focusable="false" + android:textColor="@color/colorTextPrimary1InvertedStable" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/title2" + tools:text="Andrea Schneider" /> + + <TextView + android:id="@+id/test_time" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginBottom="@dimen/spacing_normal" + android:accessibilityHeading="true" + android:focusable="false" + android:textColor="@color/colorTextPrimary1InvertedStable" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/prc_icon" + app:layout_constraintTop_toBottomOf="@id/person_name" + app:layout_constraintVertical_bias="1" + app:layout_constraintVertical_chainStyle="packed" + tools:text="Test durchgeführt am 12.04.21, 18:01 Uhr" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/fragment_certificates.xml b/Corona-Warn-App/src/main/res/layout/fragment_certificates.xml index 1ce0b67b87f725d81edb1179dd1cc278950d5768..54fc467de2af5903216a7a1d94e0f275eddc0180 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_certificates.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_certificates.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".greencertificate.ui.certificates.CertificatesFragment"> + tools:context=".covidcertificate.test.ui.CertificatesFragment"> <com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_greencertificate_details.xml b/Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml similarity index 92% rename from Corona-Warn-App/src/main/res/layout/fragment_greencertificate_details.xml rename to Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml index 057a3da6aa7a3487beec29da4cee0fa991857ce9..fbc6609fc04b6843a7471cc82f45db687a79a15b 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_greencertificate_details.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_covid_certificate_details.xml @@ -44,10 +44,10 @@ android:layout_marginTop="90dp" android:layout_marginBottom="12dp" android:gravity="center" + android:text="@string/detail_green_certificate_title" android:textColor="@android:color/white" android:textSize="20sp" - android:textStyle="bold" - android:text="@string/detail_green_certificate_title"/> + android:textStyle="bold" /> <TextView android:id="@+id/subtitle" @@ -59,7 +59,7 @@ android:gravity="center" android:text="@string/detail_green_certificate_test_type" android:textColor="@android:color/white" - android:textSize="18sp"/> + android:textSize="18sp" /> </LinearLayout> @@ -67,8 +67,10 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + android:theme="@style/CWAToolbar.Theme" app:layout_collapseMode="pin" app:layout_scrollFlags="scroll|enterAlways" + app:menu="@menu/menu_covid_certificate_detail" app:navigationIcon="@drawable/ic_back" app:navigationIconTint="@android:color/white"> @@ -82,7 +84,7 @@ <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginEnd="72dp" + android:layout_marginEnd="32dp" android:importantForAccessibility="no" app:srcCompat="@drawable/ic_cwa_logo_white" /> @@ -115,7 +117,7 @@ android:layout_marginBottom="6dp" /> <LinearLayout - style="@style/Card.Greencertificate" + style="@style/Card.GreenCertificate" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="24dp" @@ -126,7 +128,6 @@ style="@style/body1Medium" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="12dp" android:text="@string/green_certificate_attribute_name" /> <TextView @@ -134,7 +135,6 @@ style="@style/body1" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Andrea Schneider" tools:text="Andrea Schneider" /> <TextView @@ -150,7 +150,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="18.04.1943" tools:text="18.04.1943" /> <TextView @@ -166,7 +165,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="SARS-CoV-2" tools:text="SARS-CoV-2" /> <TextView @@ -182,7 +180,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="Rapid Antigen Test" tools:text="Rapid Antigen Test" /> <TextView @@ -193,12 +190,11 @@ android:text="@string/green_certificate_attribute_test_name" /> <TextView - android:id="@+id/medical_product_name" + android:id="@+id/test_name" style="@style/body1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="Xep" tools:text="Xep" /> <TextView @@ -214,7 +210,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="Xup" tools:text="Xup" /> <TextView @@ -230,7 +225,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="12.05.21 19:00" tools:text="12.05.21 19:00" /> <TextView @@ -246,7 +240,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="negative" tools:text="negative" /> <TextView @@ -262,7 +255,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="AB123" tools:text="AB123" /> <TextView @@ -273,12 +265,11 @@ android:text="@string/green_certificate_attribute_state_of_testing" /> <TextView - android:id="@+id/test_country" + android:id="@+id/certificate_country" style="@style/body1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="Germany" tools:text="Germany" /> <TextView @@ -294,7 +285,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="G05930482748454836478695764787840" tools:text="G05930482748454836478695764787840" /> @@ -311,14 +301,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" - android:text="05930482748454836478695764787840" tools:text="05930482748454836478695764787840" /> </LinearLayout> <LinearLayout - style="@style/Card.Greencertificate" + style="@style/Card.GreenCertificate" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="24dp" @@ -331,8 +320,7 @@ style="@style/body2" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" - android:text="@string/green_certificate_attribute_certificate_travel_notice_german"/> + android:text="@string/green_certificate_attribute_certificate_travel_notice_german" /> <TextView android:id="@+id/travel_notice_english" @@ -340,7 +328,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" - android:text="@string/green_certificate_attribute_certificate_travel_notice_english"/> + android:text="@string/green_certificate_attribute_certificate_travel_notice_english" /> </LinearLayout> </LinearLayout> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_request_covid_certificate.xml b/Corona-Warn-App/src/main/res/layout/fragment_request_covid_certificate.xml index 7129eb51e8c8e4b22b94a8866299d6d63fece73f..31c266359a5d50b349373be2e991227bd614083e 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_request_covid_certificate.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_request_covid_certificate.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorBackground" - tools:context="ui.submission.greencertificate.RequestCovidCertificateFragment"> + tools:context="ui.submission.covidcertificate.RequestCovidCertificateFragment"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" diff --git a/Corona-Warn-App/src/main/res/layout/tracing_details_find_details_in_journal.xml b/Corona-Warn-App/src/main/res/layout/tracing_details_find_details_in_journal.xml index 943d16269bbf2c13e5607be54e75c52bb30bdad7..e5e2d9d83c0cbf75eabe881a9c0328ed770e6fb1 100644 --- a/Corona-Warn-App/src/main/res/layout/tracing_details_find_details_in_journal.xml +++ b/Corona-Warn-App/src/main/res/layout/tracing_details_find_details_in_journal.xml @@ -1,14 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/risk_details_find_details_in_journal" - style="@style/body1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/spacing_normal" - android:layout_marginTop="@dimen/spacing_normal" - android:focusable="true" - android:text="@string/risk_details_find_details_in_journal" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <TextView + android:id="@+id/risk_details_find_details_in_journal" + style="@style/body1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/spacing_normal" + android:layout_marginTop="@dimen/spacing_normal" + android:focusable="true" + android:text="@string/risk_details_find_details_in_journal" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> +</layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_consent_fragment.xml b/Corona-Warn-App/src/main/res/layout/vaccination_consent_fragment.xml index 340a37d67711f201d31569b43ee49985906fc420..3cff980a655c035a27775b1683995824b2ad2736 100644 --- a/Corona-Warn-App/src/main/res/layout/vaccination_consent_fragment.xml +++ b/Corona-Warn-App/src/main/res/layout/vaccination_consent_fragment.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorBackground" - tools:context="de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment"> + tools:context="de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragment"> <com.google.android.material.appbar.MaterialToolbar android:id="@+id/toolbar" diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_home_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_home_card.xml index 6b257338d0594edc84e27b44740d00190c9554df..8d6853daebc5542e665479f3d2284195825f1590 100644 --- a/Corona-Warn-App/src/main/res/layout/vaccination_home_card.xml +++ b/Corona-Warn-App/src/main/res/layout/vaccination_home_card.xml @@ -5,75 +5,75 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/vaccination_incomplete" - android:padding="@dimen/card_padding" + android:minHeight="200dp" tools:ignore="UnusedAttribute"> - - <ImageView - android:id="@+id/show_more_action" - android:layout_width="@dimen/icon_size_risk_card" - android:layout_height="@dimen/icon_size_risk_card" - android:importantForAccessibility="no" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/ic_forward" - app:tint="@color/colorStableLight" /> - <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="16dp" android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/show_more_action" - app:srcCompat="@drawable/vaccination_card_icon_incomplete" /> - + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_incomplete_vaccination_shield" /> <TextView - android:id="@+id/card_title" - style="@style/body2" + android:id="@+id/title1" + style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="8dp" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" android:accessibilityHeading="true" android:focusable="false" - android:text="@string/vaccination_card_status_title" + android:text="@string/vaccination_card_registration_title_line_1" android:textColor="@color/colorTextPrimary1InvertedStable" - app:layout_constraintEnd_toStartOf="@id/show_more_action" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView - android:id="@+id/vaccination_label" - style="@style/headline5Bold" + android:id="@+id/title2" + style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="16dp" + android:layout_marginHorizontal="@dimen/card_padding" android:accessibilityHeading="true" android:focusable="false" - android:text="@string/vaccination_card_status_vaccination_name" + android:text="@string/vaccination_card_registration_title_line_2" android:textColor="@color/colorTextPrimary1InvertedStable" - app:layout_constraintEnd_toStartOf="@+id/icon" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/card_title" /> + app:layout_constraintTop_toBottomOf="@id/title1" /> + + <ImageView + android:id="@+id/shield_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="@dimen/card_padding" + android:importantForAccessibility="no" + app:layout_constraintBottom_toBottomOf="@id/vaccination_state" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/vaccination_state" + app:srcCompat="@drawable/ic_half_white_half_gray_shield" /> <TextView android:id="@+id/vaccination_state" - style="@style/body1" + style="@style/body2" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" + android:layout_marginStart="8dp" + android:layout_marginBottom="@dimen/spacing_normal" android:accessibilityHeading="true" android:focusable="false" android:text="@string/vaccination_card_status_vaccination_incomplete" android:textColor="@color/colorTextPrimary1InvertedStable" - app:layout_constraintBottom_toTopOf="@+id/person_name" - app:layout_constraintEnd_toStartOf="@+id/icon" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/vaccination_label" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/shield_icon" + app:layout_constraintTop_toBottomOf="@id/person_name" + app:layout_constraintVertical_bias="1" app:layout_constraintVertical_chainStyle="packed" /> <TextView @@ -81,16 +81,14 @@ style="@style/body1" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" android:accessibilityHeading="true" android:focusable="false" android:textColor="@color/colorTextPrimary1InvertedStable" - android:visibility="gone" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/icon" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/vaccination_state" + app:layout_constraintTop_toBottomOf="@id/title2" tools:text="Andrea Schneider" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/vaccination_home_immune_card.xml b/Corona-Warn-App/src/main/res/layout/vaccination_home_immune_card.xml index 611102ebee2f729324ed8ab39c8e427abf2993de..8da269c8163145d38375e0e578e9fa33dde004b6 100644 --- a/Corona-Warn-App/src/main/res/layout/vaccination_home_immune_card.xml +++ b/Corona-Warn-App/src/main/res/layout/vaccination_home_immune_card.xml @@ -5,20 +5,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/vaccination_compelete_gradient" - android:padding="@dimen/card_padding" + android:minHeight="200dp" tools:ignore="UnusedAttribute"> - - <ImageView - android:id="@+id/show_more_action" - android:layout_width="@dimen/icon_size_risk_card" - android:layout_height="@dimen/icon_size_risk_card" - android:importantForAccessibility="no" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/ic_forward" - app:tint="@color/colorStableLight" /> - <ImageView android:id="@+id/icon" android:layout_width="wrap_content" @@ -26,52 +15,80 @@ android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@+id/show_more_action" - app:srcCompat="@drawable/vaccination_card_icon_immune" /> + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_complete_vaccination_shield" /> <TextView - android:id="@+id/card_title" - style="@style/body2" + android:id="@+id/title1" + style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="8dp" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" android:accessibilityHeading="true" android:focusable="false" - android:text="@string/vaccination_card_status_title" + android:text="@string/vaccination_card_registration_title_line_1" android:textColor="@color/colorTextPrimary1InvertedStable" - app:layout_constraintEnd_toStartOf="@id/show_more_action" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView - android:id="@+id/vaccination_label" - style="@style/headline5Bold" + android:id="@+id/title2" + style="@style/headline5" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginEnd="16dp" + android:layout_marginHorizontal="@dimen/card_padding" android:accessibilityHeading="true" android:focusable="false" - android:text="@string/vaccination_card_status_vaccination_name" + android:text="@string/vaccination_card_registration_title_line_2" android:textColor="@color/colorTextPrimary1InvertedStable" - app:layout_constraintEnd_toStartOf="@+id/icon" + android:textStyle="bold" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/card_title" /> + app:layout_constraintTop_toBottomOf="@id/title1" /> + + <ImageView + android:id="@+id/shield_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_marginStart="@dimen/card_padding" + android:importantForAccessibility="no" + app:layout_constraintBottom_toBottomOf="@id/vaccination_state" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="@id/vaccination_state" + app:srcCompat="@drawable/ic_white_shield_with_checkmark" /> + + <TextView + android:id="@+id/vaccination_state" + style="@style/body2" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginBottom="@dimen/spacing_normal" + android:accessibilityHeading="true" + android:focusable="false" + android:textColor="@color/colorTextPrimary1InvertedStable" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/shield_icon" + app:layout_constraintTop_toBottomOf="@id/person_name" + app:layout_constraintVertical_bias="1" + app:layout_constraintVertical_chainStyle="packed" + tools:text="@string/vaccination_card_status_vaccination_complete" /> <TextView android:id="@+id/person_name" style="@style/body1" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:layout_marginEnd="16dp" + android:layout_marginHorizontal="@dimen/card_padding" + android:layout_marginTop="@dimen/spacing_small" android:accessibilityHeading="true" android:focusable="false" android:textColor="@color/colorTextPrimary1InvertedStable" - android:visibility="gone" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/icon" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/vaccination_label" + app:layout_constraintTop_toBottomOf="@id/title2" tools:text="Andrea Schneider" /> </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/menu/menu_covid_certificate_detail.xml b/Corona-Warn-App/src/main/res/menu/menu_covid_certificate_detail.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ae3a0733d51d12c97901516365d9ec307aedcad --- /dev/null +++ b/Corona-Warn-App/src/main/res/menu/menu_covid_certificate_detail.xml @@ -0,0 +1,6 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/menu_covid_certificate_delete" + android:title="@string/green_certificate_details_menu_item_delete" /> +</menu> diff --git a/Corona-Warn-App/src/main/res/navigation/green_certificate_graph.xml b/Corona-Warn-App/src/main/res/navigation/green_certificate_graph.xml index 3605a787c2a0d0ae5ca3b732be091d62d7a506c3..f38cdb7fb4db60bb48e3abf7159527a05fd8e3e2 100644 --- a/Corona-Warn-App/src/main/res/navigation/green_certificate_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/green_certificate_graph.xml @@ -7,21 +7,28 @@ <fragment android:id="@+id/certificatesFragment" - android:name="de.rki.coronawarnapp.greencertificate.ui.certificates.CertificatesFragment" + android:name="de.rki.coronawarnapp.covidcertificate.test.ui.CertificatesFragment" android:label="fragment_certificates" - tools:layout="@layout/fragment_certificates" > + tools:layout="@layout/fragment_certificates"> <action android:id="@+id/action_certificatesFragment_to_vaccinationNavGraph" app:destination="@id/vaccination_nav_graph" /> + <action + android:id="@+id/action_certificatesFragment_to_covidCertificateDetailsFragment" + app:destination="@id/covidCertificateDetailsFragment" /> + + <action + android:id="@+id/action_certificatesFragment_to_consentFragment" + app:destination="@id/vaccinationConsentFragment" /> </fragment> <fragment android:id="@+id/vaccinationConsentFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.consent.VaccinationConsentFragment" + android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.consent.VaccinationConsentFragment" android:label="vaccination_consent_fragment" - tools:layout="@layout/vaccination_consent_fragment" > + tools:layout="@layout/vaccination_consent_fragment"> <argument android:name="showBottomNav" android:defaultValue="true" @@ -42,4 +49,14 @@ android:label="privacyFragment" tools:layout="@layout/fragment_information_privacy" /> + <fragment + android:id="@+id/covidCertificateDetailsFragment" + android:name="de.rki.coronawarnapp.covidcertificate.test.ui.details.CovidCertificateDetailsFragment" + android:label="CovidCertificateDetailsFragment" + tools:layout="@layout/fragment_covid_certificate_details"> + + <argument + android:name="testCertificateIdentifier" + app:argType="string" /> + </fragment> </navigation> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index 919fadb96bacaf782dd41ab5ee9e32c21e9fbf64..8d879a4bb77ffea0650d57ebc6590f0d199bfeae 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -830,7 +830,7 @@ app:destination="@id/qrCodeFullScreenFragment" /> <fragment android:id="@+id/requestCovidCertificateFragment" - android:name="de.rki.coronawarnapp.ui.submission.greencertificate.RequestCovidCertificateFragment" + android:name="de.rki.coronawarnapp.ui.submission.covidcertificate.RequestCovidCertificateFragment" android:label="fragment_request_covid_certificate" tools:layout="@layout/fragment_request_covid_certificate"> <argument diff --git a/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml index 74ba33a60f5af566e4f12927b5e33765ad4e985d..aeb23461ad7891fb411110910168880b6693ae5d 100644 --- a/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml @@ -7,7 +7,7 @@ <fragment android:id="@+id/vaccinationQrCodeScanFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.scan.VaccinationQrCodeScanFragment" + android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.scan.VaccinationQrCodeScanFragment" android:label="VaccinationQrCodeScanFragment" tools:layout="@layout/fragment_scan_qr_code"> <action @@ -20,7 +20,7 @@ <fragment android:id="@+id/vaccinationListFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragment" + android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.list.VaccinationListFragment" android:label="fragment_vaccination_list" tools:layout="@layout/fragment_vaccination_list"> <argument @@ -37,7 +37,7 @@ <fragment android:id="@+id/vaccinationDetailsFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.details.VaccinationDetailsFragment" + android:name="de.rki.coronawarnapp.covidcertificate.vaccination.ui.details.VaccinationDetailsFragment" android:label="fragment_vaccination_details" tools:layout="@layout/fragment_vaccination_details"> <argument diff --git a/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml index a68f5f5d1b1316c18578d74b68211659f5cb5996..100652ed537682e1f90c188696f927b0ad0de261 100644 --- a/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/antigen_strings.xml @@ -6,8 +6,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"РегиÑтрирайте ÑÐ²Ð¾Ñ Ñ‚ÐµÑÑ‚"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Използвайте приложението, за да региÑтрирате теÑтовете Ñи и да извлечете резултатите от Ñ‚ÑÑ…, за да можете предупредите оÑтаналите по-бързо."</string> - + <string name="ag_homescreen_card_test_register_body">"Използвайте приложението, за да региÑтрирате теÑтовете Ñи и да извлечете резултатите от Ñ‚ÑÑ…, за да можете да предупредите оÑтаналите по-бързо или за да заÑвите ÑÐ²Ð¾Ñ Ñертификат за теÑтване за COVID."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml index fb165acc4816cc19117521807662ccf0fc0ecfd0..3de30155be44db04c32e8c5a1fd62c7dfe83f991 100644 --- a/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/contact_diary_strings.xml @@ -182,4 +182,13 @@ <string name="contact_diary_location_visit_duration_hour">"ч"</string> <!-- XTXT: Option - person encounter - circumstances hint--> <string name="contact_diary_location_visit_circumstances_hint">"Забележка (напр. препълнено)"</string> + + <!-- XTXT: PCR test title in the day overview --> + <string name="contact_diary_corona_test_pcr_title">"PCR теÑÑ‚ÑŠÑ‚ е региÑтриран"</string> + <!-- XTXT: RAT test title in the day overview --> + <string name="contact_diary_corona_test_rat_title">"БързиÑÑ‚ теÑÑ‚ е извършен"</string> + <!-- XTXT: positive test result in the day overview --> + <string name="contact_diary_corona_test_positive">"Положителен резултат"</string> + <!-- XTXT: negative test result in the day overview --> + <string name="contact_diary_corona_test_negative">"Отрицателен резултат"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-bg/event_registration_strings.xml index b2ad81cbba3031fe16075204c7edeedcbb7a65d5..1a5025ded05a1c659e401ec4f2a939b92e3631f0 100644 --- a/Corona-Warn-App/src/main/res/values-bg/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/event_registration_strings.xml @@ -210,6 +210,8 @@ <string name="trace_location_organizer_detail_item_duration">"%1$s, %2$s - %3$s"</string> <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> + <!-- XTXT: Past Event Info duration --> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"Дублиране"</string> diff --git a/Corona-Warn-App/src/main/res/values-bg/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-bg/green_certificate_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..ddae0a81127cfe8a2349f50c4d572b3b68e19b95 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-bg/green_certificate_strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <!-- XTXT: Request green certificate title --> + <string name="request_green_certificate_title">"Сертификат за теÑÑ‚ за COVID"</string> + <!-- XTXT: Request green certificate subtitle --> + <string name="request_green_certificate_subtitle">"Можете да използвате приложението, за да заÑвите официален електронен Ñертификат за теÑтване, който ще бъде добавен в приложението."</string> + <!-- XTXT: Request green certificate body section 1 --> + <string name="request_green_certificate_body_1">"Сертификатът за теÑтване Ñе издава Ñамо ако получите отрицателен резултат от теÑта."</string> + <!-- XTXT: Request green certificate body section 2 --> + <string name="request_green_certificate_body_2">"Сертификатът за теÑтване е валидно доказателÑтво за отрицателен резултат от теÑÑ‚ в рамките на ЕС (например при пътуване)."</string> + <!-- XTXT: Request green certificate body section 3 --> + <string name="request_green_certificate_body_3">"Сертификатът за теÑтване Ñъдържа валидни данни, които позволÑват на приложението за проверка да валидира Ñертификата ви."</string> + <!-- XBUT: Request green certificate agree button --> + <string name="request_green_certificate_agree_button">"ЗаÑвете теÑтов Ñертификат"</string> + <!-- XBUT: Request green certificate disagree button --> + <string name="request_green_certificate_disagree_button">"Ðе, благодарÑ!"</string> + <!-- XTXT: Request green certificate birth date description --> + <string name="request_green_certificate_birthdate_description">"С цел ÑигурноÑÑ‚, резултатът от теÑта е защитен Ñ Ð´Ð°Ñ‚Ð°Ñ‚Ð° Ви на раждане. Той може да бъде издаден Ñамо ако поÑочите правилната дата."</string> + <!-- XTXT: Request green certificate birth date hint --> + <string name="request_green_certificate_birthdate_hint">"Дата на раждане"</string> + <!-- XTXT: Request green certificate exit dialog title --> + <string name="request_gc_dialog_title">"ОтмÑна на региÑтрациÑ"</string> + <!-- XTXT: Request green certificate exit dialog message --> + <string name="request_gc_dialog_message">"Ðко отмените региÑтрациÑта на теÑта, нÑма да може да получите резултата от него в приложението."</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_positive_button">"ОК"</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_negative_button">"Отказ"</string> + <!-- XTXT: Detail green certificate title --> + <string name="detail_green_certificate_title">"Електронен Ñертификат на ЕС за теÑтване за COVID"</string> + <!-- XTXT: Detail green certificate test type --> + <string name="detail_green_certificate_test_type">"ТеÑÑ‚ за SARS-CoV-2"</string> + <!-- XTXT: Detail green certificate card title --> + <string name="detail_green_certificate_card_title">"Сертификат за теÑтване"</string> + <!-- XTXT: Detail green certificate travel notice link en --> + <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> + <!-- XTXT: Detail green certificate travel notice link de --> + <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Green certificate main screen title --> + <string name="certification_screen_title">"Сертификати"</string> + <!-- XTXT: Green certificate menu item --> + <string name="menu_certification_information">"ИнформациÑ"</string> + <!-- XTXT: Green certificate main screen header text --> + <string name="green_certification_header_info">"Тук Ñе показват електронните ви Ñертификати за вакÑиниране и за теÑтваниÑ."</string> + <!-- XTXT: Green certificate info card title 1st line --> + <string name="info_banner_title_1">"Електронен COVID-19"</string> + <!-- XTXT: Green certificate info card title 2nd line --> + <string name="info_banner_title_2">"Сертификат за теÑÑ‚ за COVID"</string> + <!-- XTXT: Green certificate info card body --> + <string name="info_banner_body">"РегиÑтрирайте теÑÑ‚ на Ð½Ð°Ñ‡Ð°Ð»Ð½Ð¸Ñ ÐµÐºÑ€Ð°Ð½ и дайте ÑъглаÑие да получите електронен Ñертификат за теÑтване. Той ще Ñе покаже тук веднага щом е наличен."</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml index c36ffbe1836a1a5f7bbe701ea251a8f2810d7e63..335aaa6f5ae1578642ab3329cc65f47890fe16bd 100644 --- a/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/release_info_strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -16,22 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Електронно доказателÑтво за вакÑиниране"</item> + <item>"Сертификат за теÑÑ‚ за COVID"</item> + <item>"Разширение на дневника Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¸"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Вече можете да добавите в приложението Ñертификатите Ñи за вакÑиниране и да ги предÑтавите чрез QR код. Приложението ще покаже пълна защита от вакÑиниране 14 дни Ñлед крайната вакÑинациÑ."</item> + <item>"Вече можете да заÑвите чрез приложението официален електронен Ñертификат за теÑтване и Ñлед това да го добавите в него. Този Ñертификат може да Ñе използва (чрез QR код) в рамките на ЕС като доказателÑтво за отрицателен резултат от теÑÑ‚ (например при международни пътуваниÑ)."</item> + <item>"Ð’ момента, в който резултатът от теÑта Ви е наличен, той ще бъде добавен към дневника Ви Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¸."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> + <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> + <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-bg/strings.xml b/Corona-Warn-App/src/main/res/values-bg/strings.xml index 9f392a30db6e2f798aaf9f265e735983dde4d7b9..9d754260bca4c26af7a54b40655eac90a195aa73 100644 --- a/Corona-Warn-App/src/main/res/values-bg/strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/strings.xml @@ -281,6 +281,8 @@ <string name="risk_details_behavior_body_wear_mask">"ÐоÑете маÑка при контакт Ñ Ð´Ñ€ÑƒÐ³Ð¸ хора."</string> <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"Спазвайте диÑÑ‚Ð°Ð½Ñ†Ð¸Ñ Ð¾Ñ‚ поне 1,5 м от околните."</string> + <!-- XTXT: find details about exposures in the contact journal --> + <string name="risk_details_find_details_in_journal">"Ще намерите още Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ñ‚Ð½Ð¾Ñно излаганиÑта ви на риÑк във Ð²Ð°ÑˆÐ¸Ñ Ð´Ð½ÐµÐ²Ð½Ð¸Ðº на контактите."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"Ðко Ñе теÑтвате, вижте Процедура за теÑтване в ЧЗВ, за да получите допълнителна информациÑ."</string> @@ -1027,6 +1029,8 @@ <string name="submission_consent_call_test_result_scan_your_test_only">"Сега Ñканирайте QR кода от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚, за да получите резултата."</string> <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Можете да Ñканирате Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚ Ñамо веднъж. Приложението може да обработва едновременно Ñамо един бърз теÑÑ‚ и един PCR теÑÑ‚."</string> + <!-- YTXT: Body sub text 3 for Submission Consent call test result --> + <string name="submission_consent_call_test_result_checkmark_text">"Ðко резултатът от теÑта ви е отрицателен, може да го потвърдите Ñ Ð¾Ñ„Ð¸Ñ†Ð¸Ð°Ð»ÐµÐ½ Ñертификат за теÑтване за COVID. За да направите това, заÑвете Ñертификата като Ñледвате Ñтъпките."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"МолÑ, помогнете на хората, Ñ ÐºÐ¾Ð¸Ñ‚Ð¾ Ñте имали контакт, като ги предупредите!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1087,6 +1091,15 @@ <!-- YTXT: Body text for next steps section of waiting test result page --> <string name="submission_test_result_pending_steps_contact_diary_body">"Резултатът от Ð’Ð°ÑˆÐ¸Ñ Ñ‚ÐµÑÑ‚ ще Ñе покаже в приложението веднага щом бъде готов."</string> + <!-- XHED: Page headline for test result certifcate info title --> + <string name="submission_test_result_pending_steps_test_certificate_heading">"ВашиÑÑ‚ Ñертификат за теÑтване"</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"Ðе е наличен Ñертификат за теÑтване, защото този пункт за теÑтване не поддържа издаването на такива Ñертификати."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Сертификатът Ви за теÑтване вÑе още не е наличен. Когато това Ñе Ñлучи, той ще Ñе покаже в приложението Ви."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"Ðе е издаден Ñертификат, отговарÑщ на иÑкането Ви."</string> + <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Ðктуализиране"</string> <!-- XBUT: test result pending : remove the test button --> @@ -1179,7 +1192,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"Сканиране на QR код"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Получете резултата Ñи в приложението и предупредете оÑтаналите."</string> + <string name="submission_dispatcher_qr_card_text">"Получете резултата Ñи в приложението и предупредете оÑтаналите или заÑвете ÑÐ²Ð¾Ñ Ñертификат за теÑтване за COVID."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"Въведете ТÐРкод за PCR теÑÑ‚"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1838,7 +1851,8 @@ <string name="bottom_nav_diary_title">"Дневник"</string> <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"РегиÑтрациÑ"</string> - + <!-- XHED: Title for BottomNav certificates screen title --> + <string name="bottom_nav_certificates_title">"Сертификати"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-bg/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-bg/vaccination_strings.xml index 13a3e4af27d0abdb57084860df4927f1802e62a7..6eeeb4c291f99bd1526977a9094253cd04784d8e 100644 --- a/Corona-Warn-App/src/main/res/values-bg/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values-bg/vaccination_strings.xml @@ -36,7 +36,7 @@ <string name="vaccination_list_vaccination_card_subtitle">"Извършено на %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"Получихте вÑички планирани за момента вакÑинации, но защитата ви от вакÑиниране ще е пълна едва Ñлед %1$d ден."</item> + <item quantity="one">"Получихте вÑички планирани за момента вакÑинации, но защитата ви от вакÑиниране ще е пълна Ñлед %1$d ден."</item> <item quantity="other">"Получихте вÑички планирани за момента вакÑинации, но защитата ви от вакÑиниране ще е пълна едва Ñлед %1$d дни."</item> <item quantity="zero">"Получихте вÑички планирани за момента вакÑинации, но защитата ви от вакÑиниране ще е пълна едва Ñлед %1$d дни."</item> <item quantity="two">"Получихте вÑички планирани за момента вакÑинации, но защитата ви от вакÑиниране ще е пълна едва Ñлед %1$d дни."</item> @@ -62,9 +62,11 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Добавете Ñертификат за вакÑиниране"</string> + <string name="vaccination_card_registration_title_line_1">"Добавете електронно доказателÑтво"</string> + <!-- XHED: Title for Vaccination Certificate Registration Home Card --> + <string name="vaccination_card_registration_title_line_2">"за вакÑиниране"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"Добавете вакÑинационни Ñертификати в приложението, за да Ñа винаги Ñ Ð²Ð°Ñ. За да направите това, Ñканирайте QR кода от Ð²Ð°ÑˆÐ¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚."</string> + <string name="vaccination_card_registration_body">"Добавете вакÑинационните Ñи Ñертификати в приложението, за да Ñа винаги Ñ Ð²Ð°Ñ. За да направите това, Ñканирайте QR кода от Ð²Ð°ÑˆÐ¸Ñ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"ДобавÑне"</string> <!-- XHED: Homescreen vaccination status card title --> @@ -97,17 +99,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Вашето ÑъглаÑие"</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Добавете Ñертификат за вакÑиниране"</string> + <string name="vaccination_consent_headline">"Добавете Ñертификати"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"Добавете ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¸Ñ Ñи Ñертификат за вакÑиниране. Когато вакÑинационната ви защита бъде завършена, можете да предÑтавÑте QR кода в приложението Ñи като доказателÑтво за вакÑиниране."</string> + <string name="vaccination_consent_info_subtitle_text">"Добавете електронните Ñи Ñертификати за COVID в приложението. Така ще можете да доказвате вакÑинационната Ñи защита или резултата от отрицателен теÑÑ‚ чрез приложението."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"За да добавите Ñертификат за вакÑиниране в приложението, Ñканирайте QR кода, който Ñте получили по време на вакÑинирането."</string> + <string name="vaccination_consent_qr_info_text">"Можете да добавите в приложението Ñертификатите Ñи за вакÑиниране Ñрещу COVID или Ñертификатите за теÑÑ‚Ð²Ð°Ð½Ð¸Ñ Ð·Ð° COVID."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"Приложението чете информациÑта от QR кода и го запазва в защитена облаÑÑ‚ в Ñмартфона ви."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"Тези Ñертификати Ñе Ñчитат за валидно доказателÑтво в рамките на ЕС (например при пътуване)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"Данните ще оÑтанат в Ñмартфона. Те ще бъдат предадени на другите Ñамо ако предÑтавите вакÑÐ¸Ð½Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñи Ñертификат за проверка."</string> + <string name="vaccination_consent_qr_info_time_text">"След като добавите Ñертификат, той ще Ñе запази в Ñмартфона Ви. Ще Ñе ÑÐ¿Ð¾Ð´ÐµÐ»Ñ Ñ Ð¾Ñтаналите Ñамо ако предÑтавите Ñертификат за вакÑиниране."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¾Ñ‡ÐµÑ‚ÐµÑ‚Ðµ декларациÑта за поверителноÑÑ‚."</string> + <string name="vaccination_consent_onboarding_legal_information">"За повече Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾Ñ‚Ð½Ð¾Ñно обработката на данни, прочетете декларациÑта за поверителноÑÑ‚."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Ðапред"</string> diff --git a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml index c9b3779b5b3df64ea415c7a12a21924f4d712c37..3eb70a980ce8286f10c6cb13bcb86e4971d746de 100644 --- a/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/antigen_strings.xml @@ -7,7 +7,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"Test registrieren"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Nutzen Sie die App, um Ihr Testergebnis abzurufen und andere schneller warnen zu können, oder um Ihr COVID-19-Testzertifikat anzufordern."</string> + <string name="ag_homescreen_card_test_register_body">"Nutzen Sie die App, um Ihr Testergebnis abzurufen und andere schneller warnen zu können, oder um Ihr COVID-Testzertifikat anzufordern."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-de/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-de/green_certificate_strings.xml index 0ee17fb2adc309974247759ad6fdbc37ba8cfd61..bd91fd49db9ec7b6a5e8639e4dcab7e007dbbf12 100644 --- a/Corona-Warn-App/src/main/res/values-de/green_certificate_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/green_certificate_strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <!-- XTXT: Request green certificate title --> - <string name="request_green_certificate_title">COVID-19-Testzertifikat</string> + <string name="request_green_certificate_title">COVID-Testzertifikat</string> <!-- XTXT: Request green certificate subtitle --> <string name="request_green_certificate_subtitle">Sie können über die App ein offizielles digitales Testzertifikat anfordern, das anschließend in der App hinzugefügt wird.</string> <!-- XTXT: Request green certificate body section 1 --> @@ -32,12 +32,25 @@ <string name="detail_green_certificate_test_type">SARS-CoV-2-Test</string> <!-- XTXT: Detail green certificate card title --> <string name="detail_green_certificate_card_title">"Testzertifikat"</string> + <!-- XTXT: Detail green certificate card subtitle --> + <string name="detail_green_certificate_card_subtitle">"Test durchgeführt am %1$s %2$s"</string> <!-- XTXT: Detail green certificate travel notice link en --> <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> <!-- XTXT: Detail green certificate travel notice link de --> <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Detail green certificate menu item: delete --> + <string name="green_certificate_details_menu_item_delete">"Entfernen"</string> + <!-- XTXT: Detail green certificate diaglog title --> + <string name="green_certificate_details_dialog_remove_test_title">"Wollen Sie das Testzertifikat wirklich entfernen?"</string> + <!-- XTXT: Detail green certificate diaglog message --> + <string name="green_certificate_details_dialog_remove_test_message">"Wenn Sie das Testzertifikat entfernen, können Sie es nicht mehr als Nachweis in der App verwenden."</string> + <!-- XTXT: Detail green certificate diaglog positive --> + <string name="green_certificate_details_dialog_remove_test_button_positive">"Entfernen"</string> + <!-- XTXT: Detail green certificate diaglog negative --> + <string name="green_certificate_details_dialog_remove_test_button_negative">"Abbrechen"</string> + <!-- XTXT: Green certificate main screen title --> - <string name="certification_screen_title">Nachweise</string> + <string name="certification_screen_title">Zertifikate</string> <!-- XTXT: Green certificate menu item --> <string name="menu_certification_information">Informationen</string> <!-- XTXT: Green certificate main screen header text --> @@ -45,7 +58,46 @@ <!-- XTXT: Green certificate info card title 1st line --> <string name="info_banner_title_1">Digitales</string> <!-- XTXT: Green certificate info card title 2nd line --> - <string name="info_banner_title_2">COVID-19-Testzertifikat</string> + <string name="info_banner_title_2">COVID-Testzertifikat</string> <!-- XTXT: Green certificate info card body --> <string name="info_banner_body">Registrieren Sie einen Test auf der Startseite und stimmen Sie zu, ein digitales Testzertifikat zu erhalten. Sobald das Zertifikat vorliegt, wird es hier angezeigt.</string> + + <!-- XTXT: Test certificate time --> + <string name="test_certificate_time">Test durchgeführt am %1$s, %2$s Uhr</string> + <!-- XTXT: Test error label --> + <string name="test_certificate_error_label">Fehler bei der Zertifikatsabfrage</string> + <!-- XBUT: Test error retry button --> + <string name="test_certificate_error_retry_button">Nochmal versuchen</string> + <!-- XTXT: Error text --> + <string name="error_tc_try_again">Es konnte keine Verbindung hergestellt werden. Bitte versuchen Sie es erneut.</string> + <!-- XTXT: Error text --> + <string name="error_tc_dcc_not_supported_by_lab">Ein Testzertifikat kann nicht angefordert werden, da diese Teststelle die Ausstellung von Testzertifikaten nicht unterstützt. Bitte entfernen Sie das Zertifikat oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_no_network">Ihre Internetverbindung wurde unterbrochen. Bitte prüfen Sie die Verbindung und versuchen Sie es erneut.</string> + <!-- XTXT: Error text --> + <string name="error_tc_e2e_error_call_hotline">Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_try_again_dcc_not_available_yet">Ihr Zertifikat liegt noch nicht vor. Bitte versuchen Sie es noch einmal. Sollte der Fehler weiterhin bestehen, kontaktieren Sie bitte die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_client_error_call_hotline">Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_dcc_expired">Das Zertifikat ist nicht mehr aktuell, Sie können es aus der Corona-App entfernen.</string> + <!-- XBUT: Test error delete button --> + <string name="test_certificate_error_delete_button">Testzertifikat entfernen</string> + <!-- XTXT: Test error refresh dialog title --> + <string name="test_certificate_refresh_dialog_title">"Es gibt weiterhin Probleme bei der Abfrage"</string> + <!-- XBUT: Test error refresh dialog confirm button --> + <string name="test_certificate_refresh_dialog_confirm_button">"OK"</string> + <!-- XTXT: Test error delete dialog title --> + <string name="test_certificate_delete_dialog_title">"Wollen Sie das Zertifikat wirklich entfernen?"</string> + <!-- XTXT: Test error delete dialog body --> + <string name="test_certificate_delete_dialog_body">"Wenn das Zertifikat enfernt wird, kann es nicht noch einmal angefordert werden."</string> + <!-- XBUT: Test error delete dialog confirm button --> + <string name="test_certificate_delete_dialog_confirm_button">"Entfernen"</string> + <!-- XBUT: Test error delete dialog cancel button --> + <string name="test_certificate_delete_dialog_cancel_button">"Abbrechen"</string> + <!-- XTXT: Test error card body refreshing --> + <string name="test_certificate_error_label_refreshing">"Ihr Zertifikat wird gerade erstellt…"</string> + <!-- XTXT: Test error card body refreshing --> + <string name="test_certificate_error_refreshing_status">"Ihr Zertifikat wird gerade angefragt, dies kann einige Minuten dauern…"</string> </resources> diff --git a/Corona-Warn-App/src/main/res/values-de/legal_strings.xml b/Corona-Warn-App/src/main/res/values-de/legal_strings.xml index 2b0d81b0c5cefe7062a88339699da55e7c339d1b..d10ff685cd7ce7fee733a4757f8237505e5aad53 100644 --- a/Corona-Warn-App/src/main/res/values-de/legal_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/legal_strings.xml @@ -185,13 +185,13 @@ <!-- XTXT: Request GC title for privacy card --> <string name="request_green_certificate_privacy_title">Datenschutz und Datensicherheit</string> <!-- XTXT: Request GC bullet point 1 for privacy card --> - <string name="request_green_certificate_privacy_section_1" translatable="false">Die Anforderung und Verwendung des digitalen COVID-19-Testzertifikats ist freiwillig. Der Nachweis eines negativen Testergebnisses kann auch auf andere Weise erbracht werden.</string> + <string name="request_green_certificate_privacy_section_1" translatable="false">Die Anforderung und Verwendung des digitalen COVID-Testzertifikats ist freiwillig. Der Nachweis eines negativen Testergebnisses kann auch auf andere Weise erbracht werden.</string> <!-- XTXT: Request GC bullet point 3 for privacy card --> <string name="request_green_certificate_privacy_section_2" translatable="false">Zur Erstellung Ihres Testzertifikats werden Daten einmalig verschlüsselt an das RKI übermittelt. Das RKI signiert die Daten elektronisch, um die Gültigkeit zu bestätigen. Die Daten werden beim RKI nicht gespeichert.</string> <!-- XTXT: Request GC extra bullet point PCR test for privacy card --> <string name="request_green_certificate_privacy_pcr_extra_section" translatable="false">Um sicherzustellen, dass niemand mit Ihrem QR-Code Ihr Testergebnis abrufen kann, wird es mit Ihrem Geburtsdatum geschützt. Ihr Geburtsdatum wird verschlüsselt an das Labor übermittelt, um das Testergebnis abzurufen.</string> <!-- XTXT: Request GC bullet point 3 for privacy card --> - <string name="request_green_certificate_privacy_section_3" translatable="false">Das COVID-19-Testzertifikat enthält die Daten über Ihren Corona-Test. Zum Nachweis des negativen Testergebnisses in den gesetzlich vorgesehenen Fällen genügt das Vorzeigen des QR-Codes in der App. Stellen Sie das Testzertifikat niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten ausgelesen werden.</string> + <string name="request_green_certificate_privacy_section_3" translatable="false">Das COVID-Testzertifikat enthält die Daten über Ihren Corona-Test. Zum Nachweis des negativen Testergebnisses in den gesetzlich vorgesehenen Fällen genügt das Vorzeigen des QR-Codes in der App. Stellen Sie das Testzertifikat niemandem zur Verfügung, wenn Sie nicht wollen, dass die Daten ausgelesen werden.</string> <!-- XTXT: Request GC bullet point 4 for privacy card --> <string name="request_green_certificate_privacy_section_4" translatable="false">Wenn Sie den QR-Code des Testzertifikats in der App vorzeigen und dieser mit der Prüf-App gescannt wird, können andere Personen Ihr Testergebnis nachvollziehen. Bei der Prüfung werden in der offiziellen Prüf-App auch Ihr Name und Ihr Geburtsdatum angezeigt.</string> <!-- XTXT: Request GC bullet point 5 for privacy card --> diff --git a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml index a6cbbe492b6992fdf059623c3e726c29ee41198c..bf012a6726c6eb8605a6c00fbd1cdc72000ac910 100644 --- a/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/release_info_strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -17,22 +17,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>Digitaler Impfnachweis</item> + <item>COVID-Testzertifikat</item> + <item>Erweiterung Kontakt-Tagebuch</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>Sie können jetzt Ihre Impfzertifikate in der App hinzufügen und per QR-Code vorweisen. 14 Tage nach der letzten Impfung zeigt die App den vollständigen Impfschutz an.</item> + <item>Sie können nun über die App ein offizielles digitales Testzertifikat anfordern und anschließend in der App hinzufügen. Sie können das Testzertifikat (QR-Code) innerhalb der EU verwenden, um ein negatives Testergebnis nachzuweisen (z.B. für Auslandsreisen).</item> + <item>Sobald Ihr Testergebnis vorliegt, wird es nun auch Ihrem Kontakt-Tagebuch hinzugefügt.</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item></item> + <item></item> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item></item> + <item></item> </string-array> </resources> diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml index 84d82a8f6995062c1ae472b157ac0de81af3b1af..3c2ca8c99dbbce8683002e84e14fedda9ea8a9e0 100644 --- a/Corona-Warn-App/src/main/res/values-de/strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/strings.xml @@ -1031,7 +1031,7 @@ <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Jeder Test kann nur einmal gescannt werden. Die App kann maximal einen Schnelltest und einen PCR-Test gleichzeitig verwalten."</string> <!-- YTXT: Body sub text 3 for Submission Consent call test result --> - <string name="submission_consent_call_test_result_checkmark_text">"Wenn Ihr Testergebnis negativ ist, können Sie dies in Form eines offiziellen COVID-19-Testzertifikats bestätigen lassen. Fordern Sie hierfür in den folgenden Schritten das Testzertifikat an."</string> + <string name="submission_consent_call_test_result_checkmark_text">"Wenn Ihr Testergebnis negativ ist, können Sie dies in Form eines offiziellen COVID-Testzertifikats bestätigen lassen. Fordern Sie hierfür in den folgenden Schritten das Testzertifikat an."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Helfen Sie mit, indem Sie andere warnen, denen Sie begegnet sind!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1193,7 +1193,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"QR-Code scannen"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Erhalten Sie Ihr Testergebnis in der App und warnen Sie andere oder fordern Sie Ihr COVID-19-Testzertifikat an."</string> + <string name="submission_dispatcher_qr_card_text">"Erhalten Sie Ihr Testergebnis in der App und warnen Sie andere oder fordern Sie Ihr COVID-Testzertifikat an."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"TAN für PCR-Test eingeben"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1853,7 +1853,7 @@ <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">Check-in</string> <!-- XHED: Title for BottomNav certificates screen title --> - <string name="bottom_nav_certificates_title">"Nachweise"</string> + <string name="bottom_nav_certificates_title">"Zertifikate"</string> <!-- #################################### Data Donation & Survey ###################################### --> 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 e3dbe17b033fd3d75789f0335d08e9beb91a9368..073658eb88562ccd8179fb6d7d22387431636cd3 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 @@ -71,7 +71,9 @@ <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Hinzufügen"</string> <!-- XHED: Homescreen vaccination status card title --> - <string name="vaccination_card_status_title">Digitaler Impfnachweis</string> + <string name="vaccination_card_status_title_line_1">"Digitaler"</string> + <!-- XHED: Homescreen vaccination status card title --> + <string name="vaccination_card_status_title_line_2">"Impfnachweis"</string> <!-- XHED: Homescreen vaccination status card vaccination name --> <string name="vaccination_card_status_vaccination_name">SARS-CoV-2 Impfschutz</string> <!-- XTXT: Homescreen card incomplete vaccination status label --> @@ -85,6 +87,8 @@ <item quantity="few">Vollständiger Impfschutz in %1$d Tagen</item> <item quantity="many">Vollständiger Impfschutz in %1$d Tagen</item> </plurals> + <!-- XTXT: Homescreen card complete vaccination status label --> + <string name="vaccination_card_status_vaccination_complete">"Gültig bis einschließlich %s"</string> <!-- XTXT: Vaccination QR code scan error message--> <string name="error_vc_invalid">Dieser QR-Code ist kein gültiges Impfzertifikat.</string> diff --git a/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml index be0ef44805562242bd599d048b3af7c66de14283..566c614eb2cf7ad4f67557ea834c5a065deca493 100644 --- a/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/antigen_strings.xml @@ -6,8 +6,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"Register Your Test"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Use the app to register your tests and retrieve your test results, so you can warn others more quickly."</string> - + <string name="ag_homescreen_card_test_register_body">"Use the app to register your tests and retrieve your test results, so you can warn others more quickly, or to request your COVID test certificate."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml index d323982a0fcdcb275c2296e8ba8af2a7a94781c1..27451f4224fd79befd9bd7c158edcc459f5835e7 100644 --- a/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/contact_diary_strings.xml @@ -182,4 +182,13 @@ <string name="contact_diary_location_visit_duration_hour">"hrs"</string> <!-- XTXT: Option - person encounter - circumstances hint--> <string name="contact_diary_location_visit_circumstances_hint">"Note (e.g. very full)"</string> + + <!-- XTXT: PCR test title in the day overview --> + <string name="contact_diary_corona_test_pcr_title">"PCR Test Registered"</string> + <!-- XTXT: RAT test title in the day overview --> + <string name="contact_diary_corona_test_rat_title">"Rapid Test Performed"</string> + <!-- XTXT: positive test result in the day overview --> + <string name="contact_diary_corona_test_positive">"Positive Diagnosis"</string> + <!-- XTXT: negative test result in the day overview --> + <string name="contact_diary_corona_test_negative">"Negative Diagnosis"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-en/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-en/event_registration_strings.xml index 5c5c18dfeed7053489d00d31ed5824149ff60271..c6b317bfcf80acb99f637af506137f0f6578d223 100644 --- a/Corona-Warn-App/src/main/res/values-en/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/event_registration_strings.xml @@ -210,6 +210,8 @@ <string name="trace_location_organizer_detail_item_duration">"%1$s, %2$s - %3$s"</string> <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> + <!-- XTXT: Past Event Info duration --> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"Duplicate"</string> diff --git a/Corona-Warn-App/src/main/res/values-en/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-en/green_certificate_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..4d945372179f5d78a3cd8891514823e8e52f5377 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-en/green_certificate_strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <!-- XTXT: Request green certificate title --> + <string name="request_green_certificate_title">"COVID Test Certificate"</string> + <!-- XTXT: Request green certificate subtitle --> + <string name="request_green_certificate_subtitle">"You can use the app to request an official digital test certificate, which is then added in the app."</string> + <!-- XTXT: Request green certificate body section 1 --> + <string name="request_green_certificate_body_1">"The test certificate is only issued if your test result is negative."</string> + <!-- XTXT: Request green certificate body section 2 --> + <string name="request_green_certificate_body_2">"The test certificate is valid proof of a negative test result within the EU (e.g. for travel)."</string> + <!-- XTXT: Request green certificate body section 3 --> + <string name="request_green_certificate_body_3">"The test certificate contains valid that enables the verification app to validate your certificate."</string> + <!-- XBUT: Request green certificate agree button --> + <string name="request_green_certificate_agree_button">"Request Test Certificate"</string> + <!-- XBUT: Request green certificate disagree button --> + <string name="request_green_certificate_disagree_button">"No Thank You"</string> + <!-- XTXT: Request green certificate birth date description --> + <string name="request_green_certificate_birthdate_description">"For security purposes, your test result is protected with your date of birth. The test result can only be issued if you specify the correct date of birth."</string> + <!-- XTXT: Request green certificate birth date hint --> + <string name="request_green_certificate_birthdate_hint">"Date of Birth"</string> + <!-- XTXT: Request green certificate exit dialog title --> + <string name="request_gc_dialog_title">"Cancel Registration"</string> + <!-- XTXT: Request green certificate exit dialog message --> + <string name="request_gc_dialog_message">"If you cancel the test registration, you cannot receive your test result in the app."</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_positive_button">"OK"</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_negative_button">"Cancel"</string> + <!-- XTXT: Detail green certificate title --> + <string name="detail_green_certificate_title">"EU Digital COVID Test Certificate"</string> + <!-- XTXT: Detail green certificate test type --> + <string name="detail_green_certificate_test_type">"SARS-CoV-2 Test"</string> + <!-- XTXT: Detail green certificate card title --> + <string name="detail_green_certificate_card_title">"Test Certificate"</string> + <!-- XTXT: Detail green certificate travel notice link en --> + <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> + <!-- XTXT: Detail green certificate travel notice link de --> + <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Green certificate main screen title --> + <string name="certification_screen_title">"Certificates"</string> + <!-- XTXT: Green certificate menu item --> + <string name="menu_certification_information">"Information"</string> + <!-- XTXT: Green certificate main screen header text --> + <string name="green_certification_header_info">"Your digital vaccination certificates and test certificates are displayed here."</string> + <!-- XTXT: Green certificate info card title 1st line --> + <string name="info_banner_title_1">"Digital COVID-19"</string> + <!-- XTXT: Green certificate info card title 2nd line --> + <string name="info_banner_title_2">"COVID Test Certificate"</string> + <!-- XTXT: Green certificate info card body --> + <string name="info_banner_body">"Register a test on the home screen and agree to receive a digital test certificate. As soon as the certificate is available, it is displayed here."</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml index 5302462e1984ec8a7b1b38c1fffa68c67b1c991a..275ba2d9f3649f533c1a874bc1ae18d6de887ee7 100644 --- a/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/release_info_strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -16,22 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Digital Proof of Vaccination"</item> + <item>"COVID Test Certificate"</item> + <item>"Contact Journal Enhancement"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"You can now add your vaccination certificates in the app and present them via QR code. The app displays full vaccination protection 14 days after the final vaccination."</item> + <item>"You can now request an official digital test certificate through the app and then add it in the app. You can use this test certificate (QR code) within the EU to prove a negative test result (for international travel, for example)."</item> + <item>"As soon as your test result is available, it is now also added to your contact journal."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> + <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> + <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-en/strings.xml b/Corona-Warn-App/src/main/res/values-en/strings.xml index 08e17636898a81ca4b380c7e6f3baf6a56b1a3ed..d4eeb5af5155c76c7800fd3ce6fc77badebc2c26 100644 --- a/Corona-Warn-App/src/main/res/values-en/strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/strings.xml @@ -281,6 +281,8 @@ <string name="risk_details_behavior_body_wear_mask">"Wear a face mask when you encounter other people."</string> <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"Keep at least 1.5 meters distance from other people."</string> + <!-- XTXT: find details about exposures in the contact journal --> + <string name="risk_details_find_details_in_journal">"You will find more information about your exposures in your contact journal."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"If you get tested, you will find additional information about the testing procedure in the FAQ."</string> @@ -1027,6 +1029,8 @@ <string name="submission_consent_call_test_result_scan_your_test_only">"Now scan the QR code for your test and retrieve your test result."</string> <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Each test can only be scanned once. The app can manage a maximum of one rapid test and one PCR test at the same time."</string> + <!-- YTXT: Body sub text 3 for Submission Consent call test result --> + <string name="submission_consent_call_test_result_checkmark_text">"If your test result is negative, you can confirm this with an official COVID test certificate. To do so, request the test certificate in the following steps."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Please help others you have encountered by warning them!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1087,6 +1091,15 @@ <!-- YTXT: Body text for next steps section of waiting test result page --> <string name="submission_test_result_pending_steps_contact_diary_body">"As soon as your test result becomes available, it will be displayed in the app."</string> + <!-- XHED: Page headline for test result certifcate info title --> + <string name="submission_test_result_pending_steps_test_certificate_heading">"Your Test Certificate"</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"No test certificate is available because this testing point does not support the issuing of test certificates."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Your test certificate is not available yet. As soon as it becomes available, it will be displayed in your app."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"No test certificate was issued, as per your request."</string> + <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Update"</string> <!-- XBUT: test result pending : remove the test button --> @@ -1179,7 +1192,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"Scan QR Code"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Receive your test result in the app and warn others."</string> + <string name="submission_dispatcher_qr_card_text">"Receive your test result in the app and warn others or request your COVID test certificate."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"Enter TAN for PCR Test"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1838,7 +1851,8 @@ <string name="bottom_nav_diary_title">"Journal"</string> <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"Check In"</string> - + <!-- XHED: Title for BottomNav certificates screen title --> + <string name="bottom_nav_certificates_title">"Certificates"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-en/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-en/vaccination_strings.xml index 544947973a502df92d8333e1759afba156598408..7ba8457914219ee27e49c4a26182a820c56031a0 100644 --- a/Corona-Warn-App/src/main/res/values-en/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values-en/vaccination_strings.xml @@ -8,7 +8,7 @@ <!-- XTXT: Vaccination Details vaccine manufacturer --> <string name="vaccination_details_vaccine_manufacturer">"Manufacturer"</string> <!-- XTXT: Vaccination Details vaccine type --> - <string name="vaccination_details_vaccine_medical_product_name">"Vaccine type"</string> + <string name="vaccination_details_vaccine_medical_product_name">"Vaccine Type"</string> <!-- XTXT: Vaccination Details certificate issuer --> <string name="vaccination_details_certificate_issuer">"Issuer"</string> <!-- XTXT: Vaccination Details certificate country --> @@ -20,9 +20,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">"Vaccination %1$d of %2$d"</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qrcode_card_title">"Vaccination certificate %1$d of %2$d"</string> + <string name="vaccination_qrcode_card_title">"Vaccination Certificate %1$d of %2$d"</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qrcode_card_subtitle">"Vaccinated %1$s - valid to %2$s"</string> + <string name="vaccination_qrcode_card_subtitle">"Vaccinated %1$s - Valid to %2$s"</string> <!-- XTXT: Vaccination List title--> <string name="vaccination_list_title">"Digital Proof of Vaccination"</string> @@ -36,12 +36,12 @@ <string name="vaccination_list_vaccination_card_subtitle">"Performed on %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d day."</item> - <item quantity="other">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="zero">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="two">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="few">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="many">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="one">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d day."</item> + <item quantity="other">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="zero">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="two">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="few">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="many">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> </plurals> <!-- XBUT: Vaccination List register additional vaccination button --> <string name="vaccination_list_register_new_vaccination_button">"Register Another Vaccination"</string> @@ -62,9 +62,11 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Add Vaccination Certificate"</string> + <string name="vaccination_card_registration_title_line_1">"Add Digital Proof"</string> + <!-- XHED: Title for Vaccination Certificate Registration Home Card --> + <string name="vaccination_card_registration_title_line_2">"of Vaccination"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"Add vaccination certificates in the app so you always have them with you. To do so, scan the QR code on your document."</string> + <string name="vaccination_card_registration_body">"Add your vaccination certificates in the app so you always have them with you. To do so, scan the QR code on your document."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Add"</string> <!-- XHED: Homescreen vaccination status card title --> @@ -97,17 +99,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Your Consent"</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Add Vaccination Certificate"</string> + <string name="vaccination_consent_headline">"Add Certificates"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"Add your digital vaccination certificate in the app. As soon as your vaccination protection is complete, you can present the QR code in your app as proof of your vaccination."</string> + <string name="vaccination_consent_info_subtitle_text">"Add your digital COVID certificates in the app. This will enable you to prove your vaccination protection or a negative test result with the app."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"To add a vaccination certificate in the app, scan the QR code that you received during your vaccination."</string> + <string name="vaccination_consent_qr_info_text">"You can add digital COVID vaccination certificates or COVID test certificates in the app."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"The app reads the information from the QR code and saves it in a secure area on your smartphone."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"These certificates are considered to be valid proof within the EU (e.g. for travel)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"The data will remain on the smartphone. It will only be transmitted to others if you present your vaccination certificate for verification."</string> + <string name="vaccination_consent_qr_info_time_text">"After you add a certificate, it will remain on your smartphone. It will only be shared with others if you present a certificate for verification."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"For more information, please refer to the privacy notice."</string> + <string name="vaccination_consent_onboarding_legal_information">"For more detailed information about data processing, please refer to the privacy notice."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Continue"</string> diff --git a/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml index 40fe4308ce558ebe27f659c9020180cf9245dd15..076c006f193cb06bc1c01751e391bf4f5bb3e6b2 100644 --- a/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/antigen_strings.xml @@ -6,8 +6,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"Zarejestruj test"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Korzystaj z aplikacji do rejestrowania swoich testów i pobierania ich wyników, aby szybciej ostrzegać innych."</string> - + <string name="ag_homescreen_card_test_register_body">"Aplikacji możesz używać do rejestrowania testów i pobierania ich wyników, aby szybciej ostrzegać innych. Za jej poÅ›rednictwem możesz także poprosić o wydanie certyfikatu testu na COVID."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml index dcec3c8b4cf4e134170a6e6d278facaee44e05dd..0ab0471521c6c34e9e4437023f6d092014ce785b 100644 --- a/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/contact_diary_strings.xml @@ -182,4 +182,13 @@ <string name="contact_diary_location_visit_duration_hour">"godz."</string> <!-- XTXT: Option - person encounter - circumstances hint--> <string name="contact_diary_location_visit_circumstances_hint">"Uwaga (np. bardzo peÅ‚ny)"</string> + + <!-- XTXT: PCR test title in the day overview --> + <string name="contact_diary_corona_test_pcr_title">"Zarejestrowano test PCR"</string> + <!-- XTXT: RAT test title in the day overview --> + <string name="contact_diary_corona_test_rat_title">"Wykonano szybki test"</string> + <!-- XTXT: positive test result in the day overview --> + <string name="contact_diary_corona_test_positive">"Diagnoza: zakażenie"</string> + <!-- XTXT: negative test result in the day overview --> + <string name="contact_diary_corona_test_negative">"Diagnoza: brak zakażenia"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-pl/event_registration_strings.xml index 599ee758e5045059155d1660fac10953155c06a0..e699fcc2bb2acdf3f7044d21629be8f488b55f13 100644 --- a/Corona-Warn-App/src/main/res/values-pl/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/event_registration_strings.xml @@ -210,6 +210,8 @@ <string name="trace_location_organizer_detail_item_duration">"%1$s, %2$s - %3$s"</string> <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> + <!-- XTXT: Past Event Info duration --> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"Duplikuj"</string> diff --git a/Corona-Warn-App/src/main/res/values-pl/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-pl/green_certificate_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..0ab72d844ebb07cf949604ed985321f1a6f6bdb7 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-pl/green_certificate_strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <!-- XTXT: Request green certificate title --> + <string name="request_green_certificate_title">"Certyfikat testu na COVID"</string> + <!-- XTXT: Request green certificate subtitle --> + <string name="request_green_certificate_subtitle">"Za pomocÄ… aplikacji możesz poprosić o oficjalny cyfrowy certyfikat testu, który zostanie nastÄ™pnie w niej dodany."</string> + <!-- XTXT: Request green certificate body section 1 --> + <string name="request_green_certificate_body_1">"Certyfikat testu jest wydawany tylko wtedy, gdy wynik testu jest negatywny."</string> + <!-- XTXT: Request green certificate body section 2 --> + <string name="request_green_certificate_body_2">"Certyfikat testu jest ważnym dowodem negatywnego wyniku testu na terenie UE (np. dla celów podróży)."</string> + <!-- XTXT: Request green certificate body section 3 --> + <string name="request_green_certificate_body_3">"Certyfikat zawiera ważne dane, które umożliwiajÄ… aplikacji weryfikacyjnej jego walidacjÄ™."</string> + <!-- XBUT: Request green certificate agree button --> + <string name="request_green_certificate_agree_button">"PoproÅ› o certyfikat testu"</string> + <!-- XBUT: Request green certificate disagree button --> + <string name="request_green_certificate_disagree_button">"Nie, dziÄ™kujÄ™"</string> + <!-- XTXT: Request green certificate birth date description --> + <string name="request_green_certificate_birthdate_description">"Ze wzglÄ™dów bezpieczeÅ„stwa wynik testu jest zabezpieczony za pomocÄ… daty urodzenia. Wynik testu może zostać wydany tylko po podaniu prawidÅ‚owej daty urodzenia."</string> + <!-- XTXT: Request green certificate birth date hint --> + <string name="request_green_certificate_birthdate_hint">"Data urodzenia"</string> + <!-- XTXT: Request green certificate exit dialog title --> + <string name="request_gc_dialog_title">"Anuluj rejestracjÄ™"</string> + <!-- XTXT: Request green certificate exit dialog message --> + <string name="request_gc_dialog_message">"Anulowanie rejestracji testu uniemożliwi otrzymanie wyniku testu w aplikacji."</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_positive_button">"OK"</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_negative_button">"Anuluj"</string> + <!-- XTXT: Detail green certificate title --> + <string name="detail_green_certificate_title">"Unijny cyfrowy certyfikat testu na COVID"</string> + <!-- XTXT: Detail green certificate test type --> + <string name="detail_green_certificate_test_type">"Test SARS-CoV-2"</string> + <!-- XTXT: Detail green certificate card title --> + <string name="detail_green_certificate_card_title">"Certyfikat testu"</string> + <!-- XTXT: Detail green certificate travel notice link en --> + <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> + <!-- XTXT: Detail green certificate travel notice link de --> + <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Green certificate main screen title --> + <string name="certification_screen_title">"Certyfikaty"</string> + <!-- XTXT: Green certificate menu item --> + <string name="menu_certification_information">"Informacje"</string> + <!-- XTXT: Green certificate main screen header text --> + <string name="green_certification_header_info">"Tutaj wyÅ›wietlane sÄ… Twoje cyfrowe Å›wiadectwa szczepieÅ„ i certyfikaty testów."</string> + <!-- XTXT: Green certificate info card title 1st line --> + <string name="info_banner_title_1">"Cyfrowy COVID-19"</string> + <!-- XTXT: Green certificate info card title 2nd line --> + <string name="info_banner_title_2">"Certyfikat testu na COVID"</string> + <!-- XTXT: Green certificate info card body --> + <string name="info_banner_body">"Zarejestruj test na ekranie głównym i wyraź zgodÄ™ na otrzymanie cyfrowego certyfikatu testu. Gdy tylko certyfikat bÄ™dzie dostÄ™pny, pojawi siÄ™ w tym miejscu."</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml index db68e982dfac05b2d6862c89aadd7f113c60398f..36caa53180961d8cf85a5a88791ab0b99c3e4a64 100644 --- a/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/release_info_strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -16,22 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Cyfrowy dowód szczepienia"</item> + <item>"Certyfikat testu na COVID"</item> + <item>"Rozszerzenie dziennika kontaktów"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Teraz możesz dodać swoje Å›wiadectwa szczepienia w aplikacji i okazywać je za pomocÄ… kodu QR. Aplikacja wyÅ›wietla peÅ‚nÄ… ochronÄ™ poszczepiennÄ… 14 dni po ostatnim szczepieniu."</item> + <item>"Możesz teraz poprosić o oficjalny cyfrowy certyfikat testu za poÅ›rednictwem aplikacji, a nastÄ™pnie dodać go w aplikacji. Certyfikatu testu (kodu QR) możesz używać w UE jako dowodu negatywnego wyniku testu (na przykÅ‚ad w przypadku podróży miÄ™dzynarodowych)."</item> + <item>"Gdy tylko Twój wynik testu bÄ™dzie dostÄ™pny, zostanie on również dodany do Twojego dziennika kontaktów."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> + <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> + <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-pl/strings.xml b/Corona-Warn-App/src/main/res/values-pl/strings.xml index 0ceebd83a9b4b094d92da9609691f7ec2ddfad30..e7ebf5e3ecead984375f3a3f95010e35aaed009c 100644 --- a/Corona-Warn-App/src/main/res/values-pl/strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/strings.xml @@ -281,6 +281,8 @@ <string name="risk_details_behavior_body_wear_mask">"Załóż maseczkÄ™ na twarz, majÄ…c kontakt z innymi osobami."</string> <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"Zachowuj odlegÅ‚ość co najmniej 1,5 metra od innych osób."</string> + <!-- XTXT: find details about exposures in the contact journal --> + <string name="risk_details_find_details_in_journal">"WiÄ™cej informacji na temat narażeÅ„ znajdziesz w swoim dzienniku kontaktów."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"JeÅ›li poddasz siÄ™ testowi, dodatkowe informacje na temat procedury testowania znajdziesz w „CzÄ™sto zadawanych pytaniachâ€."</string> @@ -1027,6 +1029,8 @@ <string name="submission_consent_call_test_result_scan_your_test_only">"Zeskanuj teraz kod QR dla swojego testu i pobierz wynik testu."</string> <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Test można zeskanować tylko raz. Aplikacja może zarzÄ…dzać maksymalnie jednym szybkim testem i jednym testem PCR jednoczeÅ›nie."</string> + <!-- YTXT: Body sub text 3 for Submission Consent call test result --> + <string name="submission_consent_call_test_result_checkmark_text">"JeÅ›li wynik Twojego testu jest negatywny, możesz to potwierdzić oficjalnym certyfikatem testu na COVID. W tym celu poproÅ› o certyfikat testu, wykonujÄ…c nastÄ™pujÄ…ce kroki."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Pomóż osobom, z którymi miaÅ‚eÅ›(-aÅ›) kontakt, ostrzegajÄ…c ich!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1087,6 +1091,15 @@ <!-- YTXT: Body text for next steps section of waiting test result page --> <string name="submission_test_result_pending_steps_contact_diary_body">"Wynik testu zostanie wyÅ›wietlony w aplikacji, jak tylko bÄ™dzie dostÄ™pny."</string> + <!-- XHED: Page headline for test result certifcate info title --> + <string name="submission_test_result_pending_steps_test_certificate_heading">"Twój certyfikat testu "</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"Certyfikat testu jest niedostÄ™pny, ponieważ ten punkt testowania nie wydaje certyfikatów testów."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Twój certyfikat testu nie jest jeszcze dostÄ™pny. Gdy tylko stanie siÄ™ dostÄ™pny, pojawi siÄ™ w Twojej aplikacji."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"Zgodnie z TwojÄ… proÅ›bÄ… nie wydano certyfikatu testu."</string> + <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Aktualizuj"</string> <!-- XBUT: test result pending : remove the test button --> @@ -1179,7 +1192,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"Skanuj kod QR"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Odbierz wynik testu w aplikacji i ostrzegaj innych."</string> + <string name="submission_dispatcher_qr_card_text">"Odbierz wynik testu w aplikacji i ostrzegaj innych lub poproÅ› o certyfikat testu na COVID."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"Wpisz TAN do testu PCR"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1838,7 +1851,8 @@ <string name="bottom_nav_diary_title">"Dziennik"</string> <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"Zamelduj siÄ™"</string> - + <!-- XHED: Title for BottomNav certificates screen title --> + <string name="bottom_nav_certificates_title">"Certyfikaty"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-pl/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-pl/vaccination_strings.xml index cee8e892c5edb5e37b6490b96dcda379bdb770f6..b0453ba0a663964ad8981315f5c3bca5759cce57 100644 --- a/Corona-Warn-App/src/main/res/values-pl/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values-pl/vaccination_strings.xml @@ -20,9 +20,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">"Szczepienie %1$d / %2$d"</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qrcode_card_title">"Åšwiadectwo szczepienia %1$d / %2$d"</string> + <string name="vaccination_qrcode_card_title">"Åšwiadectwo szczepienia %1$d %2$d"</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qrcode_card_subtitle">"Zaszczepiony(-a) %1$s - ważne do %2$s"</string> + <string name="vaccination_qrcode_card_subtitle">"Zaszczepiono %1$s - ważne do %2$s"</string> <!-- XTXT: Vaccination List title--> <string name="vaccination_list_title">"Cyfrowy dowód szczepienia"</string> @@ -36,12 +36,12 @@ <string name="vaccination_list_vaccination_card_subtitle">"Wykonano dnia %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dzieÅ„."</item> - <item quantity="other">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> - <item quantity="zero">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> - <item quantity="two">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> - <item quantity="few">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> - <item quantity="many">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> + <item quantity="one">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dzieÅ„."</item> + <item quantity="other">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dnia."</item> + <item quantity="zero">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> + <item quantity="two">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> + <item quantity="few">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> + <item quantity="many">"OtrzymaÅ‚eÅ›(-aÅ›) już wszystkie zaplanowane dotychczas szczepienia, jednak peÅ‚nÄ… ochronÄ™ poszczepiennÄ… uzyskasz dopiero za %1$d dni."</item> </plurals> <!-- XBUT: Vaccination List register additional vaccination button --> <string name="vaccination_list_register_new_vaccination_button">"Zarejestruj kolejne szczepienie"</string> @@ -62,9 +62,11 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Dodaj Å›wiadectwo szczepienia"</string> + <string name="vaccination_card_registration_title_line_1">"Dodaj cyfrowy dowód"</string> + <!-- XHED: Title for Vaccination Certificate Registration Home Card --> + <string name="vaccination_card_registration_title_line_2">"szczepienia"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"Dodaj Å›wiadectwa szczepienia w aplikacji, aby mieć je zawsze przy sobie. W tym celu zeskanuj kod QR znajdujÄ…cy siÄ™ na dokumencie."</string> + <string name="vaccination_card_registration_body">"Dodaj swoje Å›wiadectwa szczepienia w aplikacji, aby mieć je zawsze przy sobie. W tym celu zeskanuj kod QR znajdujÄ…cy siÄ™ na dokumencie."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Dodaj"</string> <!-- XHED: Homescreen vaccination status card title --> @@ -97,17 +99,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Twoja zgoda"</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Dodaj Å›wiadectwo szczepienia"</string> + <string name="vaccination_consent_headline">"Dodaj Å›wiadectwa"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"Dodaj cyfrowe Å›wiadectwo szczepienia w aplikacji. Gdy uzyskasz peÅ‚nÄ… ochronÄ™ poszczepiennÄ…, bÄ™dziesz mógÅ‚ (mogÅ‚a) okazywać kod QR z aplikacji jako dowód szczepienia."</string> + <string name="vaccination_consent_info_subtitle_text">"Dodaj cyfrowe Å›wiadectwa szczepienia przeciw COVID w aplikacji. Umożliwi Ci to udowodnienie za pomocÄ… aplikacji posiadanie ochrony poszczepiennej lub negatywnego wyniku testu."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"Aby dodać Å›wiadectwo szczepienia w aplikacji, zeskanuj kod QR, który otrzymaÅ‚eÅ›(-aÅ›) podczas szczepienia."</string> + <string name="vaccination_consent_qr_info_text">"W aplikacji możesz dodać cyfrowe Å›wiadectwa szczepieÅ„ przeciw COVID lub certyfikaty testów na COVID."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"Aplikacja odczytuje informacje z kodu QR i zapisuje je w bezpiecznym miejscu na Twoim smartfonie."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"Takie Å›wiadectwa i certyfikaty sÄ… uznawane za ważny dowód w UE (np. w przypadku podróży)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"Dane pozostanÄ… na smartfonie. BÄ™dÄ… one przekazywane innym osobom tylko wtedy, gdy bÄ™dziesz przedstawiać Å›wiadectwo szczepienia do weryfikacji."</string> + <string name="vaccination_consent_qr_info_time_text">"Po dodaniu certyfikatu pozostanie on na Twoim smartfonie. Zostanie on udostÄ™pniony innym tylko podczas jego okazywania w celu weryfikacji."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"WiÄ™cej informacji znajduje siÄ™ w oÅ›wiadczeniu o ochronie prywatnoÅ›ci."</string> + <string name="vaccination_consent_onboarding_legal_information">"WiÄ™cej informacji na temat przetwarzania danych znajduje siÄ™ w oÅ›wiadczeniu o ochronie prywatnoÅ›ci."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Kontynuuj"</string> diff --git a/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml index 2a990d20c38dfc36f0632f9e6f06aa74b670fa1a..245102cd46f431beef2ed97554a2d35b0bf68f95 100644 --- a/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/antigen_strings.xml @@ -6,8 +6,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"ÃŽnregistrarea testului dvs."</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"UtilizaÈ›i aplicaÈ›ia pentru a înregistra testele dvs. È™i a afla rezultatele testelor dvs. pentru a-i putea avertiza pe ceilalÈ›i mai rapid."</string> - + <string name="ag_homescreen_card_test_register_body">"UtilizaÈ›i aplicaÈ›ia pentru a înregistra testele dvs. È™i a afla rezultatele testelor dvs. pentru a-i putea avertiza pe ceilalÈ›i mai rapid sau pentru a solicita certificatul dvs. de test COVID."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml index 74c6a300dd3ad7dd45598b880be494b962a29d81..be9b9e63c85d12a702bee2245b3f1abf8614ef80 100644 --- a/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/contact_diary_strings.xml @@ -182,4 +182,13 @@ <string name="contact_diary_location_visit_duration_hour">"ore"</string> <!-- XTXT: Option - person encounter - circumstances hint--> <string name="contact_diary_location_visit_circumstances_hint">"Notă (de ex., foarte plin)"</string> + + <!-- XTXT: PCR test title in the day overview --> + <string name="contact_diary_corona_test_pcr_title">"Test PCR înregistrat"</string> + <!-- XTXT: RAT test title in the day overview --> + <string name="contact_diary_corona_test_rat_title">"Test rapid efectuat"</string> + <!-- XTXT: positive test result in the day overview --> + <string name="contact_diary_corona_test_positive">"Diagnostic pozitiv"</string> + <!-- XTXT: negative test result in the day overview --> + <string name="contact_diary_corona_test_negative">"Diagnostic negativ"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-ro/event_registration_strings.xml index d6927fcd4b62b48262dc552417930e15c8a64afa..59e14a2ecd1a29bf638ce5b091ef83f63a4a7c55 100644 --- a/Corona-Warn-App/src/main/res/values-ro/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/event_registration_strings.xml @@ -210,6 +210,8 @@ <string name="trace_location_organizer_detail_item_duration">"%1$s, %2$s - %3$s"</string> <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> + <!-- XTXT: Past Event Info duration --> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"Duplicare"</string> diff --git a/Corona-Warn-App/src/main/res/values-ro/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-ro/green_certificate_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..348c6a1a29591723d6f8606f85c57b92eb9308d2 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-ro/green_certificate_strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <!-- XTXT: Request green certificate title --> + <string name="request_green_certificate_title">"Certificat de test COVID"</string> + <!-- XTXT: Request green certificate subtitle --> + <string name="request_green_certificate_subtitle">"PuteÈ›i utiliza aplicaÈ›ia pentru a solicita un certificat de test digital oficial, care este apoi adăugat în aplicaÈ›ie."</string> + <!-- XTXT: Request green certificate body section 1 --> + <string name="request_green_certificate_body_1">"Certificatul de test este emis doar dacă rezultatul testului dvs. este negativ."</string> + <!-- XTXT: Request green certificate body section 2 --> + <string name="request_green_certificate_body_2">"Certificatul de test este o dovadă valabilă a unui rezultat de test negativ în UE (de exemplu, în scop de călătorie)."</string> + <!-- XTXT: Request green certificate body section 3 --> + <string name="request_green_certificate_body_3">"Certificatul de test conÈ›ine date care îi permit aplicaÈ›iei de verificare să vă valideze certificatul."</string> + <!-- XBUT: Request green certificate agree button --> + <string name="request_green_certificate_agree_button">"Solicitare certificat de test"</string> + <!-- XBUT: Request green certificate disagree button --> + <string name="request_green_certificate_disagree_button">"Nu, mulÈ›umesc"</string> + <!-- XTXT: Request green certificate birth date description --> + <string name="request_green_certificate_birthdate_description">"ÃŽn scop de securitate, rezultatul testului dvs. este protejat cu data naÈ™terii dvs. Rezultatul de test poate fi emis doar dacă specificaÈ›i data corectă a naÈ™terii."</string> + <!-- XTXT: Request green certificate birth date hint --> + <string name="request_green_certificate_birthdate_hint">"Data naÈ™terii"</string> + <!-- XTXT: Request green certificate exit dialog title --> + <string name="request_gc_dialog_title">"Anulare înregistrare"</string> + <!-- XTXT: Request green certificate exit dialog message --> + <string name="request_gc_dialog_message">"Dacă anulaÈ›i înregistrarea testului, nu puteÈ›i primi rezultatul testului dvs. în aplicaÈ›ie."</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_positive_button">"OK"</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_negative_button">"Anulare"</string> + <!-- XTXT: Detail green certificate title --> + <string name="detail_green_certificate_title">"Certificat de test COVID digital UE"</string> + <!-- XTXT: Detail green certificate test type --> + <string name="detail_green_certificate_test_type">"Test SARS-CoV-2"</string> + <!-- XTXT: Detail green certificate card title --> + <string name="detail_green_certificate_card_title">"Certificat de test"</string> + <!-- XTXT: Detail green certificate travel notice link en --> + <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> + <!-- XTXT: Detail green certificate travel notice link de --> + <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Green certificate main screen title --> + <string name="certification_screen_title">"Certificate"</string> + <!-- XTXT: Green certificate menu item --> + <string name="menu_certification_information">"InformaÈ›ii"</string> + <!-- XTXT: Green certificate main screen header text --> + <string name="green_certification_header_info">"Certificatele dvs. de vaccinare digitale È™i certificatele de test digitale sunt afiÈ™ate aici."</string> + <!-- XTXT: Green certificate info card title 1st line --> + <string name="info_banner_title_1">"Certificat de test"</string> + <!-- XTXT: Green certificate info card title 2nd line --> + <string name="info_banner_title_2">"Certificat de test COVID"</string> + <!-- XTXT: Green certificate info card body --> + <string name="info_banner_body">"ÃŽnregistraÈ›i un test pe ecranul iniÈ›ial È™i consimÈ›iÈ›i să primiÈ›i un certificat de test digital. De îndată ce este disponibil certificatul, acesta este afiÈ™at aici."</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml index e9834861c1fde15d9569a64200fd224bc8d85a93..69ae9eb9e9b9c797adce02846358ec69a298ac2b 100644 --- a/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/release_info_strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -16,22 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Dovada digitală a vaccinării"</item> + <item>"Certificat de test COVID"</item> + <item>"Extinderea jurnalului de contacte"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Acum puteÈ›i adăuga certificatele dvs. de vaccinare în aplicaÈ›ie È™i le puteÈ›i prezenta prin codul QR. AplicaÈ›ia arată protecÈ›ia prin vaccinarea completă la 14 zile de la vaccinarea finală."</item> + <item>"Acum puteÈ›i solicita un certificat de test digital oficial prin aplicaÈ›ie È™i apoi îl puteÈ›i adăuga în aplicaÈ›ie. PuteÈ›i utiliza acest certificat de test (cod QR) în UE pentru a face dovada unui rezultat de test negativ (de exemplu, pentru călătoria internaÈ›ională)."</item> + <item>"De îndată ce rezultatul testului dvs. este disponibil, acum acesta este adăugat È™i la jurnalul dvs. de contacte."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> + <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> + <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-ro/strings.xml b/Corona-Warn-App/src/main/res/values-ro/strings.xml index 2307b28d73abd0aa099f4eb10ab33d7406464485..fc3960ab897d37077eb075fc61d29a13f771909b 100644 --- a/Corona-Warn-App/src/main/res/values-ro/strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/strings.xml @@ -281,6 +281,8 @@ <string name="risk_details_behavior_body_wear_mask">"PurtaÈ›i o mască facială când întâlniÈ›i alte persoane."</string> <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"PăstraÈ›i o distanță de cel puÈ›in 1,5 metri față de alte persoane."</string> + <!-- XTXT: find details about exposures in the contact journal --> + <string name="risk_details_find_details_in_journal">"VeÈ›i găsi mai multe informaÈ›ii despre expunerile dvs. în jurnalul dvs. de contacte."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"Dacă vă testaÈ›i, veÈ›i găsi mai multe informaÈ›ii despre procedura de testare în ÃŽntrebările frecvente."</string> @@ -1027,6 +1029,8 @@ <string name="submission_consent_call_test_result_scan_your_test_only">"Acum scanaÈ›i codul QR de pe testul dvs. È™i aflaÈ›i rezultatul testului."</string> <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Fiecare test poate fi scanat o singură dată. AplicaÈ›ia poate gestiona cel mult un test rapid È™i un test PCR în acelaÈ™i timp."</string> + <!-- YTXT: Body sub text 3 for Submission Consent call test result --> + <string name="submission_consent_call_test_result_checkmark_text">"Dacă rezultatul testului dvs. este negativ, puteÈ›i confirma acest lucru cu un certificat de test COVID oficial. Pentru aceasta, solicitaÈ›i certificatul de test în următoarele etape."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Vă rugăm să îi ajutaÈ›i pe ceilalÈ›i cu care v-aÈ›i întâlnit, avertizându-i!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1087,6 +1091,15 @@ <!-- YTXT: Body text for next steps section of waiting test result page --> <string name="submission_test_result_pending_steps_contact_diary_body">"Imediat ce devine disponibil, rezultatul testului dvs. va fi afiÈ™at în aplicaÈ›ie."</string> + <!-- XHED: Page headline for test result certifcate info title --> + <string name="submission_test_result_pending_steps_test_certificate_heading">"Certificatul dvs. de test"</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"Nu este disponibil niciun certificat de test deoarece acest punct de testare nu suportă emiterea de certificate de test."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Certificatul dvs. de test nu este disponibil încă. De îndată ce devine disponibil, va fi afiÈ™at în aplicaÈ›ie."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"Nu a fost emis niciun certificat de test, conform cererii dvs."</string> + <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Actualizare"</string> <!-- XBUT: test result pending : remove the test button --> @@ -1179,7 +1192,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"Scanare cod QR"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"PrimiÈ›i rezultatul testului dvs. în aplicaÈ›ie È™i avertizaÈ›i-i pe ceilalÈ›i."</string> + <string name="submission_dispatcher_qr_card_text">"PrimiÈ›i rezultatul testului dvs. în aplicaÈ›ie È™i avertizaÈ›i-i pe ceilalÈ›i sau solicitaÈ›i certificatul dvs. de test COVID."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"IntroduceÈ›i codul TAN pentru testul PCR"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1838,7 +1851,8 @@ <string name="bottom_nav_diary_title">"Jurnal"</string> <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"Check-in"</string> - + <!-- XHED: Title for BottomNav certificates screen title --> + <string name="bottom_nav_certificates_title">"Certificate"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-ro/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-ro/vaccination_strings.xml index d1de410afd00a13237bd8524e08f5efea2a4fdd7..94892166137f4678ec56347e3ab69beac5b9893e 100644 --- a/Corona-Warn-App/src/main/res/values-ro/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values-ro/vaccination_strings.xml @@ -36,12 +36,12 @@ <string name="vaccination_list_vaccination_card_subtitle">"Efectuată pe %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zi."</item> - <item quantity="other">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d de zile."</item> - <item quantity="zero">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> - <item quantity="two">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> - <item quantity="few">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> - <item quantity="many">"Acum aÈ›i efectuat toate vaccinările. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> + <item quantity="one">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zi."</item> + <item quantity="other">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d de zile."</item> + <item quantity="zero">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> + <item quantity="two">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> + <item quantity="few">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> + <item quantity="many">"Acum aÈ›i efectuat toate vaccinările planificate curente. TotuÈ™i, protecÈ›ia prin vaccinare nu va fi completă timp de încă %1$d zile."</item> </plurals> <!-- XBUT: Vaccination List register additional vaccination button --> <string name="vaccination_list_register_new_vaccination_button">"ÃŽnregistrare altă vaccinare"</string> @@ -62,9 +62,11 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Adăugare certificat de vaccinare"</string> + <string name="vaccination_card_registration_title_line_1">"Adăugarea dovezii"</string> + <!-- XHED: Title for Vaccination Certificate Registration Home Card --> + <string name="vaccination_card_registration_title_line_2">"vaccinării digitale"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"AdăugaÈ›i certificate de vaccinare în aplicaÈ›ie pentru a le avea mereu cu dvs. Pentru aceasta, scanaÈ›i codul QR din documentul dvs."</string> + <string name="vaccination_card_registration_body">"AdăugaÈ›i certificatele dvs. de vaccinare în aplicaÈ›ie pentru a le avea mereu cu dvs. Pentru aceasta, scanaÈ›i codul QR din documentul dvs."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Adăugare"</string> <!-- XHED: Homescreen vaccination status card title --> @@ -97,17 +99,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Consimțământul dvs."</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Adăugare certificat de vaccinare"</string> + <string name="vaccination_consent_headline">"Adăugarea certificatelor"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"AdăugaÈ›i certificatul de vaccinare digital în aplicaÈ›ie. De îndată ce protecÈ›ia prin vaccinare este completă, puteÈ›i prezenta codul QR în aplicaÈ›ie ca dovadă a vaccinării."</string> + <string name="vaccination_consent_info_subtitle_text">"AdăugaÈ›i certificatele dvs. de COVID digitale în aplicaÈ›ie. Acest lucru vă ajută să dovediÈ›i protecÈ›ia dvs. prin vaccinare sau să arătaÈ›i un rezultat de test negativ în aplicaÈ›ie."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"Pentru a adăuga un certificat de vaccinare în aplicaÈ›ie, scanaÈ›i codul QR pe care l-aÈ›i primit la vaccinare."</string> + <string name="vaccination_consent_qr_info_text">"PuteÈ›i adăuga certificate de vaccinare COVID digitale sau certificate de test COVID în aplicaÈ›ie."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"AplicaÈ›ia citeÈ™te informaÈ›iile de la codul QR È™i le salvează într-o zonă securizată pe smartphone-ul dvs."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"Aceste certificate sunt considerate o dovadă valabilă în UE (de exemplu, în scop de călătorie)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"Datele vor rămâne pe smartphone. Vor fi transmise altor persoane doar dacă prezentaÈ›i certificatul de vaccinare pentru verificare."</string> + <string name="vaccination_consent_qr_info_time_text">"După ce adăugaÈ›i un certificat, acesta rămâne pe smartphone-ul dvs. Va fi partajat cu alte persoane doar dacă prezentaÈ›i un certificat pentru verificare."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"Pentru mai multe informaÈ›ii, consultaÈ›i înÈ™tiinÈ›area de confidenÈ›ialitate."</string> + <string name="vaccination_consent_onboarding_legal_information">"Pentru mai multe informaÈ›ii despre prelucrarea datelor, consultaÈ›i înÈ™tiinÈ›area de confidenÈ›ialitate."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Continuare"</string> diff --git a/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml b/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml index 4efff6cfacdfd5ca21328260bf94a0d251a9fa5a..3a2d0403eb184b10f202d8ae22e5c541653714b1 100644 --- a/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/antigen_strings.xml @@ -6,8 +6,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"Testinizi Kaydedin"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"DiÄŸer kullanıcıları daha hızlı uyarmak için testlerinizi kaydetmek ve test sonuçlarınızı almak üzere uygulamayı kullanın."</string> - + <string name="ag_homescreen_card_test_register_body">"DiÄŸer kullanıcıları daha hızlı uyarmak için testlerinizi kaydetmek ve test sonuçlarınızı almak ya da COVID test sertifikanızı talep etmek üzere uygulamayı kullanın."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml index 09183f04e8434e1d2265920dd08218b601daf913..ffb433096cb24cec3b0baf6d92a7f856ba323488 100644 --- a/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/contact_diary_strings.xml @@ -182,4 +182,13 @@ <string name="contact_diary_location_visit_duration_hour">"sa"</string> <!-- XTXT: Option - person encounter - circumstances hint--> <string name="contact_diary_location_visit_circumstances_hint">"Not (ör. çok dolu)"</string> + + <!-- XTXT: PCR test title in the day overview --> + <string name="contact_diary_corona_test_pcr_title">"PCR Testi Kaydedildi"</string> + <!-- XTXT: RAT test title in the day overview --> + <string name="contact_diary_corona_test_rat_title">"Hızlı Test GerçekleÅŸtirildi"</string> + <!-- XTXT: positive test result in the day overview --> + <string name="contact_diary_corona_test_positive">"Pozitif Tanı"</string> + <!-- XTXT: negative test result in the day overview --> + <string name="contact_diary_corona_test_negative">"Negatif Tanı"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values-tr/event_registration_strings.xml index f2c3a5cdb31c2e7e142f5e5371321517b8be5245..47a6314bc43e7f89841ab5e07b159866ffceffe7 100644 --- a/Corona-Warn-App/src/main/res/values-tr/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/event_registration_strings.xml @@ -210,6 +210,8 @@ <string name="trace_location_organizer_detail_item_duration">"%1$s, %2$s - %3$s"</string> <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> + <!-- XTXT: Past Event Info duration --> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"ÇoÄŸalt"</string> diff --git a/Corona-Warn-App/src/main/res/values-tr/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-tr/green_certificate_strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc5992cfe8200a789105192fd25303a52d7474e0 --- /dev/null +++ b/Corona-Warn-App/src/main/res/values-tr/green_certificate_strings.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> + <!-- XTXT: Request green certificate title --> + <string name="request_green_certificate_title">"COVID Test Sertifikası"</string> + <!-- XTXT: Request green certificate subtitle --> + <string name="request_green_certificate_subtitle">"Uygulamayı kullanarak resmi bir dijital test sertifikası talep edebilirsiniz. Ardından bu sertifika uygulamaya eklenir."</string> + <!-- XTXT: Request green certificate body section 1 --> + <string name="request_green_certificate_body_1">"Test sertifikası yalnızca test sonucunuz negatifse düzenlenir."</string> + <!-- XTXT: Request green certificate body section 2 --> + <string name="request_green_certificate_body_2">"Test sertifikası, AB’de geçerli bir negatif test sonucu kanıtıdır (ör. seyahat için)."</string> + <!-- XTXT: Request green certificate body section 3 --> + <string name="request_green_certificate_body_3">"Test sertifikası, doÄŸrulama uygulamasının sertifikanızı doÄŸrulamasını saÄŸlayan geçerli deÄŸer içerir."</string> + <!-- XBUT: Request green certificate agree button --> + <string name="request_green_certificate_agree_button">"Test Sertifikası Talep Et"</string> + <!-- XBUT: Request green certificate disagree button --> + <string name="request_green_certificate_disagree_button">"Hayır TeÅŸekkürler"</string> + <!-- XTXT: Request green certificate birth date description --> + <string name="request_green_certificate_birthdate_description">"Güvenlik amaçları nedeniyle test sonucunuz doÄŸum tarihinizle korunmaktadır. Test sonucu yalnızca doÄŸru doÄŸum tarihini belirtirseniz düzenlenebilir."</string> + <!-- XTXT: Request green certificate birth date hint --> + <string name="request_green_certificate_birthdate_hint">"DoÄŸum Tarihi"</string> + <!-- XTXT: Request green certificate exit dialog title --> + <string name="request_gc_dialog_title">"Kaydı Ä°ptal Et"</string> + <!-- XTXT: Request green certificate exit dialog message --> + <string name="request_gc_dialog_message">"Test kaydını iptal ederseniz test sonucunuzu uygulamaya alamazsınız."</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_positive_button">"Tamam"</string> + <!-- XBUT: Request green certificate exit dialog negative button --> + <string name="request_gc_dialog_negative_button">"Ä°ptal Et"</string> + <!-- XTXT: Detail green certificate title --> + <string name="detail_green_certificate_title">"AB Dijital COVID Test Sertifikası"</string> + <!-- XTXT: Detail green certificate test type --> + <string name="detail_green_certificate_test_type">"SARS-CoV-2 Testi"</string> + <!-- XTXT: Detail green certificate card title --> + <string name="detail_green_certificate_card_title">"Test Sertifikası"</string> + <!-- XTXT: Detail green certificate travel notice link en --> + <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> + <!-- XTXT: Detail green certificate travel notice link de --> + <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Green certificate main screen title --> + <string name="certification_screen_title">"Sertifikalar"</string> + <!-- XTXT: Green certificate menu item --> + <string name="menu_certification_information">"Bilgi"</string> + <!-- XTXT: Green certificate main screen header text --> + <string name="green_certification_header_info">"Dijital aşı sertifikalarınız ve test sertifikalarınız burada görüntülenir."</string> + <!-- XTXT: Green certificate info card title 1st line --> + <string name="info_banner_title_1">"Dijital COVID-19"</string> + <!-- XTXT: Green certificate info card title 2nd line --> + <string name="info_banner_title_2">"COVID Test Sertifikası"</string> + <!-- XTXT: Green certificate info card body --> + <string name="info_banner_body">"Ana ekranda bir testi kaydedin ve dijital test sertifikası almayı kabul edin. Sertifika düzenlenir düzenlenmez burada görüntülenir."</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml index a551f52bc1ec4ddbb7332fa1c53a956b85619b23..31d323ce844cd02f3816db2e69d96193f12e4fcb 100644 --- a/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/release_info_strings.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?><resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -16,22 +16,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Dijital Aşı Kanıtı"</item> + <item>"COVID Test Sertifikası"</item> + <item>"Temas Güncesi GeliÅŸtirmesi"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"Artık aşı sertifikalarınızı uygulamaya kaydedebilir ve QR kod aracılığıyla sunabilirsiniz. Uygulama, son aşıdan 14 gün sonra tam aşı korumasını görüntüler."</item> + <item>"Artık uygulama üzerinden resmi bir dijital test sertifikası talebinde bulunabilir ve sertifikayı uygulamaya ekleyebilirsiniz. Daha sonra bu test sertifikasını (QR kod) AB içinde negatif test sonucunu kanıtlamak için kullanabilirsiniz (örneÄŸin, uluslararası seyahat için)."</item> + <item>"Artık test sonucunuz çıkar çıkmaz temas güncenize eklenir."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item/> + <item/> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item/> + <item/> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-tr/strings.xml b/Corona-Warn-App/src/main/res/values-tr/strings.xml index 6af43c944accaa5322b4e22c2a9181f479fd3b63..f8752dd07863f43e4e9e0343df0acac4d73b68ff 100644 --- a/Corona-Warn-App/src/main/res/values-tr/strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/strings.xml @@ -281,6 +281,8 @@ <string name="risk_details_behavior_body_wear_mask">"DiÄŸer insanlarla karşılaÅŸtığınızda yüz maskesi takın."</string> <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"DiÄŸer insanlarla aranızda en az 1,5 metre mesafeyi koruyun."</string> + <!-- XTXT: find details about exposures in the contact journal --> + <string name="risk_details_find_details_in_journal">"Temas güncenizde maruz kalmalarınız hakkında daha fazla bilgi bulabilirsiniz."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"Test yaptırırsanız SSS bölümünde test prosedürü hakkında ek bilgi bulabilirsiniz."</string> @@ -1027,6 +1029,8 @@ <string name="submission_consent_call_test_result_scan_your_test_only">"Åžimdi, testinizin QR kodunu tarayın ve test sonucunuzu alın."</string> <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Her bir test yalnızca bir kez taranabilir. Uygulama tek seferde en fazla bir hızlı testi ve bir PCR testini yönetebilir."</string> + <!-- YTXT: Body sub text 3 for Submission Consent call test result --> + <string name="submission_consent_call_test_result_checkmark_text">"Testinizin sonucu negatifse bunu resmi bir COVID test sertifikası ile onaylayabilirsiniz. Bu iÅŸlemi gerçekleÅŸtirmek için, aÅŸağıda belirtilen adımları uygulayarak test sertifikası talebinde bulunun."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Lütfen karşılaÅŸtığınız diÄŸer kullanıcıları uyararak onlara yardımcı olun!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1087,6 +1091,15 @@ <!-- YTXT: Body text for next steps section of waiting test result page --> <string name="submission_test_result_pending_steps_contact_diary_body">"Test sonucunuz çıkar çıkmaz uygulamada görüntülenecektir."</string> + <!-- XHED: Page headline for test result certifcate info title --> + <string name="submission_test_result_pending_steps_test_certificate_heading">"Test Sertifikanız"</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"Bu test noktasında test sertifikalarını düzenleme iÅŸlevi desteklenmediÄŸinden hiçbir test sertifikası bulunmamaktadır."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Test sertifikanız henüz çıkmadı. Çıkar çıkmaz uygulamanızda görüntülenecektir."</string> + <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"Talebiniz üzerine, hiçbir test sertifikası düzenlenmedi."</string> + <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Güncelle"</string> <!-- XBUT: test result pending : remove the test button --> @@ -1179,7 +1192,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"QR Kodu Tara"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Uygulamada test sonucunuzu alın ve diÄŸer kullanıcıları uyarın."</string> + <string name="submission_dispatcher_qr_card_text">"Uygulamada test sonucunuzu alın ve diÄŸer kullanıcıları uyarın ya da COVID test sertifikanızı talep edin."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"PCR Testi için TAN girin"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1838,7 +1851,8 @@ <string name="bottom_nav_diary_title">"Günce"</string> <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"Check In"</string> - + <!-- XHED: Title for BottomNav certificates screen title --> + <string name="bottom_nav_certificates_title">"Sertifikalar"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values-tr/vaccination_strings.xml b/Corona-Warn-App/src/main/res/values-tr/vaccination_strings.xml index 68130d5019207d80bd419c0aac3977bb55ddc2fe..90a2fe7d930d9215b73f187569875264b208e2ec 100644 --- a/Corona-Warn-App/src/main/res/values-tr/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values-tr/vaccination_strings.xml @@ -8,7 +8,7 @@ <!-- XTXT: Vaccination Details vaccine manufacturer --> <string name="vaccination_details_vaccine_manufacturer">"Ãœretici"</string> <!-- XTXT: Vaccination Details vaccine type --> - <string name="vaccination_details_vaccine_medical_product_name">"Aşı tipi"</string> + <string name="vaccination_details_vaccine_medical_product_name">"Aşı Tipi"</string> <!-- XTXT: Vaccination Details certificate issuer --> <string name="vaccination_details_certificate_issuer">"Düzenleyen"</string> <!-- XTXT: Vaccination Details certificate country --> @@ -20,9 +20,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">"Aşı %1$d / %2$d"</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qrcode_card_title">"%1$d / %2$d aşı sertifikası"</string> + <string name="vaccination_qrcode_card_title">"%1$d / %2$d Aşı Sertifikası"</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qrcode_card_subtitle">"%1$s için aşı yapıldı - geçerlilik sonu: %2$s"</string> + <string name="vaccination_qrcode_card_subtitle">"%1$s için Aşı Yapıldı - Geçerlilik Sonu: %2$s"</string> <!-- XTXT: Vaccination List title--> <string name="vaccination_list_title">"Dijital Aşı Kanıtı"</string> @@ -36,12 +36,12 @@ <string name="vaccination_list_vaccination_card_subtitle">"GerçekleÅŸtirilme tarihi: %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> - <item quantity="other">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> - <item quantity="zero">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> - <item quantity="two">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> - <item quantity="few">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> - <item quantity="many">"Artık tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="one">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="other">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="zero">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="two">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="few">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> + <item quantity="many">"Åžu anda planlanan tüm aşıları aldınız ancak aşı korumanız %1$d gün daha tamamlanmayacaktır."</item> </plurals> <!-- XBUT: Vaccination List register additional vaccination button --> <string name="vaccination_list_register_new_vaccination_button">"BaÅŸka Aşı Kaydet"</string> @@ -62,9 +62,11 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Aşı Sertifikası Ekle"</string> + <string name="vaccination_card_registration_title_line_1">"Dijital Aşı Kanıtı"</string> + <!-- XHED: Title for Vaccination Certificate Registration Home Card --> + <string name="vaccination_card_registration_title_line_2">"Ekle"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"Her zaman eriÅŸebilmek için uygulamaya aşı sertifikaları ekleyin. Bunun için, belgenizdeki QR kodu tarayın."</string> + <string name="vaccination_card_registration_body">"Her zaman eriÅŸebilmek için uygulamaya aşı sertifikalarınızı ekleyin. Bunun için, belgenizdeki QR kodu tarayın."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Ekle"</string> <!-- XHED: Homescreen vaccination status card title --> @@ -97,17 +99,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Ä°zniniz"</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Aşı Sertifikası Ekle"</string> + <string name="vaccination_consent_headline">"Sertifikalar Ekle"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"Uygulamaya dijital aşı sertifikanızı ekleyin. Aşı korumanız tamamlanır tamamlanmaz uygulamanıza aşınızın kanıtı olarak QR kodu sunabilirsiniz."</string> + <string name="vaccination_consent_info_subtitle_text">"Uygulamaya dijital COVID sertifikalarınızı ekleyin. Bu sayede uygulamada aşı korumanızı ya da negatif test sonucunu kanıtlayabilirsiniz."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"Uygulamaya bir aşı sertifikası eklemek için aşınız sırasında size verilen QR kodu tarayın."</string> + <string name="vaccination_consent_qr_info_text">"Uygulamaya dijital COVID aşı sertifikaları ya da COVID test sertifikaları ekleyebilirsiniz."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"Uygulama, QR kodun bilgilerini okur ve akıllı telefonunuzda güvenli bir alana kaydeder."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"Bu sertifikalar, AB’de geçerli bir kanıt olarak deÄŸerlendirilir (ör. seyahat için)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"Veriler, akıllı telefonda kalır. Yalnızca, doÄŸrulama için aşı sertifikanızı sunarsanız diÄŸer kullanıcılara aktarılır."</string> + <string name="vaccination_consent_qr_info_time_text">"Siz sertifikayı ekledikten sonra sertifika akıllı telefonunuzda kalır. Yalnızca doÄŸrulama için bir aşı sunarsanız diÄŸer kullanıcılarla paylaşılır."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"Daha fazla bilgi için lütfen gizlilik bildirimine baÅŸvurun."</string> + <string name="vaccination_consent_onboarding_legal_information">"Veri iÅŸleme hakkında daha ayrıntılı bilgi için lütfen gizlilik bildirimine baÅŸvurun."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Devam Et"</string> diff --git a/Corona-Warn-App/src/main/res/values/antigen_strings.xml b/Corona-Warn-App/src/main/res/values/antigen_strings.xml index 736cd35cfdc6f3b9f83d73048c72ee2dc105a9db..525e5937055eca395d017b6a1745b89c8754106f 100644 --- a/Corona-Warn-App/src/main/res/values/antigen_strings.xml +++ b/Corona-Warn-App/src/main/res/values/antigen_strings.xml @@ -7,8 +7,7 @@ <!-- XHED: Register test homescreen card: title --> <string name="ag_homescreen_card_test_register_title">"Register Your Test"</string> <!-- XTXT: Register test homescreen card: body --> - <string name="ag_homescreen_card_test_register_body">"Use the app to register your tests and retrieve your test results, so you can warn others more quickly."</string> - + <string name="ag_homescreen_card_test_register_body">"Use the app to register your tests and retrieve your test results, so you can warn others more quickly, or to request your COVID test certificate."</string> <!-- #################################### Homescreen cards - common buttons ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml index fc49c3ebb20fe28e2e6b205f8fadb43ddeb2e628..acd72cf7fdf970ff9e6432d7c2b0210a4aed2caa 100644 --- a/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml +++ b/Corona-Warn-App/src/main/res/values/contact_diary_strings.xml @@ -84,6 +84,7 @@ <!-- XTXT: Indicates this trace location caused a lows risk --> <string name="contact_diary_trace_location_risk_low">"low risk"</string> + <!-- XTXT: content description of contact journal image on home screen --> <string name="contact_diary_homescreen_card_image_content_description">"A closed book with a bookmark"</string> <!-- XTXT: content description of contact journal header image on onboarding screen --> @@ -125,34 +126,6 @@ <!-- XTXT: Message for the contact diary dialog to delete a single person --> <string name="contact_diary_delete_person_message">"If you remove a person, all the entries for that person will be removed from your journal."</string> - <!-- EXPORT --> - <!-- XHED: Title for the contact journal export email subject --> - <string name="contact_diary_export_subject" translatable="false">"Mein Kontakt-Tagebuch"</string> - <!-- XTXT: Intro text for the contact journal email export part one--> - <string name="contact_diary_export_intro_one" translatable="false">"Kontakte der letzten 15 Tage (%1$s - %2$s)"</string> - <!-- XTXT: Intro text for the contact journal email export part two--> - <string name="contact_diary_export_intro_two" translatable="false">"Die nachfolgende Liste dient dem zuständigen Gesundheitsamt zur Kontaktnachverfolgung gem. § 25 IfSG."</string> - <!-- XTXT: Phone number prefix in the contact journal export--> - <string name="contact_diary_export_prefix_phone" translatable="false">"Tel."</string> - <!-- XTXT: EMail prefix in the contact journal export--> - <string name="contact_diary_export_prefix_email" translatable="false">"E-Mail"</string> - <!-- XTXT: Additional information about duration that was longer than 15 minutes in the contact journal export--> - <string name="contact_diary_export_durations_longer_than_15min" translatable="false">"Kontaktdauer > 15 Minuten"</string> - <!-- XTXT: Additional information about duration that was less than 15 minutes in the contact journal export--> - <string name="contact_diary_export_durations_less_than_15min" translatable="false">"Kontaktdauer < 15 Minuten"</string> - <!-- XTXT: Additional information about wearing a mask in the contact journal export--> - <string name="contact_diary_export_wearing_mask" translatable="false">"mit Maske"</string> - <!-- XTXT: Additional information about wearing no mask in the contact journal export--> - <string name="contact_diary_export_wearing_no_mask" translatable="false">"ohne Maske"</string> - <!-- XTXT: Additional information about being indoors in the contact journal export--> - <string name="contact_diary_export_indoor" translatable="false">"im Gebäude"</string> - <!-- XTXT: Additional information about being outdoor in the contact journal export--> - <string name="contact_diary_export_outdoor" translatable="false">"im Freien"</string> - <!-- XTXT: Location duration prefix in the contact journal export--> - <string name="contact_diary_export_location_duration_prefix" translatable="false">"Dauer"</string> - <!-- XTXT: Location duration postfix in the contact journal export--> - <string name="contact_diary_export_location_duration_suffix" translatable="false">"h"</string> - <!-- XHED: Title for the contact diary comment info screen --> <string name="contact_diary_comment_info_screen_title">"Note"</string> <!-- XTXT: Description for contact diary comment info screen --> @@ -211,11 +184,11 @@ <string name="contact_diary_location_visit_circumstances_hint">"Note (e.g. very full)"</string> <!-- XTXT: PCR test title in the day overview --> - <string name="contact_diary_corona_test_pcr_title">PCR-Test registriert</string> + <string name="contact_diary_corona_test_pcr_title">"PCR Test Registered"</string> <!-- XTXT: RAT test title in the day overview --> - <string name="contact_diary_corona_test_rat_title">Schnelltest durchgeführt</string> + <string name="contact_diary_corona_test_rat_title">"Rapid Test Performed"</string> <!-- XTXT: positive test result in the day overview --> - <string name="contact_diary_corona_test_positive">Befund positiv</string> + <string name="contact_diary_corona_test_positive">"Positive Diagnosis"</string> <!-- XTXT: negative test result in the day overview --> - <string name="contact_diary_corona_test_negative">Befund negativ</string> + <string name="contact_diary_corona_test_negative">"Negative Diagnosis"</string> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/contact_diary_strings_export.xml b/Corona-Warn-App/src/main/res/values/contact_diary_strings_export.xml new file mode 100644 index 0000000000000000000000000000000000000000..b641a9be20ae4076fa743daa0b5ede91997136db --- /dev/null +++ b/Corona-Warn-App/src/main/res/values/contact_diary_strings_export.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> + <!-- #################################### + Special string files for the strings that should + always keep the german version independent from + the selected system language + ###################################### --> + <!-- XHED: Title for the contact journal export email subject --> + <string name="contact_diary_export_subject" translatable="false">"Mein Kontakt-Tagebuch"</string> + <!-- XTXT: Intro text for the contact journal email export part one--> + <string name="contact_diary_export_intro_one" translatable="false">"Kontakte der letzten 15 Tage (%1$s - %2$s)"</string> + <!-- XTXT: Intro text for the contact journal email export part two--> + <string name="contact_diary_export_intro_two" translatable="false">"Die nachfolgende Liste dient dem zuständigen Gesundheitsamt zur Kontaktnachverfolgung gem. § 25 IfSG."</string> + <!-- XTXT: Phone number prefix in the contact journal export--> + <string name="contact_diary_export_prefix_phone" translatable="false">"Tel."</string> + <!-- XTXT: EMail prefix in the contact journal export--> + <string name="contact_diary_export_prefix_email" translatable="false">"E-Mail"</string> + <!-- XTXT: Additional information about duration that was longer than 15 minutes in the contact journal export--> + <string name="contact_diary_export_durations_longer_than_15min" translatable="false">"Kontaktdauer > 15 Minuten"</string> + <!-- XTXT: Additional information about duration that was less than 15 minutes in the contact journal export--> + <string name="contact_diary_export_durations_less_than_15min" translatable="false">"Kontaktdauer < 15 Minuten"</string> + <!-- XTXT: Additional information about wearing a mask in the contact journal export--> + <string name="contact_diary_export_wearing_mask" translatable="false">"mit Maske"</string> + <!-- XTXT: Additional information about wearing no mask in the contact journal export--> + <string name="contact_diary_export_wearing_no_mask" translatable="false">"ohne Maske"</string> + <!-- XTXT: Additional information about being indoors in the contact journal export--> + <string name="contact_diary_export_indoor" translatable="false">"im Gebäude"</string> + <!-- XTXT: Additional information about being outdoor in the contact journal export--> + <string name="contact_diary_export_outdoor" translatable="false">"im Freien"</string> + <!-- XTXT: Location duration prefix in the contact journal export--> + <string name="contact_diary_export_location_duration_prefix" translatable="false">"Dauer"</string> + <!-- XTXT: Location duration postfix in the contact journal export--> + <string name="contact_diary_export_location_duration_suffix" translatable="false">"h"</string> +</resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/event_registration_strings.xml b/Corona-Warn-App/src/main/res/values/event_registration_strings.xml index c20aad17a42470fad13c809f0948a4be2940607a..c6b317bfcf80acb99f637af506137f0f6578d223 100644 --- a/Corona-Warn-App/src/main/res/values/event_registration_strings.xml +++ b/Corona-Warn-App/src/main/res/values/event_registration_strings.xml @@ -211,7 +211,7 @@ <!-- XTXT: Event organizer detail qr-code: duration with multiple date --> <string name="trace_location_organizer_detail_item_duration_multiple_days">"%1$s, %2$s - %3$s, %4$s"</string> <!-- XTXT: Past Event Info duration --> - <string name="trace_location_attendee_past_event_duration">"%1$s Uhr"</string> + <string name="trace_location_attendee_past_event_duration">"%1$s"</string> <!-- XBUT: Event organiser list item: menu: information button --> <string name="trace_location_organizer_list_item_menu_duplicate_btn">"Duplicate"</string> diff --git a/Corona-Warn-App/src/main/res/values/green_certificate_attribute_strings.xml b/Corona-Warn-App/src/main/res/values/green_certificate_attribute_strings.xml index 66f4eddbb30c0dc5c6e1a415b625df29fe6f5f72..97061edf82bca7596eb2cceb9b44db8599dabcb5 100644 --- a/Corona-Warn-App/src/main/res/values/green_certificate_attribute_strings.xml +++ b/Corona-Warn-App/src/main/res/values/green_certificate_attribute_strings.xml @@ -1,8 +1,9 @@ <?xml version="1.0" encoding="utf-8"?> -<resources> - +<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Special string files for the strings that should always keep the german / englisch version independent from the selected system language + Special string files for the strings + that should always keep the german / english + version independent from the selected system language ###################################### --> <!-- XTXT: Green Certificate Detail Screen Attribute: Name --> <string name="green_certificate_attribute_name" translatable="false">"Name, Vorname / Name, First Name"</string> diff --git a/Corona-Warn-App/src/main/res/values/green_certificate_strings.xml b/Corona-Warn-App/src/main/res/values/green_certificate_strings.xml index b256dd248b7b99fed45bd147de5fd858b223e2dd..ece39a2784ecdbf679e5cbfca640fee0a8a12d3d 100644 --- a/Corona-Warn-App/src/main/res/values/green_certificate_strings.xml +++ b/Corona-Warn-App/src/main/res/values/green_certificate_strings.xml @@ -1,52 +1,102 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="UTF-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation"> <!-- XTXT: Request green certificate title --> - <string name="request_green_certificate_title">COVID-19-Testzertifikat</string> + <string name="request_green_certificate_title">"COVID Test Certificate"</string> <!-- XTXT: Request green certificate subtitle --> - <string name="request_green_certificate_subtitle">Sie können über die App ein offizielles digitales Testzertifikat anfordern, das anschließend in der App hinzugefügt wird.</string> + <string name="request_green_certificate_subtitle">"You can use the app to request an official digital test certificate, which is then added in the app."</string> <!-- XTXT: Request green certificate body section 1 --> - <string name="request_green_certificate_body_1">Das Testzertifikat wird nur bei einem negativen Testergebnis ausgestellt.</string> + <string name="request_green_certificate_body_1">"The test certificate is only issued if your test result is negative."</string> <!-- XTXT: Request green certificate body section 2 --> - <string name="request_green_certificate_body_2">Das Testzertifikat gilt innerhalb der EU als gültiger Nachweis eines negativen Testergebnisses (z.B. für Reisen).</string> + <string name="request_green_certificate_body_2">"The test certificate is valid proof of a negative test result within the EU (e.g. for travel)."</string> <!-- XTXT: Request green certificate body section 3 --> - <string name="request_green_certificate_body_3">Das Testzertifikat enthält Daten, die der Prüf-App die Validierung Ihres Zertifikats erlauben.</string> + <string name="request_green_certificate_body_3">"The test certificate contains valid that enables the verification app to validate your certificate."</string> <!-- XBUT: Request green certificate agree button --> - <string name="request_green_certificate_agree_button">Testzertifikat Anfordern</string> + <string name="request_green_certificate_agree_button">"Request Test Certificate"</string> <!-- XBUT: Request green certificate disagree button --> - <string name="request_green_certificate_disagree_button">Nein Danke</string> + <string name="request_green_certificate_disagree_button">"No Thank You"</string> <!-- XTXT: Request green certificate birth date description --> - <string name="request_green_certificate_birthdate_description">Zur Sicherheit wird Ihr Testergebnis mit Ihrem Geburtsdatum geschützt. Das Testergebnis kann nur bei korrekt angegebenem Geburtstdatum zugestellt werden.</string> + <string name="request_green_certificate_birthdate_description">"For security purposes, your test result is protected with your date of birth. The test result can only be issued if you specify the correct date of birth."</string> <!-- XTXT: Request green certificate birth date hint --> - <string name="request_green_certificate_birthdate_hint">Geburtsdatum</string> + <string name="request_green_certificate_birthdate_hint">"Date of Birth"</string> <!-- XTXT: Request green certificate exit dialog title --> - <string name="request_gc_dialog_title">Registrierung abbrechen</string> + <string name="request_gc_dialog_title">"Cancel Registration"</string> <!-- XTXT: Request green certificate exit dialog message --> - <string name="request_gc_dialog_message">Wenn Sie die Test-Registrierung abbrechen, können Sie Ihr Testergebnis nicht in der App erhalten.</string> + <string name="request_gc_dialog_message">"If you cancel the test registration, you cannot receive your test result in the app."</string> <!-- XBUT: Request green certificate exit dialog negative button --> - <string name="request_gc_dialog_positive_button">OK</string> + <string name="request_gc_dialog_positive_button">"OK"</string> <!-- XBUT: Request green certificate exit dialog negative button --> - <string name="request_gc_dialog_negative_button">Abbrechen</string> + <string name="request_gc_dialog_negative_button">"Cancel"</string> <!-- XTXT: Detail green certificate title --> - <string name="detail_green_certificate_title">EU Digitales COVID-Testzertifikat</string> + <string name="detail_green_certificate_title">"EU Digital COVID Test Certificate"</string> <!-- XTXT: Detail green certificate test type --> - <string name="detail_green_certificate_test_type">SARS-CoV-2-Test</string> + <string name="detail_green_certificate_test_type">"SARS-CoV-2 Test"</string> <!-- XTXT: Detail green certificate card title --> - <string name="detail_green_certificate_card_title">"Testzertifikat"</string> + <string name="detail_green_certificate_card_title">"Test Certificate"</string> + <!-- XTXT: Detail green certificate card subtitle --> + <string name="detail_green_certificate_card_subtitle">"Test durchgeführt am %1$s %2$s"</string> <!-- XTXT: Detail green certificate travel notice link en --> <string name="green_certificate_travel_notice_link_en">"https://reopen.europa.eu/en"</string> <!-- XTXT: Detail green certificate travel notice link de --> <string name="green_certificate_travel_notice_link_de">"https://reopen.europa.eu/de"</string> + <!-- XTXT: Detail green certificate menu item: delete --> + <string name="green_certificate_details_menu_item_delete">"Entfernen"</string> + <!-- XTXT: Detail green certificate diaglog title --> + <string name="green_certificate_details_dialog_remove_test_title">"Wollen Sie das Testzertifikat wirklich entfernen?"</string> + <!-- XTXT: Detail green certificate diaglog message --> + <string name="green_certificate_details_dialog_remove_test_message">"Wenn Sie das Testzertifikat entfernen, können Sie es nicht mehr als Nachweis in der App verwenden."</string> + <!-- XTXT: Detail green certificate diaglog positive --> + <string name="green_certificate_details_dialog_remove_test_button_positive">"Abbrechen"</string> + <!-- XTXT: Detail green certificate diaglog negative --> + <string name="green_certificate_details_dialog_remove_test_button_negative">"Entfernen"</string> + <!-- XTXT: Green certificate main screen title --> - <string name="certification_screen_title">Nachweise</string> + <string name="certification_screen_title">"Certificates"</string> <!-- XTXT: Green certificate menu item --> - <string name="menu_certification_information">Information</string> + <string name="menu_certification_information">"Information"</string> <!-- XTXT: Green certificate main screen header text --> - <string name="green_certification_header_info">Hier werden Ihre digitalen Impfzertifikate und Testzertifikate angezeigt.</string> + <string name="green_certification_header_info">"Your digital vaccination certificates and test certificates are displayed here."</string> <!-- XTXT: Green certificate info card title 1st line --> - <string name="info_banner_title_1">Digitales</string> + <string name="info_banner_title_1">"Digital COVID-19"</string> <!-- XTXT: Green certificate info card title 2nd line --> - <string name="info_banner_title_2">COVID-19-Testzertifikat</string> + <string name="info_banner_title_2">"COVID Test Certificate"</string> <!-- XTXT: Green certificate info card body --> - <string name="info_banner_body">Registrieren Sie einen Test auf der Startseite und stimmen Sie zu, ein digitales Testzertifikat zu erhalten. Sobald das Zertifikat vorliegt, wird es hier angezeigt.</string> - -</resources> \ No newline at end of file + <string name="info_banner_body">"Register a test on the home screen and agree to receive a digital test certificate. As soon as the certificate is available, it is displayed here."</string> + <!-- XTXT: Test certificate time --> + <string name="test_certificate_time">Test durchgeführt am %1$s, %2$s</string> + <!-- XTXT: Test error label --> + <string name="test_certificate_error_label">Fehler bei der Zertifikatsabfrage</string> + <!-- XBUT: Test error retry button --> + <string name="test_certificate_error_retry_button">Nochmal versuchen</string> + <!-- XTXT: Error text --> + <string name="error_tc_try_again">Es konnte keine Verbindung hergestellt werden. Bitte versuchen Sie es erneut.</string> + <!-- XTXT: Error text --> + <string name="error_tc_dcc_not_supported_by_lab">Ein Testzertifikat kann nicht angefordert werden, da diese Teststelle die Ausstellung von Testzertifikaten nicht unterstützt. Bitte entfernen Sie das Zertifikat oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_no_network">Ihre Internetverbindung wurde unterbrochen. Bitte prüfen Sie die Verbindung und versuchen Sie es erneut.</string> + <!-- XTXT: Error text --> + <string name="error_tc_e2e_error_call_hotline">Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_try_again_dcc_not_available_yet">Ihr Zertifikat liegt noch nicht vor. Bitte versuchen Sie es noch einmal. Sollte der Fehler weiterhin bestehen, kontaktieren Sie bitte die technische Hotline über App-Informationen -> Technische Hotline.</string> + <!-- XTXT: Error text --> + <string name="error_tc_client_error_call_hotline">Ein Fehler ist aufgetreten. Bitte versuchen Sie es später noch einmal oder kontaktieren Sie die technische Hotline über App-Informationen -> Technische Hotline.></string> + <!-- XTXT: Error text --> + <string name="error_tc_dcc_expired">Das Zertifikat ist nicht mehr aktuell, Sie können es aus der Corona-App entfernen.</string> + <!-- XBUT: Test error delete button --> + <string name="test_certificate_error_delete_button">Testzertifikat entfernen</string> + <!-- XTXT: Test error refresh dialog title --> + <string name="test_certificate_refresh_dialog_title">"Es gibt weiterhin Probleme bei der Abfrage"</string> + <!-- XBUT: Test error refresh dialog confirm button --> + <string name="test_certificate_refresh_dialog_confirm_button">"OK"</string> + <!-- XTXT: Test error delete dialog title --> + <string name="test_certificate_delete_dialog_title">"Wollen Sie das Zertifikat wirklich entfernen?"</string> + <!-- XTXT: Test error delete dialog body --> + <string name="test_certificate_delete_dialog_body">"Wenn das Zertifikat enfernt wird, kann es nicht noch einmal angefordert werden."</string> + <!-- XBUT: Test error delete dialog confirm button --> + <string name="test_certificate_delete_dialog_confirm_button">"Entfernen"</string> + <!-- XBUT: Test error delete dialog cancel button --> + <string name="test_certificate_delete_dialog_cancel_button">"Abbrechen"</string> + <!-- XTXT: Test error card body refreshing --> + <string name="test_certificate_error_label_refreshing">"Ihr Zertifikat wird gerade erstellt…"</string> + <!-- XTXT: Test error card body refreshing --> + <string name="test_certificate_error_refreshing_status">"Ihr Zertifikat wird gerade angefragt, dies kann einige Minuten dauern…"</string> +</resources> diff --git a/Corona-Warn-App/src/main/res/values/release_info_strings.xml b/Corona-Warn-App/src/main/res/values/release_info_strings.xml index 0139fcb8d8bae46019ffc85b06b1b8b89c02f793..fe38659e640ec864f7fd2d2af7c04dca67ce7f14 100644 --- a/Corona-Warn-App/src/main/res/values/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values/release_info_strings.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" tools:ignore="MissingTranslation"> <!-- #################################### - Release Info Screen 1.14 + Release Info Screen ###################################### --> <!-- XHED: Title for the release info screen --> @@ -17,22 +17,26 @@ <!-- XHED: Titles for the release info screen bullet points --> <string-array name="new_release_title"> - <item>"Digital Proof of Vaccination"</item> + <item>"COVID Test Certificate"</item> + <item>"Contact Journal Enhancement"</item> </string-array> <!-- XTXT: Text bodies for the release info screen bullet points --> <string-array name="new_release_body"> - <item>"You can now add your vaccination certificates in the app and present them via QR code. The app displays full vaccination protection 14 days after the final vaccination."</item> + <item>"You can now request an official digital test certificate through the app and then add it in the app. You can use this test certificate (QR code) within the EU to prove a negative test result (for international travel, for example)."</item> + <item>"As soon as your test result is available, it is now also added to your contact journal."</item> </string-array> <!-- XTXT: Text labels that will be converted to Links --> <string-array name="new_release_linkified_labels"> <item /> + <item /> </string-array> <!-- XTXT: URL destinations for the lables in new_release_linkified_labels --> <string-array name="new_release_target_urls"> <item /> + <item /> </string-array> </resources> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml index e82ee117aed4299f0c3255d82845e2b001f1a28a..5f70fd55b741fd292e7e66e3375b84278211384a 100644 --- a/Corona-Warn-App/src/main/res/values/strings.xml +++ b/Corona-Warn-App/src/main/res/values/strings.xml @@ -283,7 +283,7 @@ <!-- XMSG: risk details - stay 1,5 away, something like a bullet point --> <string name="risk_details_behavior_body_stay_away">"Keep at least 1.5 meters distance from other people."</string> <!-- XTXT: find details about exposures in the contact journal --> - <string name="risk_details_find_details_in_journal"></string> + <string name="risk_details_find_details_in_journal">"You will find more information about your exposures in your contact journal."</string> <!-- XMSG: risk details - link to faq, something like a bullet point --> <string name="risk_details_increased_risk_faq_link_text">"If you get tested, you will find additional information about the testing procedure in the FAQ."</string> @@ -1031,7 +1031,7 @@ <!-- YTXT: Body sub text 2 for Submission Consent call test result --> <string name="submission_consent_call_test_result_scan_test_only_once">"Each test can only be scanned once. The app can manage a maximum of one rapid test and one PCR test at the same time."</string> <!-- YTXT: Body sub text 3 for Submission Consent call test result --> - <string name="submission_consent_call_test_result_checkmark_text">"Wenn Ihr Testergebnis negativ ist, können Sie dies in Form eines offiziellen COVID-19-Testzertifikats bestätigen lassen. Fordern Sie hierfür in den folgenden Schritten das Testzertifikat an."</string> + <string name="submission_consent_call_test_result_checkmark_text">"If your test result is negative, you can confirm this with an official COVID test certificate. To do so, request the test certificate in the following steps."</string> <!-- XHED: Page subheadline for consent help by warning others --> <string name="submission_consent_help_by_warning_others_headline">"Please help others you have encountered by warning them!"</string> <!-- YTXT: Body for consent help by warning others --> @@ -1093,13 +1093,13 @@ <string name="submission_test_result_pending_steps_contact_diary_body">"As soon as your test result becomes available, it will be displayed in the app."</string> <!-- XHED: Page headline for test result certifcate info title --> - <string name="submission_test_result_pending_steps_test_certificate_heading">"Ihr Testzertifikat"</string> + <string name="submission_test_result_pending_steps_test_certificate_heading">"Your Test Certificate"</string> <!-- YTXT: Body text for test result certifcate info, if certifcate is not supported, RAT Test --> - <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"Es liegt kein Testzertifikat vor, da diese Teststelle die Ausstellung von Testzertifikaten nicht unterstützt."</string> + <string name="submission_test_result_pending_steps_test_certificate_not_supported_body">"No test certificate is available because this testing point does not support the issuing of test certificates."</string> <!-- YTXT: Body text for test result certifcate info, if certifcate is not available yet, PCR Test consent given --> - <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Ihr Testzertifikat liegt noch nicht vor. Sobald es vorliegt, wird es Ihnen in der App angezeigt."</string> + <string name="submission_test_result_pending_steps_test_certificate_not_available_yet_body">"Your test certificate is not available yet. As soon as it becomes available, it will be displayed in your app."</string> <!-- YTXT: Body text for test result certifcate info, if certifcate is not desired by the user, PCR Test no consent given --> - <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"Auf Ihren Wunsch hin wurde kein Testzertifikat ausgestellt."</string> + <string name="submission_test_result_pending_steps_test_certificate_not_desired_by_user_body">"No test certificate was issued, as per your request."</string> <!-- XBUT: test result pending : refresh button --> <string name="submission_test_result_pending_refresh_button">"Update"</string> @@ -1193,7 +1193,7 @@ <!-- YTXT: Dispatcher text for QR code option --> <string name="submission_dispatcher_card_qr">"Scan QR Code"</string> <!-- YTXT: Body text for QR code dispatcher option --> - <string name="submission_dispatcher_qr_card_text">"Receive your test result in the app and warn others."</string> + <string name="submission_dispatcher_qr_card_text">"Receive your test result in the app and warn others or request your COVID test certificate."</string> <!-- YTXT: Dispatcher text for TAN code option --> <string name="submission_dispatcher_card_tan_code">"Enter TAN for PCR Test"</string> <!-- YTXT: Body text for TAN code dispatcher option --> @@ -1853,8 +1853,7 @@ <!-- XHED: Title for BottomNav check-in screen title --> <string name="bottom_nav_check_ins_title">"Check In"</string> <!-- XHED: Title for BottomNav certificates screen title --> - <string name="bottom_nav_certificates_title">"Nachweise"</string> - + <string name="bottom_nav_certificates_title">"Certificates"</string> <!-- #################################### Data Donation & Survey ###################################### --> diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index e81fe78b15ebf0a921ed33b1a094de345cec4f18..5577e401916f0278a009ae24ab8f956924cde910 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -239,7 +239,7 @@ <item name="android:backgroundTint">@color/colorVaccinationCardBackground</item> </style> - <style name="Card.Greencertificate" parent="Card.NoElevation"> + <style name="Card.GreenCertificate" parent="Card.NoElevation"> <item name="android:backgroundTint">@color/colorVaccinationCardBackground</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 b138356683f194ae34df51ae85d23153029f1914..fd2e7fedf15353a3b458ff5bffe859868add1115 100644 --- a/Corona-Warn-App/src/main/res/values/vaccination_strings.xml +++ b/Corona-Warn-App/src/main/res/values/vaccination_strings.xml @@ -9,7 +9,7 @@ <!-- XTXT: Vaccination Details vaccine manufacturer --> <string name="vaccination_details_vaccine_manufacturer">"Manufacturer"</string> <!-- XTXT: Vaccination Details vaccine type --> - <string name="vaccination_details_vaccine_medical_product_name">"Vaccine type"</string> + <string name="vaccination_details_vaccine_medical_product_name">"Vaccine Type"</string> <!-- XTXT: Vaccination Details certificate issuer --> <string name="vaccination_details_certificate_issuer">"Issuer"</string> <!-- XTXT: Vaccination Details certificate country --> @@ -21,9 +21,9 @@ <!-- XTXT: Vaccination Details title--> <string name="vaccination_details_title">"Vaccination %1$d of %2$d"</string> <!-- XTXT: Vaccination Qr Code card title--> - <string name="vaccination_qrcode_card_title">"Vaccination certificate %1$d of %2$d"</string> + <string name="vaccination_qrcode_card_title">"Vaccination Certificate %1$d of %2$d"</string> <!-- XTXT: Vaccination Qr Code card subtitle--> - <string name="vaccination_qrcode_card_subtitle">"Vaccinated %1$s - valid to %2$s"</string> + <string name="vaccination_qrcode_card_subtitle">"Vaccinated %1$s - Valid to %2$s"</string> <!-- XTXT: Vaccination List title--> <string name="vaccination_list_title">"Digital Proof of Vaccination"</string> @@ -37,12 +37,12 @@ <string name="vaccination_list_vaccination_card_subtitle">"Performed on %1$s"</string> <!-- XTXT: Vaccination List immunity information card body--> <plurals name="vaccination_list_immunity_card_body"> - <item quantity="one">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d day."</item> - <item quantity="other">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="zero">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="two">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="few">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> - <item quantity="many">"You have now received all the vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="one">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d day."</item> + <item quantity="other">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="zero">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="two">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="few">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> + <item quantity="many">"You have now received all the currently planned vaccinations, however, your vaccination protection will not be complete for another %1$d days."</item> </plurals> <!-- XBUT: Vaccination List register additional vaccination button --> <string name="vaccination_list_register_new_vaccination_button">"Register Another Vaccination"</string> @@ -63,17 +63,19 @@ Homescreen cards ###################################### --> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title">"Add Vaccination Certificate"</string> + <string name="vaccination_card_registration_title_line_1">"Add Digital Proof"</string> <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title_line_1">"Digitalen"</string> - <!-- XHED: Title for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_title_line_2">"Impfnachweis hinzufügen"</string> + <string name="vaccination_card_registration_title_line_2">"of Vaccination"</string> <!-- YTXT: Body text for Vaccination Certificate Registration Home Card --> - <string name="vaccination_card_registration_body">"Add vaccination certificates in the app so you always have them with you. To do so, scan the QR code on your document."</string> + <string name="vaccination_card_registration_body">"Add your vaccination certificates in the app so you always have them with you. To do so, scan the QR code on your document."</string> <!-- XBUT: button for Vaccination Certificate Registration Home Card --> <string name="vaccination_card_register">"Add"</string> <!-- XHED: Homescreen vaccination status card title --> <string name="vaccination_card_status_title">"Digital Proof of Vaccination"</string> + <!-- XHED: Homescreen vaccination status card title --> + <string name="vaccination_card_status_title_line_1">"Digitaler"</string> + <!-- XHED: Homescreen vaccination status card title --> + <string name="vaccination_card_status_title_line_2">"Impfnachweis"</string> <!-- XHED: Homescreen vaccination status card vaccination name --> <string name="vaccination_card_status_vaccination_name">"SARS-CoV-2 Vaccination"</string> <!-- XTXT: Homescreen card incomplete vaccination status label --> @@ -87,7 +89,8 @@ <item quantity="few">"Full vaccination protection in %1$d days"</item> <item quantity="many">"Full vaccination protection in %1$d days"</item> </plurals> - + <!-- XTXT: Homescreen card complete vaccination status label --> + <string name="vaccination_card_status_vaccination_complete">"Gültig bis einschließlich %s"</string> <!-- XTXT: Vaccination QR code scan error message--> <string name="error_vc_invalid">"This QR code is not a valid vaccination certificate."</string> <!-- XTXT: Vaccination QR code scan error message--> @@ -102,17 +105,17 @@ <!-- XTXT: Vaccination Consent title--> <string name="vaccination_consent_title">"Your Consent"</string> <!-- XTXT: Vaccination Consent subtitle--> - <string name="vaccination_consent_headline">"Nachweise hinzufügen"</string> + <string name="vaccination_consent_headline">"Add Certificates"</string> <!-- XTXT: Vaccination Consent subtitle of qr info --> - <string name="vaccination_consent_info_subtitle_text">"Fügen Sie ihre digitalen COVID-Zertifikate in der App hinzu, um mit der App Ihren Impfschutz oder ein negatives Testergebnis nachweisen zu können."</string> + <string name="vaccination_consent_info_subtitle_text">"Add your digital COVID certificates in the app. This will enable you to prove your vaccination protection or a negative test result with the app."</string> <!-- XTXT: Vaccination Consent text of qr info --> - <string name="vaccination_consent_qr_info_text">"Sie können digitale COVID-19-Impfzertifikate oder COVID-19-Testzertifikate in der App hinzufügen."</string> + <string name="vaccination_consent_qr_info_text">"You can add digital COVID vaccination certificates or COVID test certificates in the app."</string> <!-- XTXT: Vaccination Consent qr code text --> - <string name="vaccination_consent_qr_info_qr_code_text">"Die Zertifikate gelten innerhalb der EU als gültiger Nachweis (z.B. für Reisen)."</string> + <string name="vaccination_consent_qr_info_qr_code_text">"These certificates are considered to be valid proof within the EU (e.g. for travel)."</string> <!-- XTXT: Vaccination Consent time text --> - <string name="vaccination_consent_qr_info_time_text">"Nach dem Hinzufügen bleiben die Zertifikate auf Ihrem Smartphone. Andere Personen können Ihre Daten nur einsehen, wenn Sie diesen ein Zertifikat zur Ãœberprüfung vorzeigen."</string> + <string name="vaccination_consent_qr_info_time_text">"After you add a certificate, it will remain on your smartphone. It will only be shared with others if you present a certificate for verification."</string> <!-- XTXT: Text for vaccination consent legal information button --> - <string name="vaccination_consent_onboarding_legal_information">"Ausführliche Informationen zur Datenverarbeitung finden Sie in der Datenschutzerklärung."</string> + <string name="vaccination_consent_onboarding_legal_information">"For more detailed information about data processing, please refer to the privacy notice."</string> <!-- XBUT: Text for vaccination consent accept button --> <string name="vaccination_consent_accept_button">"Continue"</string> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/CoronaWarnApplicationTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/CoronaWarnApplicationTest.kt index c32f6f68c44a8c0b6575ebf2dcf44f826ced6fc9..4a47302e03614b11a1b1720f7916f1a958f0c195 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/CoronaWarnApplicationTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/CoronaWarnApplicationTest.kt @@ -11,6 +11,8 @@ import de.rki.coronawarnapp.coronatest.type.pcr.execution.PCRResultScheduler import de.rki.coronawarnapp.coronatest.type.pcr.notification.PCRTestResultAvailableNotificationService import de.rki.coronawarnapp.coronatest.type.rapidantigen.execution.RAResultScheduler import de.rki.coronawarnapp.coronatest.type.rapidantigen.notification.RATTestResultAvailableNotificationService +import de.rki.coronawarnapp.covidcertificate.test.core.execution.TestCertificateRetrievalScheduler +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.VaccinationUpdateScheduler import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler import de.rki.coronawarnapp.notification.GeneralNotifications @@ -27,7 +29,6 @@ import de.rki.coronawarnapp.util.WatchdogService import de.rki.coronawarnapp.util.device.ForegroundState import de.rki.coronawarnapp.util.di.AppInjector import de.rki.coronawarnapp.util.di.ApplicationComponent -import de.rki.coronawarnapp.vaccination.core.execution.VaccinationUpdateScheduler import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.every @@ -70,6 +71,7 @@ class CoronaWarnApplicationTest : BaseTest() { @MockK lateinit var presenceTracingRiskWorkScheduler: PresenceTracingRiskWorkScheduler @MockK lateinit var pcrTestResultScheduler: PCRResultScheduler @MockK lateinit var raTestResultScheduler: RAResultScheduler + @MockK lateinit var testCertificateRetrievalScheduler: TestCertificateRetrievalScheduler @MockK lateinit var pcrTestResultAvailableNotificationService: PCRTestResultAvailableNotificationService @@ -126,6 +128,7 @@ class CoronaWarnApplicationTest : BaseTest() { app.pcrTestResultAvailableNotificationService = pcrTestResultAvailableNotificationService app.raTestResultAvailableNotificationService = raTestResultAvailableNotificationService app.vaccinationUpdateScheduler = vaccinationUpdateScheduler + app.testCertificateRetrievalScheduler = testCertificateRetrievalScheduler app.appScope = TestCoroutineScope() app.rollingLogHistory = object : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { @@ -157,6 +160,7 @@ class CoronaWarnApplicationTest : BaseTest() { pcrTestResultAvailableNotificationService.setup() raTestResultAvailableNotificationService.setup() + testCertificateRetrievalScheduler.setup() vaccinationUpdateScheduler.setup() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt index d33e8d77414ac19e08efe5d0d45710e4c9d9536f..bbfdba52c11e2412fb8a13a40c53abae7d92434d 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ConfigParserTest.kt @@ -4,6 +4,7 @@ import com.google.protobuf.InvalidProtocolBufferException import de.rki.coronawarnapp.appconfig.AnalyticsConfig import de.rki.coronawarnapp.appconfig.CWAConfig import de.rki.coronawarnapp.appconfig.CoronaTestConfig +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig import de.rki.coronawarnapp.appconfig.ExposureDetectionConfig import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig import de.rki.coronawarnapp.appconfig.KeyDownloadConfig @@ -34,6 +35,7 @@ class ConfigParserTest : BaseTest() { @MockK lateinit var logUploadConfigMapper: LogUploadConfig.Mapper @MockK lateinit var presenceTracingConfigMapper: PresenceTracingConfig.Mapper @MockK lateinit var coronaTestConfigMapper: CoronaTestConfig.Mapper + @MockK lateinit var covidCertificateConfigMapper: CovidCertificateConfig.Mapper private val appConfig171 = File("src/test/resources/appconfig_1_7_1.bin") private val appConfig180 = File("src/test/resources/appconfig_1_8_0.bin") @@ -51,6 +53,7 @@ class ConfigParserTest : BaseTest() { every { logUploadConfigMapper.map(any()) } returns mockk() every { presenceTracingConfigMapper.map(any()) } returns mockk() every { coronaTestConfigMapper.map(any()) } returns mockk() + every { covidCertificateConfigMapper.map(any()) } returns mockk() appConfig171.exists() shouldBe true appConfig180.exists() shouldBe true @@ -66,6 +69,7 @@ class ConfigParserTest : BaseTest() { logUploadConfigMapper = logUploadConfigMapper, presenceTracingConfigMapper = presenceTracingConfigMapper, coronaTestConfigMapper = coronaTestConfigMapper, + covidCertificateConfigMapper = covidCertificateConfigMapper, ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapperTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..1e9af041d366a966c24535fe9aa45f2c8ddf8f76 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/CovidCertificateConfigMapperTest.kt @@ -0,0 +1,68 @@ +package de.rki.coronawarnapp.appconfig.mapping + +import de.rki.coronawarnapp.server.protocols.internal.v2.AppConfigAndroid +import de.rki.coronawarnapp.server.protocols.internal.v2.DgcParameters +import io.kotest.matchers.shouldBe +import org.joda.time.Duration +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class CovidCertificateConfigMapperTest : BaseTest() { + + private fun createInstance() = CovidCertificateConfigMapper() + + @Test + fun `values are mapped`() { + val config = AppConfigAndroid.ApplicationConfigurationAndroid.newBuilder() + .setDgcParameters( + DgcParameters.DGCParameters.newBuilder() + .setTestCertificateParameters( + DgcParameters.DGCTestCertificateParameters.newBuilder() + .setWaitForRetryInSeconds(60) + .setWaitAfterPublicKeyRegistrationInSeconds(60) + ) + ) + .build() + createInstance().map(config).apply { + testCertificate.waitAfterPublicKeyRegistration shouldBe Duration.standardSeconds(60) + testCertificate.waitForRetry shouldBe Duration.standardSeconds(60) + } + } + + @Test + fun `defaults are returned if all dcc parameters are missing`() { + createInstance().map(AppConfigAndroid.ApplicationConfigurationAndroid.getDefaultInstance()).apply { + testCertificate.waitAfterPublicKeyRegistration shouldBe Duration.standardSeconds(10) + testCertificate.waitForRetry shouldBe Duration.standardSeconds(10) + } + } + + @Test + fun `defaults are returned if just test certificate parameters are missing`() { + val config = AppConfigAndroid.ApplicationConfigurationAndroid.newBuilder() + .setDgcParameters(DgcParameters.DGCParameters.getDefaultInstance()) + .build() + createInstance().map(config).apply { + testCertificate.waitAfterPublicKeyRegistration shouldBe Duration.standardSeconds(10) + testCertificate.waitForRetry shouldBe Duration.standardSeconds(10) + } + } + + @Test + fun `values are checked for sanity`() { + val config = AppConfigAndroid.ApplicationConfigurationAndroid.newBuilder() + .setDgcParameters( + DgcParameters.DGCParameters.newBuilder() + .setTestCertificateParameters( + DgcParameters.DGCTestCertificateParameters.newBuilder() + .setWaitForRetryInSeconds(61) + .setWaitAfterPublicKeyRegistrationInSeconds(61) + ) + ) + .build() + createInstance().map(config).apply { + testCertificate.waitAfterPublicKeyRegistration shouldBe Duration.standardSeconds(10) + testCertificate.waitForRetry shouldBe Duration.standardSeconds(10) + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensorTest.kt index d76f485edcb574882a874c0d91ee5c66624af335..ed3ee7d48e3af793155877f19fa6e39e9b7bb831 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/bugreporting/censors/vaccination/CertificateQrCodeCensorTest.kt @@ -1,7 +1,8 @@ package de.rki.coronawarnapp.bugreporting.censors.vaccination -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateData +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.mockk @@ -15,19 +16,19 @@ internal class CertificateQrCodeCensorTest { private val testRawString = "HC1:6BFOXN*TS0BI\$ZD.P9UOL97O4-2HH77HRM3DSPTLRR+%3.ZH9M9ESIGUBA KWML/O6HXK 0D+4O5VC9:BPCNYKMXEE1JAA/CZIK0JK1WL260X638J3-E3GG396B-43FZT-43:S0X37*ZV+FNI6HXY0ZSVILVQJF//05MVZJ5V.499TXY9KK9+OC+G9QJPNF67J6QW67KQY466PPM4MLJE+.PDB9L6Q2+PFQ5DB96PP5/P-59A%N+892 7J235II3NJ7PK7SLQMIJSBHVA7UJQWT.+S+ND%%M%331BH.IA.C8KRDL4O54O4IGUJKJGI0JAXD15IAXMFU*GSHGHD63DAOC9JU0H11+*4.\$S6ZC0JBZAB-C3QHISKE MCAOI8%M3V96-PY\$N6XOWLIBPIAYU:*JIRHUF2XZQ4H9 XJ72WG1K36VF/9BL56%E8T1OEEG%5TW5A 6YO67N6UCE:WT6BT-UMM:ABJK2TMDN1:FW-%T+\$D78NDSC3%5F61NYS-P9LOE0%J/ZAY:N5L4H-H/LH:AO3FU JHG7K46IOIMT.RE%PHLA21JRI3HTC\$AH" - private val testCertificateData = VaccinationCertificateData( + private val testCertificateData = DccData( header = mockk(), - certificate = VaccinationDGCV1( + certificate = VaccinationDccV1( version = "1", - nameData = VaccinationDGCV1.NameData( + nameData = Dcc.NameData( familyName = "Kevin", familyNameStandardized = "KEVIN", givenName = "Bob", givenNameStandardized = "BOB" ), dob = "1969-11-16", - vaccinationDatas = listOf( - VaccinationDGCV1.VaccinationData( + payloads = listOf( + VaccinationDccV1.VaccinationData( targetId = "12345", vaccineId = "1214765", medicalProductId = "aaEd/easd", @@ -35,7 +36,7 @@ internal class CertificateQrCodeCensorTest { doseNumber = 2, totalSeriesOfDoses = 5, dt = "1969-04-20", - countryOfVaccination = "DE", + certificateCountry = "DE", certificateIssuer = "Herbert", uniqueCertificateIdentifier = "urn:uvci:01:NL:PlA8UWS60Z4RZXVALl6GAZ" ) @@ -77,7 +78,7 @@ internal class CertificateQrCodeCensorTest { "vaccinationCertificate/dateOfBirth, i have been vaccinated with: vaccinationData/targetId " + "vaccinationData/vaccineId vaccinationData/medicalProductId" + " vaccinationData/marketAuthorizationHolderId vaccinationData/vaccinatedAt" + - " vaccinationData/countryOfVaccination vaccinationData/certificateIssuer" + + " vaccinationData/certificateCountry vaccinationData/certificateIssuer" + " vaccinationData/uniqueCertificateIdentifier" } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt index 267613b4ada2052a2b2e0e5086468ed12c2ba75e..7eb566a58f9810a6458f0c3499b0d2d6243e4a1a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestRepositoryTest.kt @@ -6,9 +6,9 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult import de.rki.coronawarnapp.coronatest.storage.CoronaTestStorage import de.rki.coronawarnapp.coronatest.type.CoronaTest import de.rki.coronawarnapp.coronatest.type.pcr.PCRCoronaTest -import de.rki.coronawarnapp.coronatest.type.pcr.PCRProcessor +import de.rki.coronawarnapp.coronatest.type.pcr.PCRTestProcessor import de.rki.coronawarnapp.coronatest.type.rapidantigen.RACoronaTest -import de.rki.coronawarnapp.coronatest.type.rapidantigen.RAProcessor +import de.rki.coronawarnapp.coronatest.type.rapidantigen.RATestProcessor import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.coEvery @@ -38,9 +38,9 @@ class CoronaTestRepositoryTest : BaseTest() { registrationToken = "token", testResult = CoronaTestResult.PCR_REDEEMED, ) - @MockK lateinit var pcrProcessor: PCRProcessor + @MockK lateinit var pcrProcessor: PCRTestProcessor - @MockK lateinit var raProcessor: RAProcessor + @MockK lateinit var raProcessor: RATestProcessor private val raTest = RACoronaTest( identifier = "ra-identifier", lastUpdatedAt = Instant.EPOCH, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestTestComponent.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestTestComponent.kt new file mode 100644 index 0000000000000000000000000000000000000000..cc0db91130df0ad8c2d29e579cdfcd938471d211 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/CoronaTestTestComponent.kt @@ -0,0 +1,29 @@ +package de.rki.coronawarnapp.coronatest + +import dagger.Component +import dagger.Module +import de.rki.coronawarnapp.coronatest.type.TestCertificateContainerTest +import de.rki.coronawarnapp.covidcertificate.storage.TestCertificateStorageTest +import de.rki.coronawarnapp.util.serialization.SerializationModule +import javax.inject.Singleton + +@Singleton +@Component( + modules = [ + CoronaTestMockProvider::class, + SerializationModule::class + ] +) +interface CoronaTestTestComponent { + + fun inject(testClass: TestCertificateStorageTest) + fun inject(testClass: TestCertificateContainerTest) + + @Component.Factory + interface Factory { + fun create(): CoronaTestTestComponent + } +} + +@Module +class CoronaTestMockProvider diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/TestCertificateContainerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/TestCertificateContainerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..8c42b1e213257a704b966a54a50df594a490bf29 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/TestCertificateContainerTest.kt @@ -0,0 +1,55 @@ +package de.rki.coronawarnapp.coronatest.type + +import de.rki.coronawarnapp.coronatest.DaggerCoronaTestTestComponent +import de.rki.coronawarnapp.covidcertificate.test.TestCertificateTestData +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.mockk.mockk +import org.joda.time.Instant +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import javax.inject.Inject + +class TestCertificateContainerTest : BaseTest() { + + @Inject lateinit var certificateTestData: TestCertificateTestData + + @BeforeEach + fun setup() { + DaggerCoronaTestTestComponent.factory().create().inject(this) + } + + @Test + fun `ui facing test certificate creation and fallbacks`() { + certificateTestData.personATest2CertContainer.apply { + isPublicKeyRegistered shouldBe true + isCertificateRetrievalPending shouldBe false + certificateId shouldBe "URN:UVCI:V1:DE:7WR8CE12Y8O2AN4NK320TPNKB1" + data.testCertificateQrCode shouldBe certificateTestData.personATest2CertQRCodeString + data.certificateReceivedAt shouldBe Instant.parse("1970-01-02T10:17:36.789Z") + toTestCertificate(null) shouldNotBe null + } + } + + @Test + fun `pending check and nullability`() { + certificateTestData.personATest3CertNokeyContainer.apply { + isPublicKeyRegistered shouldBe false + isCertificateRetrievalPending shouldBe true + certificateId shouldBe null + data.testCertificateQrCode shouldBe null + data.certificateReceivedAt shouldBe null + toTestCertificate(mockk()) shouldBe null + } + + certificateTestData.personATest4CertPendingContainer.apply { + isPublicKeyRegistered shouldBe true + isCertificateRetrievalPending shouldBe true + certificateId shouldBe null + data.testCertificateQrCode shouldBe null + data.certificateReceivedAt shouldBe null + toTestCertificate(mockk()) shouldBe null + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt index 82bfa938238018c59fffc3ff91af83a5891ea49b..04e5a7110f6f46ccf7ebac591793fc89b0892b98 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/pcr/PCRProcessorTest.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runBlockingTest import org.joda.time.Duration import org.joda.time.Instant +import org.joda.time.LocalDate import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -99,7 +100,7 @@ class PCRProcessorTest : BaseTest() { runBlocking { PcrTeleTanCensor.clearTans() } } - fun createInstance() = PCRProcessor( + fun createInstance() = PCRTestProcessor( timeStamper = timeStamper, submissionService = submissionService, analyticsKeySubmissionCollector = analyticsKeySubmissionCollector, @@ -275,4 +276,51 @@ class PCRProcessorTest : BaseTest() { isAdvancedConsentGiven = false ) } + + @Test + fun `request parameters for dcc are mapped`() = runBlockingTest { + val registrationData = RegistrationData( + registrationToken = "regtoken", + testResultResponse = CoronaTestResultResponse( + coronaTestResult = PCR_OR_RAT_PENDING, + sampleCollectedAt = null, + ) + ) + coEvery { submissionService.registerTest(any()) } answers { registrationData } + + createInstance().create( + CoronaTestQRCode.PCR( + qrCodeGUID = "guid", + isDccConsentGiven = true, + dateOfBirth = LocalDate.parse("2021-06-02"), + ) + ).apply { + isDccConsentGiven shouldBe true + isDccDataSetCreated shouldBe false + isDccSupportedByPoc shouldBe true + } + + createInstance().create( + CoronaTestQRCode.PCR( + qrCodeGUID = "guid", + dateOfBirth = LocalDate.parse("2021-06-02"), + ) + ).apply { + isDccConsentGiven shouldBe false + isDccDataSetCreated shouldBe false + isDccSupportedByPoc shouldBe true + } + } + + @Test + fun `marking dcc as created`() = runBlockingTest { + val instance = createInstance() + + instance.markDccCreated(defaultTest, true) shouldBe defaultTest.copy( + isDccDataSetCreated = true + ) + instance.markDccCreated(defaultTest, false) shouldBe defaultTest.copy( + isDccDataSetCreated = false + ) + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessorTest.kt similarity index 80% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessorTest.kt index 8f5ba33f398a61df2d28ab818c2d9926d28b58d6..2846d0f54b9f17b104b72c59e83d4295f94f8f4f 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RapidAntigenProcessorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/coronatest/type/rapidantigen/RAProcessorTest.kt @@ -16,6 +16,7 @@ import de.rki.coronawarnapp.coronatest.server.CoronaTestResult.values import de.rki.coronawarnapp.coronatest.server.CoronaTestResultResponse import de.rki.coronawarnapp.coronatest.server.RegistrationData import de.rki.coronawarnapp.coronatest.server.RegistrationRequest +import de.rki.coronawarnapp.coronatest.server.VerificationKeyType import de.rki.coronawarnapp.coronatest.type.CoronaTest.Type.RAPID_ANTIGEN import de.rki.coronawarnapp.coronatest.type.CoronaTestService import de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission.AnalyticsKeySubmissionCollector @@ -26,17 +27,19 @@ 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.test.runBlockingTest import org.joda.time.Duration import org.joda.time.Instant +import org.joda.time.LocalDate import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import testhelpers.BaseTest -class RapidAntigenProcessorTest : BaseTest() { +class RAProcessorTest : BaseTest() { @MockK lateinit var timeStamper: TimeStamper @MockK lateinit var submissionService: CoronaTestService @@ -94,7 +97,7 @@ class RapidAntigenProcessorTest : BaseTest() { } } - fun createInstance() = RAProcessor( + fun createInstance() = RATestProcessor( timeStamper = timeStamper, submissionService = submissionService, analyticsKeySubmissionCollector = analyticsKeySubmissionCollector, @@ -140,6 +143,27 @@ class RapidAntigenProcessorTest : BaseTest() { instance.pollServer(past60DaysTest).testResult shouldBe RAT_REDEEMED } + @Test + fun `registering a new test`() = runBlockingTest { + val request = CoronaTestQRCode.RapidAntigen( + hash = "hash", + createdAt = Instant.EPOCH, + ) + + val instance = createInstance() + instance.create(request) + + val expectedServerRequest = RegistrationRequest( + key = request.registrationIdentifier, + type = VerificationKeyType.GUID, + dateOfBirthKey = null, + ) + + coVerify { + submissionService.registerTest(expectedServerRequest) + } + } + @Test fun `registering a new test maps invalid results to INVALID state`() = runBlockingTest { var registrationData = RegistrationData( @@ -280,4 +304,56 @@ class RapidAntigenProcessorTest : BaseTest() { isAdvancedConsentGiven = false ) } + + @Test + fun `request parameters for dcc are mapped`() = runBlockingTest { + val registrationData = RegistrationData( + registrationToken = "regtoken", + testResultResponse = CoronaTestResultResponse( + coronaTestResult = PCR_OR_RAT_PENDING, + sampleCollectedAt = null, + ) + ) + coEvery { submissionService.registerTest(any()) } answers { registrationData } + + createInstance().create( + CoronaTestQRCode.RapidAntigen( + hash = "hash", + createdAt = Instant.EPOCH, + isDccConsentGiven = false, + dateOfBirth = LocalDate.parse("2021-06-02"), + isDccSupportedByPoc = false + ) + ).apply { + isDccConsentGiven shouldBe false + isDccDataSetCreated shouldBe false + isDccSupportedByPoc shouldBe false + } + + createInstance().create( + CoronaTestQRCode.RapidAntigen( + hash = "hash", + createdAt = Instant.EPOCH, + isDccConsentGiven = true, + dateOfBirth = LocalDate.parse("2021-06-02"), + isDccSupportedByPoc = true + ) + ).apply { + isDccConsentGiven shouldBe true + isDccDataSetCreated shouldBe false + isDccSupportedByPoc shouldBe true + } + } + + @Test + fun `marking dcc as created`() = runBlockingTest { + val instance = createInstance() + + instance.markDccCreated(defaultTest, true) shouldBe defaultTest.copy( + isDccDataSetCreated = true + ) + instance.markDccCreated(defaultTest, false) shouldBe defaultTest.copy( + isDccDataSetCreated = false + ) + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifierTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/common/CertificatePersonIdentifierTest.kt similarity index 64% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifierTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/common/CertificatePersonIdentifierTest.kt index 5822f0e6951db3d730211a1a609c0c3d8c5fc608..f0b34bd137c7cd1357012aeeb38eb4877a7218cd 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifierTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/common/CertificatePersonIdentifierTest.kt @@ -1,7 +1,9 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.common -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_DOB_MISMATCH +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_NAME_MISMATCH +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe @@ -9,14 +11,14 @@ import org.joda.time.LocalDate import org.junit.jupiter.api.Test import testhelpers.BaseTest -class VaccinatedPersonIdentifierTest : BaseTest() { - private val testPersonMaxData = VaccinatedPersonIdentifier( +class CertificatePersonIdentifierTest : BaseTest() { + private val testPersonMaxData = CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1966-11-11"), firstNameStandardized = "ANDREAS", lastNameStandardized = "ASTRA<EINS" ) - private val testPersonMin = VaccinatedPersonIdentifier( + private val testPersonMin = CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1900-01-01"), lastNameStandardized = "#", firstNameStandardized = null @@ -37,7 +39,7 @@ class VaccinatedPersonIdentifierTest : BaseTest() { @Test fun `person equality`() { val person1 = testPersonMaxData - val person2 = VaccinatedPersonIdentifier( + val person2 = CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1966-11-11"), firstNameStandardized = "ANDREAS", lastNameStandardized = "ASTRA<EINS" @@ -54,16 +56,16 @@ class VaccinatedPersonIdentifierTest : BaseTest() { testPersonMaxData.requireMatch(testPersonMaxData) } - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { testPersonMaxData.requireMatch(testPersonMaxData.copy(firstNameStandardized = "nope")) - }.errorCode shouldBe ErrorCode.VC_NAME_MISMATCH + }.errorCode shouldBe VC_NAME_MISMATCH - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { testPersonMaxData.requireMatch(testPersonMaxData.copy(lastNameStandardized = "nope")) - }.errorCode shouldBe ErrorCode.VC_NAME_MISMATCH + }.errorCode shouldBe VC_NAME_MISMATCH - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { testPersonMaxData.requireMatch(testPersonMaxData.copy(dateOfBirth = LocalDate.parse("1900-12-31"))) - }.errorCode shouldBe ErrorCode.VC_DOB_MISMATCH + }.errorCode shouldBe VC_DOB_MISMATCH } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptographyTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptographyTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..b98b171ad5c9667de587c4dd45a260678d4fcab4 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/cryptography/AesCryptographyTest.kt @@ -0,0 +1,19 @@ +package de.rki.coronawarnapp.covidcertificate.cryptography + +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class AesCryptographyTest : BaseTest() { + + @Test + fun `decrypt Hello World`() { + val des = "d56t/juMw5r4qNx1n1igs1pobUjZBT5yq0Ct7MHUuKM=".decodeBase64()!!.toByteArray() + val encryptedString = "WFOLewp8DWqY/8IWUHEDwg==".decodeBase64()!!.toByteArray() + AesCryptography().decrypt( + des, + encryptedData = encryptedString + ) shouldBe "Hello World".toByteArray() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateProcessorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateProcessorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..605e0e21c8a1a7792f4a8c4b1fa4cd8b757f3191 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateProcessorTest.kt @@ -0,0 +1,120 @@ +package de.rki.coronawarnapp.covidcertificate.execution + +import de.rki.coronawarnapp.appconfig.AppConfigProvider +import de.rki.coronawarnapp.appconfig.ConfigData +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateComponents +import de.rki.coronawarnapp.covidcertificate.test.core.server.TestCertificateServer +import de.rki.coronawarnapp.covidcertificate.test.core.storage.PCRCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.StoredTestCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateProcessor +import de.rki.coronawarnapp.util.TimeStamper +import de.rki.coronawarnapp.util.encryption.rsa.RSACryptography +import de.rki.coronawarnapp.util.encryption.rsa.RSAKeyPairGenerator +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 io.mockk.mockk +import kotlinx.coroutines.flow.flowOf +import okio.ByteString +import org.joda.time.Duration +import org.joda.time.Instant +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.coroutines.runBlockingTest2 + +class TestCertificateProcessorTest : BaseTest() { + + @MockK lateinit var timeStamper: TimeStamper + @MockK lateinit var certificateServer: TestCertificateServer + @MockK lateinit var rsaCryptography: RSACryptography + @MockK lateinit var qrCodeExtractor: TestCertificateQRCodeExtractor + @MockK lateinit var appConfigProvider: AppConfigProvider + @MockK lateinit var appConfigData: ConfigData + @MockK lateinit var covidTestCertificateConfig: CovidCertificateConfig.TestCertificate + + private val testCertificateNew = PCRCertificateData( + identifier = "identifier1", + registrationToken = "regtoken1", + registeredAt = Instant.EPOCH, + ) + + private val testCertificateWithPubKey = testCertificateNew.copy( + publicKeyRegisteredAt = Instant.EPOCH, + rsaPublicKey = mockk(), + rsaPrivateKey = mockk(), + ) + + private val testCerticateComponents = mockk<TestCertificateComponents>().apply { + every { dataEncryptionKeyBase64 } returns "dek" + every { encryptedCoseTestCertificateBase64 } returns "" + } + + private var storageSet = mutableSetOf<StoredTestCertificateData>() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + every { timeStamper.nowUTC } returns Instant.EPOCH + + every { appConfigProvider.currentConfig } returns flowOf(appConfigData) + every { appConfigData.covidCertificateParameters } returns mockk<CovidCertificateConfig>().apply { + every { testCertificate } returns covidTestCertificateConfig + } + + covidTestCertificateConfig.apply { + every { waitForRetry } returns Duration.standardSeconds(10) + every { waitAfterPublicKeyRegistration } returns Duration.standardSeconds(10) + } + + certificateServer.apply { + coEvery { registerPublicKeyForTest(any(), any()) } just Runs + coEvery { requestCertificateForTest(any()) } returns testCerticateComponents + } + + every { rsaCryptography.decrypt(any(), any()) } returns ByteString.Companion.EMPTY + + coEvery { qrCodeExtractor.extract(any(), any()) } returns mockk<TestCertificateQRCode>().apply { + every { qrCode } returns "qrCode" + every { data } returns mockk() + } + } + + private fun createInstance() = TestCertificateProcessor( + qrCodeExtractor = qrCodeExtractor, + timeStamper = timeStamper, + certificateServer = certificateServer, + rsaKeyPairGenerator = RSAKeyPairGenerator(), + rsaCryptography = rsaCryptography, + appConfigProvider = appConfigProvider, + ) + + @Test + fun `public key registration`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance() + instance.registerPublicKey(testCertificateNew) + + coVerify { + certificateServer.registerPublicKeyForTest(testCertificateNew.registrationToken, any()) + } + } + + @Test + fun `obtain certificate components`() = runBlockingTest2(ignoreActive = true) { + val instance = createInstance() + instance.obtainCertificate(testCertificateWithPubKey) + + coVerify { + covidTestCertificateConfig.waitAfterPublicKeyRegistration + certificateServer.requestCertificateForTest(testCertificateNew.registrationToken) + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalSchedulerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalSchedulerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..26617d4a7fedc92d88d0cf9facac1f1e8f9a153e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalSchedulerTest.kt @@ -0,0 +1,138 @@ +package de.rki.coronawarnapp.covidcertificate.execution + +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkInfo +import androidx.work.WorkManager +import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.coronatest.type.CoronaTest +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateWrapper +import de.rki.coronawarnapp.covidcertificate.test.core.execution.TestCertificateRetrievalScheduler +import de.rki.coronawarnapp.util.device.ForegroundState +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 io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.coroutines.runBlockingTest2 +import testhelpers.gms.MockListenableFuture + +class TestCertificateRetrievalSchedulerTest : BaseTest() { + + @MockK lateinit var workManager: WorkManager + @MockK lateinit var certificateRepo: TestCertificateRepository + @MockK lateinit var testRepo: CoronaTestRepository + @MockK lateinit var foregroundState: ForegroundState + @MockK lateinit var workInfo: WorkInfo + + private val mockTest = mockk<CoronaTest>().apply { + every { identifier } returns "identifier1" + every { isDccConsentGiven } returns true + every { isDccDataSetCreated } returns false + every { isDccSupportedByPoc } returns true + every { isNegative } returns true + } + + private val mockCertificate = mockk<TestCertificateWrapper>().apply { + every { identifier } returns "UUID" + every { isCertificateRetrievalPending } returns true + every { isUpdatingData } returns false + } + + private val testsFlow = MutableStateFlow(setOf(mockTest)) + private val certificatesFlow = MutableStateFlow(setOf(mockCertificate)) + private val foregroundFlow = MutableStateFlow(false) + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + workManager.apply { + every { enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } returns mockk() + every { getWorkInfosForUniqueWork(any()) } returns MockListenableFuture.forResult(listOf(workInfo)) + } + + every { workInfo.state } returns WorkInfo.State.SUCCEEDED + + testRepo.apply { + every { testRepo.coronaTests } returns testsFlow + coEvery { markDccAsCreated(any(), any()) } just Runs + } + certificateRepo.apply { + every { certificates } returns certificatesFlow + coEvery { requestCertificate(any()) } returns mockk() + } + + every { foregroundState.isInForeground } returns foregroundFlow + } + + private fun createInstance(scope: CoroutineScope) = TestCertificateRetrievalScheduler( + appScope = scope, + workManager = workManager, + certificateRepo = certificateRepo, + foregroundState = foregroundState, + testRepo = testRepo, + ) + + @Test + fun `new negative corona tests create a dcc if supported and consented`() = runBlockingTest2(ignoreActive = true) { + createInstance(scope = this).setup() + coVerify { certificateRepo.requestCertificate(mockTest) } + } + + @Test + fun `certificates only for negative results`() = runBlockingTest2(ignoreActive = true) { + every { mockTest.isNegative } returns false + createInstance(scope = this).setup() + advanceUntilIdle() + coVerify(exactly = 0) { certificateRepo.requestCertificate(any()) } + } + + @Test + fun `no duplicate certificates for flaky test results`() = runBlockingTest2(ignoreActive = true) { + every { mockTest.isDccDataSetCreated } returns true + createInstance(scope = this).setup() + advanceUntilIdle() + coVerify(exactly = 0) { certificateRepo.requestCertificate(any()) } + } + + @Test + fun `refresh on foreground`() = runBlockingTest2(ignoreActive = true) { + testsFlow.value = emptySet() + + createInstance(scope = this).setup() + advanceUntilIdle() + coVerify(exactly = 1) { workManager.enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } + + foregroundFlow.value = true + advanceUntilIdle() + coVerify(exactly = 2) { workManager.enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } + } + + @Test + fun `refresh on new certificate entry`() = runBlockingTest2(ignoreActive = true) { + testsFlow.value = emptySet() + createInstance(scope = this).setup() + + advanceUntilIdle() + coVerify(exactly = 1) { workManager.enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } + + val mockCertificate2 = mockk<TestCertificateWrapper>().apply { + every { identifier } returns "UUID2" + every { isCertificateRetrievalPending } returns true + every { isUpdatingData } returns false + } + + certificatesFlow.value = setOf(mockCertificate, mockCertificate2) + advanceUntilIdle() + coVerify(exactly = 2) { workManager.enqueueUniqueWork(any(), any(), any<OneTimeWorkRequest>()) } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalWorkerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalWorkerTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..7739eb15d5af474f35a7418ff0a142cc2f557faf --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/execution/TestCertificateRetrievalWorkerTest.kt @@ -0,0 +1,75 @@ +package de.rki.coronawarnapp.covidcertificate.execution + +import android.content.Context +import androidx.work.ListenableWorker +import androidx.work.WorkRequest +import androidx.work.WorkerParameters +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.execution.TestCertificateRetrievalWorker +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.impl.annotations.RelaxedMockK +import kotlinx.coroutines.test.runBlockingTest +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import java.net.UnknownHostException + +class TestCertificateRetrievalWorkerTest : BaseTest() { + @MockK lateinit var context: Context + @MockK lateinit var request: WorkRequest + @MockK lateinit var testCertificateRepository: TestCertificateRepository + + @RelaxedMockK lateinit var workerParams: WorkerParameters + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + + coEvery { testCertificateRepository.refresh() } returns emptySet() + } + + private fun createWorker( + runAttempts: Int = 0 + ) = TestCertificateRetrievalWorker( + context = context, + workerParams = workerParams.also { + every { it.runAttemptCount } returns runAttempts + }, + testCertificateRepository = testCertificateRepository, + ) + + @Test + fun `certificate refresh`() = runBlockingTest { + val result = createWorker().doWork() + + coVerify(exactly = 1) { testCertificateRepository.refresh() } + + result shouldBe ListenableWorker.Result.success() + } + + @Test + fun `retry on error`() = runBlockingTest { + coEvery { testCertificateRepository.refresh() } throws UnknownHostException() + + val result = createWorker().doWork() + + coVerify(exactly = 1) { testCertificateRepository.refresh() } + + result shouldBe ListenableWorker.Result.retry() + } + + @Test + fun `failure after 2 retries`() = runBlockingTest { + + val result = createWorker(runAttempts = 3).doWork() + + coVerify(exactly = 0) { testCertificateRepository.refresh() } + + result shouldBe ListenableWorker.Result.failure() + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/PCRTestCertificateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/PCRTestCertificateTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..1c1beb77369e5bf92735c86069d46e0149749884 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/PCRTestCertificateTest.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.covidcertificate.storage + +import testhelpers.BaseTest + +class PCRTestCertificateTest : BaseTest() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/RATestCertificateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/RATestCertificateTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..a954074ce438de9d12fe48d501495f099671e3a0 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/RATestCertificateTest.kt @@ -0,0 +1,5 @@ +package de.rki.coronawarnapp.covidcertificate.storage + +import testhelpers.BaseTest + +class RATestCertificateTest : BaseTest() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/TestCertificateStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/TestCertificateStorageTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..449f154a80d0a3b24da537e9cc5f3158724878cc --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/storage/TestCertificateStorageTest.kt @@ -0,0 +1,108 @@ +package de.rki.coronawarnapp.covidcertificate.storage + +import android.content.Context +import androidx.core.content.edit +import de.rki.coronawarnapp.coronatest.DaggerCoronaTestTestComponent +import de.rki.coronawarnapp.covidcertificate.test.TestCertificateTestData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateStorage +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.ContainerPostProcessor +import de.rki.coronawarnapp.util.serialization.SerializationModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.extensions.toComparableJsonPretty +import testhelpers.preferences.MockSharedPreferences +import javax.inject.Inject + +@Suppress("MaxLineLength") +class TestCertificateStorageTest : BaseTest() { + @MockK lateinit var context: Context + private lateinit var mockPreferences: MockSharedPreferences + @Inject lateinit var certificateTestData: TestCertificateTestData + @Inject lateinit var postProcessor: ContainerPostProcessor + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + DaggerCoronaTestTestComponent.factory().create().inject(this) + + mockPreferences = MockSharedPreferences() + + every { + context.getSharedPreferences("coronatest_certificate_localdata", Context.MODE_PRIVATE) + } returns mockPreferences + } + + private fun createInstance() = TestCertificateStorage( + context = context, + baseGson = SerializationModule().baseGson(), + containerPostProcessor = postProcessor, + ) + + @Test + fun `init is sideeffect free`() { + createInstance() + } + + @Test + fun `storing empty set deletes data`() { + mockPreferences.edit { + putString("dontdeleteme", "test") + putString("testcertificate.data.ra", "test") + putString("testcertificate.data.pcr", "test") + } + createInstance().testCertificates = emptySet() + + mockPreferences.dataMapPeek.keys.single() shouldBe "dontdeleteme" + } + + @Test + fun `store two containers, one for each type`() { + createInstance().testCertificates = setOf( + certificateTestData.personATest1StoredData, + certificateTestData.personATest2CertStoredData + ) + + (mockPreferences.dataMapPeek["testcertificate.data.pcr"] as String).toComparableJsonPretty() shouldBe """ + [ + { + "identifier": "identifier", + "registrationToken": "registrationToken", + "registeredAt": 12345, + "publicKeyRegisteredAt": 6789, + "rsaPublicKey": "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA2+WCCvy0SNqZMy/V1FYYMkBTGp/5BQt/NxUW1nIkj84u6duqNNQh4GjugoDc8epyl/yi3D61Jt7qArwk+eTcnW4/jEOexT5pCabRKrFm6IMndSefYrP3CeaD86ZU47uhnRuCG3TcPhIqUN2E37EbOsI9Z59JXc5tmmB71CxTF0bjE0PNLgbTU2snnsO6+oz/JLo7D2nw6E9yxSJ8JBjM5j+FC4sYLuO2nYi/BzAGZL/wsKrajg2hjA3f8r1cgst8HdzAJjMUG90pb3UG2K2KVRScbvF8pvRrzLCvJ/gqAGDXX/M00jr407vU8V4O2A9YdSavaC02iRFTNail65cbOW96p3ptjeejofj8l5PO5eBYWERla8NrlD9EcW93+aSmswn4w9iSSq+j38GMyhYulLcOlhKTeWumc5goDjcHyri48Ki70ddGzrxFxggaC/FqlCG85A6/43fVaWH/Wi2uPDPzaRGNQzXRy4LCuE/dvUzp8TlkpcT0QFy/Q4Ke0u1dAgMBAAE\u003d", + "rsaPrivateKey": "MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDb5YIK/LRI2pkzL9XUVhgyQFMan/kFC383FRbWciSPzi7p26o01CHgaO6CgNzx6nKX/KLcPrUm3uoCvCT55Nydbj+MQ57FPmkJptEqsWbogyd1J59is/cJ5oPzplTju6GdG4IbdNw+EipQ3YTfsRs6wj1nn0ldzm2aYHvULFMXRuMTQ80uBtNTayeew7r6jP8kujsPafDoT3LFInwkGMzmP4ULixgu47adiL8HMAZkv/CwqtqODaGMDd/yvVyCy3wd3MAmMxQb3SlvdQbYrYpVFJxu8Xym9GvMsK8n+CoAYNdf8zTSOvjTu9TxXg7YD1h1Jq9oLTaJEVM1qKXrlxs5b3qnem2N56Oh+PyXk87l4FhYRGVrw2uUP0Rxb3f5pKazCfjD2JJKr6PfwYzKFi6Utw6WEpN5a6ZzmCgONwfKuLjwqLvR10bOvEXGCBoL8WqUIbzkDr/jd9VpYf9aLa48M/NpEY1DNdHLgsK4T929TOnxOWSlxPRAXL9Dgp7S7V0CAwEAAQKCAYBaazDh2682FczQ42aFfTFN2G1TkVwP2v5gY+eUHjMyfpGDz7NZLbEQWZVZTCuNvd2I6XT+IzrR1O9cWIjLyHN+uIqg3l02tcbzFQkFCRVLnkJnRfef2mhGRecUFNzrF4gI1frV12OIkmecALpWULjlnGErbq/4Rp2C0RGZ2PABrkBI96QyvNPAhVsxSUJlK/zt2TXXzLQmkiSbMubg4OG/+3Z1nKhA/5ljhYsnJXQ7kUEjI93ic3Bt6naflYWosop/jUa1QksEMv0HL2if8PIBymgTGKmU79MeQOuBJN0ggrmttk41df+lPzWQY0EFnBC7Kf1AtbenllDm8zCoqldwu5OBTp8pZs7vFbOaRp1zBdtQS9OeTy22HvRU14CMwJ7HXOUC4RuVhXXeNLqLjLEkXJPRGvUem0Wq+ppBliDDoq9ljHiqvR/LgnaH0OqxM6o4fo1OgKvgVhJ1ItPeTdYxu2ikuJUNzwFf32feectjncXUf18wF1OExlwgVpvTinECgcEA7lnLCAdufw18Moe2VqudLmU2vUsJl1SR2nLlIYNfM7bHlbXqT/Ido2odKXX8WVDZi/ChV43OAw2PKUgcVPIxSGmEiDg8bj+K+v8hZ/VFbQAjnfD9+olikRbNmFMOued2IazfFv2ydbZADjPDMcfK1W3+7qcHT2LxigEWB8XNA5NDBaMYU+EN+tATOcG0QZr3fNPxfUT4m4TKOY00jhBdOhubfyF5pU5rQQCZvkVqVIffcq6J1x7Jh7CGLwQ53lQ7AoHBAOwt5N4/GY/pFiIE/V85MlJN37HfBhB8K29CEPzqOdHfICnYZ3dqNXtXIAQqVE0lG+49O5moQjU/dTAr39kJwzDydzJaFsCGsR/rzxo+Ishz2SrjJ8+97g8B6Oxgy9qwMs9X5A+EvrWw5Lb3woZDjaZ0pPl4yb7y5IEDlYnNM/9QcmHFP0IFK2h6S3Qmm0XjboeVe1POz0oPD3z+xYruCnKr1Vj/X5eiLIbt2hxWlVQ9N+tvufFusR+OdgsBhxijRwKBwQCvfaN0ZOxhVY91MOD6zV5sc48rLl2Ac370JRY5Z52n2NL4krlTZYOW9yFDjqBfLp0OYPyaF0lwjAI1NefOT4gjtbUkCqvLzLNKfKCfB0K3r5uJxY9qcM8G3pA/sB+ulxIuVzbmmaJU8vwUuN3mACGCpXtHQemq9MG8h3IuBOAe2sVFGEFoONLvMVaGdu1+RFgmK3KpdifJcarnVuU0GC5cA0mo//+ty6BCeuu34SoZ1PSbXpEUt5FQe5NAeM8WuFMCgcEAuaA0kqz7dU1YVPKhBZeZwnBsUYudY5WEOdSuL2oUeawpxlnMsGFsmX1Xr45pZZy2ACBmWJWTO/CdNXg2Xoo6vJzFLHD8EuOKETGwO8r8YZoT5I5WuwNnOKpinG5TqpTzyl0k5UGK9piKmnfOjuJHUb258E2MGyUijXf4ry72IEPlMozp9ATGIj6EUU0Kmvpu4+eL38nayDVgEfjX4CLJWWlOrL1CL5aJ8p6836r5gRUAf233shcy5T997ZaMzMN/AoHADfrS372Vuovx21p8txO2w+VFTEUoR80XGGdy30NrIdweY2bfz4XYpGSiyXE41TWzpNBfrSZNCyBXzvJ7d3dBXhlruFZi3Ji3IR+fe+KpEz4FTssKLEWm+gSmbGIjFxGe0nAIy77jCMYjqfOjoFdhksQN1On1tcq3Y3XauAc4L82wDU30rOgxWt8kdbblJKCSdOaYPXm/D+4c+8ROvlcxY4afl+FDcroHNMvD3jjZ1TMd1Bef1E0qFN/oJJU2Pc2/", + "certificateReceivedAt": 123456789, + "encryptedDataEncryptionkey": "ZW5jcnlwdGVkRGF0YUVuY3J5cHRpb25rZXk\u003d", + "testCertificateQrCode": "${certificateTestData.personATest1CertQRCodeString}" + } + ] + """.toComparableJsonPretty() + + (mockPreferences.dataMapPeek["testcertificate.data.ra"] as String).toComparableJsonPretty() shouldBe """ + [ + { + "identifier": "identifier2", + "registrationToken": "registrationToken2", + "registeredAt": 12345, + "publicKeyRegisteredAt": 6789, + "rsaPublicKey": "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnrJ8PbmbOmEEmHB/8yg1bnkUT7jcF4Xfy2Me5imJgLVYQ0cL9UNP91cfFUrgMFV3fHOc0Uuay10TmrBLaEdzqDEZQH4Kj0uZ+hVCtzntVKFviuUoh08wxFlogtc5Sy0NhuTGyC1W/i2AX1SDvet2xMcc1fE44rITQEEAlG8+nfGpbppFHezUOxuZjs9XBTxavDjQyWeFwMD30UAJGakPhOOOj6ihXA19OvQ/tYuYTJ5C9QzeK90C/rYbg2fn+os3EGlb7iJZ2V3KGrNLdMcEtkiG5IiHicaNCn8OS/cI3d29iJE4ECaF711fyF8MG1H2tbkjULS3bsPNUvyvHfM2cjOPRhejayOh+CxQkc3wKar8ApvQCjiVRW05nO0ufHdPMcWJhUlchWYO5mOJTSO8vG/9YqpnTuDc2Gelc4gMK7KATdH3v1FsACPKNJdpt68IfZXgGYn5LtJ7zJB6Yw8Rewj1SaF/wFKXpYd+5JyK18wJTLVYSpiDzidh4DP+R6ZTAgMBAAE\u003d", + "rsaPrivateKey": "MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQCesnw9uZs6YQSYcH/zKDVueRRPuNwXhd/LYx7mKYmAtVhDRwv1Q0/3Vx8VSuAwVXd8c5zRS5rLXROasEtoR3OoMRlAfgqPS5n6FUK3Oe1UoW+K5SiHTzDEWWiC1zlLLQ2G5MbILVb+LYBfVIO963bExxzV8TjishNAQQCUbz6d8alumkUd7NQ7G5mOz1cFPFq8ONDJZ4XAwPfRQAkZqQ+E446PqKFcDX069D+1i5hMnkL1DN4r3QL+thuDZ+f6izcQaVvuIlnZXcoas0t0xwS2SIbkiIeJxo0Kfw5L9wjd3b2IkTgQJoXvXV/IXwwbUfa1uSNQtLduw81S/K8d8zZyM49GF6NrI6H4LFCRzfApqvwCm9AKOJVFbTmc7S58d08xxYmFSVyFZg7mY4lNI7y8b/1iqmdO4NzYZ6VziAwrsoBN0fe/UWwAI8o0l2m3rwh9leAZifku0nvMkHpjDxF7CPVJoX/AUpelh37knIrXzAlMtVhKmIPOJ2HgM/5HplMCAwEAAQKCAYEAhw8Bu4pduFZfEdkUm31J0+YJyjtaXE6cAr0ty9Xn5vjuz/sEC0ypHqgvlPBvUdM66FiASoMcjxx8lbaZxnqgzLBUfFWIaSF/Pp2fdM5A1Di79CpIzrcvmrs4vbmrUfZav8WuAyjLE3DoArmrkRN2tct7F/y+W/gPeCyZ8LmoQcUsXCvAzNIEYPWBP0/oEFWoJu33iqCm7T+M6LGlzQfbZE5BwrNR+ESmomjCW6AdEn/SHjlAT3Y9mUakrbXdcJXPAI+RleS90kn8AHiQuyjotlb32xhBVw6SOtfd0xkMyY67AbCo9R1f0ir54PayA38xs4yQ0O2OgNUSLTWYXV1T/mSQSQMaxNw556IEXWVQWRWIc91QwOI2TD/N+vIxLPbNtuW5lEyMCzrmBdxq7wIOGIpy62B11TW6UYU26GOkhHTXEnn7pmHGVtbCXPGoKzncxxKNhRFuGOPcd+yQkM5eYAf7dad2NySrOokMQ2eIacPwKxKFfA/QN0v9aPLj7Qa5AoHBAOynNxSKErA25ndt/xBaLwSdzPynkE5zkO3gedgqvO6/6bAGOsRkawTNGalkVTwhXEnGBUIidPqhW7ex5/ad1QPUsWT8YeeYzXoM+Gqgu6M8awpFK4cwUMCrpRJwaUBFUCNzYNDZgJoOZAOX2TKvSNH+9zFrmGrKY0KRZ0aK9T0Ksbxn8KrErvXYj05nG4A3MrsKRA8mwBtZm0/17bBtg0nYH6/Omt787LBO/sxCicwTZioJlUgndzAIwTw7BtFDzwKBwQCrq8Wx/MK+LA+HUzDmdTK04sgIebulBSTV95aSsWoN+MoNwmi9wVt3OJfpq4L7g1NVv0vNSIajk9BDIFeKvgmDcde8RV51LRCObQ9enCOQUH7e0eC3XI9Nxg3nIhQiYJuggG6QAtt07bybx3dWpYEXL4ZOPOEkTVXR5JRkx9MWw8VDTbLKTCfZJ34PPF4AdCrs3yId9FXk9pUmS68oJVRsFhnI+dSdky32Bc01G6kk0SlGOudKLzqx4fbr0itHIj0CgcAHDWCd0xOFfs1VZ8i/EwDtsUoniVLKk7UQ8ayP3Y4tyzhKj5T2v0tVJEuMebn0hcX7SNRlSSOVSHO0QK/58HAlohP7P24nea094t8QRmPxFF7YOoF2kOEHLNZJe2IXkTk3JTwQXTrw3FbsqHzHfuO7pk51gZBUNl3I4Q5j0sZGIGh1hd9tJ1lTaDW1D2uJYZu4aTDoBq6Y4g23z0tbA5hy/ebL1WtWE9F125TKP31dwII95HU3Zj2uB8TCZ7vnRo8CgcBfeUiZlFk6Kob4W+v2P3fT4cwd6pXRUOsLlIbJTqIM4zB8NoLKBZ84zuCttBVEi+Ts61bc9Fjs4GgS7QnCv63KzKWOr4W45Tcv/rdthqjAugPVKCQx1ehc+KkCwpEwDUqAGO1kajJi9VTPzj8wkRsaKfQnzvPnnJr+AIIHCpr7LiWnKK8mkvQWcUBKeOhOmEzHL9Fpl1mt3PVWNwFS8m/hLOlqPIdim1gUW2WlA50uPKUXyeqX92xNQb5xqJEpHoECgcEAw4FGJb47FivG25fD+e61GxzG/KrzQL0eVS3T2YRAiN5ZB7QyInm6vMTi0QKCScCRJjOjRyoI3VtCO7G8vUnm0UiCW4l11WqW9G4vVh5VuR0HJ+kH1CQcq1aheqF7bbZGjjK47iyZskehfa6kcEOfThE6n6G7mIE/oe5k8A6+wHoLGmBbdxwE2xuG3PorH0PgbAgva1KAgC57rTBJhHnm6ntT21vlPLev9QvrE5syo+LEDbagr5zHMC14qAwMH2fi", + "certificateReceivedAt": 123456789, + "encryptedDataEncryptionkey": "ZW5jcnlwdGVkRGF0YUVuY3J5cHRpb25rZXk\u003d", + "testCertificateQrCode": "${certificateTestData.personATest2CertQRCodeString}" + } + ] + """.toComparableJsonPretty() + + createInstance().testCertificates shouldBe setOf( + certificateTestData.personATest1StoredData, + certificateTestData.personATest2CertStoredData + ) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateRepositoryTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..75e7d3204b206d34ced87d863b56b867d7307535 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateRepositoryTest.kt @@ -0,0 +1,79 @@ +package de.rki.coronawarnapp.covidcertificate.test + +import de.rki.coronawarnapp.appconfig.CovidCertificateConfig +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.test.core.storage.PCRCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.StoredTestCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateProcessor +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateStorage +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository +import io.mockk.MockKAnnotations +import io.mockk.coEvery +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.emptyFlow +import org.joda.time.Duration +import org.joda.time.Instant +import org.junit.jupiter.api.BeforeEach +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider + +class TestCertificateRepositoryTest : BaseTest() { + + @MockK lateinit var storage: TestCertificateStorage + @MockK lateinit var qrCodeExtractor: TestCertificateQRCodeExtractor + @MockK lateinit var covidTestCertificateConfig: CovidCertificateConfig.TestCertificate + @MockK lateinit var valueSetsRepository: ValueSetsRepository + @MockK lateinit var testCertificateProcessor: TestCertificateProcessor + + private val testCertificateNew = PCRCertificateData( + identifier = "identifier1", + registrationToken = "regtoken1", + registeredAt = Instant.EPOCH, + ) + + private val testCertificateWithPubKey = testCertificateNew.copy( + publicKeyRegisteredAt = Instant.EPOCH, + rsaPublicKey = mockk(), + rsaPrivateKey = mockk(), + ) + + private var storageSet = mutableSetOf<StoredTestCertificateData>() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + covidTestCertificateConfig.apply { + every { waitForRetry } returns Duration.standardSeconds(10) + every { waitAfterPublicKeyRegistration } returns Duration.standardSeconds(10) + } + + storage.apply { + every { storage.testCertificates = any() } answers { + storageSet.clear() + storageSet.addAll(arg(0)) + } + every { storage.testCertificates } answers { storageSet } + } + + coEvery { qrCodeExtractor.extract(any(), any()) } returns mockk<TestCertificateQRCode>().apply { + every { qrCode } returns "qrCode" + every { data } returns mockk() + } + every { valueSetsRepository.latestTestCertificateValueSets } returns emptyFlow() + } + + private fun createInstance(scope: CoroutineScope) = TestCertificateRepository( + appScope = scope, + dispatcherProvider = TestDispatcherProvider(), + storage = storage, + qrCodeExtractor = qrCodeExtractor, + valueSetsRepository = valueSetsRepository, + processor = testCertificateProcessor, + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateTestData.kt new file mode 100644 index 0000000000000000000000000000000000000000..e6da753eb92a102b7916032147d183d729219e3d --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestCertificateTestData.kt @@ -0,0 +1,105 @@ +package de.rki.coronawarnapp.covidcertificate.test + +import de.rki.coronawarnapp.covidcertificate.test.core.qrcode.TestCertificateQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.test.core.storage.PCRCertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.RACertificateData +import de.rki.coronawarnapp.covidcertificate.test.core.storage.TestCertificateContainer +import de.rki.coronawarnapp.util.encryption.rsa.RSAKey +import okio.ByteString.Companion.decodeBase64 +import org.joda.time.Instant +import javax.inject.Inject + +@Suppress("MaxLineLength") +class TestCertificateTestData @Inject constructor( + qrCodeExtractor: TestCertificateQRCodeExtractor +) { + + val personATest1CertQRCodeString = + "HC1:6BFQ$9FY7$\$Q00019C-5HQ57WDQTLUMS256TUMP49JLZCX/N4VB14NRINX6J7+KF\$UB.N83CMMPVNPAHJ035FT88%AJ1R*UOGR3ZWLC24+F7DO276HB.O*BEVJODVSKNOP0T4\$FZ.R/03N6QK3A05EQVBONNHSJ9WJT+B15H NBEZ3VI8-V77AFL4NY9V*JM *KOPC2H8PR5OG9:VIG$969FJRV28N9PORZJQ43EOIHIJ+83XDDRB201N.L58EB7 8GS8. 9GPNGHFV/6\$I3R3R4930TC/ZGZESM929.D59GO13E\$H7LMUGS18ABF6YD955C C6VPNMZU+3FJR1RVLBZDR2F*8A4MKWIH+/JPU1*H6E2CTHGYHPPU6U*FYFJ6RJO*I8P1T59S:V3V1SA86L8Y8A5XB*10112G8GHK77CBOK9QEI960TTNC.I3A4P8BM6DO:MI/98PC/ZP2:JW3JB.J:R2.5GX0J$1J.OOCYSS8A*DS\$AFVJR9RT-L6N%PS%CV%B8KL0%EL SXLBSA6 %M6JEN+E0Q8A:RAGW16KGUA627-Y2:EDH\$VFVT:29FWUTJSANSYCV4PSKNP\$ZV-8T27SV4CC3CJIV20TKBS22O$/MT/V*0Q0IUE0DBPT+UP03IR1CRFOP3" + + val personATest1StoredData = run { + val publicKey = RSAKey.Public( + "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA2+WCCvy0SNqZMy/V1FYYMkBTGp/5BQt/NxUW1nIkj84u6duqNNQh4GjugoDc8epyl/yi3D61Jt7qArwk+eTcnW4/jEOexT5pCabRKrFm6IMndSefYrP3CeaD86ZU47uhnRuCG3TcPhIqUN2E37EbOsI9Z59JXc5tmmB71CxTF0bjE0PNLgbTU2snnsO6+oz/JLo7D2nw6E9yxSJ8JBjM5j+FC4sYLuO2nYi/BzAGZL/wsKrajg2hjA3f8r1cgst8HdzAJjMUG90pb3UG2K2KVRScbvF8pvRrzLCvJ/gqAGDXX/M00jr407vU8V4O2A9YdSavaC02iRFTNail65cbOW96p3ptjeejofj8l5PO5eBYWERla8NrlD9EcW93+aSmswn4w9iSSq+j38GMyhYulLcOlhKTeWumc5goDjcHyri48Ki70ddGzrxFxggaC/FqlCG85A6/43fVaWH/Wi2uPDPzaRGNQzXRy4LCuE/dvUzp8TlkpcT0QFy/Q4Ke0u1dAgMBAAE\u003d".decodeBase64()!! + ) + val privateKey = RSAKey.Private( + "MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDb5YIK/LRI2pkzL9XUVhgyQFMan/kFC383FRbWciSPzi7p26o01CHgaO6CgNzx6nKX/KLcPrUm3uoCvCT55Nydbj+MQ57FPmkJptEqsWbogyd1J59is/cJ5oPzplTju6GdG4IbdNw+EipQ3YTfsRs6wj1nn0ldzm2aYHvULFMXRuMTQ80uBtNTayeew7r6jP8kujsPafDoT3LFInwkGMzmP4ULixgu47adiL8HMAZkv/CwqtqODaGMDd/yvVyCy3wd3MAmMxQb3SlvdQbYrYpVFJxu8Xym9GvMsK8n+CoAYNdf8zTSOvjTu9TxXg7YD1h1Jq9oLTaJEVM1qKXrlxs5b3qnem2N56Oh+PyXk87l4FhYRGVrw2uUP0Rxb3f5pKazCfjD2JJKr6PfwYzKFi6Utw6WEpN5a6ZzmCgONwfKuLjwqLvR10bOvEXGCBoL8WqUIbzkDr/jd9VpYf9aLa48M/NpEY1DNdHLgsK4T929TOnxOWSlxPRAXL9Dgp7S7V0CAwEAAQKCAYBaazDh2682FczQ42aFfTFN2G1TkVwP2v5gY+eUHjMyfpGDz7NZLbEQWZVZTCuNvd2I6XT+IzrR1O9cWIjLyHN+uIqg3l02tcbzFQkFCRVLnkJnRfef2mhGRecUFNzrF4gI1frV12OIkmecALpWULjlnGErbq/4Rp2C0RGZ2PABrkBI96QyvNPAhVsxSUJlK/zt2TXXzLQmkiSbMubg4OG/+3Z1nKhA/5ljhYsnJXQ7kUEjI93ic3Bt6naflYWosop/jUa1QksEMv0HL2if8PIBymgTGKmU79MeQOuBJN0ggrmttk41df+lPzWQY0EFnBC7Kf1AtbenllDm8zCoqldwu5OBTp8pZs7vFbOaRp1zBdtQS9OeTy22HvRU14CMwJ7HXOUC4RuVhXXeNLqLjLEkXJPRGvUem0Wq+ppBliDDoq9ljHiqvR/LgnaH0OqxM6o4fo1OgKvgVhJ1ItPeTdYxu2ikuJUNzwFf32feectjncXUf18wF1OExlwgVpvTinECgcEA7lnLCAdufw18Moe2VqudLmU2vUsJl1SR2nLlIYNfM7bHlbXqT/Ido2odKXX8WVDZi/ChV43OAw2PKUgcVPIxSGmEiDg8bj+K+v8hZ/VFbQAjnfD9+olikRbNmFMOued2IazfFv2ydbZADjPDMcfK1W3+7qcHT2LxigEWB8XNA5NDBaMYU+EN+tATOcG0QZr3fNPxfUT4m4TKOY00jhBdOhubfyF5pU5rQQCZvkVqVIffcq6J1x7Jh7CGLwQ53lQ7AoHBAOwt5N4/GY/pFiIE/V85MlJN37HfBhB8K29CEPzqOdHfICnYZ3dqNXtXIAQqVE0lG+49O5moQjU/dTAr39kJwzDydzJaFsCGsR/rzxo+Ishz2SrjJ8+97g8B6Oxgy9qwMs9X5A+EvrWw5Lb3woZDjaZ0pPl4yb7y5IEDlYnNM/9QcmHFP0IFK2h6S3Qmm0XjboeVe1POz0oPD3z+xYruCnKr1Vj/X5eiLIbt2hxWlVQ9N+tvufFusR+OdgsBhxijRwKBwQCvfaN0ZOxhVY91MOD6zV5sc48rLl2Ac370JRY5Z52n2NL4krlTZYOW9yFDjqBfLp0OYPyaF0lwjAI1NefOT4gjtbUkCqvLzLNKfKCfB0K3r5uJxY9qcM8G3pA/sB+ulxIuVzbmmaJU8vwUuN3mACGCpXtHQemq9MG8h3IuBOAe2sVFGEFoONLvMVaGdu1+RFgmK3KpdifJcarnVuU0GC5cA0mo//+ty6BCeuu34SoZ1PSbXpEUt5FQe5NAeM8WuFMCgcEAuaA0kqz7dU1YVPKhBZeZwnBsUYudY5WEOdSuL2oUeawpxlnMsGFsmX1Xr45pZZy2ACBmWJWTO/CdNXg2Xoo6vJzFLHD8EuOKETGwO8r8YZoT5I5WuwNnOKpinG5TqpTzyl0k5UGK9piKmnfOjuJHUb258E2MGyUijXf4ry72IEPlMozp9ATGIj6EUU0Kmvpu4+eL38nayDVgEfjX4CLJWWlOrL1CL5aJ8p6836r5gRUAf233shcy5T997ZaMzMN/AoHADfrS372Vuovx21p8txO2w+VFTEUoR80XGGdy30NrIdweY2bfz4XYpGSiyXE41TWzpNBfrSZNCyBXzvJ7d3dBXhlruFZi3Ji3IR+fe+KpEz4FTssKLEWm+gSmbGIjFxGe0nAIy77jCMYjqfOjoFdhksQN1On1tcq3Y3XauAc4L82wDU30rOgxWt8kdbblJKCSdOaYPXm/D+4c+8ROvlcxY4afl+FDcroHNMvD3jjZ1TMd1Bef1E0qFN/oJJU2Pc2/".decodeBase64()!! + ) + PCRCertificateData( + identifier = "identifier", + registrationToken = "registrationToken", + registeredAt = Instant.ofEpochMilli(12345), + publicKeyRegisteredAt = Instant.ofEpochMilli(6789), + rsaPublicKey = publicKey, + rsaPrivateKey = privateKey, + certificateReceivedAt = Instant.ofEpochMilli(123456789), + encryptedDataEncryptionkey = "ZW5jcnlwdGVkRGF0YUVuY3J5cHRpb25rZXk=".decodeBase64()!!, + testCertificateQrCode = personATest1CertQRCodeString, + ) + } + + val personATest1Container = TestCertificateContainer( + data = personATest1StoredData, + qrCodeExtractor = qrCodeExtractor, + ) + + val personATest2CertQRCodeString = + "HC1:6BFT$9W08+J2DO3L6COM9W\$B8MG9DIV*GZHTGBS46FQ5G+/N1D9K1LV-50OIETJPEU63TT MA5B-+B%95V72UUSRST4662Z8NME.249C1Y*5TIT+TI0QG*%9%VBYBOIZV7UNM%2A8P1N3+IDXN38BO%619B4D515FT*5CR4F:U8M/P-7T%TGX2CDDTB/5GR8:0I$ G*+N0-CIF51925USMI2WX4XKJA0G2XHP268P7ODSZYC4TKJOO:WQ437D:E78V-5UM4LGFBKZ80YIU 8%/GWAIM%KG/VIM2 M4BKP7NI/204CE9BSYP2JGI9GJGQUJ-UEYM7YAQR6E DHZ3-RM3:BQ3KRDMIV5.WVBJ8HT8DG7T GMJ47O2HSA+P6UFGUNIPCNOPFJIPWP8E 8BB0C08F 75N6UW20BA800 8LWN3D NV:U6XRDWF9FOQ1LT+L07BFUBZU37D8ZBP+FBKWO:X1ZK8.0V9-AX02UD5PP8K81+VO*.L4*DJRR7WPEL4G-LYCL+ 9\$TBHFNU0R%GD0/ESSOQH7PZD2 BJA3R74E4WG3W02B67EF8WZN7L0J2*3R5EG0VB7OM2I92W/UMJ7G1 FNSCF O 4W4VSO2DOJUF6I8%10-65UR/JJI3AN2EW-BLRVHQN77J" + + val personATest2CertStoredData = run { + val publicKey = RSAKey.Public( + "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnrJ8PbmbOmEEmHB/8yg1bnkUT7jcF4Xfy2Me5imJgLVYQ0cL9UNP91cfFUrgMFV3fHOc0Uuay10TmrBLaEdzqDEZQH4Kj0uZ+hVCtzntVKFviuUoh08wxFlogtc5Sy0NhuTGyC1W/i2AX1SDvet2xMcc1fE44rITQEEAlG8+nfGpbppFHezUOxuZjs9XBTxavDjQyWeFwMD30UAJGakPhOOOj6ihXA19OvQ/tYuYTJ5C9QzeK90C/rYbg2fn+os3EGlb7iJZ2V3KGrNLdMcEtkiG5IiHicaNCn8OS/cI3d29iJE4ECaF711fyF8MG1H2tbkjULS3bsPNUvyvHfM2cjOPRhejayOh+CxQkc3wKar8ApvQCjiVRW05nO0ufHdPMcWJhUlchWYO5mOJTSO8vG/9YqpnTuDc2Gelc4gMK7KATdH3v1FsACPKNJdpt68IfZXgGYn5LtJ7zJB6Yw8Rewj1SaF/wFKXpYd+5JyK18wJTLVYSpiDzidh4DP+R6ZTAgMBAAE\u003d".decodeBase64()!! + ) + val privateKey = RSAKey.Private( + "MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQCesnw9uZs6YQSYcH/zKDVueRRPuNwXhd/LYx7mKYmAtVhDRwv1Q0/3Vx8VSuAwVXd8c5zRS5rLXROasEtoR3OoMRlAfgqPS5n6FUK3Oe1UoW+K5SiHTzDEWWiC1zlLLQ2G5MbILVb+LYBfVIO963bExxzV8TjishNAQQCUbz6d8alumkUd7NQ7G5mOz1cFPFq8ONDJZ4XAwPfRQAkZqQ+E446PqKFcDX069D+1i5hMnkL1DN4r3QL+thuDZ+f6izcQaVvuIlnZXcoas0t0xwS2SIbkiIeJxo0Kfw5L9wjd3b2IkTgQJoXvXV/IXwwbUfa1uSNQtLduw81S/K8d8zZyM49GF6NrI6H4LFCRzfApqvwCm9AKOJVFbTmc7S58d08xxYmFSVyFZg7mY4lNI7y8b/1iqmdO4NzYZ6VziAwrsoBN0fe/UWwAI8o0l2m3rwh9leAZifku0nvMkHpjDxF7CPVJoX/AUpelh37knIrXzAlMtVhKmIPOJ2HgM/5HplMCAwEAAQKCAYEAhw8Bu4pduFZfEdkUm31J0+YJyjtaXE6cAr0ty9Xn5vjuz/sEC0ypHqgvlPBvUdM66FiASoMcjxx8lbaZxnqgzLBUfFWIaSF/Pp2fdM5A1Di79CpIzrcvmrs4vbmrUfZav8WuAyjLE3DoArmrkRN2tct7F/y+W/gPeCyZ8LmoQcUsXCvAzNIEYPWBP0/oEFWoJu33iqCm7T+M6LGlzQfbZE5BwrNR+ESmomjCW6AdEn/SHjlAT3Y9mUakrbXdcJXPAI+RleS90kn8AHiQuyjotlb32xhBVw6SOtfd0xkMyY67AbCo9R1f0ir54PayA38xs4yQ0O2OgNUSLTWYXV1T/mSQSQMaxNw556IEXWVQWRWIc91QwOI2TD/N+vIxLPbNtuW5lEyMCzrmBdxq7wIOGIpy62B11TW6UYU26GOkhHTXEnn7pmHGVtbCXPGoKzncxxKNhRFuGOPcd+yQkM5eYAf7dad2NySrOokMQ2eIacPwKxKFfA/QN0v9aPLj7Qa5AoHBAOynNxSKErA25ndt/xBaLwSdzPynkE5zkO3gedgqvO6/6bAGOsRkawTNGalkVTwhXEnGBUIidPqhW7ex5/ad1QPUsWT8YeeYzXoM+Gqgu6M8awpFK4cwUMCrpRJwaUBFUCNzYNDZgJoOZAOX2TKvSNH+9zFrmGrKY0KRZ0aK9T0Ksbxn8KrErvXYj05nG4A3MrsKRA8mwBtZm0/17bBtg0nYH6/Omt787LBO/sxCicwTZioJlUgndzAIwTw7BtFDzwKBwQCrq8Wx/MK+LA+HUzDmdTK04sgIebulBSTV95aSsWoN+MoNwmi9wVt3OJfpq4L7g1NVv0vNSIajk9BDIFeKvgmDcde8RV51LRCObQ9enCOQUH7e0eC3XI9Nxg3nIhQiYJuggG6QAtt07bybx3dWpYEXL4ZOPOEkTVXR5JRkx9MWw8VDTbLKTCfZJ34PPF4AdCrs3yId9FXk9pUmS68oJVRsFhnI+dSdky32Bc01G6kk0SlGOudKLzqx4fbr0itHIj0CgcAHDWCd0xOFfs1VZ8i/EwDtsUoniVLKk7UQ8ayP3Y4tyzhKj5T2v0tVJEuMebn0hcX7SNRlSSOVSHO0QK/58HAlohP7P24nea094t8QRmPxFF7YOoF2kOEHLNZJe2IXkTk3JTwQXTrw3FbsqHzHfuO7pk51gZBUNl3I4Q5j0sZGIGh1hd9tJ1lTaDW1D2uJYZu4aTDoBq6Y4g23z0tbA5hy/ebL1WtWE9F125TKP31dwII95HU3Zj2uB8TCZ7vnRo8CgcBfeUiZlFk6Kob4W+v2P3fT4cwd6pXRUOsLlIbJTqIM4zB8NoLKBZ84zuCttBVEi+Ts61bc9Fjs4GgS7QnCv63KzKWOr4W45Tcv/rdthqjAugPVKCQx1ehc+KkCwpEwDUqAGO1kajJi9VTPzj8wkRsaKfQnzvPnnJr+AIIHCpr7LiWnKK8mkvQWcUBKeOhOmEzHL9Fpl1mt3PVWNwFS8m/hLOlqPIdim1gUW2WlA50uPKUXyeqX92xNQb5xqJEpHoECgcEAw4FGJb47FivG25fD+e61GxzG/KrzQL0eVS3T2YRAiN5ZB7QyInm6vMTi0QKCScCRJjOjRyoI3VtCO7G8vUnm0UiCW4l11WqW9G4vVh5VuR0HJ+kH1CQcq1aheqF7bbZGjjK47iyZskehfa6kcEOfThE6n6G7mIE/oe5k8A6+wHoLGmBbdxwE2xuG3PorH0PgbAgva1KAgC57rTBJhHnm6ntT21vlPLev9QvrE5syo+LEDbagr5zHMC14qAwMH2fi".decodeBase64()!! + ) + RACertificateData( + identifier = "identifier2", + registrationToken = "registrationToken2", + registeredAt = Instant.ofEpochMilli(12345), + publicKeyRegisteredAt = Instant.ofEpochMilli(6789), + rsaPublicKey = publicKey, + rsaPrivateKey = privateKey, + certificateReceivedAt = Instant.ofEpochMilli(123456789), + encryptedDataEncryptionkey = "ZW5jcnlwdGVkRGF0YUVuY3J5cHRpb25rZXk=".decodeBase64()!!, + testCertificateQrCode = personATest2CertQRCodeString + ) + } + + val personATest2CertContainer = TestCertificateContainer( + data = personATest2CertStoredData, + qrCodeExtractor = qrCodeExtractor, + ) + + val personATest3CertNokeyStoredData = run { + RACertificateData( + identifier = "identifier2", + registrationToken = "registrationToken2", + registeredAt = Instant.ofEpochMilli(12345), + ) + } + val personATest3CertNokeyContainer = TestCertificateContainer( + data = personATest3CertNokeyStoredData, + qrCodeExtractor = qrCodeExtractor, + ) + val personATest4CertPendingStoredData = run { + val publicKey = RSAKey.Public( + "MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnrJ8PbmbOmEEmHB/8yg1bnkUT7jcF4Xfy2Me5imJgLVYQ0cL9UNP91cfFUrgMFV3fHOc0Uuay10TmrBLaEdzqDEZQH4Kj0uZ+hVCtzntVKFviuUoh08wxFlogtc5Sy0NhuTGyC1W/i2AX1SDvet2xMcc1fE44rITQEEAlG8+nfGpbppFHezUOxuZjs9XBTxavDjQyWeFwMD30UAJGakPhOOOj6ihXA19OvQ/tYuYTJ5C9QzeK90C/rYbg2fn+os3EGlb7iJZ2V3KGrNLdMcEtkiG5IiHicaNCn8OS/cI3d29iJE4ECaF711fyF8MG1H2tbkjULS3bsPNUvyvHfM2cjOPRhejayOh+CxQkc3wKar8ApvQCjiVRW05nO0ufHdPMcWJhUlchWYO5mOJTSO8vG/9YqpnTuDc2Gelc4gMK7KATdH3v1FsACPKNJdpt68IfZXgGYn5LtJ7zJB6Yw8Rewj1SaF/wFKXpYd+5JyK18wJTLVYSpiDzidh4DP+R6ZTAgMBAAE\u003d".decodeBase64()!! + ) + val privateKey = RSAKey.Private( + "MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQCesnw9uZs6YQSYcH/zKDVueRRPuNwXhd/LYx7mKYmAtVhDRwv1Q0/3Vx8VSuAwVXd8c5zRS5rLXROasEtoR3OoMRlAfgqPS5n6FUK3Oe1UoW+K5SiHTzDEWWiC1zlLLQ2G5MbILVb+LYBfVIO963bExxzV8TjishNAQQCUbz6d8alumkUd7NQ7G5mOz1cFPFq8ONDJZ4XAwPfRQAkZqQ+E446PqKFcDX069D+1i5hMnkL1DN4r3QL+thuDZ+f6izcQaVvuIlnZXcoas0t0xwS2SIbkiIeJxo0Kfw5L9wjd3b2IkTgQJoXvXV/IXwwbUfa1uSNQtLduw81S/K8d8zZyM49GF6NrI6H4LFCRzfApqvwCm9AKOJVFbTmc7S58d08xxYmFSVyFZg7mY4lNI7y8b/1iqmdO4NzYZ6VziAwrsoBN0fe/UWwAI8o0l2m3rwh9leAZifku0nvMkHpjDxF7CPVJoX/AUpelh37knIrXzAlMtVhKmIPOJ2HgM/5HplMCAwEAAQKCAYEAhw8Bu4pduFZfEdkUm31J0+YJyjtaXE6cAr0ty9Xn5vjuz/sEC0ypHqgvlPBvUdM66FiASoMcjxx8lbaZxnqgzLBUfFWIaSF/Pp2fdM5A1Di79CpIzrcvmrs4vbmrUfZav8WuAyjLE3DoArmrkRN2tct7F/y+W/gPeCyZ8LmoQcUsXCvAzNIEYPWBP0/oEFWoJu33iqCm7T+M6LGlzQfbZE5BwrNR+ESmomjCW6AdEn/SHjlAT3Y9mUakrbXdcJXPAI+RleS90kn8AHiQuyjotlb32xhBVw6SOtfd0xkMyY67AbCo9R1f0ir54PayA38xs4yQ0O2OgNUSLTWYXV1T/mSQSQMaxNw556IEXWVQWRWIc91QwOI2TD/N+vIxLPbNtuW5lEyMCzrmBdxq7wIOGIpy62B11TW6UYU26GOkhHTXEnn7pmHGVtbCXPGoKzncxxKNhRFuGOPcd+yQkM5eYAf7dad2NySrOokMQ2eIacPwKxKFfA/QN0v9aPLj7Qa5AoHBAOynNxSKErA25ndt/xBaLwSdzPynkE5zkO3gedgqvO6/6bAGOsRkawTNGalkVTwhXEnGBUIidPqhW7ex5/ad1QPUsWT8YeeYzXoM+Gqgu6M8awpFK4cwUMCrpRJwaUBFUCNzYNDZgJoOZAOX2TKvSNH+9zFrmGrKY0KRZ0aK9T0Ksbxn8KrErvXYj05nG4A3MrsKRA8mwBtZm0/17bBtg0nYH6/Omt787LBO/sxCicwTZioJlUgndzAIwTw7BtFDzwKBwQCrq8Wx/MK+LA+HUzDmdTK04sgIebulBSTV95aSsWoN+MoNwmi9wVt3OJfpq4L7g1NVv0vNSIajk9BDIFeKvgmDcde8RV51LRCObQ9enCOQUH7e0eC3XI9Nxg3nIhQiYJuggG6QAtt07bybx3dWpYEXL4ZOPOEkTVXR5JRkx9MWw8VDTbLKTCfZJ34PPF4AdCrs3yId9FXk9pUmS68oJVRsFhnI+dSdky32Bc01G6kk0SlGOudKLzqx4fbr0itHIj0CgcAHDWCd0xOFfs1VZ8i/EwDtsUoniVLKk7UQ8ayP3Y4tyzhKj5T2v0tVJEuMebn0hcX7SNRlSSOVSHO0QK/58HAlohP7P24nea094t8QRmPxFF7YOoF2kOEHLNZJe2IXkTk3JTwQXTrw3FbsqHzHfuO7pk51gZBUNl3I4Q5j0sZGIGh1hd9tJ1lTaDW1D2uJYZu4aTDoBq6Y4g23z0tbA5hy/ebL1WtWE9F125TKP31dwII95HU3Zj2uB8TCZ7vnRo8CgcBfeUiZlFk6Kob4W+v2P3fT4cwd6pXRUOsLlIbJTqIM4zB8NoLKBZ84zuCttBVEi+Ts61bc9Fjs4GgS7QnCv63KzKWOr4W45Tcv/rdthqjAugPVKCQx1ehc+KkCwpEwDUqAGO1kajJi9VTPzj8wkRsaKfQnzvPnnJr+AIIHCpr7LiWnKK8mkvQWcUBKeOhOmEzHL9Fpl1mt3PVWNwFS8m/hLOlqPIdim1gUW2WlA50uPKUXyeqX92xNQb5xqJEpHoECgcEAw4FGJb47FivG25fD+e61GxzG/KrzQL0eVS3T2YRAiN5ZB7QyInm6vMTi0QKCScCRJjOjRyoI3VtCO7G8vUnm0UiCW4l11WqW9G4vVh5VuR0HJ+kH1CQcq1aheqF7bbZGjjK47iyZskehfa6kcEOfThE6n6G7mIE/oe5k8A6+wHoLGmBbdxwE2xuG3PorH0PgbAgva1KAgC57rTBJhHnm6ntT21vlPLev9QvrE5syo+LEDbagr5zHMC14qAwMH2fi".decodeBase64()!! + ) + RACertificateData( + identifier = "identifier2", + registrationToken = "registrationToken2", + registeredAt = Instant.ofEpochMilli(12345), + publicKeyRegisteredAt = Instant.ofEpochMilli(6789), + rsaPublicKey = publicKey, + rsaPrivateKey = privateKey, + ) + } + + val personATest4CertPendingContainer = TestCertificateContainer( + data = personATest4CertPendingStoredData, + qrCodeExtractor = qrCodeExtractor, + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestData.java b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestData.java new file mode 100644 index 0000000000000000000000000000000000000000..d1cab7986ed9589691167a7a27bdc82652826277 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/TestData.java @@ -0,0 +1,22 @@ +package de.rki.coronawarnapp.covidcertificate.test; + +public class TestData { + + static public class EllenCheng { + public String dek = "/9o5eVNb9us5CsGD4F3J36Ju1enJ71Y6+FpVvScGWkE="; + public String coseWithDecryptedPayload = "0oRNogEmBEiLxYhcyl5BXkBZAWGkAWtjd2EtYXBwLWNsaQQaYpdzwAYaYLZAQDkBA6EBpGN2ZXJlMS4wLjBjbmFtpGJnbmVFbGxlbmJmbmVDaGVuZ2NnbnRlRUxMRU5jZm50ZUNIRU5HY2RvYmoxOTY3LTA3LTE5YXSBq2J0Z2k4NDA1MzkwMDZidHRqTFAyMTcxOTgtM2JubXgZSVZDSUoxR1FaTTc4NzhFTE1VSU1BMDhER2JtYWQxMjQ0YnNjeBgyMDIxLTA1LTMxVDE2OjEzOjE2LjgzNlpiZHJ4GDIwMjEtMDYtMDFUMTQ6MTI6MTYuODM2WmJ0cmkyNjA0MTUwMDBidGNtVGVzdCBDZW50cmUgMWJjb2JERWJpc3ghQnVuZGVzbWluaXN0ZXJpdW0gZsO8ciBHZXN1bmRoZWl0YmNpeC8wMURFLzAwMDAwLzExMTkzNDkwMDcvRzdQU0JBWE1YVkEyTjBITTNVOFlWN1pNVFhA1NqKSb93S2El9dA0icVjK+DV4LbwVWajZmTmhqcsgzWhvl4/PmtAJ5/iT57FfoQvuOvlyhxRPgGSg33IuDnBCg=="; + public String coseWithEncryptedPayload = "0oRNogEmBEiLxYhcyl5BXkBZAXBxvo73+06cLc73F5KIFuQdo7fLUnb7yF9QFtX9tIEmgSzHIXKbHcEiep5RTtb2UVS80vybmnwYa1k36HR2R2yTKGwvDWAUumw2ZjCnfp8CxKx3zQVRl6JrVdLiskWmo4qiK/EwyTHrw/5PZy4rd11vt9Y6wuZtlpOvFGDIDhGKpcgK93zfIQWY59xjxusr/4J3FCWpcy9YNehB6m4Az1NozXxOrL9DmFM38mWCkiHaPeWgedbqfKTg3x/vSrXSkXYnLpc6QHsRqW99r7yTXJffbK8X44KvgkUI9sIlVU5+2+IuwT4XBY2p/MLW4d9gfnAhZYTsn0nGuoj4KFHTo6fNkXsuZ6BWm5MurXR0dqiCd00B1ZKuTNV0QhdzaaB2pYtwBnxD65TW8D0VDrDDjZuYRzni032f5hgB7YDlvcWYWiv7o6T8DeCNAsJ0RdL/X1qe3bHvLOBvzF9XlTrg4vNF/3aeRn9libOf+0ufr5dEcVhA1NqKSb93S2El9dA0icVjK+DV4LbwVWajZmTmhqcsgzWhvl4/PmtAJ5/iT57FfoQvuOvlyhxRPgGSg33IuDnBCg=="; + } + + static public class BrianCalamandrei { + public String dek = "RinMlpTdzQGw7kllamU9Pz6bTHEWVZi1Ocb4q8wfFSk="; + public String coseWithEncryptedPayload = "0oRNogEmBEiLxYhcyl5BXkBZAXAN4hKvLEngs3MYcLe/cIyy0q0+0auk5A/Bme/WlymolXU8JSLJJcj3D7kwXCJoEOvsnU9P/IrVlTNF2fJBdWF6Oq22UzhyOPRuQiF7PvspbVzyeEo+H/PmtvbTZss6l/wLDoXPixjtCOaFn7Com6Z2pNQOZqkYGZzz4BanJfchoggM4HaH20H4AzANfMMqYa/rytHnz4BjlR82ZSOlg5e/Jbl78NRen6RkgLTwT3YSI1XV+gbLPK6Fhp5saqRmQgUQTTSO99Q/rdk6BG18RZxqw70zKb77ddxBzolgySbmRdUrpWdK9SvnsnivN3V1Auv5X18KpHO58SwyFoex7OUq73q6FAS9p+MdI2jh1e3LcwU12ZJaN56bRbTEAmT5MelZsYY+c6WWvcIND7tj3aDI5o8D9PyWZHPdz/uHn/Cesn7MgVEXvLQnfCVvuPkLDSGAGi47nRRmUoaN7+7GjPRYvTyrX5VWwnMK31QLADg9kFhAQ7d9IZ02KQ5OXt/fc3bpcombylOcXT2U+JXDwQadrFwHQdjeK1dw+RZM7UkD4l/TOQjO9B8JN13DlidhiqljGw=="; + public String coseWithDecryptedPayload = "kzriyBRYZu5L/VqCz6qsomYv8vjOdjXp/giOGRa8vpXBM7Mjc3mjGaSUpHy8QLlCrFfrDAh8vwY4PKsI/ad/jFyEpyZwI13R6PW+Vft1Ld1nxHD74BwiNbhNv1ZjlmDohQsHXaq6MEbfNfT/uqPbZHToZlTPO3V9aeOD0E+kuMx8npVIbB+zbL48AS3VsrVyYthINdpplYmNsXyFdxsCvZfefGO3G5XIRsDfMBqFPVZO3uCIq1nLb4U8dcbZ5O2yrMG5UOJ2fiwWiiqSLevyCnlYjOvyv8Ot6nM1X7fncXwze3i4OT7Y/Y+36SAds/bCcpypOso0QIim8BBVecy3MN7bQOivc/DKyRX9dHHtc5iD/dgT5i5HBwFEcvTqAa/rlJyTSO4XL6CRevHD7lt3wdd/guRMWxilY1kgjao1w/KEum48frD/FD+J2IgFX+R9aP0CLqvvwvC8OuIj8O+F1WlGp55gKEd+Ps35o1Y8UfHyKDTUMVN5yZno6V15bVFP"; + } + + public static String qrCodeTestCertificate = "HC1:NCFOXN%TS3DH3ZSUZK+.V0ETD%65NL-AH+VEIOOP-IARC$E08WAN$6%W0AT4V22F/8X*G3M9BM9Z0BFU2P4JY73JC3KD34LT7A3523*BBXSJ$IJGX8R/S+:KLD3JJ3.+IJYCDN0TA3RK37MBZD3K%I17JLXKB6J57TJK57ALD-I3 28XGL4LQ/SLE50$499TVU1PK9PYLPN1VUU8C1VTE5ZM376 IE2IML07Z ESF6C.M1EE*2NY*6UJE4:6Q56FPEXM6IS6-Q64Y6N$E.%66PP33M19V2+PFQ51C5EWAC1A.GUQ$9WC54FQ68ENV0XTCM*4CZKHKB-43.E3KD3OAJA70:ZH/O1:O1AT1NQ1 WUQRENS431TN$IK.G47HB%0WT072HFHN/SNP.0FVV/SN7Y431TCRV$.PTW5CL52U50$EZ*N4IU4FKCPACPI2YUFJ6LX3+KG% BTVB3UQFJ6GL28LHXOAYJAUVPQRHIY1* HOQ1PRAAUICO1 IGBY05*H%XGU-PNQCSJ1A+3HJ5VBQR6G/:DS3GH9FW-SAOPNRFBYMBPJEODEN6%99SLNS9KKFC$OKT1GVKJTCW.IIX2S3V0$1D243B/J.H1RJVN01HI7T.H"; + + public static String cborObject = "a4041a60bc9f38061a60b9fc3801624154390103a101a4617481a962736374323032312d30322d32305431323a33343a35365a626d6164313233326274746a4c503231373139382d336274637754657374696e672063656e746572205669656e6e61203162636f624154626369783155524e3a555643493a30313a41543a37314545323535394445333843364246373330344642363541314134353145432333626973781b4d696e6973747279206f66204865616c74682c20417573747269616274676938343035333930303662747269323630343135303030636e616da463666e74754d5553544552465241553c474f455353494e47455262666e754d7573746572667261752d47c3b6c39f696e67657263676e74684741425249454c4562676e684761627269656c656376657265312e322e3163646f626a313939382d30322d3236"; + + public static String qrCodeMssingValues = "HC1:NCFOXN%TS3DH3ZSUZK+.V0ETD%65NL-AH0 MIOO6+IRKULA7GJLH1WU9T+QI6M8SA3/-2E%5TR5RVBFVAPUB1VCSWC%PDGZKBTC$JC6VCQVD. CL%2VTAQPIAK2K.C*/C/QDEZI8NBMZIVZJ4RLUTIT0KXRS*IJCSIYYKN63QR35VA2TBQMISVBYIJ33M+ZJ::A4O2OVPX0QON9Y46PK9$-0BZIKYJGCC:H3J1D1I3-*TW CXBDW33::SGKD2YC29DHSTY5TMGSR.CK.C2YC6YCBKDZJC9VC..DAVC55MVIJGDB0D48UJ06J9UBSVAXCIF4LEIIPBJ7OICWK%5BBS22T9PF5RBQ746B46O1N646BQ99Q9O$B ZJY1B QTOD3CQSP$SR$S3NDC9UOD3Y.TJET9G3DZI65BDM1F0J*XI-XIFRLL:F*ZR/MV$CT:Z2 /KU%CW.4WV2L4L$XKV7J$%25I3IC33835AL5:4A930JBB8G0 O4POY.6/IIYTE5NJJD2E9O%Q5G9WARD:QFMUU9K79LG-H9%K5.6MRXPDW5V$3/UR83LIBF2IVO7GXD9R$OB9MXUNTIDX 6-Z1YCN+VDKS0F5UV/H"; +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateDccParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateDccParserTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..f9f50f5c6da1cda8b97095a696dcd1055dc7515c --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateDccParserTest.kt @@ -0,0 +1,44 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.qrcode + +import com.google.gson.Gson +import com.upokecenter.cbor.CBORObject +import de.rki.coronawarnapp.covidcertificate.test.TestData +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccParser +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeHex +import org.joda.time.LocalDate +import org.junit.jupiter.api.Test + +class TestCertificateDccParserTest { + + private val bodyParser = TestDccParser(Gson()) + + @Test + fun `happy path cose decryption with Ellen Cheng`() { + val coseObject = CBORObject.DecodeFromBytes(TestData.cborObject.decodeHex().toByteArray()) + with(bodyParser.parse(coseObject)) { + + with(nameData) { + familyName shouldBe "Musterfrau-Gößinger" + familyNameStandardized shouldBe "MUSTERFRAU<GOESSINGER" + givenName shouldBe "Gabriele" + givenNameStandardized shouldBe "GABRIELE" + } + dob shouldBe "1998-02-26" + dateOfBirth shouldBe LocalDate.parse("1998-02-26") + version shouldBe "1.2.1" + + with(payloads[0]) { + uniqueCertificateIdentifier shouldBe "URN:UVCI:01:AT:71EE2559DE38C6BF7304FB65A1A451EC#3" + certificateCountry shouldBe "AT" + certificateIssuer shouldBe "Ministry of Health, Austria" + targetId shouldBe "840539006" + sampleCollectedAt shouldBe org.joda.time.Instant.parse("2021-02-20T12:34:56+00:00") + testType shouldBe "LP217198-3" + testCenter shouldBe "Testing center Vienna 1" + testNameAndManufactor shouldBe "1232" + testResult shouldBe "260415000" + } + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractorTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..39226fcec68b6fdf0526183fc6aba5a6b1cf5a75 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/qrcode/TestCertificateQRCodeExtractorTest.kt @@ -0,0 +1,130 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.qrcode + +import com.google.gson.Gson +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccCoseDecoder +import de.rki.coronawarnapp.covidcertificate.common.decoder.DccHeaderParser +import de.rki.coronawarnapp.covidcertificate.cryptography.AesCryptography +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidTestCertificateException +import de.rki.coronawarnapp.covidcertificate.test.TestData +import de.rki.coronawarnapp.covidcertificate.test.core.certificate.TestDccParser +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationQrCodeTestData +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import okio.ByteString.Companion.decodeBase64 +import org.joda.time.Instant +import org.joda.time.LocalDate +import org.junit.jupiter.api.Test +import testhelpers.BaseTest + +class TestCertificateQRCodeExtractorTest : BaseTest() { + private val coseDecoder = DccCoseDecoder(AesCryptography()) + private val headerParser = DccHeaderParser() + private val bodyParser = TestDccParser(Gson()) + private val extractor = TestCertificateQRCodeExtractor(coseDecoder, headerParser, bodyParser) + + @Test + fun `happy path qr code`() { + val qrCode = extractor.extract(TestData.qrCodeTestCertificate) + with(qrCode.data.header) { + issuer shouldBe "AT" + issuedAt shouldBe Instant.parse("2021-06-01T10:12:48.000Z") + expiresAt shouldBe Instant.parse("2021-06-03T10:12:48.000Z") + } + + with(qrCode.data.certificate) { + with(nameData) { + familyName shouldBe "Musterfrau-Gößinger" + familyNameStandardized shouldBe "MUSTERFRAU<GOESSINGER" + givenName shouldBe "Gabriele" + givenNameStandardized shouldBe "GABRIELE" + } + dob shouldBe "1998-02-26" + dateOfBirth shouldBe LocalDate.parse("1998-02-26") + version shouldBe "1.2.1" + + with(payloads[0]) { + uniqueCertificateIdentifier shouldBe "URN:UVCI:01:AT:71EE2559DE38C6BF7304FB65A1A451EC#3" + certificateCountry shouldBe "AT" + certificateIssuer shouldBe "Ministry of Health, Austria" + targetId shouldBe "840539006" + sampleCollectedAt shouldBe Instant.parse("2021-02-20T12:34:56+00:00") + testType shouldBe "LP217198-3" + testCenter shouldBe "Testing center Vienna 1" + testNameAndManufactor shouldBe "1232" + testResult shouldBe "260415000" + } + } + } + + @Test + fun `happy path cose decryption with Ellen Cheng`() { + with(TestData.EllenCheng()) { + val coseObject = coseWithEncryptedPayload.decodeBase64()!!.toByteArray() + val dek = dek.decodeBase64()!!.toByteArray() + val result = extractor.extract(dek, coseObject) + with(result.data.certificate.nameData) { + familyName shouldBe "Cheng" + givenName shouldBe "Ellen" + } + val result2 = extractor.extract(result.qrCode) + with(result2.data.certificate.nameData) { + familyName shouldBe "Cheng" + givenName shouldBe "Ellen" + } + } + } + + @Test + fun `happy path cose decryption with Brian Calamandrei`() { + with(TestData.BrianCalamandrei()) { + val coseObject = coseWithEncryptedPayload.decodeBase64()!!.toByteArray() + val dek = dek.decodeBase64()!!.toByteArray() + val result = extractor.extract(dek, coseObject) + with(result.data.certificate.nameData) { + familyName shouldBe "Calamandrei" + givenName shouldBe "Brian" + } + val result2 = extractor.extract(result.qrCode) + with(result2.data.certificate.nameData) { + familyName shouldBe "Calamandrei" + givenName shouldBe "Brian" + } + } + } + + @Test + fun `valid encoding but not a health certificate fails with HC_CWT_NO_ISS`() { + shouldThrow<InvalidTestCertificateException> { + extractor.extract(VaccinationQrCodeTestData.validEncoded) + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_ISS + } + + @Test + fun `random string fails with HC_BASE45_DECODING_FAILED`() { + shouldThrow<InvalidTestCertificateException> { + extractor.extract("nothing here to see") + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED + } + + @Test + fun `uncompressed base45 string fails with HC_ZLIB_DECOMPRESSION_FAILED`() { + shouldThrow<InvalidTestCertificateException> { + extractor.extract("6BFOABCDEFGHIJKLMNOPQRSTUVWXYZ %*+-./:") + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED + } + + @Test + fun `vaccination certificate fails with NO_TEST_ENTRY`() { + shouldThrow<InvalidTestCertificateException> { + extractor.extract(VaccinationQrCodeTestData.certificateMissing) + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.NO_TEST_ENTRY + } + + @Test + fun `null values fail with JSON_SCHEMA_INVALID`() { + shouldThrow<InvalidTestCertificateException> { + extractor.extract(TestData.qrCodeMssingValues) + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.JSON_SCHEMA_INVALID + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/server/CovidCertificateApiV1Test.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/server/CovidCertificateApiV1Test.kt new file mode 100644 index 0000000000000000000000000000000000000000..cd0da93b8ce592dbd6f673a1ab5cba56ad14802a --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/test/core/server/CovidCertificateApiV1Test.kt @@ -0,0 +1,67 @@ +package de.rki.coronawarnapp.covidcertificate.test.core.server + +import de.rki.coronawarnapp.http.HttpModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import kotlinx.coroutines.runBlocking +import okhttp3.mockwebserver.MockWebServer +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.extensions.toJsonResponse +import java.util.concurrent.TimeUnit + +class CovidCertificateApiV1Test : BaseTest() { + + private lateinit var webServer: MockWebServer + private lateinit var serverAddress: String + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + + webServer = MockWebServer() + webServer.start() + serverAddress = "http://${webServer.hostName}:${webServer.port}" + } + + @AfterEach + fun teardown() { + webServer.shutdown() + } + + private fun createAPI(): TestCertificateApiV1 { + val httpModule = HttpModule() + + return TestCertificateServerModule().let { + val downloadHttpClient = httpModule.defaultHttpClient() + it.apiV1( + httpClient = downloadHttpClient, + url = serverAddress, + gsonConverterFactory = httpModule.provideGSONConverter() + ) + } + } + + @Test + fun `test upload`(): Unit = runBlocking { + val api = createAPI() + """ + { + + } + """.toJsonResponse().apply { webServer.enqueue(this) } + + api.sendPublicKey( + TestCertificateApiV1.PublicKeyUploadRequest( + registrationToken = "token1", + publicKey = "key1" + ) + ) + + webServer.takeRequest(5, TimeUnit.SECONDS)!!.apply { + path shouldBe "/version/v1/publicKey" + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinatedPersonTest.kt similarity index 90% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinatedPersonTest.kt index c63461ce23343dccbe41d1d0efe3c1b21856707b..bd02d7face41c0a7a78cd705544b2e474caba389 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinatedPersonTest.kt @@ -1,7 +1,7 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinatedPersonData -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationContainer +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinatedPersonData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationContainer import io.kotest.matchers.shouldBe import io.mockk.every import io.mockk.mockk @@ -36,14 +36,12 @@ class VaccinatedPersonTest : BaseTest() { ) certificate.apply { - every { firstName } returns "Straw" - every { lastName } returns "Berry" + every { fullName } returns "Straw Berry" } vaccinatedPerson.fullName shouldBe "Straw Berry" certificate.apply { - every { firstName } returns null // Thermo - every { lastName } returns "Siphon" + every { fullName } returns "Siphon" } vaccinatedPerson.fullName shouldBe "Siphon" } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationQrCodeTestData.java b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationQrCodeTestData.java similarity index 76% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationQrCodeTestData.java rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationQrCodeTestData.java index 82558ff23b6f13453b7bfd5f22557633710f2d6f..57d6ad6ef5d0542319e8a2716773624f6ca2ec45 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationQrCodeTestData.java +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationQrCodeTestData.java @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.vaccination.core; +package de.rki.coronawarnapp.covidcertificate.vaccination.core; public class VaccinationQrCodeTestData { static public String validVaccinationQrCode = "HC1:6BFOXN*TS0BI$ZD4N9:9S6RCVN5+O30K3/XIV0W23NTDEXWK G2EP4J0BGJLFX3R3VHXK.PJ:2DPF6R:5SVBHABVCNN95SWMPHQUHQN%A0SOE+QQAB-HQ/HQ7IR.SQEEOK9SAI4- 7Y15KBPD34 QWSP0WRGTQFNPLIR.KQNA7N95U/3FJCTG90OARH9P1J4HGZJKBEG%123ZC$0BCI757TLXKIBTV5TN%2LXK-$CH4TSXKZ4S/$K%0KPQ1HEP9.PZE9Q$95:UENEUW6646936HRTO$9KZ56DE/.QC$Q3J62:6LZ6O59++9-G9+E93ZM$96TV6NRN3T59YLQM1VRMP$I/XK$M8PK66YBTJ1ZO8B-S-*O5W41FD$ 81JP%KNEV45G1H*KESHMN2/TU3UQQKE*QHXSMNV25$1PK50C9B/9OK5NE1 9V2:U6A1ELUCT16DEETUM/UIN9P8Q:KPFY1W+UN MUNU8T1PEEG%5TW5A 6YO67N6BBEWED/3LS3N6YU.:KJWKPZ9+CQP2IOMH.PR97QC:ACZAH.SYEDK3EL-FIK9J8JRBC7ADHWQYSK48UNZGG NAVEHWEOSUI2L.9OR8FHB0T5HM7I"; @@ -8,4 +8,6 @@ public class VaccinationQrCodeTestData { static public String validVaccinationQrCode3 = "HC1:NCFOXN%TS3DH3ZSUZK+.V0ETD%65NL-AH%TAIOOW%I-1W0658WA/UAN9AAT4V22F/8X*G3M9JUPY0BX/KR96R/S09T./0LWTKD33236J3TA3M*4VV2 73-E3ND3DAJ-43%*48YIB73A*G3W19UEBY5:PI0EGSP4*2D$43B+2SEB7:I/2DY73CIBC:G 7376BXBJBAJ UNFMJCRN0H3PQN*E33H3OA70M3FMJIJN523S+0B/S7-SN2H N37J3JFTULJ5CB3ZCIATULV:SNS8F-67N%21Q21$48X2+36D-I/2DBAJDAJCNB-43SZ4RZ4E%5B/9OK53:UCT16DEZIE IE9.M CVCT1+9V*QERU1MK93P5 U02Y9.G9/G9F:QQ28R3U6/V.*NT*QM.SY$N-P1S29 34S0BYBRC.UYS1U%O6QKN*Q5-QFRMLNKNM8JI0EUGP$I/XK$M8-L9KDI:ZH2E4EVS6O0FVAQNJT:EZ6Q%D0*T1.XSDYV0.VI2OKSNODA.BOD:C.OTXS02:M5OGJIF4LHJW7FFJ2NLGFL/EE%CJF+KM%V$AUS:H+NARLK IBMMG"; static public String validVaccinationQrCode4 = "HC1:6BFOXN*TS0BI$ZD.P9UOL97O4-2HH77HRM3DSPTLRR+%3KXH9M9ESIGUBA KWML%6S5B9-+P70Q5VC9:BPCNYKMXEE1JAA/CXGG0JK1WL260X638J3-E3ND3DAJ-43TTTO3HK1H3QBCWNZ83UQJ:T0/8F7V0HKN:Q8.HBV+0SZ4GH00T9UKP0T9WC5PF6846A$Q$76QW6%V98T5$FQMI5DN9QZ5Y0Q$UPE%5MZ5*T57ZA$O7T6LEJOA+MZ55EII-EB1EKC422JBBD0D2K.EJJ14B2MP41WTRZPQEC5L64HX6IAS 8S8FT/MAMXP6QS03L0QIRR97I2HOAXL92L0. KOKG8VG5SI:TU+MMPZ55%PBT1YEGEA7IB65C94JBQ2NLEE:NQ% GC3MXHFLF9OIFN0IZ95LJL80P1FDLW452I8941:HH3M41GTNP8EFUNT$.FTD852IWKP/HLIJL8JF8JF172IMAS EDAHMXFBFBQSKJE72KV$FHJ%3O%6:XM+1QD+T2/VKKER3L3%1THL7MGY.1S:T:GLOX6OCE7+RWYL3.C-L27WNV0G::M74O%K7C50AAEI4"; static public String qrCodeWithNonsenseCountry = "HC1:NCF3Y28.P-O0PS3JPU7RBWBA2*9VTS/9VZ+PLUOVTJ$EB7W3R9B3VN/3A44E./EZ.6Y8C$.C.IK6MA$00J1TQZ9$9IU+S7HP%X9%*MW09:4WB/5SWB20V5VFBBREWO+GIIUF4+PBZR7MNX/N1JIIML/X3Z.Q67RMB6:BJYE26A5NNL:CIM-A*/UZTM+QO: ACV6212500GUC+KM-5AUYGUD1330PFBA855/SNDPCSOC3KMR9X$DB61.0AESG$:THFGP-M/VI2SG/ 22SS+V8OP3R8LDJ50HR6S94JMN-84Q0C+2/8FUV9HH6N91GB3/YCHN6ALFFZL3M116O/IBU6QKJK/3FMQ0TLK-.UQOO$%A $J%H0%*J:DE6/DOKTG*F605WRK8G7S96JG0 4IF:B9VM0CIBRF/XNBOH9 SGIFJ/2CX593I0GE7FFDEQ6+UO5D+HM/2IDBI.ET/L725IHPKB/T/Q9KRJ* NOWN$6K8VOZIHJ5R29KQWSPYKYSDZRJ+1IVBFGPMXEVY6JIYI/ CVBTJ-FY%MO%RUTF17S:1OL8PVXHRPTUOTK/VF%U%:IR G"; + static public String qrCodeWithNullValues = "HC1:NCFOXN%TS3DH3ZSUZK+.V0ETD%65NL-AH.TAIOO6+I2HU7A28WAI1G$H4AT4V22F/8X*G8QHJUPZ0BR/S09T./0LWTKD33236J3TA3M*4VV2 73-E3GG396B-43O058YIB73A*G3W19UEBY5:PIDHGNTI4L6YO1%UG/YL WO*Z7ON1 *L:O8PN1QP5O PLU9A/RUX96 B0V1ZZB.T12.H.ZJ$%HN 9GTBIQ16-I5NI5K1*TB3:U-1VVS1UU15%HVLIWQHYZKOP6OH6XO9IE5IVU5P2-GA*PE1H6IO2OO9$G40GHS-O:S9UZ4+FJE 4Y3L 78OAJ/9TL4T1C9 UPVD5BT17$1MV15K1DR1FIEC2F5+1T+UC2FSH9 UP+/UXJDTW5CL52U50$EZ*N.KUW*P .UUQKC.U%KIP3FY5LG1A614I%KZYNNEVQ KB+P8$JG+SB.V Q5FN9ZK1BCTD PPQ3X:J15RM*F9TVYPVE6G1OU-5Q.-OP*17:FX+52AWI1C0:HPE2%90Q:H2SFUGVP56O1W:W7MEGZNBC3WJ0J:*JITJ%W6XK2L3S.GA/S14 FD$G"; + static public String qrCodeBlankLastNameStandardized = "HC1:NCFOXN%TS3DH3ZSUZK+.V0ETD%65NL-AH:XIIOO6+ID7BO:H.I5B88MQ3ZMIN9HNO4*J8OX4W$C2VL*LA 43/IE%TE6UG+ZEAT1HQ13W1:O1YUI%F1PN1/T1%%HRP5 R14SI.J9DYHZROVZ05QNZ 20OP748$NI4L6YO1%UG/YL WO*Z7ON1 *L:O8PN1QP5O PLU9A/RUX96 B0V1ZZB.T12.H.ZJ$%HN 9GTBIQ16-I5NI5K1*TB3:U-1VVS1UU15%HVLIWQHYZKOP6OH6XO9IE5IVU5P2-GA*PE1H6IO2OO9$G40GHS-O:S9UZ4+FJE 4Y3L 78OAJ/9TL4T1C9 UP+B9MK93P5AW0:G9.G9/G9F:QQ28R3U6/V.*NT*QM.SY$N-P1S29 34S0BYBRC.UYS1U%O6QKN*Q5-QFRMLNKNM8LK4IYOP$I/XK$M8-L9HCI:ZH2E4UE1B4GC:DN5E:UNJ17$CS*J7.HRT:A /NFZ1FG7K-NXM693K+DD1ET2 D13ST$LGMUW/O7P6CJSZ9NC+Q6DJ1ZJ65QG9MS 1M$FRFTS50*G5V0"; } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestComponent.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestComponent.kt similarity index 59% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestComponent.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestComponent.kt index 181970bd621adbbc1eb156126f6bd70dcbd01dd7..8d1e04bbb267f7c39592ba80aa9f8e725072164e 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestComponent.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestComponent.kt @@ -1,12 +1,12 @@ -package de.rki.coronawarnapp.vaccination.core +package de.rki.coronawarnapp.covidcertificate.vaccination.core import dagger.Component import dagger.Module +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractorTest +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepositoryTest +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationContainerTest +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationStorageTest import de.rki.coronawarnapp.util.serialization.SerializationModule -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractorTest -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepositoryTest -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationContainerTest -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationStorageTest import javax.inject.Singleton @Singleton diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestData.kt similarity index 77% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestData.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestData.kt index 5652d7054cc15d06706e37f97a889ad54e6ecc93..079022777951abd1eedc80dfd9e383b08853b2f4 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/VaccinationTestData.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/VaccinationTestData.kt @@ -1,12 +1,13 @@ -package de.rki.coronawarnapp.vaccination.core - -import de.rki.coronawarnapp.vaccination.core.certificate.HealthCertificateHeader -import de.rki.coronawarnapp.vaccination.core.certificate.VaccinationDGCV1 -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateData -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationCertificateQRCode -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractor -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinatedPersonData -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationContainer +package de.rki.coronawarnapp.covidcertificate.vaccination.core + +import de.rki.coronawarnapp.covidcertificate.common.certificate.Dcc +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccData +import de.rki.coronawarnapp.covidcertificate.common.certificate.DccHeader +import de.rki.coronawarnapp.covidcertificate.vaccination.core.certificate.VaccinationDccV1 +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationCertificateQRCode +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinatedPersonData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationContainer import org.joda.time.Instant import javax.inject.Inject @@ -19,17 +20,17 @@ class VaccinationTestData @Inject constructor( val personAVac1QRCodeString = "HC1:6BFOXN*TS0BI\$ZD.P9UOL97O4-2HH77HRM3DSPTLRR+%3KXH9M9ESIGUBA KWML%6S5B9-+P70Q5VC9:BPCNYKMXEE1JAA/CXGG0JK1WL260X638J3-E3ND3DAJ-43TTTO3HK1H3QBCWNZ83UQJ:T0/8F7V0HKN:Q8.HBV+0SZ4GH00T9UKP0T9WC5PF6846A\$Q$76QW6%V98T5\$FQMI5DN9QZ5Y0Q\$UPE%5MZ5*T57ZA\$O7T6LEJOA+MZ55EII-EB1EKC422JBBD0D2K.EJJ14B2MP41WTRZPQEC5L64HX6IAS 8S8FT/MAMXP6QS03L0QIRR97I2HOAXL92L0. KOKG8VG5SI:TU+MMPZ55%PBT1YEGEA7IB65C94JBQ2NLEE:NQ% GC3MXHFLF9OIFN0IZ95LJL80P1FDLW452I8941:HH3M41GTNP8EFUNT$.FTD852IWKP/HLIJL8JF8JF172IMAS EDAHMXFBFBQSKJE72KV\$FHJ%3O%6:XM+1QD+T2/VKKER3L3%1THL7MGY.1S:T:GLOX6OCE7+RWYL3.C-L27WNV0G::M74O%K7C50AAEI4" - val personAVac1Certificate = VaccinationDGCV1( + val personAVac1Certificate = VaccinationDccV1( version = "1.0.0", - nameData = VaccinationDGCV1.NameData( + nameData = Dcc.NameData( givenName = "Andreas", givenNameStandardized = "ANDREAS", familyName = "Astrá Eins", familyNameStandardized = "ASTRA<EINS", ), dob = "1966-11-11", - vaccinationDatas = listOf( - VaccinationDGCV1.VaccinationData( + payloads = listOf( + VaccinationDccV1.VaccinationData( targetId = "840539006", vaccineId = "1119305005", medicalProductId = "EU/1/21/1529", @@ -37,27 +38,27 @@ class VaccinationTestData @Inject constructor( doseNumber = 1, totalSeriesOfDoses = 2, dt = "2021-03-01", - countryOfVaccination = "DE", + certificateCountry = "DE", certificateIssuer = "Bundesministerium für Gesundheit - Test01", uniqueCertificateIdentifier = "01DE/00001/1119305005/7T1UG87G61Y7NRXIBQJDTYQ9#S", ) ) ) - val personAVac1CertificateHeader = HealthCertificateHeader( + val personAVac1CertificateHeader = DccHeader( issuer = "DE", issuedAt = Instant.parse("2021-05-11T09:25:00.000Z"), expiresAt = Instant.parse("2022-05-11T09:25:00.000Z"), ) - val personAVac1CertificateData = VaccinationCertificateData( + val personAVac1CertificateData = DccData( certificate = personAVac1Certificate, header = personAVac1CertificateHeader ) val personAVac1QRCode = VaccinationCertificateQRCode( - qrCodeString = personAVac1QRCodeString, - parsedData = personAVac1CertificateData, + qrCode = personAVac1QRCodeString, + data = personAVac1CertificateData, ) val personAVac1Container = VaccinationContainer( @@ -71,17 +72,17 @@ class VaccinationTestData @Inject constructor( val personAVac2QRCodeString = "6BFOXN*TS0BI\$ZD.P9UOL97O4-2HH77HRM3DSPTLRR+%3D H9M9ESIGUBA KWMLYX1HXK 0DV:D5VC9:BPCNYKMXEE1JAA/CZIK0JK1WL260X638J3-E3ND3DAJ-43TTTMDF6S8:B73QN VNZ.0K6HYI3CNN96BPHNW*0I85V.499TXY9KK9%OC+G9QJPNF67J6QW67KQ9G66PPM4MLJE+.PDB9L6Q2+PFQ5DB96PP5/P-59A%N+892 7J235II3NJ7PK7SLQMIPUBN9CIZI.EJJ14B2MP41IZRZPQEC5L64HX6IAS 8SAFT/MAMXP6QS03L0QIRR97I2HOAXL92L0. KOKGGVG5SI:TU+MMPZ55%PBT1YEGEA7IB65C94JBQ2NLEE:NQ% GC3MXHFLF9OIFN0IZ95LJL80P1FDLW452I8941:HH3M41GTNP8EFUNT\$.FTD852IWKP/HLIJL8JF8JF172E2JA0K*WDQMPB8T3%KLUSR43M.F\$QBQDR\$VT7V01Y7J0BOZLH+D-QF6MO\$R3%XB+.4QI596GY\$SITJP5BS0DFROC.7B.2RTB*UNYSM$*00HIL+H" - val personAVac2Certificate = VaccinationDGCV1( + val personAVac2Certificate = VaccinationDccV1( version = "1.0.0", - nameData = VaccinationDGCV1.NameData( + nameData = Dcc.NameData( givenName = "Andreas", givenNameStandardized = "ANDREAS", familyName = "Astrá Eins", familyNameStandardized = "ASTRA<EINS", ), dob = "1966-11-11", - vaccinationDatas = listOf( - VaccinationDGCV1.VaccinationData( + payloads = listOf( + VaccinationDccV1.VaccinationData( targetId = "840539006", vaccineId = "1119305005", medicalProductId = "EU/1/21/1529", @@ -89,27 +90,27 @@ class VaccinationTestData @Inject constructor( doseNumber = 2, totalSeriesOfDoses = 2, dt = "2021-04-27", - countryOfVaccination = "DE", + certificateCountry = "DE", certificateIssuer = "Bundesministerium für Gesundheit - Test01", uniqueCertificateIdentifier = "01DE/00001/1119305005/6IPYBAIDWEWRWW73QEP92FQSN#S", ) ) ) - val personAVac2CertificateHeader = HealthCertificateHeader( + val personAVac2CertificateHeader = DccHeader( issuer = "DE", issuedAt = Instant.parse("2021-05-11T09:26:08.000Z"), expiresAt = Instant.parse("2022-05-11T09:26:08.000Z"), ) - val personAVac2CertificateData = VaccinationCertificateData( + val personAVac2CertificateData = DccData( certificate = personAVac2Certificate, header = personAVac2CertificateHeader ) val personAVac2QRCode = VaccinationCertificateQRCode( - qrCodeString = personAVac2QRCodeString, - parsedData = personAVac2CertificateData, + qrCode = personAVac2QRCodeString, + data = personAVac2CertificateData, ) val personAVac2Container = VaccinationContainer( @@ -127,17 +128,17 @@ class VaccinationTestData @Inject constructor( val personBVac1QRCodeString = "HC1:6BFOXN*TS0BI\$ZD.P9UOL97O4-2HH77HRM3DSPTLRR+%3QVH9M9ESIGUBA KWML:SPHXK 0DMYF5VC9:BPCNYKMXEE1JAA/CZIK0JK1WL260X638J3-E3ND3DAJ-43 QTCPFFIJRF3O8H43HX37DUF GFE VMJJYC3SM74E5V.499TXY9KK9+OC+G9QJPNF67J6QW67KQ2G66PPM4MLJE+.PDB9L6Q2+PFQ5DB96PP5/P-59A%N+892 7J235II3NJ7PK7SLQMIPUBN9CIZI.EJJ14B2MP41AZRSEQEC5L64HX6IAS3DS2980IQ.DPUHLW\$GAHLW 70SO:GOLIROGO3T59YLQM14+OP\$I/XK\$M8CL6PZB*L8PK99Q9E\$BDZIF9J8-I\$GI0 J1ALL:F71APC9*KF6LF/NLR/FZ.COKEH-BB4OQ9OG4C5AO**HOELK2AZ7LBLEH-BHPLV5GK3DNKE\$JDVPLW1KD0KCZG.M1LUSB5BCQRJ\$DB5N9%V/GO4IHIBBJ-BI%NWRS%LR%\$KR46325NABFDDAFHD9PZP11COD5U*2KQXCA5W8HH/K51DQO8O0-SOSENFH9101U8$3" - val personBVac1Certificate = VaccinationDGCV1( + val personBVac1Certificate = VaccinationDccV1( version = "1.0.0", - nameData = VaccinationDGCV1.NameData( + nameData = Dcc.NameData( givenName = "Boris", givenNameStandardized = "BORIS", familyName = "Johnson Gültig", familyNameStandardized = "JOHNSON<GUELTIG", ), dob = "1966-11-11", - vaccinationDatas = listOf( - VaccinationDGCV1.VaccinationData( + payloads = listOf( + VaccinationDccV1.VaccinationData( targetId = "840539006", vaccineId = "1119305005", medicalProductId = "EU/1/20/1525", @@ -145,27 +146,27 @@ class VaccinationTestData @Inject constructor( doseNumber = 1, totalSeriesOfDoses = 1, dt = "2021-04-20", - countryOfVaccination = "DE", + certificateCountry = "DE", certificateIssuer = "Bundesministerium für Gesundheit - Test01", uniqueCertificateIdentifier = "01DE/00001/1119305005/3H24U2KVOTPCSINK7N64F2OB9#S", ) ) ) - val personBVac1CertificateHeader = HealthCertificateHeader( + val personBVac1CertificateHeader = DccHeader( issuer = "DE", issuedAt = Instant.parse("2021-05-11T09:23:03.000Z"), expiresAt = Instant.parse("2022-05-11T09:23:03.000Z"), ) - val personBVac1CertificateData = VaccinationCertificateData( + val personBVac1CertificateData = DccData( certificate = personBVac1Certificate, header = personBVac1CertificateHeader ) val personBVac1QRCode = VaccinationCertificateQRCode( - qrCodeString = personBVac1QRCodeString, - parsedData = personBVac1CertificateData, + qrCode = personBVac1QRCodeString, + data = personBVac1CertificateData, ) val personBVac1Container = VaccinationContainer( diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/ValueSetTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/ValueSetTestData.kt new file mode 100644 index 0000000000000000000000000000000000000000..732fab8d22391c94e62838b2ed913094e5814ca5 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/ValueSetTestData.kt @@ -0,0 +1,78 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core + +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.DefaultValueSet +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.TestCertificateValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsContainer +import java.util.Locale + +object ValueSetTestData { + + // Shared + val tgItemDe = "tg" to "Ziel-Name" + val tgItemEn = tgItemDe.copy(second = "Target-Name") + + // Vaccination + val vpItemDe = "1119305005" to "Impfstoff-Name" + val mpItemDe = "EU/1/21/1529" to "Arzneimittel-Name" + val maItemDe = "ORG-100001699" to "Hersteller-Name" + + val vpItemEn = vpItemDe.copy(second = "Vaccine-Name") + val mpItemEn = mpItemDe.copy(second = "MedicalProduct-Name") + val maItemEn = maItemDe.copy(second = "Manufactorer-Name") + + // Test certificate + val ttItemDe = "tt" to "Test-Typ" + val tcMaItemDe = "tcMa" to "RAT-Test-Name-und-Hersteller" + val trItemDe = "tr" to "Test-Ergebnis" + + val ttItemEn = vpItemDe.copy(second = "Test-Type") + val tcMaItemEn = mpItemDe.copy(second = "RAT-Test-Name-and-Manufacturer") + val trItemEn = maItemDe.copy(second = "Test-Result") + + private fun Pair<String, String>.toValueSet() = DefaultValueSet( + items = listOf(DefaultValueSet.DefaultItem(key = first, displayText = second)) + ) + + val vaccinationValueSetsDe = VaccinationValueSets( + languageCode = Locale.GERMAN, + tg = tgItemDe.toValueSet(), + vp = vpItemDe.toValueSet(), + mp = mpItemDe.toValueSet(), + ma = maItemDe.toValueSet() + ) + + val vaccinationValueSetsEn = VaccinationValueSets( + languageCode = Locale.GERMAN, + tg = tgItemEn.toValueSet(), + vp = vpItemEn.toValueSet(), + mp = mpItemEn.toValueSet(), + ma = maItemEn.toValueSet() + ) + + val testCertificateValueSetsDe = TestCertificateValueSets( + languageCode = Locale.GERMAN, + tg = tgItemDe.toValueSet(), + tt = ttItemDe.toValueSet(), + ma = tcMaItemDe.toValueSet(), + tr = trItemDe.toValueSet() + ) + + val testCertificateValueSetsEn = TestCertificateValueSets( + languageCode = Locale.GERMAN, + tg = tgItemEn.toValueSet(), + tt = ttItemEn.toValueSet(), + ma = tcMaItemEn.toValueSet(), + tr = trItemEn.toValueSet() + ) + + val valueSetsContainerDe = ValueSetsContainer( + vaccinationValueSets = vaccinationValueSetsDe, + testCertificateValueSets = testCertificateValueSetsDe + ) + + val valueSetsContainerEn = ValueSetsContainer( + vaccinationValueSets = vaccinationValueSetsEn, + testCertificateValueSets = testCertificateValueSetsEn + ) +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt similarity index 90% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt index 4488f0b27dd0db557456c40ab0b14bb0527d6035..d3358a2c812be102861b3553ecd3682eebf5f843 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/VaccinationUpdateSchedulerTest.kt @@ -1,16 +1,16 @@ -package de.rki.coronawarnapp.vaccination.core.execution +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution import androidx.work.PeriodicWorkRequest import androidx.work.WorkInfo import androidx.work.WorkManager +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinatedPerson +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker.VaccinationUpdateWorkerRequestBuilder +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.TaskRequest import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.device.ForegroundState -import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson -import de.rki.coronawarnapp.vaccination.core.execution.worker.VaccinationUpdateWorkerRequestBuilder -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import io.mockk.Called import io.mockk.MockKAnnotations import io.mockk.Runs diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt similarity index 84% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt index 59c1af64093c344318739a48559cbbb27884342f..918e72b86509ca245242071bb4b8bfc56a04f1f0 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/task/VaccinationUpdateTaskTest.kt @@ -1,6 +1,6 @@ -package de.rki.coronawarnapp.vaccination.core.execution.task +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import io.mockk.MockKAnnotations import io.mockk.Runs import io.mockk.coEvery diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt similarity index 93% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt index 60c8a9d2558c3bd8218fa729b74a167e46af3a1d..657ee5993bdec0d28114ef3228dc626615d96f52 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/execution/worker/VaccinationUpdateWorkerTest.kt @@ -1,15 +1,15 @@ -package de.rki.coronawarnapp.vaccination.core.execution.worker +package de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.worker import android.content.Context import androidx.work.ListenableWorker import androidx.work.WorkerParameters +import de.rki.coronawarnapp.covidcertificate.vaccination.core.execution.task.VaccinationUpdateTask import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.task.TaskFactory import de.rki.coronawarnapp.task.TaskRequest import de.rki.coronawarnapp.task.TaskState import de.rki.coronawarnapp.task.common.DefaultTaskRequest import de.rki.coronawarnapp.task.submitBlocking -import de.rki.coronawarnapp.vaccination.core.execution.task.VaccinationUpdateTask import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.coEvery diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt similarity index 62% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt index ef2d3bd05a7f431767e5bf92ce403b1a1f3b1100..de06cb9760fff4d41c49d07d7c1c5bd6078a7755 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/qrcode/VaccinationQRCodeExtractorTest.kt @@ -1,13 +1,14 @@ -package de.rki.coronawarnapp.vaccination.core.qrcode - -import de.rki.coronawarnapp.vaccination.core.DaggerVaccinationTestComponent -import de.rki.coronawarnapp.vaccination.core.VaccinationQrCodeTestData -import de.rki.coronawarnapp.vaccination.core.VaccinationTestData -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_HC_CWT_NO_ISS -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException.ErrorCode.VC_NO_VACCINATION_ENTRY +package de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode + +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_BASE45_DECODING_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_CWT_NO_ISS +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.HC_ZLIB_DECOMPRESSION_FAILED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_NO_VACCINATION_ENTRY +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.DaggerVaccinationTestComponent +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationQrCodeTestData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationTestData import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import org.joda.time.Instant @@ -41,13 +42,13 @@ class VaccinationQRCodeExtractorTest : BaseTest() { fun `happy path extraction with data`() { val qrCode = extractor.extract(VaccinationQrCodeTestData.validVaccinationQrCode3) - with(qrCode.parsedData.header) { + with(qrCode.data.header) { issuer shouldBe "AT" issuedAt shouldBe Instant.ofEpochSecond(1620392021) expiresAt shouldBe Instant.ofEpochSecond(1620564821) } - with(qrCode.parsedData.certificate) { + with(qrCode.data.certificate) { with(nameData) { familyName shouldBe "Musterfrau-Gößinger" familyNameStandardized shouldBe "MUSTERFRAU<GOESSINGER" @@ -58,9 +59,9 @@ class VaccinationQRCodeExtractorTest : BaseTest() { dateOfBirth shouldBe LocalDate.parse("1998-02-26") version shouldBe "1.0.0" - with(vaccinationDatas[0]) { + with(payloads[0]) { uniqueCertificateIdentifier shouldBe "urn:uvci:01:AT:10807843F94AEE0EE5093FBC254BD813P" - countryOfVaccination shouldBe "AT" + certificateCountry shouldBe "AT" doseNumber shouldBe 1 dt shouldBe "2021-02-18" certificateIssuer shouldBe "BMSGPK Austria" @@ -80,29 +81,29 @@ class VaccinationQRCodeExtractorTest : BaseTest() { } @Test - fun `valid encoding but not a health certificate fails with VC_HC_CWT_NO_ISS`() { - shouldThrow<InvalidHealthCertificateException> { + fun `valid encoding but not a health certificate fails with HC_CWT_NO_ISS`() { + shouldThrow<InvalidVaccinationCertificateException> { extractor.extract(VaccinationQrCodeTestData.validEncoded) - }.errorCode shouldBe VC_HC_CWT_NO_ISS + }.errorCode shouldBe HC_CWT_NO_ISS } @Test fun `random string fails with HC_BASE45_DECODING_FAILED`() { - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { extractor.extract("nothing here to see") }.errorCode shouldBe HC_BASE45_DECODING_FAILED } @Test fun `uncompressed base45 string fails with HC_ZLIB_DECOMPRESSION_FAILED`() { - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { extractor.extract("6BFOABCDEFGHIJKLMNOPQRSTUVWXYZ %*+-./:") }.errorCode shouldBe HC_ZLIB_DECOMPRESSION_FAILED } @Test fun `vaccination certificate missing fails with VC_NO_VACCINATION_ENTRY`() { - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { extractor.extract(VaccinationQrCodeTestData.certificateMissing) }.errorCode shouldBe VC_NO_VACCINATION_ENTRY } @@ -118,4 +119,18 @@ class VaccinationQRCodeExtractorTest : BaseTest() { val extracted = extractor.extract(vaccinationTestData.personBVac1QRCodeString) extracted shouldBe vaccinationTestData.personBVac1QRCode } + + @Test + fun `null values fail with JSON_SCHEMA_INVALID`() { + shouldThrow<InvalidVaccinationCertificateException> { + extractor.extract(VaccinationQrCodeTestData.qrCodeWithNullValues) + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.JSON_SCHEMA_INVALID + } + + @Test + fun `blank name fails with JSON_SCHEMA_INVALID`() { + shouldThrow<InvalidVaccinationCertificateException> { + extractor.extract(VaccinationQrCodeTestData.qrCodeBlankLastNameStandardized) + }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.JSON_SCHEMA_INVALID + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepositoryTest.kt similarity index 81% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepositoryTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepositoryTest.kt index f0703302cb0f92b034fdb75003771bfd254ef56e..1f5e4170631a3f677690f6756924f2bb15499e3f 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/VaccinationRepositoryTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/VaccinationRepositoryTest.kt @@ -1,14 +1,17 @@ -package de.rki.coronawarnapp.vaccination.core.repository - +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository + +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_ALREADY_REGISTERED +import de.rki.coronawarnapp.covidcertificate.exception.InvalidHealthCertificateException.ErrorCode.VC_NAME_MISMATCH +import de.rki.coronawarnapp.covidcertificate.exception.InvalidVaccinationCertificateException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.DaggerVaccinationTestComponent +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationTestData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.qrcode.VaccinationQRCodeExtractor +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.errors.VaccinationCertificateNotFoundException +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinatedPersonData +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage.VaccinationStorage +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets import de.rki.coronawarnapp.util.TimeStamper -import de.rki.coronawarnapp.vaccination.core.DaggerVaccinationTestComponent -import de.rki.coronawarnapp.vaccination.core.VaccinationTestData -import de.rki.coronawarnapp.vaccination.core.certificate.InvalidHealthCertificateException -import de.rki.coronawarnapp.vaccination.core.qrcode.VaccinationQRCodeExtractor -import de.rki.coronawarnapp.vaccination.core.repository.errors.VaccinationCertificateNotFoundException -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinatedPersonData -import de.rki.coronawarnapp.vaccination.core.repository.storage.VaccinationStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations @@ -32,7 +35,7 @@ class VaccinationRepositoryTest : BaseTest() { @MockK lateinit var storage: VaccinationStorage @MockK lateinit var valueSetsRepository: ValueSetsRepository - @MockK lateinit var vaccinationValueSet: VaccinationValueSet + @MockK lateinit var vaccinationValueSet: VaccinationValueSets @MockK lateinit var qrCodeExtractor: VaccinationQRCodeExtractor private var testStorage: Set<VaccinatedPersonData> = emptySet() @@ -50,7 +53,7 @@ class VaccinationRepositoryTest : BaseTest() { every { timeStamper.nowUTC } returns nowUTC - every { valueSetsRepository.latestValueSet } returns flowOf(vaccinationValueSet) + every { valueSetsRepository.latestVaccinationValueSets } returns flowOf(vaccinationValueSet) storage.apply { every { personContainers } answers { testStorage } @@ -109,9 +112,9 @@ class VaccinationRepositoryTest : BaseTest() { val instance = createInstance(this) advanceUntilIdle() - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { instance.registerVaccination(vaccinationTestData.personBVac1QRCode) - }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.VC_NAME_MISMATCH + }.errorCode shouldBe VC_NAME_MISMATCH testStorage shouldBe setOf(vaccinationTestData.personAData2Vac) } @@ -127,9 +130,9 @@ class VaccinationRepositoryTest : BaseTest() { val instance = createInstance(this) advanceUntilIdle() - shouldThrow<InvalidHealthCertificateException> { + shouldThrow<InvalidVaccinationCertificateException> { instance.registerVaccination(vaccinationTestData.personAVac1QRCode) - }.errorCode shouldBe InvalidHealthCertificateException.ErrorCode.VC_ALREADY_REGISTERED + }.errorCode shouldBe VC_ALREADY_REGISTERED testStorage.first() shouldBe dataBefore } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainerTest.kt similarity index 71% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainerTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainerTest.kt index fad39f683f0e946dda8f94afb044c82a403a554d..7c9d5d97fbaabf88fb8e2f33441b956fa62a9746 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationContainerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationContainerTest.kt @@ -1,12 +1,11 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage -import de.rki.coronawarnapp.vaccination.core.DaggerVaccinationTestComponent -import de.rki.coronawarnapp.vaccination.core.VaccinatedPersonIdentifier -import de.rki.coronawarnapp.vaccination.core.VaccinationTestData -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.vaccination.core.DaggerVaccinationTestComponent +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationTestData +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.DefaultValueSet +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.VaccinationValueSets import io.kotest.matchers.shouldBe -import io.mockk.every -import io.mockk.mockk import org.joda.time.Instant import org.joda.time.LocalDate import org.junit.jupiter.api.BeforeEach @@ -26,7 +25,7 @@ class VaccinationContainerTest : BaseTest() { @Test fun `person identifier calculation`() { - testData.personAVac1Container.personIdentifier shouldBe VaccinatedPersonIdentifier( + testData.personAVac1Container.personIdentifier shouldBe CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1966-11-11"), firstNameStandardized = "ANDREAS", lastNameStandardized = "ASTRA<EINS" @@ -63,6 +62,7 @@ class VaccinationContainerTest : BaseTest() { testData.personAVac1Container.toVaccinationCertificate(null, userLocale = Locale.GERMAN).apply { firstName shouldBe "Andreas" lastName shouldBe "Astrá Eins" + fullName shouldBe "Andreas Astrá Eins" dateOfBirth shouldBe LocalDate.parse("1966-11-11") vaccinatedAt shouldBe LocalDate.parse("2021-03-01") vaccineTypeName shouldBe "1119305005" @@ -73,7 +73,7 @@ class VaccinationContainerTest : BaseTest() { certificateIssuer shouldBe "Bundesministerium für Gesundheit - Test01" certificateCountry shouldBe "Deutschland" certificateId shouldBe "01DE/00001/1119305005/7T1UG87G61Y7NRXIBQJDTYQ9#S" - personIdentifier shouldBe VaccinatedPersonIdentifier( + personIdentifier shouldBe CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1966-11-11"), firstNameStandardized = "ANDREAS", lastNameStandardized = "ASTRA<EINS" @@ -83,34 +83,33 @@ class VaccinationContainerTest : BaseTest() { @Test fun `mapping to user facing data - with valueset`() { - val vpItem = mockk<VaccinationValueSet.ValueSet.Item> { - every { key } returns "1119305005" - every { displayText } returns "Vaccine-Name" - } - - val mpItem = mockk<VaccinationValueSet.ValueSet.Item> { - every { key } returns "EU/1/21/1529" - every { displayText } returns "MedicalProduct-Name" - } + val vpItem = DefaultValueSet.DefaultItem( + key = "1119305005", + displayText = "Vaccine-Name" + ) - val maItem = mockk<VaccinationValueSet.ValueSet.Item> { - every { key } returns "ORG-100001699" - every { displayText } returns "Manufactorer-Name" - } + val mpItem = DefaultValueSet.DefaultItem( + key = "EU/1/21/1529", + displayText = "MedicalProduct-Name" + ) - val vpMockk = mockk<VaccinationValueSet.ValueSet> { - every { items } returns listOf(vpItem, mpItem, maItem) - } + val maItem = DefaultValueSet.DefaultItem( + key = "ORG-100001699", + displayText = "Manufactorer-Name" + ) - val valueSet = mockk<VaccinationValueSet> { - every { vp } returns vpMockk - every { mp } returns vpMockk - every { ma } returns vpMockk - } + val vaccinationValueSets = VaccinationValueSets( + languageCode = Locale.GERMAN, + tg = DefaultValueSet(), + vp = DefaultValueSet(items = listOf(vpItem)), + mp = DefaultValueSet(items = listOf(mpItem)), + ma = DefaultValueSet(items = listOf(maItem)) + ) - testData.personAVac1Container.toVaccinationCertificate(valueSet, userLocale = Locale.GERMAN).apply { + testData.personAVac1Container.toVaccinationCertificate(vaccinationValueSets, userLocale = Locale.GERMAN).apply { firstName shouldBe "Andreas" lastName shouldBe "Astrá Eins" + fullName shouldBe "Andreas Astrá Eins" dateOfBirth shouldBe LocalDate.parse("1966-11-11") vaccinatedAt shouldBe LocalDate.parse("2021-03-01") vaccineTypeName shouldBe "Vaccine-Name" @@ -121,7 +120,7 @@ class VaccinationContainerTest : BaseTest() { certificateIssuer shouldBe "Bundesministerium für Gesundheit - Test01" certificateCountry shouldBe "Deutschland" certificateId shouldBe "01DE/00001/1119305005/7T1UG87G61Y7NRXIBQJDTYQ9#S" - personIdentifier shouldBe VaccinatedPersonIdentifier( + personIdentifier shouldBe CertificatePersonIdentifier( dateOfBirth = LocalDate.parse("1966-11-11"), firstNameStandardized = "ANDREAS", lastNameStandardized = "ASTRA<EINS" diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorageTest.kt similarity index 83% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorageTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorageTest.kt index 9af9da65bd8172c743c89b1078d28416502a678b..4da0bfea9e53f3b7075bcb43193124031d147681 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/VaccinationStorageTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/VaccinationStorageTest.kt @@ -1,11 +1,12 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage import android.content.Context import androidx.core.content.edit +import de.rki.coronawarnapp.covidcertificate.vaccination.core.DaggerVaccinationTestComponent +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationTestData import de.rki.coronawarnapp.util.serialization.SerializationModule -import de.rki.coronawarnapp.vaccination.core.DaggerVaccinationTestComponent -import de.rki.coronawarnapp.vaccination.core.VaccinationTestData import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK @@ -88,4 +89,11 @@ class VaccinationStorageTest : BaseTest() { ) } } + + @Test + fun `post processor injects data extractors`() { + createInstance().personContainers = setOf(testData.personAData2Vac) + + createInstance().personContainers.single().vaccinations.first().qrCodeExtractor shouldNotBe null + } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ValueSetsStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ValueSetsStorageTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..9bddcf4c849d9f46912cf6d06f8307424db1690e --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/vaccination/core/repository/storage/ValueSetsStorageTest.kt @@ -0,0 +1,188 @@ +package de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.storage + +import android.content.Context +import androidx.core.content.edit +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.valueSetsContainerDe +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.valueSetsContainerEn +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsStorage +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.emptyValueSetsContainer +import de.rki.coronawarnapp.util.serialization.SerializationModule +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.extensions.toComparableJsonPretty +import testhelpers.preferences.MockSharedPreferences + +class ValueSetsStorageTest : BaseTest() { + + @MockK lateinit var context: Context + lateinit var prefs: MockSharedPreferences + + private val gson = SerializationModule().baseGson() + + @BeforeEach + fun setup() { + MockKAnnotations.init(this) + prefs = MockSharedPreferences() + every { context.getSharedPreferences("valuesets_localdata", Context.MODE_PRIVATE) } returns prefs + } + + private fun createInstance() = ValueSetsStorage( + context = context, + gson = gson + ) + + @Test + fun `Updates values`() { + createInstance().run { + valueSetsContainer = valueSetsContainerDe + valueSetsContainer shouldBe valueSetsContainerDe + + valueSetsContainer = valueSetsContainerEn + valueSetsContainer shouldBe valueSetsContainerEn + } + } + + @Test + fun `storage inits empty without sideeffects`() { + createInstance() + prefs.dataMapPeek.isEmpty() shouldBe true + } + + @Test + fun `storage format`() { + createInstance().valueSetsContainer = valueSetsContainerDe + (prefs.dataMapPeek["valuesets_container"] as String).toComparableJsonPretty() shouldBe """ + { + "vaccinationValueSets": { + "languageCode": "de", + "tg": { + "items": [ + { + "key": "tg", + "displayText": "Ziel-Name" + } + ] + }, + "vp": { + "items": [ + { + "key": "1119305005", + "displayText": "Impfstoff-Name" + } + ] + }, + "mp": { + "items": [ + { + "key": "EU/1/21/1529", + "displayText": "Arzneimittel-Name" + } + ] + }, + "ma": { + "items": [ + { + "key": "ORG-100001699", + "displayText": "Hersteller-Name" + } + ] + } + }, + "testCertificateValueSets": { + "languageCode": "de", + "tg": { + "items": [ + { + "key": "tg", + "displayText": "Ziel-Name" + } + ] + }, + "tt": { + "items": [ + { + "key": "tt", + "displayText": "Test-Typ" + } + ] + }, + "ma": { + "items": [ + { + "key": "tcMa", + "displayText": "RAT-Test-Name-und-Hersteller" + } + ] + }, + "tr": { + "items": [ + { + "key": "tr", + "displayText": "Test-Ergebnis" + } + ] + } + } + } + """.toComparableJsonPretty() + + createInstance().apply { + valueSetsContainer shouldBe valueSetsContainerDe + valueSetsContainer = emptyValueSetsContainer + } + (prefs.dataMapPeek["valuesets_container"] as String).toComparableJsonPretty() shouldBe """ + { + "vaccinationValueSets": { + "languageCode": "en", + "tg": { + "items": [] + }, + "vp": { + "items": [] + }, + "mp": { + "items": [] + }, + "ma": { + "items": [] + } + }, + "testCertificateValueSets": { + "languageCode": "en", + "tg": { + "items": [] + }, + "tt": { + "items": [] + }, + "ma": { + "items": [] + }, + "tr": { + "items": [] + } + } + } + """.toComparableJsonPretty() + + createInstance().valueSetsContainer shouldBe emptyValueSetsContainer + } + + @Test + fun `removes leftover`() { + val leftover = "I'm a malicious leftover" + val valueSet = "valueset" + prefs.edit(commit = true) { + putString(valueSet, leftover) + } + + prefs.dataMapPeek[valueSet] shouldBe leftover + createInstance().valueSetsContainer shouldBe emptyValueSetsContainer + prefs.dataMapPeek.isEmpty() shouldBe true + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepositoryTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..fe97082431a3af107392e0208662b6fa2ef8f7c8 --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/ValueSetsRepositoryTest.kt @@ -0,0 +1,128 @@ +package de.rki.coronawarnapp.covidcertificate.valueset + +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.testCertificateValueSetsDe +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.testCertificateValueSetsEn +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.vaccinationValueSetsDe +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.vaccinationValueSetsEn +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.valueSetsContainerDe +import de.rki.coronawarnapp.covidcertificate.vaccination.core.ValueSetTestData.valueSetsContainerEn +import de.rki.coronawarnapp.covidcertificate.valueset.server.CertificateValueSetServer +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.ValueSetsStorage +import de.rki.coronawarnapp.covidcertificate.valueset.valuesets.emptyValueSetsContainer +import io.kotest.matchers.shouldBe +import io.mockk.MockKAnnotations +import io.mockk.Ordering +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.just +import io.mockk.runs +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import testhelpers.BaseTest +import testhelpers.TestDispatcherProvider +import testhelpers.coroutines.runBlockingTest2 +import java.util.Locale + +class ValueSetsRepositoryTest : BaseTest() { + + @MockK lateinit var certificateValueSetServer: CertificateValueSetServer + @MockK lateinit var valueSetsStorage: ValueSetsStorage + + private val testDispatcherProvider = TestDispatcherProvider() + + private fun createInstance(scope: CoroutineScope) = ValueSetsRepository( + certificateValueSetServer = certificateValueSetServer, + valueSetsStorage = valueSetsStorage, + scope = scope, + dispatcherProvider = testDispatcherProvider + ) + + @BeforeEach + fun setUp() { + MockKAnnotations.init(this) + coEvery { certificateValueSetServer.getVaccinationValueSets(any()) } returns + null + coEvery { certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns + valueSetsContainerEn + coEvery { certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns + valueSetsContainerDe + every { certificateValueSetServer.clear() } just runs + + every { valueSetsStorage.valueSetsContainer = any() } just runs + every { valueSetsStorage.valueSetsContainer } returns emptyValueSetsContainer + } + + @Test + fun `successful update for de`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { + triggerUpdateValueSet(languageCode = Locale.GERMAN) + latestVaccinationValueSets.first() shouldBe vaccinationValueSetsDe + latestTestCertificateValueSets.first() shouldBe testCertificateValueSetsDe + } + + coVerify { + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.GERMAN) + valueSetsStorage.valueSetsContainer = valueSetsContainerDe + } + + coVerify(exactly = 0) { + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + valueSetsStorage.valueSetsContainer = valueSetsContainerEn + } + } + + @Test + fun `fallback to en`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { + triggerUpdateValueSet(languageCode = Locale.FRENCH) + latestVaccinationValueSets.first() shouldBe vaccinationValueSetsEn + latestTestCertificateValueSets.first() shouldBe testCertificateValueSetsEn + } + + coVerify(ordering = Ordering.ORDERED) { + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.FRENCH) + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + valueSetsStorage.valueSetsContainer = valueSetsContainerEn + } + } + + @Test + fun `server returns nothing`() = runBlockingTest2(ignoreActive = true) { + coEvery { certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns null + coEvery { certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns null + + createInstance(this).run { + triggerUpdateValueSet(languageCode = Locale.GERMAN) + emptyValueSetsContainer.also { + latestVaccinationValueSets.first() shouldBe it.vaccinationValueSets + latestTestCertificateValueSets.first() shouldBe it.testCertificateValueSets + } + } + + coVerify(ordering = Ordering.ORDERED) { + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.GERMAN) + certificateValueSetServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) + } + } + + @Test + fun `clear data of server and local storage`() = runBlockingTest2(ignoreActive = true) { + createInstance(this).run { + clear() + + emptyValueSetsContainer.also { + latestVaccinationValueSets.first() shouldBe it.vaccinationValueSets + latestTestCertificateValueSets.first() shouldBe it.testCertificateValueSets + } + + coVerify { + certificateValueSetServer.clear() + valueSetsStorage.valueSetsContainer = emptyValueSetsContainer + } + } + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServerTest.kt similarity index 90% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServerTest.kt index 1c78f8412ea1d883b5e9881b0e4b79d626959539..bd18b0305bdffb453a2d1a7d25a613f236e17116 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/server/valueset/VaccinationServerTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/valueset/server/CertificateValueSetServerTest.kt @@ -1,9 +1,9 @@ -package de.rki.coronawarnapp.vaccination.core.server.valueset +package de.rki.coronawarnapp.covidcertificate.valueset.server import dagger.Lazy +import de.rki.coronawarnapp.covidcertificate.valueset.internal.ValueSetInvalidSignatureException import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.security.SignatureValidation -import de.rki.coronawarnapp.vaccination.core.server.valueset.internal.ValueSetInvalidSignatureException import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test import testhelpers.BaseTest import java.io.File -class VaccinationServerTest : BaseTest() { +class CertificateValueSetServerTest : BaseTest() { /** * contains both binary and signature @@ -32,7 +32,7 @@ class VaccinationServerTest : BaseTest() { private val invalidExportZip = File("src/test/resources/vaccination/valueset_invalid.zip") @MockK lateinit var cache: Cache - @MockK lateinit var apiV1: Lazy<VaccinationValueSetApiV1> + @MockK lateinit var apiV1: Lazy<CertificateValueSetApiV1> @MockK lateinit var dispatcherProvider: DispatcherProvider @MockK lateinit var signatureValidation: SignatureValidation @@ -43,7 +43,7 @@ class VaccinationServerTest : BaseTest() { invalidExportZip.exists() shouldBe true } - private fun createInstance() = VaccinationServer( + private fun createInstance() = CertificateValueSetServer( cache, apiV1, dispatcherProvider, diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt index c2cd808c38cfc65f4d2b39d1834cfd60ae45aac0..5557a2ee486d001221de84d956d13d31a09f4d29 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/testresult/AnalyticsRATestResultDonorTest.kt @@ -65,13 +65,13 @@ class AnalyticsRATestResultDonorTest : BaseTest() { @Test fun `No donation when test result is INVALID`() = runBlockingTest { - every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_INVALID) + every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.RAT_INVALID) testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution } @Test fun `No donation when test result is REDEEMED`() = runBlockingTest { - every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_REDEEMED) + every { testResultSettings.testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.RAT_REDEEMED) testResultDonor.beginDonation(TestRequest) shouldBe AnalyticsTestResultDonor.TestResultMetadataNoContribution } @@ -98,7 +98,7 @@ class AnalyticsRATestResultDonorTest : BaseTest() { testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution with(donation.testResultMetadata) { riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW - testResult shouldBe PpaData.PPATestResult.TEST_RESULT_PENDING + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_RAT_PENDING hoursSinceTestRegistration shouldBe 24 hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 @@ -110,14 +110,14 @@ class AnalyticsRATestResultDonorTest : BaseTest() { fun `Donation is collected when test result is POSITIVE`() { runBlockingTest { every { testResultSettings.testResultAtRegistration } returns - mockFlowPreference(CoronaTestResult.PCR_POSITIVE) + mockFlowPreference(CoronaTestResult.RAT_POSITIVE) every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution with(donation.testResultMetadata) { riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW - testResult shouldBe PpaData.PPATestResult.TEST_RESULT_POSITIVE + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_RAT_POSITIVE hoursSinceTestRegistration shouldBe 0 hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 @@ -129,14 +129,14 @@ class AnalyticsRATestResultDonorTest : BaseTest() { fun `Donation is collected when test result is NEGATIVE`() { runBlockingTest { every { testResultSettings.testResultAtRegistration } returns - mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) + mockFlowPreference(CoronaTestResult.RAT_NEGATIVE) every { testResultSettings.finalTestResultReceivedAt } returns mockFlowPreference(baseTime) val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution with(donation.testResultMetadata) { riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW - testResult shouldBe PpaData.PPATestResult.TEST_RESULT_NEGATIVE + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_RAT_NEGATIVE hoursSinceTestRegistration shouldBe 0 hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 daysSinceMostRecentDateAtRiskLevelAtTestRegistration shouldBe 1 @@ -147,7 +147,7 @@ class AnalyticsRATestResultDonorTest : BaseTest() { @Test fun `Scenario 1 LowRisk`() = runBlockingTest { with(testResultSettings) { - every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_NEGATIVE) + every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.RAT_NEGATIVE) every { finalTestResultReceivedAt } returns mockFlowPreference( Instant.parse("2021-03-20T20:00:00Z") ) @@ -161,7 +161,7 @@ class AnalyticsRATestResultDonorTest : BaseTest() { val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution with(donation.testResultMetadata) { - testResult shouldBe PpaData.PPATestResult.TEST_RESULT_NEGATIVE + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_RAT_NEGATIVE hoursSinceTestRegistration shouldBe 20 riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_LOW hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 @@ -172,7 +172,7 @@ class AnalyticsRATestResultDonorTest : BaseTest() { @Test fun `Scenario 2 HighRisk`() = runBlockingTest { with(testResultSettings) { - every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.PCR_POSITIVE) + every { testResultAtRegistration } returns mockFlowPreference(CoronaTestResult.RAT_POSITIVE) every { finalTestResultReceivedAt } returns mockFlowPreference( Instant.parse("2021-03-20T20:00:00Z") ) @@ -187,7 +187,7 @@ class AnalyticsRATestResultDonorTest : BaseTest() { val donation = testResultDonor.beginDonation(TestRequest) as AnalyticsTestResultDonor.TestResultMetadataContribution with(donation.testResultMetadata) { - testResult shouldBe PpaData.PPATestResult.TEST_RESULT_POSITIVE + testResult shouldBe PpaData.PPATestResult.TEST_RESULT_RAT_POSITIVE hoursSinceTestRegistration shouldBe 20 // hours riskLevelAtTestRegistration shouldBe PpaData.PPARiskLevel.RISK_LEVEL_HIGH hoursSinceHighRiskWarningAtTestRegistration shouldBe 1 diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt index 12b178e9db9fd377c5eadcb406fb522a264d50e9..b1a4b64e49f5795c3d56b1ebabf26af5edcdb7c5 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/environment/EnvironmentSetupTest.kt @@ -68,6 +68,7 @@ class EnvironmentSetupTest : BaseTest() { dataDonationCdnUrl shouldBe "https://datadonation-${env.rawKey}" logUploadServerUrl shouldBe "https://logupload-${env.rawKey}" crowdNotifierPublicKey shouldBe "123_abc-${env.rawKey}" + dccServerUrl shouldBe "https://dcc-${env.rawKey}" } } } @@ -127,7 +128,8 @@ class EnvironmentSetupTest : BaseTest() { EnvironmentSetup.EnvKey.LOG_UPLOAD.rawKey shouldBe "LOG_UPLOAD_SERVER_URL" EnvironmentSetup.EnvKey.SAFETYNET_API_KEY.rawKey shouldBe "SAFETYNET_API_KEY" EnvironmentSetup.EnvKey.CROWD_NOTIFIER_PUBLIC_KEY.rawKey shouldBe "CROWD_NOTIFIER_PUBLIC_KEY" - EnvironmentSetup.EnvKey.values().size shouldBe 9 + EnvironmentSetup.EnvKey.DCC.rawKey shouldBe "DCC_SERVER_URL" + EnvironmentSetup.EnvKey.values().size shouldBe 10 } companion object { @@ -152,7 +154,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-PROD", "SAFETYNET_API_KEY": "placeholder-PROD", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-PROD", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-PROD" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-PROD", + "DCC_SERVER_URL": "https://dcc-PROD" }, "DEV": { "USE_EUR_KEY_PKGS" : false, @@ -164,7 +167,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-DEV", "SAFETYNET_API_KEY": "placeholder-DEV", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-DEV", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-DEV" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-DEV", + "DCC_SERVER_URL": "https://dcc-DEV" }, "INT": { "USE_EUR_KEY_PKGS" : false, @@ -176,7 +180,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-INT", "SAFETYNET_API_KEY": "placeholder-INT", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-INT", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-INT" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-INT", + "DCC_SERVER_URL": "https://dcc-INT" }, "WRU": { "USE_EUR_KEY_PKGS" : false, @@ -189,7 +194,8 @@ class EnvironmentSetupTest : BaseTest() { "SAFETYNET_API_KEY": "placeholder-WRU", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU", "CREATE_TRACELOCATION_URL": "https://tracelocation-WRU", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU", + "DCC_SERVER_URL": "https://dcc-WRU" }, "WRU-XD": { "USE_EUR_KEY_PKGS" : true, @@ -201,7 +207,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-WRU-XD", "SAFETYNET_API_KEY": "placeholder-WRU-XD", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XD", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XD" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XD", + "DCC_SERVER_URL": "https://dcc-WRU-XD" }, "WRU-XA": { "USE_EUR_KEY_PKGS" : true, @@ -213,7 +220,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-WRU-XA", "SAFETYNET_API_KEY": "placeholder-WRU-XA", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-WRU-XA", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XA" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-WRU-XA", + "DCC_SERVER_URL": "https://dcc-WRU-XA" }, "TESTER-MOCK": { "USE_EUR_KEY_PKGS" : true, @@ -225,7 +233,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-TESTER-MOCK", "SAFETYNET_API_KEY": "placeholder-TESTER-MOCK", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-TESTER-MOCK", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-TESTER-MOCK" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-TESTER-MOCK", + "DCC_SERVER_URL": "https://dcc-TESTER-MOCK" }, "LOCAL": { "USE_EUR_KEY_PKGS" : true, @@ -237,7 +246,8 @@ class EnvironmentSetupTest : BaseTest() { "VACCINATION_CDN_URL": "https://vaccination-LOCAL", "SAFETYNET_API_KEY": "placeholder-LOCAL", "PUB_KEYS_SIGNATURE_VERIFICATION": "12345678-LOCAL", - "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-LOCAL" + "CROWD_NOTIFIER_PUBLIC_KEY": "123_abc-LOCAL", + "DCC_SERVER_URL": "https://dcc-LOCAL" } } """ diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt index 0a3643766fb3685c31d66345b3c777a2d1b90ee9..7207e5d906348755d0391d887d1ba26c67c8103e 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/MainActivityViewModelTest.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.main import de.rki.coronawarnapp.contactdiary.ui.ContactDiarySettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings import de.rki.coronawarnapp.environment.EnvironmentSetup import de.rki.coronawarnapp.playbook.BackgroundNoise import de.rki.coronawarnapp.presencetracing.TraceLocationSettings @@ -9,7 +10,6 @@ import de.rki.coronawarnapp.storage.OnboardingSettings import de.rki.coronawarnapp.ui.main.MainActivityViewModel import de.rki.coronawarnapp.util.CWADebug import de.rki.coronawarnapp.util.device.BackgroundModeStatus -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.every diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt index 7d9d96f3fa5151fff19408411c86ef342dc572ee..4ebef57b07e20691b991a50b97adb85bbede6c76 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/main/home/HomeFragmentViewModelTest.kt @@ -3,6 +3,8 @@ package de.rki.coronawarnapp.main.home import android.content.Context import de.rki.coronawarnapp.appconfig.AppConfigProvider import de.rki.coronawarnapp.coronatest.CoronaTestRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationSettings +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.environment.BuildConfigWrap import de.rki.coronawarnapp.main.CWASettings import de.rki.coronawarnapp.statistics.source.StatisticsProvider @@ -21,8 +23,6 @@ import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.bluetooth.BluetoothSupport import de.rki.coronawarnapp.util.encryptionmigration.EncryptionErrorResetTool import de.rki.coronawarnapp.util.shortcuts.AppShortcutsHelper -import de.rki.coronawarnapp.vaccination.core.VaccinationSettings -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import io.kotest.matchers.shouldBe import io.mockk.MockKAnnotations import io.mockk.coEvery diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModelTest.kt similarity index 99% rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModelTest.kt rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModelTest.kt index bd9be00a552e0f6721b82097711e3917eb2e901f..009f839c5c79d3bfed61a95225a0f6cea8335c89 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/greencertificate/RequestCovidCertificateViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/submission/covidcertificate/RequestCovidCertificateViewModelTest.kt @@ -1,4 +1,4 @@ -package de.rki.coronawarnapp.ui.submission.greencertificate +package de.rki.coronawarnapp.ui.submission.covidcertificate import androidx.lifecycle.MutableLiveData import de.rki.coronawarnapp.coronatest.CoronaTestRepository @@ -20,8 +20,8 @@ import kotlinx.coroutines.flow.flowOf import org.joda.time.Instant import org.joda.time.LocalDate import org.joda.time.format.DateTimeFormat -import org.junit.jupiter.api.Test import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import testhelpers.BaseTest import testhelpers.extensions.InstantExecutorExtension diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt index 6f8799d44b824ac8e8c98399a916ed11934e1e52..50d196f8cf8b6fc920755e1344c9f5df78b92943 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/DataResetTest.kt @@ -6,6 +6,10 @@ import de.rki.coronawarnapp.contactdiary.storage.ContactDiaryPreferences import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.antigen.profile.RATProfileSettings +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationPreferences +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository +import de.rki.coronawarnapp.covidcertificate.valueset.ValueSetsRepository import de.rki.coronawarnapp.datadonation.analytics.Analytics import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings import de.rki.coronawarnapp.datadonation.survey.SurveySettings @@ -24,9 +28,6 @@ import de.rki.coronawarnapp.storage.TracingSettings import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.submission.SubmissionSettings import de.rki.coronawarnapp.ui.presencetracing.TraceLocationPreferences -import de.rki.coronawarnapp.vaccination.core.VaccinationPreferences -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository -import de.rki.coronawarnapp.vaccination.core.repository.ValueSetsRepository import io.mockk.MockKAnnotations import io.mockk.coVerify import io.mockk.impl.annotations.MockK @@ -64,6 +65,7 @@ internal class DataResetTest : BaseTest() { @MockK lateinit var vaccinationRepository: VaccinationRepository @MockK lateinit var vaccinationPreferences: VaccinationPreferences @MockK lateinit var valueSetsRepository: ValueSetsRepository + @MockK lateinit var testCertificateRepository: TestCertificateRepository @BeforeEach fun setUp() { @@ -97,7 +99,8 @@ internal class DataResetTest : BaseTest() { ratProfileSettings = ratProfileSettings, vaccinationPreferences = vaccinationPreferences, vaccinationRepository = vaccinationRepository, - valueSetsRepository = valueSetsRepository + valueSetsRepository = valueSetsRepository, + testCertificateRepository = testCertificateRepository, ) @Test diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt index d5ec4edc888f4f9282dad53a14fea4e194f454fe..c04bcf04992aabd614ba0a0420a86f734e00e92a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/worker/WorkerBinderTest.kt @@ -10,6 +10,8 @@ import de.rki.coronawarnapp.coronatest.CoronaTestRepository import de.rki.coronawarnapp.coronatest.type.pcr.execution.PCRResultScheduler import de.rki.coronawarnapp.coronatest.type.pcr.notification.PCRTestResultAvailableNotificationService import de.rki.coronawarnapp.coronatest.type.rapidantigen.execution.RAResultScheduler +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository +import de.rki.coronawarnapp.covidcertificate.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.datadonation.analytics.Analytics import de.rki.coronawarnapp.datadonation.analytics.worker.DataDonationAnalyticsScheduler import de.rki.coronawarnapp.deadman.DeadmanNotificationScheduler @@ -28,7 +30,6 @@ import de.rki.coronawarnapp.submission.SubmissionRepository import de.rki.coronawarnapp.task.TaskController import de.rki.coronawarnapp.util.di.AppContext import de.rki.coronawarnapp.util.serialization.BaseGson -import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import io.github.classgraph.ClassGraph import io.kotest.matchers.collections.shouldContainAll import io.mockk.mockk @@ -172,4 +173,7 @@ class MockProvider { @Provides fun vaccinationRepository(): VaccinationRepository = mockk() + + @Provides + fun testCertificateRepository(): TestCertificateRepository = mockk() } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt deleted file mode 100644 index 67e7a01f66f51fee0b09e1f2ea82cdec55a1a1af..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/ValueSetTestData.kt +++ /dev/null @@ -1,87 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core - -import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.DefaultVaccinationValueSet -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationValueSet -import io.kotest.matchers.shouldBe -import java.util.Locale - -object ValueSetTestData { - - val vpItemDe = "1119305005" to "Impfstoff-Name" - val mpItemDe = "EU/1/21/1529" to "Arzneimittel-Name" - val maItemDe = "ORG-100001699" to "Hersteller-Name" - - val vpItemEn = vpItemDe.copy(second = "Vaccine-Name") - val mpItemEn = mpItemDe.copy(second = "MedicalProduct-Name") - val maItemEn = maItemDe.copy(second = "Manufactorer-Name") - - val storedValueSetDe = ValueSetsStorage.StoredVaccinationValueSet( - languageCode = Locale.GERMAN, - vp = createStoredValueSet(vpItemDe), - mp = createStoredValueSet(mpItemDe), - ma = createStoredValueSet(maItemDe) - ) - - val storedValueSetEn = ValueSetsStorage.StoredVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = createStoredValueSet(vpItemEn), - mp = createStoredValueSet(mpItemEn), - ma = createStoredValueSet(maItemEn) - ) - - val emptyStoredValueSet = ValueSetsStorage.StoredVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()), - mp = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()), - ma = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = emptyList()) - ) - - val valueSetDe = DefaultVaccinationValueSet( - languageCode = Locale.GERMAN, - vp = createValueSet(vpItemDe), - mp = createValueSet(mpItemDe), - ma = createValueSet(maItemDe) - ) - - val valueSetEn = DefaultVaccinationValueSet( - languageCode = Locale.ENGLISH, - vp = createValueSet(vpItemEn), - mp = createValueSet(mpItemEn), - ma = createValueSet(maItemEn) - ) - - val emptyValueSetEn = emptyStoredValueSet - - fun createStoredValueSet(keyText: Pair<String, String>): ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet { - val item = ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet.StoredItem( - key = keyText.first, - displayText = keyText.second - ) - return ValueSetsStorage.StoredVaccinationValueSet.StoredValueSet(items = listOf(item)) - } - - fun createValueSet(keyText: Pair<String, String>): DefaultVaccinationValueSet.DefaultValueSet { - val item = DefaultVaccinationValueSet.DefaultValueSet.DefaultItem( - key = keyText.first, - displayText = keyText.second - ) - return DefaultVaccinationValueSet.DefaultValueSet(items = listOf(item)) - } -} - -fun VaccinationValueSet.validateValues(v2: VaccinationValueSet) { - languageCode shouldBe v2.languageCode - vp.validateValues(v2.vp) - mp.validateValues(v2.mp) - ma.validateValues(v2.ma) -} - -fun VaccinationValueSet.ValueSet.validateValues(v2: VaccinationValueSet.ValueSet) { - items.forEachIndexed { index, item1 -> - val item2 = v2.items[index] - - item1.key shouldBe item2.key - item1.displayText shouldBe item2.displayText - } -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt deleted file mode 100644 index 3ac9576d02d0740117dafee40677906c2aed214d..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/ValueSetsRepositoryTest.kt +++ /dev/null @@ -1,113 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository - -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyValueSetEn -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetDe -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetEn -import de.rki.coronawarnapp.vaccination.core.repository.storage.ValueSetsStorage -import de.rki.coronawarnapp.vaccination.core.server.valueset.VaccinationServer -import de.rki.coronawarnapp.vaccination.core.validateValues -import io.mockk.MockKAnnotations -import io.mockk.Ordering -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.just -import io.mockk.runs -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.first -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import testhelpers.BaseTest -import testhelpers.TestDispatcherProvider -import testhelpers.coroutines.runBlockingTest2 -import java.util.Locale - -class ValueSetsRepositoryTest : BaseTest() { - - @MockK lateinit var vaccinationServer: VaccinationServer - @MockK lateinit var valueSetsStorage: ValueSetsStorage - - private val testDispatcherProvider = TestDispatcherProvider() - - private fun createInstance(scope: CoroutineScope) = ValueSetsRepository( - vaccinationServer = vaccinationServer, - valueSetsStorage = valueSetsStorage, - scope = scope, - dispatcherProvider = testDispatcherProvider - ) - - @BeforeEach - fun setUp() { - MockKAnnotations.init(this) - coEvery { vaccinationServer.getVaccinationValueSets(any()) } returns null - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns valueSetEn - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns valueSetDe - every { vaccinationServer.clear() } just runs - - every { valueSetsStorage.vaccinationValueSet = any() } just runs - every { valueSetsStorage.vaccinationValueSet } returns emptyValueSetEn - } - - @Test - fun `successful update for de`() = runBlockingTest2(ignoreActive = true) { - createInstance(this).run { - triggerUpdateValueSet(languageCode = Locale.GERMAN) - latestValueSet.first() - }.also { it.validateValues(valueSetDe) } - - coVerify { - vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) - valueSetsStorage.vaccinationValueSet = valueSetDe - } - - coVerify(exactly = 0) { - vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) - valueSetsStorage.vaccinationValueSet = valueSetEn - } - } - - @Test - fun `fallback to en`() = runBlockingTest2(ignoreActive = true) { - createInstance(this).run { - triggerUpdateValueSet(languageCode = Locale.FRENCH) - latestValueSet.first() - }.also { it.validateValues(valueSetEn) } - - coVerify(ordering = Ordering.ORDERED) { - vaccinationServer.getVaccinationValueSets(languageCode = Locale.FRENCH) - vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) - valueSetsStorage.vaccinationValueSet = valueSetEn - } - } - - @Test - fun `server returns nothing`() = runBlockingTest2(ignoreActive = true) { - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) } returns null - coEvery { vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) } returns null - - createInstance(this).run { - triggerUpdateValueSet(languageCode = Locale.GERMAN) - latestValueSet.first() - }.also { it.validateValues(emptyValueSetEn) } - - coVerify(ordering = Ordering.ORDERED) { - vaccinationServer.getVaccinationValueSets(languageCode = Locale.GERMAN) - vaccinationServer.getVaccinationValueSets(languageCode = Locale.ENGLISH) - } - } - - @Test - fun `clear data of server and local storage`() = runBlockingTest2(ignoreActive = true) { - createInstance(this).run { - clear() - - latestValueSet.first().validateValues(emptyValueSetEn) - - coVerify { - vaccinationServer.clear() - valueSetsStorage.vaccinationValueSet = emptyValueSetEn - } - } - } -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt deleted file mode 100644 index 8b68c38cc0cd1b7424eec1a14e22858d4974e26d..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/vaccination/core/repository/storage/ValueSetsStorageTest.kt +++ /dev/null @@ -1,142 +0,0 @@ -package de.rki.coronawarnapp.vaccination.core.repository.storage - -import android.content.Context -import de.rki.coronawarnapp.util.serialization.SerializationModule -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyStoredValueSet -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.emptyValueSetEn -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.storedValueSetDe -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.storedValueSetEn -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetDe -import de.rki.coronawarnapp.vaccination.core.ValueSetTestData.valueSetEn -import de.rki.coronawarnapp.vaccination.core.validateValues -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import testhelpers.BaseTest -import testhelpers.extensions.toComparableJsonPretty -import testhelpers.preferences.MockSharedPreferences - -class ValueSetsStorageTest : BaseTest() { - - @MockK lateinit var context: Context - lateinit var prefs: MockSharedPreferences - - private val gson = SerializationModule().baseGson() - - @BeforeEach - fun setup() { - MockKAnnotations.init(this) - prefs = MockSharedPreferences() - every { context.getSharedPreferences("valuesets_localdata", Context.MODE_PRIVATE) } returns prefs - } - - private fun createInstance() = ValueSetsStorage( - context = context, - gson = gson - ) - - @Test - fun `Default value is an empty value set`() { - createInstance().vaccinationValueSet.validateValues(emptyValueSetEn) - } - - @Test - fun `Updates values`() { - createInstance().run { - vaccinationValueSet = storedValueSetDe - vaccinationValueSet shouldBe storedValueSetDe - - vaccinationValueSet = storedValueSetEn - vaccinationValueSet shouldBe storedValueSetEn - } - } - - @Test - fun `Check mapping is correct`() { - createInstance().run { - storedValueSetDe.also { it.toStoredVaccinationValueSet() shouldBe it } - - storedValueSetEn.also { it.toStoredVaccinationValueSet() shouldBe it } - - valueSetDe.also { - it.toStoredVaccinationValueSet() shouldBe storedValueSetDe - it.toStoredVaccinationValueSet().validateValues(it) - } - - valueSetEn.also { - it.toStoredVaccinationValueSet() shouldBe storedValueSetEn - it.toStoredVaccinationValueSet().validateValues(it) - } - - emptyValueSetEn.also { - it.toStoredVaccinationValueSet() shouldBe emptyStoredValueSet - it.toStoredVaccinationValueSet().validateValues(it) - } - } - } - - @Test - fun `storage inits empty without sideeffects`() { - createInstance().vaccinationValueSet shouldNotBe null - prefs.dataMapPeek.isEmpty() shouldBe true - } - - @Test - fun `storage format`() { - createInstance().vaccinationValueSet = storedValueSetDe - (prefs.dataMapPeek["valueset"] as String).toComparableJsonPretty() shouldBe """ - { - "languageCode": "de", - "vp": { - "items": [ - { - "key": "1119305005", - "displayText": "Impfstoff-Name" - } - ] - }, - "mp": { - "items": [ - { - "key": "EU/1/21/1529", - "displayText": "Arzneimittel-Name" - } - ] - }, - "ma": { - "items": [ - { - "key": "ORG-100001699", - "displayText": "Hersteller-Name" - } - ] - } - } - """.toComparableJsonPretty() - - createInstance().apply { - vaccinationValueSet shouldBe storedValueSetDe - vaccinationValueSet = emptyStoredValueSet - } - (prefs.dataMapPeek["valueset"] as String).toComparableJsonPretty() shouldBe """ - { - "languageCode": "en", - "vp": { - "items": [] - }, - "mp": { - "items": [] - }, - "ma": { - "items": [] - } - } - """.toComparableJsonPretty() - - createInstance().vaccinationValueSet shouldBe emptyStoredValueSet - } -} diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt index c1b802516a2f5aa590dd3cf5ba6fcbd6d23e5906..d4089a51bba7db5dcd3b5f2afe18f5dc82a6702f 100644 --- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt +++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/debugoptions/ui/DebugOptionsFragmentViewModelTest.kt @@ -33,13 +33,10 @@ class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() { every { environmentSetup.logUploadServerUrl } returns "logUploadServerUrl" every { environmentSetup.crowdNotifierPublicKey } returns "crowdNotifierPublicKey" every { environmentSetup.appConfigPublicKey } returns "appConfigPublicKey" + every { environmentSetup.dccServerUrl } returns "dccServerUrl" - every { environmentSetup.currentEnvironment = any() } answers { - currentEnvironment = arg(0) - } - every { environmentSetup.currentEnvironment } answers { - currentEnvironment - } + every { environmentSetup.currentEnvironment = any() } answers { currentEnvironment = arg(0) } + every { environmentSetup.currentEnvironment } answers { currentEnvironment } } private fun createViewModel(): DebugOptionsFragmentViewModel = DebugOptionsFragmentViewModel( @@ -48,7 +45,7 @@ class DebugOptionsFragmentViewModelTest : BaseTestInstrumentation() { ) @Test - fun `toggeling the env works`() { + fun `toggling the env works`() { currentEnvironment = EnvironmentSetup.Type.DEV val vm = createViewModel() vm.environmentState.getOrAwaitValue().current shouldBe EnvironmentSetup.Type.DEV diff --git a/build.gradle b/build.gradle index 9e108b1eec46c28d5926b0b95ea0e377c676be41..0d2649f9d90739ee876b3f77651c95d26fa55228 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { ext.kotlin_version = '1.5.0' ext.protobufVersion = '0.8.12' - ext.navVersion = "2.2.2" + ext.nav_version = "2.3.5" repositories { google() @@ -16,7 +16,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.google.protobuf:protobuf-gradle-plugin:$protobufVersion" - classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion" + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" classpath "org.jlleitschuh.gradle:ktlint-gradle:10.0.0" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.17.0" diff --git a/gradle.properties b/gradle.properties index d21f5a095e379f7f1140f76bb831b4b51da3ecd6..d98e11727136326d6d0f7342711860e6961f8f7a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,4 +20,4 @@ org.gradle.dependency.verification.console=verbose VERSION_MAJOR=2 VERSION_MINOR=4 VERSION_PATCH=0 -VERSION_BUILD=5 +VERSION_BUILD=7 diff --git a/translation_v2.json b/translation_v2.json index 286c27bb7e472ca064376dfb75f361d424b53a59..a5c864007ef79c13cfbbc071c2fb16fd65c8b0c9 100644 --- a/translation_v2.json +++ b/translation_v2.json @@ -12,7 +12,8 @@ "event_registration_strings.xml", "antigen_strings.xml", "vaccination_strings.xml", - "green_certificate_strings.xml" + "green_certificate_strings.xml", + "green_certificate_attribute_strings.xml" ], "targetFolderPath": "../values-[langCode]" }