diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragmentTest.kt index 0f6ea690c60..ff9e981b648 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragmentTest.kt @@ -9,6 +9,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState import de.rki.coronawarnapp.covidcertificate.ScreenshotCertificateTestData import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate @@ -24,6 +25,7 @@ import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.BoosterCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateItem import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateReissuanceCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CwaUserCard +import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.MaskRequirementsCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.RecoveryCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.TestCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.VaccinationCertificateCard @@ -89,7 +91,7 @@ class PersonDetailsFragmentTest : BaseUITest() { @Test @Screenshot fun capture_fragment_cwa_user() { - every { viewModel.uiState } returns certificateData(true) + every { viewModel.uiState } returns certificateData(true, withoutMask = null) launchFragmentInContainer2(fragmentArgs = args) takeScreenshot("cwa") @@ -101,7 +103,7 @@ class PersonDetailsFragmentTest : BaseUITest() { @Test @Screenshot fun capture_fragment_not_cwa_user() { - every { viewModel.uiState } returns certificateData() + every { viewModel.uiState } returns certificateData(withoutMask = null) launchFragmentInContainer2(fragmentArgs = args) takeScreenshot("not_cwa") } @@ -109,11 +111,28 @@ class PersonDetailsFragmentTest : BaseUITest() { @Test @Screenshot fun capture_fragment_admission_berlin() { - every { viewModel.uiState } returns certificateData(admissionSubtitle = "Berlin") + every { viewModel.uiState } returns certificateData(admissionSubtitle = "Berlin", withoutMask = null) launchFragmentInContainer2(fragmentArgs = args) takeScreenshot("admission_berlin") } + @Test + @Screenshot + fun capture_fragment_without_mask() { + every { viewModel.currentColorShade } returns MutableLiveData(PersonColorShade.GREEN) + every { viewModel.uiState } returns certificateData(withoutMask = true) + launchFragmentInContainer2(fragmentArgs = args) + takeScreenshot("no_mask_required") + } + + @Test + @Screenshot + fun capture_fragment_with_mask() { + every { viewModel.uiState } returns certificateData(withoutMask = false) + launchFragmentInContainer2(fragmentArgs = args) + takeScreenshot("no_mask_required") + } + @Test @Screenshot fun capture_fragment_booster() { @@ -127,9 +146,11 @@ class PersonDetailsFragmentTest : BaseUITest() { private fun certificateData( isCwa: Boolean = false, - admissionSubtitle: String = "2G+ PCR-Test" + admissionSubtitle: String = "2G+ PCR-Test", + withoutMask: Boolean? ): LiveData { var name: String + var colorShade = PersonColorShade.COLOR_1 val certificateItems = mutableListOf().apply { val testCertificate = mockTestCertificate().also { name = it.fullName } val vaccinationCertificate1 = mockVaccinationCertificate(number = 1, final = false) @@ -141,10 +162,35 @@ class PersonDetailsFragmentTest : BaseUITest() { isCwaUser = isCwa ) + if (withoutMask != null) { + add( + if (withoutMask) { + colorShade = PersonColorShade.GREEN + MaskRequirementsCard.Item( + titleText = "Keine Maskenpflicht", + subtitleText = "Eine Maske ist dennoch empfohlen", + badgeState = MaskState.MaskStateIdentifier.OPTIONAL, + longText = "Von der Maskenpflicht sind alle Personen befreit, die innerhalb der letzten 3 Monate geimpft wurden oder genesen sind oder innerhalb der letzten 24 Stunden negativ getestet wurden.", + faqAnchor = "FAQ", + colorShade = PersonColorShade.GREEN + ) + } else { + MaskRequirementsCard.Item( + titleText = "Maskenpflicht", + subtitleText = "Sie sind nicht von der Maskenpflicht ausgenommen", + badgeState = MaskState.MaskStateIdentifier.REQUIRED, + longText = "Von der Maskenpflicht sind alle Personen befreit, die innerhalb der letzten 3 Monate geimpft wurden oder genesen sind oder innerhalb der letzten 24 Stunden negativ getestet wurden.", + faqAnchor = "FAQ", + colorShade = PersonColorShade.COLOR_1 + ) + } + ) + } + add( when (Locale.getDefault()) { Locale.GERMANY, Locale.GERMAN -> AdmissionStatusCard.Item( - colorShade = PersonColorShade.COLOR_1, + colorShade = colorShade, titleText = "Status-Nachweis", subtitleText = admissionSubtitle, badgeText = "2G+", @@ -153,7 +199,7 @@ class PersonDetailsFragmentTest : BaseUITest() { longTextWithBadge = "Ihr Status hat sich geändert. Ihre Zertifikate erfüllen jetzt die 2G-Regel. Wenn Sie Ihren aktuellen Status vorweisen müssen, schließen Sie diese Ansicht und zeigen Sie den QR-Code auf der Zertifikatsübersicht." ) else -> AdmissionStatusCard.Item( - colorShade = PersonColorShade.COLOR_1, + colorShade = colorShade, titleText = "Proof of Status", subtitleText = admissionSubtitle, badgeText = "2G+", diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragmentTest.kt index 16cd5c36573..66a6d66d696 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewFragmentTest.kt @@ -14,30 +14,14 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import dagger.Module import dagger.android.ContributesAndroidInjector import de.rki.coronawarnapp.R -import de.rki.coronawarnapp.covidcertificate.ScreenshotCertificateTestData -import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier -import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate -import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId -import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId import de.rki.coronawarnapp.covidcertificate.person.ui.admission.AdmissionScenariosSharedViewModel import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonOverviewViewModel.UiState import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.AdmissionTileProvider -import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard -import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard -import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard.Item.OverviewCertificate -import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificatesItem -import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate -import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateWrapper -import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate -import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUserTz -import de.rki.coronawarnapp.util.qrcode.coil.CoilQrCode import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.ui.updateCountBadge import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import org.joda.time.Instant import org.junit.After import org.junit.Before import org.junit.Test @@ -50,7 +34,6 @@ import testhelpers.recyclerScrollTo import testhelpers.selectBottomNavTab import testhelpers.setupFakeImageLoader import testhelpers.takeScreenshot -import java.util.Locale @RunWith(AndroidJUnit4::class) class PersonOverviewFragmentTest : BaseUITest() { @@ -195,6 +178,48 @@ class PersonOverviewFragmentTest : BaseUITest() { takeSelfieWithBottomNavBadge("one_person_with_badge", R.id.covid_certificates_graph, 1) } + @Test + @Screenshot + fun capture_fragment_mask_free_with_badge() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(maskFree())) + takeSelfieWithBottomNavBadge("mask_free_with_badge", R.id.covid_certificates_graph, 1) + } + + @Test + @Screenshot + fun capture_fragment_mask_free_multiline_with_badge() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(maskFreeMultiLine())) + takeSelfieWithBottomNavBadge("mask_free_multiline_with_badge", R.id.covid_certificates_graph, 1) + } + + @Test + @Screenshot + fun capture_fragment_mask_required_nostatus_with_badge() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(maskReqiredAndNoStatus())) + takeSelfieWithBottomNavBadge("mask_required_nostatus_with_badge", R.id.covid_certificates_graph, 1) + } + + @Test + @Screenshot + fun capture_fragment_no_mask_info_status_info() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(noMaskInfoStatusInfo())) + takeSelfieWithBottomNavBadge("no_mask_info_status_info", R.id.covid_certificates_graph, 1) + } + + @Test + @Screenshot + fun capture_fragment_no_mask_info_no_status_info() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(noMaskInfoNoStatusInfo())) + takeSelfieWithBottomNavBadge("no_mask_info_no_status_info", R.id.covid_certificates_graph, 1) + } + + @Test + @Screenshot + fun capture_fragment_mask_invalid() { + every { viewModel.uiState } returns MutableLiveData(UiState.Done(maskInvalidOutdated())) + takeSelfieWithBottomNavBadge("mask_invalid", R.id.covid_certificates_graph, 1) + } + @Test @Screenshot fun capture_fragment_many_persons() { @@ -231,323 +256,6 @@ class PersonOverviewFragmentTest : BaseUITest() { onView(withId(R.id.fake_bottom_navigation)).perform(selectBottomNavTab(R.id.covid_certificates_graph)) takeScreenshot(suffix) } - - private fun listItemWithPendingItem() = mutableListOf() - .apply { - add( - CovidTestCertificatePendingCard.Item( - certificate = mockTestCertificateWrapper(false), - onDeleteAction = {}, - onRetryAction = {}, - ) - ) - - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ) - ), - admissionBadgeText = "", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun listItemWithUpdatingItem() = mutableListOf() - .apply { - add( - CovidTestCertificatePendingCard.Item( - certificate = mockTestCertificateWrapper(true), - onDeleteAction = {}, - onRetryAction = {}, - ) - ) - - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ) - ), - admissionBadgeText = "", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun personsItems() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockTestCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" - else -> "Test Certificate" - } - ), - ), - admissionBadgeText = "3G", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 5, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockTestCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" - else -> "Test Certificate" - } - ) - ), - admissionBadgeText = "3G", - colorShade = PersonColorShade.COLOR_2, - badgeCount = 3, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ) - ), - admissionBadgeText = "2G", - colorShade = PersonColorShade.COLOR_3, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun onePersonItem() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ) - ), - admissionBadgeText = "2G", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun onePersonItemWithBadgeCount() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ) - ), - admissionBadgeText = "2G", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 1, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun twoGPlusCertificate() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ), - OverviewCertificate( - mockTestCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" - else -> "Test Certificate" - } - ) - ), - admissionBadgeText = "2G+", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun threeCertificates() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Geimpft" - else -> "2G Certificate" - } - ), - OverviewCertificate( - mockTestCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Getestet" - else -> "Test Certificate" - } - ), - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Genesen" - else -> "Recovery Certificate" - } - ) - ), - admissionBadgeText = "2G+", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 0, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun twoGPlusCertificateWithBadge() = mutableListOf() - .apply { - add( - PersonCertificateCard.Item( - overviewCertificates = listOf( - OverviewCertificate( - mockVaccinationCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" - else -> "2G Certificate" - } - ), - OverviewCertificate( - mockTestCertificate("Andrea Schneider"), - buttonText = when (Locale.getDefault()) { - Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" - else -> "Test Certificate" - } - ) - ), - admissionBadgeText = "2G+", - colorShade = PersonColorShade.COLOR_1, - badgeCount = 1, - onClickAction = { _, _ -> }, - onCovPassInfoAction = {}, - onCertificateSelected = {}, - ) - ) - } - - private fun mockTestCertificate( - name: String, - isPending: Boolean = false, - isUpdating: Boolean = false - ): TestCertificate = mockk().apply { - every { headerExpiresAt } returns Instant.now().plus(20) - every { isCertificateRetrievalPending } returns isPending - every { isUpdatingData } returns isUpdating - every { fullName } returns name - every { registeredAt } returns Instant.parse("2021-05-21T11:35:00.000Z") - every { personIdentifier } returns CertificatePersonIdentifier( - firstNameStandardized = "firstNameStandardized", - lastNameStandardized = "lastNameStandardized", - dateOfBirthFormatted = "1943-04-18" - ) - every { qrCodeToDisplay } returns CoilQrCode(ScreenshotCertificateTestData.testCertificate) - every { isDisplayValid } returns true - every { sampleCollectedAt } returns Instant.parse("2021-05-21T11:35:00.000Z") - every { state } returns CwaCovidCertificate.State.Valid(headerExpiresAt) - every { isNew } returns false - } - - private fun mockTestCertificateWrapper(isUpdating: Boolean) = mockk().apply { - every { isCertificateRetrievalPending } returns true - every { isUpdatingData } returns isUpdating - every { registeredAt } returns Instant.EPOCH - every { containerId } returns TestCertificateContainerId("testCertificateContainerId") - } - - private fun mockVaccinationCertificate(name: String): VaccinationCertificate = - mockk().apply { - every { headerExpiresAt } returns Instant.now().plus(20) - every { containerId } returns VaccinationCertificateContainerId("2") - val localDate = Instant.parse("2021-06-01T11:35:00.000Z").toLocalDateUserTz() - every { fullName } returns name - every { fullNameFormatted } returns name - every { doseNumber } returns 2 - every { totalSeriesOfDoses } returns 2 - every { vaccinatedOn } returns localDate.minusDays(15) - every { personIdentifier } returns CertificatePersonIdentifier( - firstNameStandardized = "firstNameStandardized", - lastNameStandardized = "lastNameStandardized", - dateOfBirthFormatted = "1943-04-18" - ) - every { isDisplayValid } returns true - every { state } returns CwaCovidCertificate.State.Valid(headerExpiresAt) - every { qrCodeToDisplay } returns CoilQrCode(ScreenshotCertificateTestData.vaccinationCertificate) - } } @Module diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/TestData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/TestData.kt new file mode 100644 index 00000000000..40b795469f5 --- /dev/null +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/TestData.kt @@ -0,0 +1,472 @@ +package de.rki.coronawarnapp.covidcertificate.person.ui.overview + +import de.rki.coronawarnapp.covidcertificate.ScreenshotCertificateTestData +import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier +import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate +import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId +import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificatesItem +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate +import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateWrapper +import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate +import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUserTz +import de.rki.coronawarnapp.util.qrcode.coil.CoilQrCode +import io.mockk.every +import io.mockk.mockk +import org.joda.time.Instant +import java.util.Locale + +fun listItemWithPendingItem() = mutableListOf() + .apply { + add( + CovidTestCertificatePendingCard.Item( + certificate = mockTestCertificateWrapper(false), + onDeleteAction = {}, + onRetryAction = {}, + ) + ) + + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ) + ), + admissionBadgeText = "", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun listItemWithUpdatingItem() = mutableListOf() + .apply { + add( + CovidTestCertificatePendingCard.Item( + certificate = mockTestCertificateWrapper(true), + onDeleteAction = {}, + onRetryAction = {}, + ) + ) + + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ) + ), + admissionBadgeText = "", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun maskFree() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider") + ) + ), + admissionBadgeText = "2G", + hasMaskState = true, + maskBadgeText = "Keine Maskenpflicht", + colorShade = PersonColorShade.GREEN, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun maskFreeMultiLine() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider") + ) + ), + admissionBadgeText = "2G", + hasMaskState = true, + maskBadgeText = "Keine Maskenpflicht, Multi Line, aber mindestens zwei", + colorShade = PersonColorShade.GREEN, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun maskReqiredAndNoStatus() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider") + ) + ), + admissionBadgeText = "2G", + hasMaskState = true, + maskBadgeText = "Maskenpflicht", + colorShade = PersonColorShade.COLOR_3, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun maskInvalidOutdated() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockInvalidVaccinationCertificate("Andrea Schneider") + ) + ), + hasMaskState = true, + maskBadgeText = "Maskenpflicht", + colorShade = PersonColorShade.COLOR_INVALID, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun noMaskInfoStatusInfo() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider") + ) + ), + admissionBadgeText = "2G", + hasMaskState = false, + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun noMaskInfoNoStatusInfo() = mutableListOf().apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider") + ) + ), + hasMaskState = false, + colorShade = PersonColorShade.COLOR_3, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) +} + +fun personsItems() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockTestCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" + else -> "Test Certificate" + } + ), + ), + admissionBadgeText = "3G", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 5, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockTestCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" + else -> "Test Certificate" + } + ) + ), + admissionBadgeText = "3G", + colorShade = PersonColorShade.COLOR_2, + badgeCount = 3, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ) + ), + admissionBadgeText = "2G", + colorShade = PersonColorShade.COLOR_3, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun onePersonItem() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ) + ), + admissionBadgeText = "2G", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun onePersonItemWithBadgeCount() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ) + ), + admissionBadgeText = "2G", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 1, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun twoGPlusCertificate() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ), + PersonCertificateCard.Item.OverviewCertificate( + mockTestCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" + else -> "Test Certificate" + } + ) + ), + admissionBadgeText = "2G+", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun threeCertificates() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Geimpft" + else -> "2G Certificate" + } + ), + PersonCertificateCard.Item.OverviewCertificate( + mockTestCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Getestet" + else -> "Test Certificate" + } + ), + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Genesen" + else -> "Recovery Certificate" + } + ) + ), + admissionBadgeText = "2G+", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 0, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +fun twoGPlusCertificateWithBadge() = mutableListOf() + .apply { + add( + PersonCertificateCard.Item( + overviewCertificates = listOf( + PersonCertificateCard.Item.OverviewCertificate( + mockVaccinationCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "2G-Zertifikat" + else -> "2G Certificate" + } + ), + PersonCertificateCard.Item.OverviewCertificate( + mockTestCertificate("Andrea Schneider"), + buttonText = when (Locale.getDefault()) { + Locale.GERMANY, Locale.GERMAN -> "Testzertifikat" + else -> "Test Certificate" + } + ) + ), + admissionBadgeText = "2G+", + colorShade = PersonColorShade.COLOR_1, + badgeCount = 1, + onClickAction = { _, _ -> }, + onCovPassInfoAction = {}, + onCertificateSelected = {}, + ) + ) + } + +private fun mockTestCertificate( + name: String, + isPending: Boolean = false, + isUpdating: Boolean = false +): TestCertificate = mockk().apply { + every { headerExpiresAt } returns Instant.now().plus(20) + every { isCertificateRetrievalPending } returns isPending + every { isUpdatingData } returns isUpdating + every { fullName } returns name + every { registeredAt } returns Instant.parse("2021-05-21T11:35:00.000Z") + every { personIdentifier } returns CertificatePersonIdentifier( + firstNameStandardized = "firstNameStandardized", + lastNameStandardized = "lastNameStandardized", + dateOfBirthFormatted = "1943-04-18" + ) + every { qrCodeToDisplay } returns CoilQrCode(ScreenshotCertificateTestData.testCertificate) + every { isDisplayValid } returns true + every { sampleCollectedAt } returns Instant.parse("2021-05-21T11:35:00.000Z") + every { state } returns CwaCovidCertificate.State.Valid(headerExpiresAt) + every { isNew } returns false +} + +private fun mockTestCertificateWrapper(isUpdating: Boolean) = mockk().apply { + every { isCertificateRetrievalPending } returns true + every { isUpdatingData } returns isUpdating + every { registeredAt } returns Instant.EPOCH + every { containerId } returns TestCertificateContainerId("testCertificateContainerId") +} + +private fun mockVaccinationCertificate(name: String): VaccinationCertificate = + mockk().apply { + every { headerExpiresAt } returns Instant.now().plus(20) + every { containerId } returns VaccinationCertificateContainerId("2") + val localDate = Instant.parse("2021-06-01T11:35:00.000Z").toLocalDateUserTz() + every { fullName } returns name + every { fullNameFormatted } returns name + every { doseNumber } returns 2 + every { totalSeriesOfDoses } returns 2 + every { vaccinatedOn } returns localDate.minusDays(15) + every { personIdentifier } returns CertificatePersonIdentifier( + firstNameStandardized = "firstNameStandardized", + lastNameStandardized = "lastNameStandardized", + dateOfBirthFormatted = "1943-04-18" + ) + every { isDisplayValid } returns true + every { state } returns CwaCovidCertificate.State.Valid(headerExpiresAt) + every { qrCodeToDisplay } returns CoilQrCode(ScreenshotCertificateTestData.vaccinationCertificate) + } + +private fun mockInvalidVaccinationCertificate(name: String): VaccinationCertificate = + mockk().apply { + every { headerExpiresAt } returns Instant.now().plus(20) + every { containerId } returns VaccinationCertificateContainerId("2") + val localDate = Instant.parse("2019-06-01T11:35:00.000Z").toLocalDateUserTz() + every { fullName } returns name + every { fullNameFormatted } returns name + every { doseNumber } returns 2 + every { totalSeriesOfDoses } returns 2 + every { vaccinatedOn } returns localDate.minusDays(15) + every { personIdentifier } returns CertificatePersonIdentifier( + firstNameStandardized = "firstNameStandardized", + lastNameStandardized = "lastNameStandardized", + dateOfBirthFormatted = "1943-04-18" + ) + every { isDisplayValid } returns false + every { state } returns CwaCovidCertificate.State.Invalid() + every { qrCodeToDisplay } returns CoilQrCode(ScreenshotCertificateTestData.vaccinationCertificate) + } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoOutput.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoOutput.kt index 02e295a10ba..5292ca019a1 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoOutput.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoOutput.kt @@ -6,6 +6,8 @@ import com.fasterxml.jackson.annotation.JsonSubTypes import com.fasterxml.jackson.annotation.JsonTypeInfo import com.fasterxml.jackson.annotation.JsonValue import com.fasterxml.jackson.databind.node.ObjectNode +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState.MaskStateIdentifier.OPTIONAL +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState.MaskStateIdentifier.REQUIRED import de.rki.coronawarnapp.dccreissuance.core.reissuer.ACTION_RENEW import de.rki.coronawarnapp.util.HashExtensions.toSHA256 import org.joda.time.Instant @@ -33,8 +35,10 @@ data class DccWalletInfo( val certificateReissuance: CertificateReissuance? = null, @JsonProperty("certificatesRevokedByInvalidationRules") - val certificatesRevokedByInvalidationRules: List? = null + val certificatesRevokedByInvalidationRules: List? = null, + @JsonProperty("maskState") + val maskState: MaskState? = null ) { @get:JsonIgnore val validUntilInstant: Instant @@ -42,8 +46,12 @@ data class DccWalletInfo( @get:JsonIgnore val hasReissuance: Boolean - get() = certificateReissuance != null && - certificateReissuance.reissuanceDivision.visible + get() = certificateReissuance?.reissuanceDivision?.visible ?: false + + @get:JsonIgnore + val hasMaskState: Boolean + get() = (maskState?.visible ?: false) && + maskState?.identifier in listOf(REQUIRED, OPTIONAL) } @JsonTypeInfo( @@ -181,19 +189,19 @@ data class BoosterNotification( val visible: Boolean, @JsonProperty("titleText") - val titleText: CclText?, + val titleText: CclText? = null, @JsonProperty("subtitleText") - val subtitleText: CclText?, + val subtitleText: CclText? = null, @JsonProperty("longText") - val longText: CclText?, + val longText: CclText? = null, @JsonProperty("faqAnchor") - val faqAnchor: String?, + val faqAnchor: String? = null, @JsonProperty("identifier") - val identifier: String? + val identifier: String? = null ) data class CertificateRef( @@ -339,7 +347,7 @@ data class ReissuanceDivision( val faqAnchor: String?, @JsonProperty("identifier") - val identifier: String? = DEFAULT_IDENTIFIER, + val identifier: String? = REISSUANCE_DEFAULT_IDENTIFIER, @JsonProperty("listTitleText") val listTitleText: CclText? = null, @@ -353,4 +361,36 @@ data class CertificatesRevokedByInvalidationRules( val certificateRef: CertificateRef ) -internal const val DEFAULT_IDENTIFIER = "renew" +data class MaskState( + @JsonProperty("visible") + val visible: Boolean = false, + + @JsonProperty("badgeText") + val badgeText: CclText? = null, + + @JsonProperty("titleText") + val titleText: CclText? = null, + + @JsonProperty("subtitleText") + val subtitleText: CclText? = null, + + @JsonProperty("longText") + val longText: CclText? = null, + + @JsonProperty("faqAnchor") + val faqAnchor: String? = null, + + @JsonProperty("identifier") + val identifier: MaskStateIdentifier? = null +) { + + enum class MaskStateIdentifier(private val identifier: String) { + REQUIRED("MASK_REQUIRED"), + OPTIONAL("MASK_OPTIONAL"), + OTHER("OTHER"); + @JsonValue + fun value() = identifier + } +} + +internal const val REISSUANCE_DEFAULT_IDENTIFIER = "renew" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt index 29d11d47742..2e5256f0ed0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/core/PersonCertificates.kt @@ -2,6 +2,7 @@ package de.rki.coronawarnapp.covidcertificate.person.core import de.rki.coronawarnapp.ccl.dccwalletinfo.model.CclText import de.rki.coronawarnapp.ccl.dccwalletinfo.model.DccWalletInfo +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate @@ -50,3 +51,9 @@ data class VerificationCertificate( val cwaCertificate: CwaCovidCertificate, val buttonText: CclText? = null ) + +val PersonCertificates.isMaskOptional: Boolean + get() = dccWalletInfo?.maskState?.identifier == MaskState.MaskStateIdentifier.OPTIONAL + +val PersonCertificates.isHighestCertificateDisplayValid: Boolean + get() = highestPriorityCertificate?.isDisplayValid == true diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsAdapter.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsAdapter.kt index 87083476038..7fe574e6eb7 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsAdapter.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsAdapter.kt @@ -3,11 +3,12 @@ package de.rki.coronawarnapp.covidcertificate.person.ui.details import android.view.ViewGroup import androidx.annotation.LayoutRes import androidx.viewbinding.ViewBinding +import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.AdmissionStatusCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.BoosterCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateItem import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateReissuanceCard -import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.AdmissionStatusCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CwaUserCard +import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.MaskRequirementsCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.RecoveryCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.TestCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.VaccinationCertificateCard @@ -31,6 +32,7 @@ class PersonDetailsAdapter : listOf( StableIdMod(data), DataBinderMod>(data), + TypedVHCreatorMod({ data[it] is MaskRequirementsCard.Item }) { MaskRequirementsCard(it) }, TypedVHCreatorMod({ data[it] is CertificateReissuanceCard.Item }) { CertificateReissuanceCard(it) }, TypedVHCreatorMod({ data[it] is BoosterCard.Item }) { BoosterCard(it) }, TypedVHCreatorMod({ data[it] is AdmissionStatusCard.Item }) { AdmissionStatusCard(it) }, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModel.kt index e50daf62b81..597f52c56e0 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModel.kt @@ -8,6 +8,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.ccl.dccwalletinfo.model.AdmissionState import de.rki.coronawarnapp.ccl.dccwalletinfo.model.BoosterNotification +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState import de.rki.coronawarnapp.ccl.dccwalletinfo.model.ReissuanceDivision import de.rki.coronawarnapp.ccl.dccwalletinfo.model.VaccinationState import de.rki.coronawarnapp.ccl.ui.text.CclTextFormatter @@ -16,16 +17,20 @@ import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContai import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesSettings +import de.rki.coronawarnapp.covidcertificate.person.core.isHighestCertificateDisplayValid +import de.rki.coronawarnapp.covidcertificate.person.core.isMaskOptional import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.AdmissionStatusCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.BoosterCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateItem import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CertificateReissuanceCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CwaUserCard +import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.MaskRequirementsCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.RecoveryCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.TestCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.VaccinationCertificateCard import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.VaccinationInfoCard import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade.Companion.colorForState import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificate import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificate import de.rki.coronawarnapp.covidcertificate.vaccination.core.VaccinationCertificate @@ -81,22 +86,30 @@ class PersonDetailsViewModel @AssistedInject constructor( return UiState(name = "", emptyList()) } - val color = if (priorityCertificate.isDisplayValid) colorShade else PersonColorShade.COLOR_INVALID + val color = colorForState( + validCertificate = personCertificates.isHighestCertificateDisplayValid, + isMaskOptional = personCertificates.isMaskOptional, + // Person certificates can change in here, therefore if Mask is required again ignore Blue color shade from + // PersonOverview + currentColor = if (personCertificates.isMaskOptional) colorShade else PersonColorShade.COLOR_1 + ) colorShadeData.postValue(color) val certificateItems = mutableListOf() personCertificates.dccWalletInfo?.let { info -> - // 1. Admission state tile + // 1. Mask state tile + if (info.hasMaskState) info.maskState?.let { state -> certificateItems.add(maskStateItem(state, color)) } + // 2. Admission state tile if (info.admissionState.visible) - certificateItems.add(admissionStateItem(info.admissionState, personCertificates)) - // 2. Dcc reissuance tile + certificateItems.add(admissionStateItem(info.admissionState, personCertificates, color)) + // 3. Dcc reissuance tile if (info.hasReissuance) info.certificateReissuance?.reissuanceDivision?.let { division -> certificateItems.add(dccReissuanceItem(division, personCertificates)) } - // 3. Booster notification tile + // 4. Booster notification tile if (info.boosterNotification.visible) certificateItems.add(boosterItem(info.boosterNotification, personCertificates)) - // 4.Vaccination state tile + // 5.Vaccination state tile if (info.vaccinationState.visible) certificateItems.add(vaccinationInfoItem(info.vaccinationState)) } @@ -112,7 +125,8 @@ class PersonDetailsViewModel @AssistedInject constructor( certificateItems.addCardItem( certificate = cwaCert, priorityCertificate = priorityCertificate, - isLoading = isLoading + isLoading = isLoading, + colorShade = color, ) } @@ -126,16 +140,35 @@ class PersonDetailsViewModel @AssistedInject constructor( private fun MutableList.addCardItem( certificate: CwaCovidCertificate, priorityCertificate: CwaCovidCertificate, - isLoading: Boolean + isLoading: Boolean, + colorShade: PersonColorShade, ) { val isCurrentCertificate = certificate.containerId == priorityCertificate.containerId when (certificate) { - is TestCertificate -> add(tcItem(certificate, isCurrentCertificate, isLoading)) - is VaccinationCertificate -> add(vcItem(certificate, isCurrentCertificate, isLoading)) - is RecoveryCertificate -> add(rcItem(certificate, isCurrentCertificate, isLoading)) + is TestCertificate -> add( + tcItem(certificate, isCurrentCertificate, isLoading, colorShade) + ) + is VaccinationCertificate -> add( + vcItem(certificate, isCurrentCertificate, isLoading, colorShade) + ) + is RecoveryCertificate -> add( + rcItem(certificate, isCurrentCertificate, isLoading, colorShade) + ) } } + private suspend fun maskStateItem( + maskState: MaskState, + colorShade: PersonColorShade + ) = MaskRequirementsCard.Item( + titleText = format(maskState.titleText), + subtitleText = format(maskState.subtitleText), + badgeState = maskState.identifier, + longText = format(maskState.longText), + faqAnchor = format(maskState.faqAnchor), + colorShade = colorShade + ) + private suspend fun vaccinationInfoItem( vaccinationState: VaccinationState ) = VaccinationInfoCard.Item( @@ -147,7 +180,8 @@ class PersonDetailsViewModel @AssistedInject constructor( private suspend fun admissionStateItem( admissionState: AdmissionState, - personCertificates: PersonCertificates + personCertificates: PersonCertificates, + colorShade: PersonColorShade, ) = AdmissionStatusCard.Item( titleText = format(admissionState.titleText), subtitleText = format(admissionState.subtitleText), @@ -204,7 +238,8 @@ class PersonDetailsViewModel @AssistedInject constructor( private fun rcItem( certificate: RecoveryCertificate, isCurrentCertificate: Boolean, - isLoading: Boolean + isLoading: Boolean, + colorShade: PersonColorShade, ) = RecoveryCertificateCard.Item( certificate = certificate, isCurrentCertificate = isCurrentCertificate, @@ -215,7 +250,7 @@ class PersonDetailsViewModel @AssistedInject constructor( events.postValue( OpenRecoveryCertificateDetails( containerId = certificate.containerId, - colorShade = getItemColorShade(certificate.isDisplayValid, isCurrentCertificate) + colorShade = getDetailsColorShade(isCurrentCertificate, colorShade) ) ) }, @@ -231,7 +266,8 @@ class PersonDetailsViewModel @AssistedInject constructor( private fun vcItem( certificate: VaccinationCertificate, isCurrentCertificate: Boolean, - isLoading: Boolean + isLoading: Boolean, + colorShade: PersonColorShade, ) = VaccinationCertificateCard.Item( certificate = certificate, isCurrentCertificate = isCurrentCertificate, @@ -242,7 +278,7 @@ class PersonDetailsViewModel @AssistedInject constructor( events.postValue( OpenVaccinationCertificateDetails( containerId = certificate.containerId, - colorShade = getItemColorShade(certificate.isDisplayValid, isCurrentCertificate) + colorShade = getDetailsColorShade(isCurrentCertificate, colorShade) ) ) }, @@ -254,7 +290,8 @@ class PersonDetailsViewModel @AssistedInject constructor( private fun tcItem( certificate: TestCertificate, isCurrentCertificate: Boolean, - isLoading: Boolean + isLoading: Boolean, + colorShade: PersonColorShade, ) = TestCertificateCard.Item( certificate = certificate, isCurrentCertificate = isCurrentCertificate, @@ -265,7 +302,7 @@ class PersonDetailsViewModel @AssistedInject constructor( events.postValue( OpenTestCertificateDetails( containerId = certificate.containerId, - colorShade = getItemColorShade(certificate.isDisplayValid, isCurrentCertificate) + colorShade = getDetailsColorShade(isCurrentCertificate, colorShade) ) ) }, @@ -283,12 +320,13 @@ class PersonDetailsViewModel @AssistedInject constructor( } } - private fun getItemColorShade( - isValid: Boolean, - isCurrentCertificate: Boolean + private fun getDetailsColorShade( + isCurrentCertificate: Boolean, + colorShade: PersonColorShade ): PersonColorShade = when { - isValid && isCurrentCertificate -> colorShade - else -> PersonColorShade.COLOR_INVALID + !isCurrentCertificate -> PersonColorShade.COLOR_INVALID + colorShade == PersonColorShade.GREEN -> PersonColorShade.COLOR_1 + else -> colorShade } data class UiState( diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/items/MaskRequirementsCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/items/MaskRequirementsCard.kt new file mode 100644 index 00000000000..bd683872024 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/items/MaskRequirementsCard.kt @@ -0,0 +1,54 @@ +package de.rki.coronawarnapp.covidcertificate.person.ui.details.items + +import android.view.ViewGroup +import androidx.core.view.isVisible +import de.rki.coronawarnapp.R +import de.rki.coronawarnapp.ccl.dccwalletinfo.model.MaskState +import de.rki.coronawarnapp.covidcertificate.person.ui.details.PersonDetailsAdapter +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade +import de.rki.coronawarnapp.databinding.MaskRequirementsCardBinding +import de.rki.coronawarnapp.util.ContextExtensions.getDrawableCompat +import de.rki.coronawarnapp.util.convertToHyperlink +import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer + +class MaskRequirementsCard(parent: ViewGroup) : + PersonDetailsAdapter.PersonDetailsItemVH( + layoutRes = R.layout.mask_requirements_card, + parent = parent + ) { + + override val viewBinding: Lazy = lazy { + MaskRequirementsCardBinding.bind(itemView) + } + + override val onBindData: MaskRequirementsCardBinding.( + item: Item, + payloads: List + ) -> Unit = { item, payloads -> + val curItem = payloads.filterIsInstance().lastOrNull() ?: item + + title.text = curItem.titleText + subtitle.text = curItem.subtitleText + body.text = curItem.longText + if (curItem.badgeState != null) { + badge.isVisible = true + badge.background = context.getDrawableCompat(curItem.colorShade.maskSmallBadge) + } + faq.isVisible = curItem.faqAnchor != null + curItem.faqAnchor?.let { url -> + faq.convertToHyperlink(url) + } + } + + data class Item( + val titleText: String, + val subtitleText: String, + val badgeState: MaskState.MaskStateIdentifier?, + val longText: String, + val faqAnchor: String?, + val colorShade: PersonColorShade + ) : CertificateItem, HasPayloadDiffer { + override fun diffPayload(old: Any, new: Any): Any? = if (old::class == new::class) new else null + override val stableId: Long = Item::class.hashCode().toLong() + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonColorShade.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonColorShade.kt index 859a2b02654..c7205e00a66 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonColorShade.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonColorShade.kt @@ -4,48 +4,65 @@ import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import de.rki.coronawarnapp.R +@Suppress("LongParameterList") enum class PersonColorShade( @ColorRes val starsTint: Int, @DrawableRes val background: Int, @DrawableRes val currentCertificateBg: Int, @DrawableRes val bookmarkIcon: Int, @DrawableRes val admissionBadgeBg: Int, + @DrawableRes val maskIcon: Int = R.drawable.ic_mask, + @DrawableRes val maskLargeBadgeBg: Int = R.drawable.mask_badge_bg, + @DrawableRes val maskSmallBadge: Int = R.drawable.mask_small_badge, + @DrawableRes val noMaskSmallBadge: Int = R.drawable.no_mask_small_badge, + @ColorRes val maskBadgeTextColor: Int = R.color.maskBadgeTextColorRegular ) { COLOR_1( - R.color.starsColor1, - R.drawable.bg_person_overview_1, - R.drawable.bg_certificate_blue_1, - R.drawable.ic_bookmark_blue_1, - R.drawable.ic_admission_badge_1, + starsTint = R.color.starsColor1, + background = R.drawable.bg_person_overview_1, + currentCertificateBg = R.drawable.bg_certificate_blue_1, + bookmarkIcon = R.drawable.ic_bookmark_blue_1, + admissionBadgeBg = R.drawable.ic_admission_badge_1, ), COLOR_2( - R.color.starsColor2, - R.drawable.bg_person_overview_2, - R.drawable.bg_certificate_blue_2, - R.drawable.ic_bookmark_blue_2, - R.drawable.ic_admission_badge_2, + starsTint = R.color.starsColor2, + background = R.drawable.bg_person_overview_2, + currentCertificateBg = R.drawable.bg_certificate_blue_2, + bookmarkIcon = R.drawable.ic_bookmark_blue_2, + admissionBadgeBg = R.drawable.ic_admission_badge_2, ), COLOR_3( - R.color.starsColor3, - R.drawable.bg_person_overview_3, - R.drawable.bg_certificate_blue_3, - R.drawable.ic_bookmark_blue_3, - R.drawable.ic_admission_badge_3, + starsTint = R.color.starsColor3, + background = R.drawable.bg_person_overview_3, + currentCertificateBg = R.drawable.bg_certificate_blue_3, + bookmarkIcon = R.drawable.ic_bookmark_blue_3, + admissionBadgeBg = R.drawable.ic_admission_badge_3, ), COLOR_INVALID( - R.color.starsColorInvalid, - R.drawable.bg_person_overview_invalid, - R.drawable.bg_certificate_grey, - R.drawable.ic_bookmark, - R.drawable.ic_admission_badge_1, + starsTint = R.color.starsColorInvalid, + background = R.drawable.bg_person_overview_invalid, + currentCertificateBg = R.drawable.bg_certificate_grey, + bookmarkIcon = R.drawable.ic_bookmark, + admissionBadgeBg = R.drawable.ic_admission_badge_1, ), COLOR_UNDEFINED( - R.color.starsColorInvalid, - R.drawable.bg_person_overview_invalid, - R.drawable.bg_certificate_grey, - R.drawable.ic_bookmark, - R.drawable.ic_admission_badge_1, + starsTint = R.color.starsColorInvalid, + background = R.drawable.bg_person_overview_invalid, + currentCertificateBg = R.drawable.bg_certificate_grey, + bookmarkIcon = R.drawable.ic_bookmark, + admissionBadgeBg = R.drawable.ic_admission_badge_1, + ), + GREEN( + starsTint = R.color.starsGreenColor, + background = R.drawable.bg_person_overview_green, + currentCertificateBg = R.drawable.bg_certificate_blue_1, + bookmarkIcon = R.drawable.ic_bookmark_green, + admissionBadgeBg = R.drawable.ic_admission_badge_green, + maskIcon = R.drawable.ic_no_mask, + maskLargeBadgeBg = R.drawable.no_mask_badge_bg, + maskSmallBadge = R.drawable.no_mask_small_badge, + maskBadgeTextColor = R.color.maskBadgeTextColorGreenBg ); @DrawableRes val defaultCertificateBg: Int = R.drawable.bg_certificate_grey @@ -56,8 +73,19 @@ enum class PersonColorShade( */ fun shadeFor(index: Int): PersonColorShade { val values = values() - // Excludes COLOR_INVALID, COLOR_UNDEFINED - return values.getOrElse(index.rem(values.size - 2)) { COLOR_1 } + // Excludes COLOR_INVALID, COLOR_UNDEFINED, GREEN from position based colouring + // these colours appear on specific conditions of person's certificates + return values.getOrElse(index.rem(values.size - 3)) { COLOR_1 } + } + + fun colorForState( + validCertificate: Boolean, + isMaskOptional: Boolean, + currentColor: PersonColorShade + ): PersonColorShade = when { + isMaskOptional -> GREEN + validCertificate -> currentColor + else -> COLOR_INVALID } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt index 51bb71a74d3..cd5bf05596d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/PersonOverviewViewModel.kt @@ -11,7 +11,11 @@ import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateCo import de.rki.coronawarnapp.covidcertificate.person.core.MigrationCheck import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider +import de.rki.coronawarnapp.covidcertificate.person.core.isHighestCertificateDisplayValid +import de.rki.coronawarnapp.covidcertificate.person.core.isMaskOptional import de.rki.coronawarnapp.covidcertificate.person.ui.admission.AdmissionScenariosSharedViewModel +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade.Companion.colorForState +import de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade.Companion.shadeFor import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.AdmissionTileProvider import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.CovidTestCertificatePendingCard import de.rki.coronawarnapp.covidcertificate.person.ui.overview.items.PersonCertificateCard @@ -102,13 +106,19 @@ class PersonOverviewViewModel @AssistedInject constructor( .mapIndexed { index, person -> val admissionState = person.dccWalletInfo?.admissionState val certificates = person.verificationCertificates - val color = PersonColorShade.shadeFor(index) + val color = colorForState( + validCertificate = person.isHighestCertificateDisplayValid, + isMaskOptional = person.isMaskOptional, + currentColor = shadeFor(index) + ) PersonCertificateCard.Item( overviewCertificates = certificates.map { OverviewCertificate(it.cwaCertificate, format(it.buttonText)) }, admissionBadgeText = format(admissionState?.badgeText), + hasMaskState = person.dccWalletInfo?.hasMaskState ?: false, + maskBadgeText = format(person.dccWalletInfo?.maskState?.badgeText), colorShade = color, badgeCount = person.badgeCount, certificateSelection = selections[person.personIdentifier.groupingKey] ?: CertificateSelection.FIRST, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/items/PersonCertificateCard.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/items/PersonCertificateCard.kt index a43f37e7e3b..a0b3dd4012e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/items/PersonCertificateCard.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/overview/items/PersonCertificateCard.kt @@ -39,6 +39,8 @@ class PersonCertificateCard(parent: ViewGroup) : val primaryCertificateText: String = "", val secondaryCertificateText: String = "", val admissionBadgeText: String = "", + val hasMaskState: Boolean = false, + val maskBadgeText: String = "", val certificateSelection: CertificateSelection = CertificateSelection.FIRST, val colorShade: PersonColorShade, val badgeCount: Int, diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoFragment.kt index f87d8647211..3894084e466 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoFragment.kt @@ -51,7 +51,14 @@ class NewReleaseInfoFragment : Fragment(R.layout.new_release_info_screen_fragmen toolbar.setNavigationIcon(R.drawable.ic_back) } - recyclerView.adapter = ItemAdapter(getItems()) + val items = getItems() + recyclerView.adapter = ItemAdapter(items) + + newReleaseInfoBody.text = if (items.isNotEmpty()) { + getText(R.string.release_info_version_body) + } else { + getText(R.string.release_info_version_body_no_new_features) + } } // Override android back button to bypass the infinite loop diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModel.kt index d9a4238bc0c..44b973e6258 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/release/NewReleaseInfoViewModel.kt @@ -48,7 +48,7 @@ class NewReleaseInfoViewModel @AssistedInject constructor( } val items = mutableListOf() titles.indices.forEach { i -> - if (linkifiedLabels[i].isNullOrBlank() || linkTargets[i].isNullOrBlank()) { + if (linkifiedLabels[i].isBlank() || linkTargets[i].isBlank()) { items.add(NewReleaseInfoItemText(titles[i], bodies[i])) } else { items.add(NewReleaseInfoItemLinked(titles[i], bodies[i], linkifiedLabels[i], linkTargets[i])) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CertificateStateHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CertificateStateHelper.kt index f83e057e60b..527d951021b 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CertificateStateHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/CertificateStateHelper.kt @@ -6,6 +6,7 @@ import android.view.View import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isGone +import androidx.core.view.isInvisible import androidx.core.view.isVisible import coil.loadAny import com.google.android.material.button.MaterialButton @@ -125,11 +126,7 @@ fun PersonOverviewItemBinding.setUIState( val context = root.context val valid = firstCertificate.cwaCertificate.isDisplayValid - val color = when { - valid -> item.colorShade - else -> PersonColorShade.COLOR_INVALID - } - + val color = item.colorShade val badgeCount = item.badgeCount backgroundImage.setImageResource(color.background) starsImage.setImageDrawable(starsDrawable(context, color)) @@ -141,11 +138,23 @@ fun PersonOverviewItemBinding.setUIState( val statusBadgeText = item.admissionBadgeText qrCodeCard.apply { loadQrImage(firstCertificate.cwaCertificate) - statusText.isVisible = statusBadgeText.isNotEmpty() - statusBadge.isVisible = statusBadgeText.isNotEmpty() - if (statusBadgeText.isNotEmpty()) { - statusBadge.text = statusBadgeText + if (item.hasMaskState) { + setMaskBadge(maskBadge, item, color) + } + statusBadge.setBackgroundResource(color.admissionBadgeBg) + statusBadge.text = statusBadgeText + + when (statusBadgeText.isEmpty()) { + true -> { + when (item.hasMaskState) { + true -> statusBadge.visibility = View.INVISIBLE + false -> statusBadge.visibility = View.GONE + } + } + false -> statusBadge.visibility = View.VISIBLE } + + maskBadge.isInvisible = item.maskBadgeText.isEmpty() covpassInfoTitle.isVisible = valid covpassInfoButton.isVisible = valid covpassInfoButton.setOnClickListener { item.onCovPassInfoAction() } @@ -175,6 +184,13 @@ fun PersonOverviewItemBinding.setUIState( } } +private fun setMaskBadge(maskBadge: TextView, item: PersonCertificateCard.Item, color: PersonColorShade) { + maskBadge.text = item.maskBadgeText + maskBadge.setBackgroundResource(color.maskLargeBadgeBg) + maskBadge.setCompoundDrawablesWithIntrinsicBounds(color.maskIcon, 0, 0, 0) + maskBadge.setTextColor(maskBadge.resources.getColor(color.maskBadgeTextColor, null)) +} + private fun IncludeCertificateOverviewQrCardBinding.bindButtonToggleGroup( secondCertificate: OverviewCertificate?, thirdCertificate: OverviewCertificate?, diff --git a/Corona-Warn-App/src/main/res/drawable/bg_person_overview_green.xml b/Corona-Warn-App/src/main/res/drawable/bg_person_overview_green.xml new file mode 100644 index 00000000000..4ca44242cd5 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/bg_person_overview_green.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/ic_admission_badge_1.xml b/Corona-Warn-App/src/main/res/drawable/ic_admission_badge_1.xml index cdbddfabfc1..3823eae9c28 100644 --- a/Corona-Warn-App/src/main/res/drawable/ic_admission_badge_1.xml +++ b/Corona-Warn-App/src/main/res/drawable/ic_admission_badge_1.xml @@ -2,12 +2,7 @@ - - + - - + - - + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_1.xml b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_1.xml index 175afb27fe1..a8ab2a7eada 100644 --- a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_1.xml +++ b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_1.xml @@ -4,24 +4,12 @@ android:height="22dp" android:viewportWidth="22" android:viewportHeight="22"> - - - - - - - - - - + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_2.xml b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_2.xml index 57a7559f46b..76b890e26e7 100644 --- a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_2.xml +++ b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_2.xml @@ -4,21 +4,11 @@ android:height="22dp" android:viewportWidth="22" android:viewportHeight="22"> - - - - - - - - - + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_3.xml b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_3.xml index 6351bb542f3..cd78891c5d5 100644 --- a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_3.xml +++ b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_blue_3.xml @@ -4,21 +4,11 @@ android:height="22dp" android:viewportWidth="22" android:viewportHeight="22"> - - - - - - - - - + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_bookmark_green.xml b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_green.xml new file mode 100644 index 00000000000..58b6b4af7b6 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_bookmark_green.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_mask.xml b/Corona-Warn-App/src/main/res/drawable/ic_mask.xml new file mode 100644 index 00000000000..f7af554a41c --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_mask.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/ic_no_mask.xml b/Corona-Warn-App/src/main/res/drawable/ic_no_mask.xml new file mode 100644 index 00000000000..26dae92a588 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_no_mask.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/Corona-Warn-App/src/main/res/drawable/mask_badge_bg.xml b/Corona-Warn-App/src/main/res/drawable/mask_badge_bg.xml new file mode 100644 index 00000000000..4a0b307a226 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/mask_badge_bg.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/mask_small_badge.xml b/Corona-Warn-App/src/main/res/drawable/mask_small_badge.xml new file mode 100644 index 00000000000..f1b7048e5cd --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/mask_small_badge.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/no_mask_badge_bg.xml b/Corona-Warn-App/src/main/res/drawable/no_mask_badge_bg.xml new file mode 100644 index 00000000000..ca68c3d0742 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/no_mask_badge_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/drawable/no_mask_small_badge.xml b/Corona-Warn-App/src/main/res/drawable/no_mask_small_badge.xml new file mode 100644 index 00000000000..5ce0ee77a39 --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/no_mask_small_badge.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/layout/confirmed_status_card.xml b/Corona-Warn-App/src/main/res/layout/confirmed_status_card.xml index 91220cfdd23..846eb393bed 100644 --- a/Corona-Warn-App/src/main/res/layout/confirmed_status_card.xml +++ b/Corona-Warn-App/src/main/res/layout/confirmed_status_card.xml @@ -47,14 +47,11 @@ diff --git a/Corona-Warn-App/src/main/res/layout/include_certificate_overview_qr_card.xml b/Corona-Warn-App/src/main/res/layout/include_certificate_overview_qr_card.xml index 256bffe2f73..f41bee196ca 100644 --- a/Corona-Warn-App/src/main/res/layout/include_certificate_overview_qr_card.xml +++ b/Corona-Warn-App/src/main/res/layout/include_certificate_overview_qr_card.xml @@ -9,25 +9,31 @@ android:padding="12dp"> + android:id="@+id/mask_badge" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:minHeight="26dp" + android:paddingHorizontal="8dp" + android:layout_marginEnd="8dp" + android:drawablePadding="8dp" + android:gravity="center_vertical" + android:textSize="14sp" + app:layout_constraintStart_toStartOf="@id/image" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toStartOf="@id/status_badge" + tools:text="Keine Maskenpflicht" + tools:drawableLeft="@drawable/ic_no_mask" + tools:background="@drawable/no_mask_badge_bg"/> diff --git a/Corona-Warn-App/src/main/res/layout/mask_requirements_card.xml b/Corona-Warn-App/src/main/res/layout/mask_requirements_card.xml new file mode 100644 index 00000000000..42eee6a99a6 --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/mask_requirements_card.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + diff --git a/Corona-Warn-App/src/main/res/layout/person_overview_item.xml b/Corona-Warn-App/src/main/res/layout/person_overview_item.xml index f3c9f72a6c2..fc52df65091 100644 --- a/Corona-Warn-App/src/main/res/layout/person_overview_item.xml +++ b/Corona-Warn-App/src/main/res/layout/person_overview_item.xml @@ -41,6 +41,7 @@ android:layout_marginTop="20dp" android:layout_marginEnd="42dp" android:maxLines="1" + android:ellipsize="end" android:text="@string/person_details_certificate_title" android:textColor="@color/colorTextPrimary1InvertedStable" android:textSize="20sp" 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 68bbd4ee51b..4238badbbd1 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 @@ -8,7 +8,9 @@ "Версия %1$s" - "Настоящата актуализация съдържа корекциите на грешки в приложението. Не съдържа нови функции." + "" + + "Настоящата актуализация съдържа корекциите на грешки в приложението. Не съдържа нови функции." "Напред" @@ -16,18 +18,22 @@ + "" + "" + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-de/covid_certificate_strings.xml b/Corona-Warn-App/src/main/res/values-de/covid_certificate_strings.xml index 207a8907989..3736c76b83f 100644 --- a/Corona-Warn-App/src/main/res/values-de/covid_certificate_strings.xml +++ b/Corona-Warn-App/src/main/res/values-de/covid_certificate_strings.xml @@ -340,7 +340,8 @@ "Mehr Informationen finden Sie in den FAQ." - + + "Mehr Informationen zur Maskenbefreiung in den FAQ." Status für folgendes Bundesland 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 6fef07a5f5b..9ce76942b0f 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 @@ -9,7 +9,9 @@ Release %1$s - Mit diesem Update beheben wir Fehler in der App. Es enthält keine neuen Funktionen. + Mit diesem Update stellen wir Ihnen neben Fehlerbehebungen auch neue und erweiterte Funktionen zur Verfügung. + + Mit diesem Update beheben wir Fehler in der App. Es enthält keine neuen Funktionen. Weiter @@ -17,18 +19,22 @@ + Anzeige der Maskenbefreiung + Auf Ihrem digitalen COVID-Zertifikat wird angezeigt, ob Sie nach den aktuell gültigen Regeln von der Maskenpflicht befreit sind, oder eine Maske tragen müssen. + + diff --git a/Corona-Warn-App/src/main/res/values-night/colors.xml b/Corona-Warn-App/src/main/res/values-night/colors.xml index eca7524b743..dabaebbdc8d 100644 --- a/Corona-Warn-App/src/main/res/values-night/colors.xml +++ b/Corona-Warn-App/src/main/res/values-night/colors.xml @@ -91,5 +91,8 @@ @color/colorPersonOverviewQrCardBorder #000000 #434445 + #31A056 + #FFFFFF + #FFFFFF 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 f2b95a91585..e2522967993 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 @@ -8,7 +8,9 @@ "Wersja %1$s" - "Ta aktualizacja poprawia błędy w aplikacji. Nie zawiera żadnych nowych funkcji." + "" + + "Ta aktualizacja poprawia błędy w aplikacji. Nie zawiera żadnych nowych funkcji." "Dalej" @@ -16,18 +18,22 @@ + "" + "" + + \ 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 53183d12599..eebafb57c68 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 @@ -8,7 +8,9 @@ "Versiunea %1$s" - "Această actualizare corectează erori din aplicație. Nu conține caracteristici noi." + "" + + "Această actualizare corectează erori din aplicație. Nu conține caracteristici noi." "Înainte" @@ -16,18 +18,22 @@ + "" + "" + + \ 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 4c466afb757..ef5eb64b25d 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 @@ -8,7 +8,9 @@ "Sürüm %1$s" - "Bu güncelleme ile hatalar düzeltilmektedir. Bu güncellemede yeni özellik sunulmamaktadır." + "" + + "Bu güncelleme ile hatalar düzeltilmektedir. Bu güncellemede yeni özellik sunulmamaktadır." "Sonraki" @@ -16,18 +18,22 @@ + "" + "" + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values-uk/release_info_strings.xml b/Corona-Warn-App/src/main/res/values-uk/release_info_strings.xml index 3307e371c4d..f148fcfce2b 100644 --- a/Corona-Warn-App/src/main/res/values-uk/release_info_strings.xml +++ b/Corona-Warn-App/src/main/res/values-uk/release_info_strings.xml @@ -8,7 +8,9 @@ "Версія %1$s" - "Це оновлення містить виправлення помилок застосунку. У ньому немає жодних нових функцій." + "" + + "Це оновлення містить виправлення помилок застосунку. У ньому немає жодних нових функцій." "Далі" @@ -16,18 +18,22 @@ + "" + "" + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/colors.xml b/Corona-Warn-App/src/main/res/values/colors.xml index a87dbb5de8e..dbe85df4a85 100644 --- a/Corona-Warn-App/src/main/res/values/colors.xml +++ b/Corona-Warn-App/src/main/res/values/colors.xml @@ -106,6 +106,7 @@ #99FFFFFF #DEDEDE + #2F894D #0191C6 #116A9F #025A8F @@ -122,4 +123,8 @@ @color/colorPersonOverviewQrCardGrey #FFFFFF #E2E2E2 + #1F5932 + #505968 + #505968 + #FFFFFF diff --git a/Corona-Warn-App/src/main/res/values/covid_certificate_strings.xml b/Corona-Warn-App/src/main/res/values/covid_certificate_strings.xml index f5c4864d8d6..0834afdbed1 100644 --- a/Corona-Warn-App/src/main/res/values/covid_certificate_strings.xml +++ b/Corona-Warn-App/src/main/res/values/covid_certificate_strings.xml @@ -1,4 +1,5 @@ - + + "For further information, please see our FAQ page." - + + "Mehr Informationen zur Maskenbefreiung in den FAQ." "Status for the following federal state" 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 0d717dad9f5..57de5463325 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 @@ -8,7 +8,9 @@ "Release %1$s" - "This update corrects bugs in the app. It does not contain any new features." + "" + + "This update corrects bugs in the app. It does not contain any new features." "Next" @@ -16,18 +18,22 @@ + "" + "" + + \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/values/styles.xml b/Corona-Warn-App/src/main/res/values/styles.xml index 5b78eb129cd..78682567a1d 100644 --- a/Corona-Warn-App/src/main/res/values/styles.xml +++ b/Corona-Warn-App/src/main/res/values/styles.xml @@ -683,4 +683,14 @@ ?selectableItemBackground + + diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoData.kt index 53c007036ee..32e8f083a46 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoData.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoData.kt @@ -2,8 +2,10 @@ package de.rki.coronawarnapp.ccl.dccwalletinfo.model -const val qrCodeStringVac = "HC1:6BFOXN*TS0BI\$ZDYSHTRMM7QXSUJCQF*8KF6NDC2LE \$CGH9XYE+9GDJ5RPSDW1:ZH6I1$4JN:IN1MPK95%LS+1VUU8C1VTE5ZMT56QHECDVNE1FO1P56C\$QV/M8YML-1H1V*ZE846N07WW667N+H1YU1E 1PK9B/0E7J0PIBZIXJA CG8CGZK*9C%PDS1JGDBVF2$7K*IBQQKV-J2 JDZT:1BPMIMIA*NI7UJQWT.+S1QD4007/GJ2HIE9WT0K3M9UVZSVV*001HW%8UE9.955B9-NT0 2$$0X4PPZ0YUIPG1L+N1*PVD4WYHPRAAUICO1-.PU8GIFT383/9TL4T.B9NVP-PELK9D/5.B9C9Q8+ECB9UH9B/9BL5 QE-NEO+11C9P8Q+C9:6V P1HS9UM97H98\$QP3R8BH FRDRIS4FU2STU14G8R6U1YS+FRH8E0IF*+K1D4QIV9BRZX26 L**T -MEUIM3W1KI-\$BEBIU2B4+QF2HFUHBO3E/01.SW:O44BV7QU00W56:0" -const val qrCodeStringTest = "HC1:6BFOXN*TS0BI\$ZDFRH7LHUVNU.8GZ6769MBW3XHP+56R5LLPJ*5PIM*V4DZJIRPQHIZC4.OI:OIG/Q*LP1Z2GHKW/F3IKJ5QH*AF/GJ5MT0K5LO$50D$0SVV88UI*488UZ+3H0LW6WD1KG8U%8W+*3Y:CYZ00OPM98IMIWLHWVHPYH9UE*P1UU1XF820Q R1D.L%:PVLIJRH.OG4SIIRH/R2UZUWM6J$7XLH5G6TH95NI.J2IQ1TU1/PIMJEWT4SS3+Y3D.R9\$H.I8HX7B%R J8VR7* FLVKDN4FTITQI.:KO3HWBJ\$IJQFGSW5B*5:3AUM9NK9:AA945MR9EG3XFJ0%SI23D-I/2DBAJDAJCNB-43 X41Q2EG3RA3/43KD3F23.+IIYC6Q0ZIJPKJ+LJ5AL5:4A93VLJOPG32NQ3QR\$P*NIV1JK%228K*IBYZJ92KENS+7K*8BCIIM7JC0J3LIH.SO4UBR2:ZJ83B4PSZEUP637PS36JF0JEYI1DLZZL1629HQ-A6G 713JIZA213DY4F M52Q0VP25A75Q6TMR V$6L LOOUOPDVSQQR/TTJM:R7:SF90T99K4BTET0J/K%EN6 7I-DRK6*0L03SI30NVB-3" +const val qrCodeStringVac = + "HC1:6BFOXN*TS0BI\$ZDYSHTRMM7QXSUJCQF*8KF6NDC2LE \$CGH9XYE+9GDJ5RPSDW1:ZH6I1$4JN:IN1MPK95%LS+1VUU8C1VTE5ZMT56QHECDVNE1FO1P56C\$QV/M8YML-1H1V*ZE846N07WW667N+H1YU1E 1PK9B/0E7J0PIBZIXJA CG8CGZK*9C%PDS1JGDBVF2$7K*IBQQKV-J2 JDZT:1BPMIMIA*NI7UJQWT.+S1QD4007/GJ2HIE9WT0K3M9UVZSVV*001HW%8UE9.955B9-NT0 2$$0X4PPZ0YUIPG1L+N1*PVD4WYHPRAAUICO1-.PU8GIFT383/9TL4T.B9NVP-PELK9D/5.B9C9Q8+ECB9UH9B/9BL5 QE-NEO+11C9P8Q+C9:6V P1HS9UM97H98\$QP3R8BH FRDRIS4FU2STU14G8R6U1YS+FRH8E0IF*+K1D4QIV9BRZX26 L**T -MEUIM3W1KI-\$BEBIU2B4+QF2HFUHBO3E/01.SW:O44BV7QU00W56:0" +const val qrCodeStringTest = + "HC1:6BFOXN*TS0BI\$ZDFRH7LHUVNU.8GZ6769MBW3XHP+56R5LLPJ*5PIM*V4DZJIRPQHIZC4.OI:OIG/Q*LP1Z2GHKW/F3IKJ5QH*AF/GJ5MT0K5LO$50D$0SVV88UI*488UZ+3H0LW6WD1KG8U%8W+*3Y:CYZ00OPM98IMIWLHWVHPYH9UE*P1UU1XF820Q R1D.L%:PVLIJRH.OG4SIIRH/R2UZUWM6J$7XLH5G6TH95NI.J2IQ1TU1/PIMJEWT4SS3+Y3D.R9\$H.I8HX7B%R J8VR7* FLVKDN4FTITQI.:KO3HWBJ\$IJQFGSW5B*5:3AUM9NK9:AA945MR9EG3XFJ0%SI23D-I/2DBAJDAJCNB-43 X41Q2EG3RA3/43KD3F23.+IIYC6Q0ZIJPKJ+LJ5AL5:4A93VLJOPG32NQ3QR\$P*NIV1JK%228K*IBYZJ92KENS+7K*8BCIIM7JC0J3LIH.SO4UBR2:ZJ83B4PSZEUP637PS36JF0JEYI1DLZZL1629HQ-A6G 713JIZA213DY4F M52Q0VP25A75Q6TMR V$6L LOOUOPDVSQQR/TTJM:R7:SF90T99K4BTET0J/K%EN6 7I-DRK6*0L03SI30NVB-3" private val boosterNotification = BoosterNotification( visible = true, @@ -217,6 +219,32 @@ private val certificateReissuance = CertificateReissuance( ) ) +val maskState = MaskState( + visible = true, + badgeText = SingleText( + type = "string", + localizedText = mapOf("de" to "Befreiung"), + parameters = listOf() + ), + titleText = SingleText( + type = "string", + localizedText = mapOf("de" to "Masken-Befreiung"), + parameters = listOf() + ), + subtitleText = SingleText( + type = "string", + localizedText = mapOf("de" to "Für Sie gilt keine Maskenpflicht"), + parameters = listOf() + ), + longText = SingleText( + type = "string", + localizedText = mapOf("de" to "Aufgrund Ihrer Zertifikate [...]"), + parameters = listOf() + ), + faqAnchor = "dcc_admission_state", + identifier = MaskState.MaskStateIdentifier.OPTIONAL +) + val dccWalletInfoWithReissuance = dccWalletInfo.copy(certificateReissuance = certificateReissuance) val dccWalletInfoWithReissuanceLegacy = dccWalletInfo.copy(certificateReissuance = certificateReissuanceLegacy) @@ -231,3 +259,77 @@ val certificatesRevokedByInvalidationRules = CertificatesRevokedByInvalidationRu val dccWalletInfoWithCertificatesRevokedByInvalidationRules = dccWalletInfo.copy( certificatesRevokedByInvalidationRules = listOf(certificatesRevokedByInvalidationRules) ) + +val dccWalletInfoWithMaskState = DccWalletInfo( + admissionState = AdmissionState( + visible = true, + badgeText = SingleText( + type = "string", + localizedText = mapOf("de" to "2G+"), + parameters = listOf() + ), + titleText = SingleText( + type = "string", + localizedText = mapOf("de" to "Status-Nachweis"), + parameters = listOf() + ), + subtitleText = SingleText( + type = "string", + localizedText = mapOf("de" to "2G+ PCR-Test"), + parameters = listOf() + ), + longText = SingleText( + type = "string", + localizedText = mapOf("de" to "Ihre Zertifikate erfüllen [...]"), + parameters = listOf() + ), + stateChangeNotificationText = SingleText( + type = "string", + localizedText = mapOf("de" to "Lorem ipsum [...]"), + parameters = listOf() + ), + identifier = "2G_PLUS", + faqAnchor = "dcc_admission_state" + ), + vaccinationState = VaccinationState( + visible = true, + titleText = SingleText( + type = "string", + localizedText = mapOf("de" to "Impfstatus"), + parameters = listOf() + ), + subtitleText = PluralText( + type = "plural", + quantity = 25, + localizedText = mapOf( + "de" to QuantityText( + zero = "Letzte Impfung heute", + one = "Letzte Impfung vor %u Tag", + two = "Letzte Impfung vor %u Tagen", + few = "Letzte Impfung vor %u Tagen", + many = "Letzte Impfung vor %u Tagen", + other = "Letzte Impfung vor %u Tagen" + ) + ), + parameters = listOf( + Parameters( + type = Parameters.Type.NUMBER, + value = 5 + ) + ) + ), + longText = SingleText( + type = "string", + localizedText = mapOf("de" to "Sie haben nun alle derzeit [...]"), + parameters = listOf() + ), + faqAnchor = "dcc_admission_state" + ), + verification = verification, + boosterNotification = BoosterNotification( + visible = false + ), + mostRelevantCertificate = mostRelevantCertificate, + validUntil = "2022-01-14T18:43:00Z", + maskState = maskState +) diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoParserTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoParserTest.kt index c90ae527fac..22b1e1d2c80 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoParserTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ccl/dccwalletinfo/model/DccWalletInfoParserTest.kt @@ -15,7 +15,10 @@ internal class DccWalletInfoParserTest : BaseTest() { @Test fun `Deserialize DCCWalletInfo`() { javaClass.classLoader!!.getResourceAsStream("ccl/dcc_wallet_info_output.json").use { - mapper.readValue(it) shouldBe dccWalletInfo + val output = mapper.readValue(it) + output shouldBe dccWalletInfo + output.hasReissuance shouldBe false + output.hasMaskState shouldBe false } } @@ -116,6 +119,22 @@ internal class DccWalletInfoParserTest : BaseTest() { .toComparableJsonPretty() shouldBe it.readText().toComparableJsonPretty() } } + + @Test + fun `Deserialize DCCWalletInfo with MaskState`() { + javaClass.classLoader!!.getResourceAsStream("ccl/dcc_wallet_info_output_with_mask_state.json").use { + mapper.readValue(it) shouldBe dccWalletInfoWithMaskState + } + } + + @Test + fun `Serialize DCCWalletInfo with MaskState`() { + javaClass.classLoader!!.getResourceAsStream("ccl/dcc_wallet_info_output_with_mask_state.json").bufferedReader() + .use { + mapper.writeValueAsString(dccWalletInfoWithMaskState).toComparableJsonPretty() shouldBe + it.readText().toComparableJsonPretty() + } + } } private const val certificatesRevokedByInvalidationRulesPath = diff --git a/Corona-Warn-App/src/test/resources/ccl/dcc_wallet_info_output_with_mask_state.json b/Corona-Warn-App/src/test/resources/ccl/dcc_wallet_info_output_with_mask_state.json new file mode 100644 index 00000000000..885120d820b --- /dev/null +++ b/Corona-Warn-App/src/test/resources/ccl/dcc_wallet_info_output_with_mask_state.json @@ -0,0 +1,163 @@ +{ + "admissionState": { + "visible": true, + "badgeText": { + "type": "string", + "localizedText": { + "de": "2G+" + }, + "parameters": [ + ] + }, + "titleText": { + "type": "string", + "localizedText": { + "de": "Status-Nachweis" + }, + "parameters": [ + ] + }, + "subtitleText": { + "type": "string", + "localizedText": { + "de": "2G+ PCR-Test" + }, + "parameters": [ + ] + }, + "longText": { + "type": "string", + "localizedText": { + "de": "Ihre Zertifikate erfüllen [...]" + }, + "parameters": [ + ] + }, + "stateChangeNotificationText": { + "type": "string", + "localizedText": { + "de": "Lorem ipsum [...]" + }, + "parameters": [ + ] + }, + "identifier": "2G_PLUS", + "faqAnchor": "dcc_admission_state" + }, + "vaccinationState": { + "visible": true, + "titleText": { + "type": "string", + "localizedText": { + "de": "Impfstatus" + }, + "parameters": [ + ] + }, + "subtitleText": { + "type": "plural", + "quantity": 25, + "localizedText": { + "de": { + "zero": "Letzte Impfung heute", + "one": "Letzte Impfung vor %u Tag", + "two": "Letzte Impfung vor %u Tagen", + "few": "Letzte Impfung vor %u Tagen", + "many": "Letzte Impfung vor %u Tagen", + "other": "Letzte Impfung vor %u Tagen" + } + }, + "parameters": [ + { + "type": "number", + "value": 5 + } + ] + }, + "longText": { + "type": "string", + "localizedText": { + "de": "Sie haben nun alle derzeit [...]" + }, + "parameters": [ + ] + }, + "faqAnchor": "dcc_admission_state" + }, + "boosterNotification": { + "visible": false + }, + "mostRelevantCertificate": { + "certificateRef": { + "barcodeData": "HC1:6BFOXN*TS0BI$ZDYSHTRMM7QXSUJCQF*8KF6NDC2LE $CGH9XYE+9GDJ5RPSDW1:ZH6I1$4JN:IN1MPK95%LS+1VUU8C1VTE5ZMT56QHECDVNE1FO1P56C$QV/M8YML-1H1V*ZE846N07WW667N+H1YU1E 1PK9B/0E7J0PIBZIXJA CG8CGZK*9C%PDS1JGDBVF2$7K*IBQQKV-J2 JDZT:1BPMIMIA*NI7UJQWT.+S1QD4007/GJ2HIE9WT0K3M9UVZSVV*001HW%8UE9.955B9-NT0 2$$0X4PPZ0YUIPG1L+N1*PVD4WYHPRAAUICO1-.PU8GIFT383/9TL4T.B9NVP-PELK9D/5.B9C9Q8+ECB9UH9B/9BL5 QE-NEO+11C9P8Q+C9:6V P1HS9UM97H98$QP3R8BH FRDRIS4FU2STU14G8R6U1YS+FRH8E0IF*+K1D4QIV9BRZX26 L**T -MEUIM3W1KI-$BEBIU2B4+QF2HFUHBO3E/01.SW:O44BV7QU00W56:0" + } + }, + "verification": { + "certificates": [ + { + "buttonText": { + "type": "string", + "localizedText": { + "de": "2G-Zertifikat" + }, + "parameters": [ + ] + }, + "certificateRef": { + "barcodeData": "HC1:6BFOXN*TS0BI$ZDYSHTRMM7QXSUJCQF*8KF6NDC2LE $CGH9XYE+9GDJ5RPSDW1:ZH6I1$4JN:IN1MPK95%LS+1VUU8C1VTE5ZMT56QHECDVNE1FO1P56C$QV/M8YML-1H1V*ZE846N07WW667N+H1YU1E 1PK9B/0E7J0PIBZIXJA CG8CGZK*9C%PDS1JGDBVF2$7K*IBQQKV-J2 JDZT:1BPMIMIA*NI7UJQWT.+S1QD4007/GJ2HIE9WT0K3M9UVZSVV*001HW%8UE9.955B9-NT0 2$$0X4PPZ0YUIPG1L+N1*PVD4WYHPRAAUICO1-.PU8GIFT383/9TL4T.B9NVP-PELK9D/5.B9C9Q8+ECB9UH9B/9BL5 QE-NEO+11C9P8Q+C9:6V P1HS9UM97H98$QP3R8BH FRDRIS4FU2STU14G8R6U1YS+FRH8E0IF*+K1D4QIV9BRZX26 L**T -MEUIM3W1KI-$BEBIU2B4+QF2HFUHBO3E/01.SW:O44BV7QU00W56:0" + } + }, + { + "buttonText": { + "type": "string", + "localizedText": { + "de": "Testzertifikat" + }, + "parameters": [ + ] + }, + "certificateRef": { + "barcodeData": "HC1:6BFOXN*TS0BI$ZDFRH7LHUVNU.8GZ6769MBW3XHP+56R5LLPJ*5PIM*V4DZJIRPQHIZC4.OI:OIG/Q*LP1Z2GHKW/F3IKJ5QH*AF/GJ5MT0K5LO$50D$0SVV88UI*488UZ+3H0LW6WD1KG8U%8W+*3Y:CYZ00OPM98IMIWLHWVHPYH9UE*P1UU1XF820Q R1D.L%:PVLIJRH.OG4SIIRH/R2UZUWM6J$7XLH5G6TH95NI.J2IQ1TU1/PIMJEWT4SS3+Y3D.R9$H.I8HX7B%R J8VR7* FLVKDN4FTITQI.:KO3HWBJ$IJQFGSW5B*5:3AUM9NK9:AA945MR9EG3XFJ0%SI23D-I/2DBAJDAJCNB-43 X41Q2EG3RA3/43KD3F23.+IIYC6Q0ZIJPKJ+LJ5AL5:4A93VLJOPG32NQ3QR$P*NIV1JK%228K*IBYZJ92KENS+7K*8BCIIM7JC0J3LIH.SO4UBR2:ZJ83B4PSZEUP637PS36JF0JEYI1DLZZL1629HQ-A6G 713JIZA213DY4F M52Q0VP25A75Q6TMR V$6L LOOUOPDVSQQR/TTJM:R7:SF90T99K4BTET0J/K%EN6 7I-DRK6*0L03SI30NVB-3" + } + } + ] + }, + "validUntil": "2022-01-14T18:43:00Z", + "maskState": { + "visible": true, + "badgeText": { + "type": "string", + "localizedText": { + "de": "Befreiung" + }, + "parameters": [ + ] + }, + "titleText": { + "type": "string", + "localizedText": { + "de": "Masken-Befreiung" + }, + "parameters": [ + ] + }, + "subtitleText": { + "type": "string", + "localizedText": { + "de": "Für Sie gilt keine Maskenpflicht" + }, + "parameters": [ + ] + }, + "longText": { + "type": "string", + "localizedText": { + "de": "Aufgrund Ihrer Zertifikate [...]" + }, + "parameters": [ + ] + }, + "faqAnchor": "dcc_admission_state", + "identifier": "MASK_OPTIONAL" + } +} \ No newline at end of file