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 bc1b13566bd294854da6330ee9ae10fe773ea3e2..132ee6a572233c105db19bdec02a35dba146242f 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 @@ -5,6 +5,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.SavedStateHandle import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.swipeUp @@ -17,6 +18,7 @@ import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePerso import de.rki.coronawarnapp.covidcertificate.common.certificate.DccV1 import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1 import de.rki.coronawarnapp.covidcertificate.common.certificate.VaccinationDccV1 +import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId @@ -74,7 +76,9 @@ class PersonDetailsFragmentTest : BaseUITest() { object : PersonDetailsViewModel.Factory { override fun create( personIdentifierCode: String, - colorShade: PersonColorShade + colorShade: PersonColorShade, + containerId: CertificateContainerId?, + savedInstance: SavedStateHandle ): PersonDetailsViewModel = viewModel } ) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt index e69aed6781a9a69ea1b19408361c1cda9c69f05d..8f9a9b6db489360f20cacd75fc5f7db14fee6330 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanFragment.kt @@ -75,31 +75,17 @@ class DccQrCodeScanFragment : viewModel.event.observe(viewLifecycleOwner) { event -> when (event) { - is DccQrCodeScanViewModel.Event.VaccinationQrCodeScanSucceeded -> { + is DccQrCodeScanViewModel.Event.PersonDetailsScreen -> { binding.qrCodeScanSpinner.hide() doNavigate( DccQrCodeScanFragmentDirections - .actionDccQrCodeScanFragmentToVaccinationDetailsFragment( - event.containerId - ) - ) - } - is DccQrCodeScanViewModel.Event.RecoveryQrCodeScanSucceeded -> { - binding.qrCodeScanSpinner.hide() - doNavigate( - DccQrCodeScanFragmentDirections - .actionDccQrCodeScanFragmentToRecoveryCertificateDetailsFragment(event.containerId) - ) - } - is DccQrCodeScanViewModel.Event.TestQrCodeScanSucceeded -> { - binding.qrCodeScanSpinner.hide() - doNavigate( - DccQrCodeScanFragmentDirections - .actionDccQrCodeScanFragmentToTestCertificateDetailsFragment( - event.containerId + .actionDccQrCodeScanFragmentToPersonDetailsFragment( + personCode = event.codeSHA256, + containerId = event.containerId ) ) } + DccQrCodeScanViewModel.Event.QrCodeScanInProgress -> { binding.qrCodeScanSpinner.show() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt index d1ab448dee3ccc7392b252b3923187fb9fbc0781..dcaf7218d8d51ef609c5cf6b97921d8f677736c5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/common/scan/DccQrCodeScanViewModel.kt @@ -3,9 +3,7 @@ package de.rki.coronawarnapp.covidcertificate.common.scan import com.journeyapps.barcodescanner.BarcodeResult import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId -import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId -import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId +import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId import de.rki.coronawarnapp.covidcertificate.recovery.core.RecoveryCertificateRepository import de.rki.coronawarnapp.covidcertificate.recovery.core.qrcode.RecoveryCertificateQRCode import de.rki.coronawarnapp.covidcertificate.test.core.TestCertificateRepository @@ -46,17 +44,29 @@ class DccQrCodeScanViewModel @AssistedInject constructor( private suspend fun registerVaccinationCertificate(qrCode: VaccinationCertificateQRCode) { val certificate = vaccinationRepository.registerCertificate(qrCode) - event.postValue(Event.VaccinationQrCodeScanSucceeded(certificate.containerId)) + event.postValue( + Event.PersonDetailsScreen( + certificate.personIdentifier.codeSHA256, certificate.containerId + ) + ) } private suspend fun registerTestCertificate(qrCode: TestCertificateQRCode) { val certificate = testCertificateRepository.registerCertificate(qrCode) - event.postValue(Event.TestQrCodeScanSucceeded(certificate.containerId)) + event.postValue( + Event.PersonDetailsScreen( + certificate.personIdentifier.codeSHA256, certificate.containerId + ) + ) } private suspend fun registerRecoveryCertificate(qrCode: RecoveryCertificateQRCode) { val certificate = recoveryCertificateRepository.registerCertificate(qrCode) - event.postValue(Event.RecoveryQrCodeScanSucceeded(certificate.containerId)) + event.postValue( + Event.PersonDetailsScreen( + certificate.personIdentifier.codeSHA256, certificate.containerId + ) + ) } fun setCameraDeniedPermanently(denied: Boolean) { @@ -66,9 +76,10 @@ class DccQrCodeScanViewModel @AssistedInject constructor( sealed class Event { object QrCodeScanInProgress : Event() - data class VaccinationQrCodeScanSucceeded(val containerId: VaccinationCertificateContainerId) : Event() - data class TestQrCodeScanSucceeded(val containerId: TestCertificateContainerId) : Event() - data class RecoveryQrCodeScanSucceeded(val containerId: RecoveryCertificateContainerId) : Event() + data class PersonDetailsScreen( + val codeSHA256: String, + val containerId: CertificateContainerId + ) : Event() } @AssistedFactory diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragment.kt index 77f8ea54feda53b3dcd29220e8d350859e79af25..c9c3c099b1bea57a64d6f3252ea4519a32e1e585 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsFragment.kt @@ -33,11 +33,13 @@ class PersonDetailsFragment : Fragment(R.layout.person_details_fragment), AutoIn private val binding: PersonDetailsFragmentBinding by viewBinding() private val viewModel: PersonDetailsViewModel by cwaViewModelsAssisted( factoryProducer = { viewModelFactory }, - constructorCall = { factory, _ -> + constructorCall = { factory, savedInstance -> factory as PersonDetailsViewModel.Factory factory.create( - personIdentifierCode = args.personIdentifierCode, - colorShade = args.colorShade + personIdentifierCode = args.personCode, + colorShade = args.colorShade, + containerId = args.containerId, + savedInstance = savedInstance ) } ) @@ -54,7 +56,7 @@ class PersonDetailsFragment : Fragment(R.layout.person_details_fragment), AutoIn override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.apply { - root.transitionName = args.personIdentifierCode + root.transitionName = args.personCode toolbar.setNavigationOnClickListener { popBackStack() } recyclerViewCertificatesList.apply { adapter = personDetailsAdapter 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 d1d8bd982440ca3e6e03a7905615278cab113068..e70d3fcfdd7c655d06c4e6627e4d88a529b4540b 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 @@ -2,10 +2,15 @@ package de.rki.coronawarnapp.covidcertificate.person.ui.details import android.graphics.Bitmap import androidx.lifecycle.LiveData +import androidx.lifecycle.SavedStateHandle import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import de.rki.coronawarnapp.covidcertificate.common.certificate.CwaCovidCertificate +import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId +import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId +import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId +import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificates import de.rki.coronawarnapp.covidcertificate.person.core.PersonCertificatesProvider import de.rki.coronawarnapp.covidcertificate.person.ui.details.items.CwaUserCard @@ -44,10 +49,20 @@ class PersonDetailsViewModel @AssistedInject constructor( private val timeStamper: TimeStamper, @Assisted private val personIdentifierCode: String, @Assisted private val colorShade: PersonColorShade, + @Assisted private val containerId: CertificateContainerId?, + @Assisted private val savedInstance: SavedStateHandle, ) : CWAViewModel(dispatcherProvider) { val events = SingleLiveEvent<PersonDetailsEvents>() + init { + if (containerId != savedInstance[CONTAINER_ID]) { + savedInstance[CONTAINER_ID] = containerId + onOpenCertificateDetails(containerId) + } else { + Timber.d("Stay on this screen containerId=%s", containerId) + } + } private val personCertificatesFlow = personCertificatesProvider.personCertificates.mapNotNull { certificateSet -> certificateSet.first { it.personIdentifier.codeSHA256 == personIdentifierCode @@ -140,11 +155,28 @@ class PersonDetailsViewModel @AssistedInject constructor( it.identifier == certificate.personIdentifier }!! // Must be person + private fun onOpenCertificateDetails(containerId: CertificateContainerId?) { + when (containerId) { + is RecoveryCertificateContainerId -> events.postValue(OpenRecoveryCertificateDetails(containerId)) + is TestCertificateContainerId -> events.postValue(OpenTestCertificateDetails(containerId)) + is VaccinationCertificateContainerId -> events.postValue(OpenVaccinationCertificateDetails(containerId)) + null -> { + /* Stay here on PersonDetailScreen */ + } + } + } + @AssistedFactory interface Factory : CWAViewModelFactory<PersonDetailsViewModel> { fun create( personIdentifierCode: String, colorShade: PersonColorShade, + containerId: CertificateContainerId?, + savedInstance: SavedStateHandle, ): PersonDetailsViewModel } + + companion object { + private const val CONTAINER_ID = "container_id" + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt index 018abb298e4d58dfb416ddd363171607f465f136..1e223eeac9a9a2bdae287511b7d836ee4c1dc9d2 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/covidcertificate/recovery/core/storage/RecoveryCertificateContainer.kt @@ -39,6 +39,9 @@ data class RecoveryCertificateContainer( val certificateId: String get() = certificateData.certificate.recovery.uniqueCertificateIdentifier + val personIdentifier: CertificatePersonIdentifier + get() = certificateData.certificate.personIdentifier + fun toRecoveryCertificate( valueSet: VaccinationValueSets? = null, userLocale: Locale = Locale.getDefault(), 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 index 7302fae2278571d6a8b63ff13173359200c3a343..9b31887a023e13033473c183b2d02c667477d220 100644 --- 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 @@ -41,6 +41,9 @@ data class TestCertificateContainer( is GenericTestCertificateData -> null // Has none } + val personIdentifier: CertificatePersonIdentifier + get() = testCertificateQRCode.data.certificate.personIdentifier + val certificateSeenByUser: Boolean get() = when (data) { is RetrievedTestCertificate -> data.certificateSeenByUser diff --git a/Corona-Warn-App/src/main/res/navigation/covid_certificates_graph.xml b/Corona-Warn-App/src/main/res/navigation/covid_certificates_graph.xml index 107868eb9d245ebd4aeeeb6b9494d997fa16aab1..7513403d233276e41f18c786d28de01d83b55cbd 100644 --- a/Corona-Warn-App/src/main/res/navigation/covid_certificates_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/covid_certificates_graph.xml @@ -60,19 +60,18 @@ android:name="de.rki.coronawarnapp.covidcertificate.common.scan.DccQrCodeScanFragment" android:label="DccQrCodeScanFragment" tools:layout="@layout/fragment_scan_qr_code"> + + <!-- + To avoid conflicting in the navigation flow between: + 1- Scan screen -> any certificate details -> Person details screen-> Person overview screen. + 2- Person overview screen -> Person details screen -> any certificate details. + + Do navigate to "person details screen " and then to the relevant certificate details + to have the same flow all the time. + --> <action - android:id="@+id/action_dccQrCodeScanFragment_to_vaccinationDetailsFragment" - app:destination="@id/vaccinationDetailsFragment" - app:popUpTo="@id/dccQrCodeScanFragment" - app:popUpToInclusive="true" /> - <action - android:id="@+id/action_dccQrCodeScanFragment_to_testCertificateDetailsFragment" - app:destination="@id/testCertificateDetailsFragment" - app:popUpTo="@id/dccQrCodeScanFragment" - app:popUpToInclusive="true" /> - <action - android:id="@+id/action_dccQrCodeScanFragment_to_recoveryCertificateDetailsFragment" - app:destination="@id/recoveryCertificateDetailsFragment" + android:id="@+id/action_dccQrCodeScanFragment_to_personDetailsFragment" + app:destination="@id/personDetailsFragment" app:popUpTo="@id/dccQrCodeScanFragment" app:popUpToInclusive="true" /> </fragment> @@ -83,7 +82,7 @@ android:label="person_details_fragment" tools:layout="@layout/person_details_fragment"> <argument - android:name="personIdentifierCode" + android:name="personCode" app:argType="string" /> <argument @@ -91,6 +90,12 @@ android:defaultValue="COLOR_1" app:argType="de.rki.coronawarnapp.covidcertificate.person.ui.overview.PersonColorShade" /> + <argument + android:name="containerId" + android:defaultValue="@null" + app:argType="de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId" + app:nullable="true" /> + <action android:id="@+id/action_personDetailsFragment_to_vaccinationDetailsFragment" app:destination="@id/vaccinationDetailsFragment" /> diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModelTest.kt index e05b1a76a4b88d165fd6bdd73afe3f37e785da94..ae501d4cb796cef89187c766c89435ada465283a 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/covidcertificate/person/ui/details/PersonDetailsViewModelTest.kt @@ -1,9 +1,11 @@ package de.rki.coronawarnapp.covidcertificate.person.ui.details +import androidx.lifecycle.SavedStateHandle import de.rki.coronawarnapp.covidcertificate.common.certificate.CertificatePersonIdentifier import de.rki.coronawarnapp.covidcertificate.common.certificate.DccV1 import de.rki.coronawarnapp.covidcertificate.common.certificate.TestDccV1 import de.rki.coronawarnapp.covidcertificate.common.certificate.VaccinationDccV1 +import de.rki.coronawarnapp.covidcertificate.common.repository.CertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.RecoveryCertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.TestCertificateContainerId import de.rki.coronawarnapp.covidcertificate.common.repository.VaccinationCertificateContainerId @@ -148,7 +150,12 @@ class PersonDetailsViewModelTest : BaseTest() { timeStamper = timeStamper, personCertificatesProvider = personCertificatesProvider, personIdentifierCode = personCode, - colorShade = PersonColorShade.COLOR_1 + colorShade = PersonColorShade.COLOR_1, + containerId = vcContainerId, + savedInstance = mockk<SavedStateHandle>().apply { + every { get<CertificateContainerId>(any()) } returns vcContainerId + every { set(any(), any<CertificateContainerId>()) } just Runs + } ) private fun mockTestCertificate(): TestCertificate = mockk<TestCertificate>().apply {