diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt index 5bea439e2bebf790608a5251de80b9ea8440462c..0c8f3dafe73f689994352de0b5e776b84c2cc510 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/vaccination/ui/VaccinationTestFragment.kt @@ -25,14 +25,6 @@ class VaccinationTestFragment : Fragment(R.layout.fragment_test_vaccination), Au override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.openVaccinationList.setOnClickListener { - doNavigate( - VaccinationTestFragmentDirections.actionVaccinationTestFragmentToVaccinationListFragment( - "vaccinated-person-identifier" - ) - ) - } - binding.openVaccinationDetailsIncomplete.setOnClickListener { doNavigate( VaccinationTestFragmentDirections diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml index ca0a9a4d55787d6726e829e5b67495be202c7985..2c529875352f5eb503e64280a3af851313b5de82 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_vaccination.xml @@ -31,16 +31,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - <com.google.android.material.button.MaterialButton - android:id="@+id/open_vaccination_list" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:text="Vaccination List" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/textView2" /> - <com.google.android.material.button.MaterialButton android:id="@+id/open_vaccination_details_complete" android:layout_width="wrap_content" @@ -49,7 +39,7 @@ android:text="Vaccination details - complete" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/open_vaccination_list" /> + app:layout_constraintTop_toBottomOf="@+id/textView2" /> <com.google.android.material.button.MaterialButton android:id="@+id/open_vaccination_details_incomplete" diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml index bbb72a602388b89f6cb91afe7185c051017b0b28..851b0271dd7ba10d91526ea98beee6e38943f001 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/test_nav_graph.xml @@ -174,22 +174,6 @@ <action android:id="@+id/action_vaccinationTestFragment_to_vaccinationDetailsFragment" app:destination="@id/vaccinationDetailsFragment" /> - <action - android:id="@+id/action_vaccinationTestFragment_to_vaccinationListFragment" - app:destination="@id/vaccinationListFragment" /> - </fragment> - - <fragment - android:id="@+id/vaccinationListFragment" - android:name="de.rki.coronawarnapp.vaccination.ui.list.VaccinationListFragment" - android:label="fragment_vaccination_list" - tools:layout="@layout/fragment_vaccination_list"> - <argument - android:name="vaccinatedPersonId" - app:argType="string" /> - <action - android:id="@+id/action_vaccinationListFragment_to_vaccinationDetailsFragment" - app:destination="@id/vaccinationDetailsFragment" /> </fragment> <fragment diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt index 9b14e246d5143bc9a9a28d13702760f1bed26117..6b2d533a1037426f315c711ba9cd5c4cf1059754 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragment.kt @@ -127,7 +127,7 @@ class HomeFragment : Fragment(R.layout.home_fragment_layout), AutoInject { } } is HomeFragmentEvents.GoToVaccinationList -> findNavController().navigate( - VaccinationListFragment.navigationUri(event.vaccinatedPersonIdentifier) + VaccinationListFragment.navigationUri(event.personIdentifierCode) ) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt index 03805dbf9383c07ae883ff53f57da5f027137462..a26ead91322308d4b8a9d6be6b3ce9a70ba1388d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/home/HomeFragmentEvents.kt @@ -12,5 +12,5 @@ sealed class HomeFragmentEvents { data class ShowDeleteTestDialog(val type: CoronaTest.Type) : HomeFragmentEvents() - data class GoToVaccinationList(val vaccinatedPersonIdentifier: String) : HomeFragmentEvents() + data class GoToVaccinationList(val personIdentifierCode: String) : HomeFragmentEvents() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt index 81d2e6ef8d5ef15da19f2b6a9994f1dd5a35d2f3..4c0ddb72e85068d732d72f80bfeab93531098f36 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/core/VaccinatedPersonIdentifier.kt @@ -11,6 +11,7 @@ data class VaccinatedPersonIdentifier( val lastNameStandardized: String, val firstNameStandardized: String? ) { + val code: String by lazy { val dob = dateOfBirth.toString() val lastName = lastNameStandardized diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt index 9cbb482bb5cd0c03cc9afa630d15a304909ba7fd..6478749d8b0fcdef5b39f21a7ca462067855d1cd 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListFragment.kt @@ -36,7 +36,7 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au constructorCall = { factory, _ -> factory as VaccinationListViewModel.Factory factory.create( - vaccinatedPersonIdentifier = args.vaccinatedPersonId + personIdentifierCode = args.personIdentifierCode ) } ) @@ -63,11 +63,14 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au VaccinationListFragmentDirections .actionVaccinationListFragmentToVaccinationDetailsFragment(event.vaccinationCertificateId) ) + is VaccinationListViewModel.Event.NavigateToVaccinationQrCodeScanScreen -> doNavigate( + VaccinationListFragmentDirections.actionVaccinationListFragmentToVaccinationQrCodeScanFragment() + ) } } registerNewVaccinationButton.setOnClickListener { - Toast.makeText(requireContext(), "TODO \uD83D\uDEA7", Toast.LENGTH_LONG).show() + viewModel.onRegisterNewVaccinationClick() } refreshButton.setOnClickListener { @@ -78,9 +81,10 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au private fun FragmentVaccinationListBinding.bindViews(uiState: VaccinationListViewModel.UiState) = with(uiState) { - adapter.update(listItems) - val isVaccinationComplete = vaccinationStatus == VaccinatedPerson.Status.COMPLETE + setToolbarOverlay(isVaccinationComplete) + + adapter.update(listItems) val background = if (isVaccinationComplete) { R.drawable.vaccination_compelete_gradient @@ -96,8 +100,6 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au title.alpha = titleAlpha subtitle.alpha = subtitleAlpha } - - setToolbarOverlay(isVaccinationComplete) } private fun setToolbarOverlay(isVaccinationComplete: Boolean) { @@ -107,7 +109,7 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au val deviceWidth = requireContext().resources.displayMetrics.widthPixels - val params: CoordinatorLayout.LayoutParams = binding.recyclerViewVaccinationList.layoutParams + val layoutParamsRecyclerView: CoordinatorLayout.LayoutParams = binding.recyclerViewVaccinationList.layoutParams as (CoordinatorLayout.LayoutParams) val textParams = bottomTextView.layoutParams as (LinearLayout.LayoutParams) @@ -116,11 +118,13 @@ class VaccinationListFragment : Fragment(R.layout.fragment_vaccination_list), Au textParams.bottomMargin = (deviceWidth / divider) - 24 /* 24 is space between screen border and Card */ bottomTextView.requestLayout() - val behavior: AppBarLayout.ScrollingViewBehavior = params.behavior as (AppBarLayout.ScrollingViewBehavior) + val behavior: AppBarLayout.ScrollingViewBehavior = + layoutParamsRecyclerView.behavior as (AppBarLayout.ScrollingViewBehavior) behavior.overlayTop = (deviceWidth / divider) - 24 } companion object { - fun navigationUri(personIdentifier: String) = "coronawarnapp://vaccination-list/$personIdentifier".toUri() + fun navigationUri(personIdentifierCode: String) = + "coronawarnapp://vaccination-list/$personIdentifierCode".toUri() } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt index dada5d17400c77d54a2350006b3a6d8faf81130c..21b5d8023b38adc250308824d696d7d29d64306a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/list/VaccinationListViewModel.kt @@ -1,136 +1,57 @@ package de.rki.coronawarnapp.vaccination.ui.list -import android.graphics.Bitmap import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import de.rki.coronawarnapp.presencetracing.checkins.qrcode.QrCodeGenerator import de.rki.coronawarnapp.util.TimeAndDateExtensions.toDayFormat -import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUserTz -import de.rki.coronawarnapp.util.TimeStamper import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory -import de.rki.coronawarnapp.vaccination.core.ProofCertificate import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson import de.rki.coronawarnapp.vaccination.core.VaccinatedPerson.Status.COMPLETE -import de.rki.coronawarnapp.vaccination.core.VaccinationCertificate import de.rki.coronawarnapp.vaccination.core.repository.VaccinationRepository import de.rki.coronawarnapp.vaccination.ui.list.adapter.VaccinationListItem -import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListCertificateCardItemVH.VaccinationListCertificateCardItem import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListIncompleteTopCardItemVH.VaccinationListIncompleteTopCardItem import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListNameCardItemVH.VaccinationListNameCardItem import de.rki.coronawarnapp.vaccination.ui.list.adapter.viewholder.VaccinationListVaccinationCardItemVH.VaccinationListVaccinationCardItem -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.transform -import org.joda.time.Days -import org.joda.time.LocalDate class VaccinationListViewModel @AssistedInject constructor( - private val vaccinationRepository: VaccinationRepository, - private val timeStamper: TimeStamper, - private val qrCodeGenerator: QrCodeGenerator, - @Assisted private val vaccinatedPersonIdentifier: String + vaccinationRepository: VaccinationRepository, + @Assisted private val personIdentifierCode: String ) : CWAViewModel() { val events = SingleLiveEvent<Event>() - val vaccinationInfoFlow = vaccinationRepository.vaccinationInfos.map { vaccinatedPersonSet -> - // TODO: use the line below once the repository returns actual values - // val vaccinatedPerson = vaccinatedPersonSet.single { it.identifier.code == vaccinatedPersonIdentifier } + private val vaccinatedPersonFlow = vaccinationRepository.vaccinationInfos.map { vaccinatedPersonSet -> + vaccinatedPersonSet.single { it.identifier.code == personIdentifierCode } } - private val proofQrCode: Flow<Bitmap?> = vaccinationRepository.vaccinationInfos.transform { vaccinationInfos -> - - emit(null) - - // TODO: use actual values from repository instead of these mocked ones - val proofCertificates = setOf( - getMockProofCertificate() - ) - - if (proofCertificates.isNotEmpty()) { - emit(qrCodeGenerator.createQrCode("TODO create qrCode from actual value")) - } - } - - val uiState: LiveData<UiState> = combine(vaccinationInfoFlow, proofQrCode) { vaccinatedPerson, proofQrCode -> - - // For now, use mock data - val vaccinationStatus = COMPLETE - // val vaccinationStatus = COMPLETE - - val vaccinationCertificates = setOf( - getMockVaccinationCertificate(), - getMockVaccinationCertificate().copy( - doseNumber = 2 - ) - ) - - val proofCertificates = setOf( - getMockProofCertificate() - ) - - val listItems = assembleItemList( - vaccinationCertificates = vaccinationCertificates, - proofCertificates = proofCertificates, - firstName = "François-Joan", - lastName = "d'Arsøns - van Halen", - dateOfBirth = LocalDate.parse("2009-02-28"), - vaccinationStatus, - proofQrCode - ) - + val uiState: LiveData<UiState> = vaccinatedPersonFlow.map { vaccinatedPerson -> UiState( - listItems, - vaccinationStatus = vaccinationStatus + listItems = assembleItemList(vaccinatedPerson = vaccinatedPerson), + vaccinationStatus = vaccinatedPerson.vaccinationStatus ) }.catch { // TODO Error Handling in an upcoming subtask }.asLiveData() - // TODO: after using actual values from the repository, we only pass VaccinatedPerson here instead of all these - // arguments - @Suppress("LongParameterList") - private fun assembleItemList( - vaccinationCertificates: Set<VaccinationCertificate>, - proofCertificates: Set<ProofCertificate>, - firstName: String, - lastName: String, - dateOfBirth: LocalDate, - vaccinationStatus: VaccinatedPerson.Status, - proofQrCode: Bitmap? - ) = mutableListOf<VaccinationListItem>().apply { - if (vaccinationStatus == COMPLETE) { - if (proofCertificates.isNotEmpty()) { - - val proofCertificate = proofCertificates.first() - val expiresAt = proofCertificate.expiresAt.toLocalDateUserTz() - val today = timeStamper.nowUTC.toLocalDateUserTz() - val remainingValidityInDays = Days.daysBetween(today, expiresAt).days - - add( - VaccinationListCertificateCardItem( - qrCode = proofQrCode, - remainingValidityInDays = remainingValidityInDays - ) - ) - } + private fun assembleItemList(vaccinatedPerson: VaccinatedPerson) = mutableListOf<VaccinationListItem>().apply { + if (vaccinatedPerson.vaccinationStatus == COMPLETE) { + // Tbd what to show on complete vaccination - the proof certificate is now obsolete } else { add(VaccinationListIncompleteTopCardItem) } add( VaccinationListNameCardItem( - fullName = "$firstName $lastName", - dayOfBirth = dateOfBirth.toDayFormat() + fullName = "${vaccinatedPerson.firstName} ${vaccinatedPerson.lastName}", + dayOfBirth = vaccinatedPerson.dateOfBirth.toDayFormat() ) ) - vaccinationCertificates.forEach { vaccinationCertificate -> + vaccinatedPerson.vaccinationCertificates.forEach { vaccinationCertificate -> with(vaccinationCertificate) { add( VaccinationListVaccinationCardItem( @@ -138,9 +59,8 @@ class VaccinationListViewModel @AssistedInject constructor( doseNumber = doseNumber.toString(), totalSeriesOfDoses = totalSeriesOfDoses.toString(), vaccinatedAt = vaccinatedAt.toDayFormat(), - vaccinationStatus = vaccinationStatus, - isFinalVaccination = - doseNumber == totalSeriesOfDoses, + vaccinationStatus = vaccinatedPerson.vaccinationStatus, + isFinalVaccination = doseNumber == totalSeriesOfDoses, onCardClick = { certificateId -> events.postValue(Event.NavigateToVaccinationCertificateDetails(certificateId)) } @@ -150,6 +70,10 @@ class VaccinationListViewModel @AssistedInject constructor( } }.toList() + fun onRegisterNewVaccinationClick() { + events.postValue(Event.NavigateToVaccinationQrCodeScanScreen) + } + data class UiState( val listItems: List<VaccinationListItem>, val vaccinationStatus: VaccinatedPerson.Status @@ -157,12 +81,13 @@ class VaccinationListViewModel @AssistedInject constructor( sealed class Event { data class NavigateToVaccinationCertificateDetails(val vaccinationCertificateId: String) : Event() + object NavigateToVaccinationQrCodeScanScreen : Event() } @AssistedFactory interface Factory : CWAViewModelFactory<VaccinationListViewModel> { fun create( - vaccinatedPersonIdentifier: String + personIdentifierCode: String ): VaccinationListViewModel } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt index bc4c36ec027b8316284d668df909b65be66eb821..f762343be41ed3be0ae2cc817730dbe163d0014d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanFragment.kt @@ -52,7 +52,9 @@ class VaccinationQrCodeScanFragment : binding.qrCodeScanSpinner.hide() doNavigate( VaccinationQrCodeScanFragmentDirections - .actionVaccinationQrCodeScanFragmentToVaccinationDetailsFragment(event.certificateId) + .actionVaccinationQrCodeScanFragmentToVaccinationListFragment( + event.personIdentifierCode + ) ) } VaccinationQrCodeScanViewModel.Event.QrCodeScanInProgress -> { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt index 17b6d03349a9e7f953e69f55d66a0038fe6eb898..96be326e5dcd9f8928e79b2e825b48f9d4914339 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/vaccination/ui/scan/VaccinationQrCodeScanViewModel.kt @@ -25,8 +25,8 @@ class VaccinationQrCodeScanViewModel @AssistedInject constructor( try { event.postValue(Event.QrCodeScanInProgress) val qrCode = vaccinationQRCodeValidator.validate(barcodeResult.text) - val certificate = vaccinationRepository.registerVaccination(qrCode) - event.postValue(Event.QrCodeScanSucceeded(certificate.certificateId)) + val vaccinationCertificate = vaccinationRepository.registerVaccination(qrCode) + event.postValue(Event.QrCodeScanSucceeded(vaccinationCertificate.personIdentifier.code)) } catch (e: Throwable) { errorEvent.postValue(e) } @@ -39,7 +39,7 @@ class VaccinationQrCodeScanViewModel @AssistedInject constructor( sealed class Event { object QrCodeScanInProgress : Event() - data class QrCodeScanSucceeded(val certificateId: String) : Event() + data class QrCodeScanSucceeded(val personIdentifierCode: String) : Event() } @AssistedFactory diff --git a/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml index e5441a6ea4ece63cdc85f0530eadc5b03e044e2f..fddaf017adbbe72bd9c289568f9c3aecb0990910 100644 --- a/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/vaccination_nav_graph.xml @@ -26,10 +26,11 @@ android:label="VaccinationQrCodeScanFragment" tools:layout="@layout/fragment_scan_qr_code"> <action - android:id="@+id/action_vaccinationQrCodeScanFragment_to_vaccinationDetailsFragment" - app:destination="@id/vaccinationDetailsFragment" + android:id="@+id/action_vaccinationQrCodeScanFragment_to_vaccinationListFragment" + app:destination="@id/vaccinationListFragment" + app:launchSingleTop="true" app:popUpTo="@id/vaccinationQrCodeScanFragment" - app:popUpToInclusive="true" /> + app:popUpToInclusive="true"/> </fragment> <fragment @@ -38,12 +39,16 @@ android:label="fragment_vaccination_list" tools:layout="@layout/fragment_vaccination_list"> <argument - android:name="vaccinatedPersonId" + android:name="personIdentifierCode" app:argType="string" /> <action android:id="@+id/action_vaccinationListFragment_to_vaccinationDetailsFragment" app:destination="@id/vaccinationDetailsFragment" /> - <deepLink app:uri="coronawarnapp://vaccination-list/{vaccinatedPersonId}" /> + <deepLink + app:uri="coronawarnapp://vaccination-list/{personIdentifierCode}" /> + <action + android:id="@+id/action_vaccinationListFragment_to_vaccinationQrCodeScanFragment" + app:destination="@id/vaccinationQrCodeScanFragment" /> </fragment> <fragment